From adc164c17c5f572abf544a05c725c6f374ba7595 Mon Sep 17 00:00:00 2001 From: Cheng Fang Date: Wed, 25 Sep 2024 02:28:31 +0000 Subject: [PATCH] sibling of f03146d3a0652808d2d84db2b703538bc8cdd569 --- .codecov.yml | 1 - .gitattributes | 13 - .github/ISSUE_TEMPLATE/new_dev_tool.md | 43 - .github/dependabot.yml | 10 - .github/workflows/README.md | 3 +- .github/workflows/ci-build.yaml | 240 +- .github/workflows/codeql.yml | 12 +- .github/workflows/image-reuse.yaml | 22 +- .github/workflows/image.yaml | 10 +- .github/workflows/init-release.yaml | 4 +- .github/workflows/pr-title-check.yml | 2 +- .github/workflows/release.yaml | 30 +- .github/workflows/scorecard.yaml | 8 +- .github/workflows/update-snyk.yaml | 2 +- .gitignore | 1 - .gitpod.Dockerfile | 2 +- .golangci.yaml | 45 - .goreleaser.yaml | 2 - .mockery.yaml | 69 - .readthedocs.yaml => .readthedocs.yml | 0 CODEOWNERS | 13 +- Dockerfile | 12 +- Makefile | 134 +- OWNERS | 1 - Procfile | 13 +- README.md | 7 +- SECURITY-INSIGHTS.yml | 4 +- USERS.md | 60 +- VERSION | 2 +- .../controllers/applicationset_controller.go | 527 +- .../applicationset_controller_test.go | 2045 +++---- .../controllers/clustereventhandler.go | 30 +- .../controllers/clustereventhandler_test.go | 31 +- .../controllers/requeue_after_test.go | 14 +- .../controllers/template/template.go | 99 - .../controllers/template/template_test.go | 350 -- .../{template/patch.go => templatePatch.go} | 5 +- .../patch_test.go => templatePatch_test.go} | 2 +- applicationset/generators/cluster.go | 15 +- applicationset/generators/cluster_test.go | 75 +- applicationset/generators/duck_type.go | 20 +- applicationset/generators/duck_type_test.go | 29 +- .../generators/generator_spec_processor.go | 5 +- .../generator_spec_processor_test.go | 43 +- applicationset/generators/git.go | 60 +- applicationset/generators/git_test.go | 194 +- applicationset/generators/interface.go | 10 +- applicationset/generators/list.go | 15 +- applicationset/generators/list_test.go | 18 +- applicationset/generators/matrix.go | 18 +- applicationset/generators/matrix_test.go | 92 +- applicationset/generators/merge.go | 20 +- applicationset/generators/merge_test.go | 21 +- applicationset/generators/mocks/Generator.go | 100 - applicationset/generators/plugin.go | 11 +- applicationset/generators/plugin_test.go | 16 +- applicationset/generators/pull_request.go | 108 +- .../generators/pull_request_test.go | 116 +- applicationset/generators/scm_provider.go | 123 +- .../generators/scm_provider_test.go | 110 +- applicationset/generators/utils.go | 49 - .../generators/value_interpolation.go | 1 + .../generators/value_interpolation_test.go | 7 +- applicationset/metrics/fake.go | 22 - applicationset/metrics/metrics.go | 131 - applicationset/metrics/metrics_test.go | 256 - .../services/internal/github_app/client.go | 4 +- .../services/internal/http/client.go | 4 +- .../services/internal/http/client_test.go | 13 +- applicationset/services/mocks/Repos.go | 55 +- applicationset/services/mocks/RepositoryDB.go | 57 + .../services/plugin/plugin_service.go | 8 +- .../services/plugin/plugin_service_test.go | 5 +- .../services/pull_request/azure_devops.go | 18 +- .../pull_request/azure_devops_test.go | 23 +- .../services/pull_request/bitbucket_cloud.go | 14 +- .../pull_request/bitbucket_cloud_test.go | 105 +- .../services/pull_request/bitbucket_server.go | 34 +- .../pull_request/bitbucket_server_test.go | 207 +- applicationset/services/pull_request/gitea.go | 2 - .../services/pull_request/gitea_test.go | 17 +- .../services/pull_request/github.go | 6 +- .../services/pull_request/github_test.go | 2 +- .../services/pull_request/gitlab.go | 20 +- .../services/pull_request/gitlab_test.go | 111 +- .../services/pull_request/interface.go | 4 - applicationset/services/pull_request/utils.go | 4 +- .../services/pull_request/utils_test.go | 55 +- applicationset/services/repo_service.go | 33 +- applicationset/services/repo_service_test.go | 93 +- .../services/scm_provider/aws_codecommit.go | 12 +- .../mocks/AWSCodeCommitClient.go | 183 +- .../aws_codecommit/mocks/AWSTaggingClient.go | 60 +- .../scm_provider/aws_codecommit_test.go | 29 +- .../services/scm_provider/azure_devops.go | 24 +- .../azure_devops/git/mocks/Client.go | 985 +-- .../scm_provider/azure_devops_test.go | 44 +- .../services/scm_provider/bitbucket_cloud.go | 13 +- .../scm_provider/bitbucket_cloud_test.go | 17 +- .../services/scm_provider/bitbucket_server.go | 41 +- .../scm_provider/bitbucket_server_test.go | 189 +- .../services/scm_provider/gitea_test.go | 11 +- .../services/scm_provider/github.go | 16 +- .../services/scm_provider/github_test.go | 21 +- .../services/scm_provider/gitlab.go | 11 +- .../services/scm_provider/gitlab_test.go | 97 +- applicationset/services/scm_provider/mock.go | 1 + applicationset/services/scm_provider/utils.go | 6 +- .../services/scm_provider/utils_test.go | 28 +- applicationset/status/resource_status.go | 57 - applicationset/utils/applicationset_lister.go | 63 - applicationset/utils/clusterUtils.go | 19 +- applicationset/utils/clusterUtils_test.go | 22 +- applicationset/utils/createOrUpdate.go | 1 + applicationset/utils/kubernetes.go | 54 - applicationset/utils/kubernetes_test.go | 146 - applicationset/utils/map.go | 2 + applicationset/utils/map_test.go | 4 +- applicationset/utils/mocks/Renderer.go | 86 - applicationset/utils/selector.go | 7 +- applicationset/utils/utils.go | 28 +- applicationset/utils/utils_test.go | 148 +- .../github-commit-event-feature-branch.json | 186 - applicationset/webhook/webhook.go | 111 +- applicationset/webhook/webhook_test.go | 103 +- assets/badge.svg | 2 - assets/builtin-policy.csv | 6 +- assets/swagger.json | 448 +- .../commands/argocd_application_controller.go | 40 +- .../commands/applicationset_controller.go | 105 +- .../commands/argocd_cmp_server.go | 6 +- cmd/argocd-dex/commands/argocd_dex.go | 22 +- .../commands/argocd_git_ask_pass.go | 8 +- .../commands/argocd_k8s_auth.go | 2 +- cmd/argocd-k8s-auth/commands/aws.go | 24 +- cmd/argocd-k8s-auth/commands/aws_test.go | 15 +- cmd/argocd-k8s-auth/commands/azure.go | 4 +- cmd/argocd-k8s-auth/commands/gcp.go | 23 +- .../commands/controller.go | 22 +- .../commands/argocd_repo_server.go | 43 +- cmd/argocd-server/commands/argocd_server.go | 117 +- cmd/argocd/commands/account.go | 18 +- cmd/argocd/commands/admin/admin.go | 171 +- cmd/argocd/commands/admin/admin_test.go | 75 - cmd/argocd/commands/admin/app.go | 40 +- cmd/argocd/commands/admin/app_test.go | 21 +- cmd/argocd/commands/admin/backup.go | 165 +- cmd/argocd/commands/admin/backup_test.go | 87 - cmd/argocd/commands/admin/cluster.go | 80 +- cmd/argocd/commands/admin/dashboard.go | 3 +- .../commands/admin/generatespec_utils_test.go | 4 +- cmd/argocd/commands/admin/initial_password.go | 7 +- cmd/argocd/commands/admin/notifications.go | 10 +- cmd/argocd/commands/admin/project.go | 6 +- .../commands/admin/project_allowlist.go | 16 +- .../commands/admin/project_allowlist_test.go | 5 +- cmd/argocd/commands/admin/project_test.go | 27 +- .../commands/admin/redis_initial_password.go | 25 +- cmd/argocd/commands/admin/repo.go | 8 +- .../commands/admin/secrets_redactor_test.go | 94 + cmd/argocd/commands/admin/settings.go | 36 +- cmd/argocd/commands/admin/settings_rbac.go | 180 +- .../commands/admin/settings_rbac_test.go | 208 +- cmd/argocd/commands/admin/settings_test.go | 102 +- .../admin/testdata/rbac/argocd-rbac-cm.yaml | 4 - .../commands/admin/testdata/rbac/policy.csv | 2 - cmd/argocd/commands/app.go | 795 +-- cmd/argocd/commands/app_actions.go | 39 +- cmd/argocd/commands/app_resource_test.go | 16 +- cmd/argocd/commands/app_resources.go | 59 +- cmd/argocd/commands/app_test.go | 985 +-- cmd/argocd/commands/applicationset.go | 141 +- cmd/argocd/commands/applicationset_test.go | 26 +- cmd/argocd/commands/bcrypt.go | 6 +- cmd/argocd/commands/cert.go | 11 +- cmd/argocd/commands/cluster.go | 59 +- cmd/argocd/commands/cluster_test.go | 19 +- cmd/argocd/commands/common_test.go | 30 +- cmd/argocd/commands/completion.go | 34 +- cmd/argocd/commands/context.go | 6 +- cmd/argocd/commands/context_test.go | 23 +- cmd/argocd/commands/gpg.go | 24 +- cmd/argocd/commands/headless/headless.go | 88 +- cmd/argocd/commands/login.go | 39 +- cmd/argocd/commands/login_test.go | 35 - cmd/argocd/commands/logout.go | 2 +- cmd/argocd/commands/logout_test.go | 13 +- cmd/argocd/commands/project.go | 287 +- cmd/argocd/commands/project_role.go | 44 +- cmd/argocd/commands/projectwindows.go | 20 +- cmd/argocd/commands/relogin.go | 10 +- cmd/argocd/commands/relogin_test.go | 5 +- cmd/argocd/commands/repo.go | 32 +- cmd/argocd/commands/repocreds.go | 15 +- cmd/argocd/commands/root.go | 11 +- cmd/argocd/commands/tree.go | 13 +- cmd/argocd/commands/tree_test.go | 3 +- cmd/argocd/commands/version_test.go | 7 +- cmd/main.go | 2 - cmd/util/app.go | 308 +- cmd/util/app_test.go | 174 +- cmd/util/applicationset.go | 7 +- cmd/util/applicationset_test.go | 5 +- cmd/util/cluster.go | 4 +- cmd/util/cluster_test.go | 6 +- cmd/util/project.go | 39 +- cmd/util/repo.go | 2 - cmpserver/apiclient/clientset.go | 10 +- cmpserver/apiclient/plugin.pb.go | 295 +- cmpserver/plugin/config_test.go | 6 +- cmpserver/plugin/plugin.go | 51 +- cmpserver/plugin/plugin.proto | 11 - cmpserver/plugin/plugin_test.go | 190 +- cmpserver/server.go | 5 +- common/common.go | 67 +- common/common_test.go | 66 - controller/appcontroller.go | 462 +- controller/appcontroller_test.go | 411 +- controller/cache/cache.go | 83 +- controller/cache/cache_test.go | 84 +- controller/cache/info_test.go | 30 +- controller/cache/mocks/LiveStateCache.go | 101 +- controller/clusterinfoupdater.go | 39 +- controller/clusterinfoupdater_test.go | 99 +- controller/health.go | 2 +- controller/health_test.go | 38 +- controller/hook.go | 20 +- controller/metrics/clustercollector.go | 1 + controller/metrics/metrics.go | 80 +- controller/metrics/metrics_test.go | 171 +- controller/sharding/cache.go | 92 +- controller/sharding/cache_test.go | 56 +- controller/sharding/consistent/consistent.go | 275 - controller/sharding/sharding.go | 142 +- controller/sharding/sharding_test.go | 246 +- controller/sharding/shuffle_test.go | 9 +- controller/state.go | 189 +- controller/state_test.go | 361 +- controller/sync.go | 110 +- controller/sync_namespace.go | 6 +- controller/sync_namespace_test.go | 13 +- controller/sync_test.go | 123 +- controller/testdata/data.go | 3 - controller/testdata/diff-cache.yaml | 498 -- docs/assets/api-management.png | Bin 0 -> 14376 bytes docs/assets/argocd_architecture.png | Bin 152028 -> 121649 bytes docs/assets/groups-claim.png | Bin 0 -> 82650 bytes docs/assets/groups-scope.png | Bin 0 -> 59680 bytes docs/assets/okta-app.png | Bin 260259 -> 0 bytes docs/assets/okta-auth-policy-edit.png | Bin 63571 -> 0 bytes docs/assets/okta-auth-policy.png | Bin 85431 -> 0 bytes docs/assets/okta-auth-rule.png | Bin 229782 -> 0 bytes docs/assets/okta-create-oidc-app.png | Bin 360829 -> 0 bytes docs/assets/okta-groups-claim.png | Bin 144958 -> 0 bytes docs/assets/okta-groups-scope.png | Bin 187202 -> 0 bytes docs/assets/versions.css | 2 +- docs/assets/versions.js | 22 +- docs/cli_installation.md | 11 - .../architecture/components.md | 2 +- docs/developer-guide/code-contributions.md | 6 +- .../contributors-quickstart.md | 19 +- .../debugging-remote-environment.md | 2 +- docs/developer-guide/dependencies.md | 37 +- docs/developer-guide/docs-site.md | 25 - .../extensions/proxy-extensions.md | 12 +- .../extensions/ui-extensions.md | 101 +- docs/developer-guide/index.md | 22 +- .../release-process-and-cadence.md | 9 +- docs/developer-guide/site.md | 26 + docs/developer-guide/static-code-analysis.md | 2 +- docs/developer-guide/toolchain-guide.md | 10 +- docs/faq.md | 98 +- docs/getting_started.md | 22 +- docs/operator-manual/app-any-namespace.md | 11 +- .../app-sync-using-impersonation.md | 131 - docs/operator-manual/application.yaml | 38 +- docs/operator-manual/applicationset.yaml | 294 +- .../applicationset/Appset-Any-Namespace.md | 11 +- .../Controlling-Resource-Modification.md | 44 +- .../applicationset/Generators-Cluster.md | 27 +- .../Generators-Git-File-Globbing.md | 2 +- .../applicationset/Generators-Git.md | 2 - .../applicationset/Generators-List.md | 6 +- .../applicationset/Generators-Matrix.md | 4 +- .../applicationset/Generators-Plugin.md | 2 - .../Generators-Post-Selector.md | 76 +- .../applicationset/Generators-Pull-Request.md | 41 +- .../applicationset/Generators-SCM-Provider.md | 28 +- .../applicationset/GoTemplate.md | 22 +- .../applicationset/Template.md | 28 +- docs/operator-manual/argocd-cm.yaml | 45 +- .../operator-manual/argocd-cmd-params-cm.yaml | 38 +- docs/operator-manual/argocd-repositories.yaml | 2 - docs/operator-manual/cluster-bootstrapping.md | 27 +- .../config-management-plugins.md | 14 +- docs/operator-manual/core.md | 3 +- docs/operator-manual/declarative-setup.md | 189 +- docs/operator-manual/deep_links.md | 9 +- docs/operator-manual/health.md | 120 +- docs/operator-manual/high_availability.md | 68 +- docs/operator-manual/ingress.md | 76 +- docs/operator-manual/installation.md | 26 - docs/operator-manual/metrics.md | 51 +- docs/operator-manual/notifications/catalog.md | 41 +- .../notifications/functions.md | 48 - .../notifications/grafana-dashboard.json | 4 +- docs/operator-manual/notifications/index.md | 2 +- .../notifications/monitoring.md | 4 +- .../notifications/services/alertmanager.md | 8 +- .../notifications/services/awssqs.md | 23 +- .../notifications/services/email.md | 6 +- .../notifications/services/github.md | 30 +- .../notifications/services/googlechat.md | 2 +- .../notifications/services/grafana.md | 2 +- .../notifications/services/mattermost.md | 2 +- .../notifications/services/newrelic.md | 2 +- .../notifications/services/opsgenie.md | 48 +- .../notifications/services/pagerduty.md | 4 +- .../notifications/services/pagerduty_v2.md | 4 +- .../notifications/services/pushover.md | 4 +- .../notifications/services/rocketchat.md | 6 +- .../notifications/services/slack.md | 3 +- .../notifications/services/teams.md | 2 +- .../notifications/services/telegram.md | 13 +- .../notifications/services/webex.md | 2 +- .../notifications/services/webhook.md | 14 +- .../operator-manual/notifications/triggers.md | 4 +- .../notifications/troubleshooting-errors.md | 52 - .../notifications/troubleshooting.md | 2 +- docs/operator-manual/rbac.md | 497 +- docs/operator-manual/reconcile.md | 57 +- docs/operator-manual/resource_actions.md | 22 - .../resource_actions_builtin.md | 50 - docs/operator-manual/secret-management.md | 8 +- docs/operator-manual/security.md | 4 +- .../argocd-application-controller.md | 4 +- .../server-commands/argocd-repo-server.md | 5 +- .../server-commands/argocd-server.md | 156 +- docs/operator-manual/signed-release-assets.md | 34 +- .../tested-kubernetes-versions.md | 7 +- docs/operator-manual/upgrading/2.10-2.11.md | 58 - docs/operator-manual/upgrading/2.11-2.12.md | 59 - docs/operator-manual/upgrading/2.12-2.13.md | 69 - docs/operator-manual/upgrading/2.4-2.5.md | 4 +- docs/operator-manual/upgrading/overview.md | 5 +- docs/operator-manual/user-management/auth0.md | 1 - .../operator-manual/user-management/google.md | 27 +- .../user-management/identity-center.md | 2 +- docs/operator-manual/user-management/index.md | 4 +- .../user-management/keycloak.md | 6 - .../user-management/microsoft.md | 54 +- docs/operator-manual/user-management/okta.md | 107 +- .../user-management/onelogin.md | 2 +- docs/operator-manual/web_based_terminal.md | 32 +- docs/operator-manual/webhook.md | 10 - docs/proposals/application-name-identifier.md | 2 +- .../applicationset-plugin-generator.md | 2 +- docs/proposals/config-management-plugin-v2.md | 92 +- ...plication-sync-user-using-impersonation.md | 641 -- docs/proposals/images/current-summary-tab.png | Bin 115933 -> 0 bytes docs/proposals/images/helm-parameter-list.png | Bin 107160 -> 0 bytes .../images/history-and-rollback-button.png | Bin 20446 -> 0 bytes .../images/history-rollback-contents.png | Bin 128640 -> 0 bytes docs/proposals/images/new-sources-tab.png | Bin 63179 -> 0 bytes .../multiple-sources-for-applications-ui.md | 226 - docs/proposals/native-oci-support.md | 4 +- .../project-scoped-repository-enhancements.md | 131 - .../resource-deletion-with-approval.md | 171 - docs/proposals/sync-timeout.md | 126 - docs/requirements.txt | 6 +- docs/snyk/index.md | 87 +- docs/snyk/master/argocd-iac-install.html | 464 +- .../master/argocd-iac-namespace-install.html | 418 +- docs/snyk/master/argocd-test.html | 576 +- .../ghcr.io_dexidp_dex_v2.37.0.html} | 2330 +++---- .../haproxy_2.6.14-alpine.html} | 226 +- .../quay.io_argoproj_argocd_latest.html | 2443 ++++++-- .../redis_7.0.11-alpine.html} | 612 +- docs/snyk/master/redis_7.0.15-alpine.html | 484 -- docs/snyk/v2.10.16/argocd-iac-install.html | 2891 --------- .../v2.10.16/ghcr.io_dexidp_dex_v2.37.0.html | 4361 ------------- docs/snyk/v2.10.16/haproxy_2.6.14-alpine.html | 2741 --------- docs/snyk/v2.10.16/redis_7.0.15-alpine.html | 484 -- docs/snyk/v2.11.8/argocd-iac-install.html | 2891 --------- docs/snyk/v2.11.8/haproxy_2.6.14-alpine.html | 2741 --------- docs/snyk/v2.11.8/redis_7.0.15-alpine.html | 484 -- docs/snyk/v2.12.3/argocd-iac-install.html | 2891 --------- docs/snyk/v2.12.3/argocd-test.html | 1086 ---- ...ws_docker_library_redis_7.0.15-alpine.html | 484 -- docs/snyk/v2.12.3/redis_7.0.15-alpine.html | 484 -- docs/snyk/v2.13.0-rc2/argocd-iac-install.html | 2891 --------- ...ws_docker_library_redis_7.0.15-alpine.html | 484 -- .../quay.io_argoproj_argocd_v2.13.0-rc2.html | 2012 ------ .../snyk/v2.13.0-rc2/redis_7.0.15-alpine.html | 484 -- .../argocd-iac-install.html} | 438 +- .../argocd-iac-namespace-install.html | 402 +- docs/snyk/v2.6.15/argocd-test.html | 3792 ++++++++++++ .../ghcr.io_dexidp_dex_v2.37.0.html} | 2330 +++---- .../haproxy_2.6.14-alpine.html} | 226 +- .../quay.io_argoproj_argocd_v2.6.15.html} | 4443 +++++++------- .../redis_7.0.11-alpine.html} | 612 +- .../argocd-iac-install.html} | 438 +- .../argocd-iac-namespace-install.html | 402 +- .../{v2.11.8 => v2.7.14}/argocd-test.html | 2600 ++++---- .../v2.7.14/ghcr.io_dexidp_dex_v2.37.0.html | 2862 +++++++++ .../haproxy_2.6.14-alpine.html} | 316 +- .../quay.io_argoproj_argocd_v2.7.14.html} | 3202 ++++++---- .../redis_7.0.11-alpine.html} | 612 +- docs/snyk/v2.8.5/argocd-iac-install.html | 2679 ++++++++ .../v2.8.5/argocd-iac-namespace-install.html | 2679 ++++++++ docs/snyk/v2.8.5/argocd-test.html | 1031 ++++ .../v2.8.5/ghcr.io_dexidp_dex_v2.37.0.html | 2862 +++++++++ .../haproxy_2.6.14-alpine.html} | 217 +- .../quay.io_argoproj_argocd_v2.8.5.html} | 2491 +++++--- docs/snyk/v2.8.5/redis_7.0.11-alpine.html | 1335 ++++ docs/snyk/v2.9.0-rc3/argocd-iac-install.html | 2679 ++++++++ .../argocd-iac-namespace-install.html | 2679 ++++++++ .../{v2.10.16 => v2.9.0-rc3}/argocd-test.html | 2616 ++++---- .../ghcr.io_dexidp_dex_v2.37.0.html | 2862 +++++++++ .../v2.9.0-rc3/haproxy_2.6.14-alpine.html | 683 +++ .../quay.io_argoproj_argocd_v2.9.0-rc3.html | 3366 ++++++++++ docs/snyk/v2.9.0-rc3/redis_7.0.11-alpine.html | 1335 ++++ docs/user-guide/annotations-and-labels.md | 1 - docs/user-guide/application-set.md | 20 +- docs/user-guide/build-environment.md | 1 - docs/user-guide/commands/argocd.md | 5 +- docs/user-guide/commands/argocd_account.md | 3 +- .../commands/argocd_account_bcrypt.md | 3 +- .../commands/argocd_account_can-i.md | 3 +- .../commands/argocd_account_delete-token.md | 3 +- .../commands/argocd_account_generate-token.md | 3 +- .../commands/argocd_account_get-user-info.md | 3 +- .../user-guide/commands/argocd_account_get.md | 3 +- .../commands/argocd_account_list.md | 3 +- .../argocd_account_update-password.md | 3 +- docs/user-guide/commands/argocd_admin.md | 78 +- docs/user-guide/commands/argocd_admin_app.md | 3 +- ...argocd_admin_app_diff-reconcile-results.md | 3 +- .../argocd_admin_app_generate-spec.md | 13 +- .../argocd_admin_app_get-reconcile-results.md | 3 +- .../commands/argocd_admin_cluster.md | 3 +- .../argocd_admin_cluster_generate-spec.md | 4 +- .../argocd_admin_cluster_kubeconfig.md | 3 +- .../argocd_admin_cluster_namespaces.md | 3 +- ...ster_namespaces_disable-namespaced-mode.md | 3 +- ...uster_namespaces_enable-namespaced-mode.md | 3 +- .../commands/argocd_admin_cluster_shards.md | 5 +- .../commands/argocd_admin_cluster_stats.md | 5 +- .../commands/argocd_admin_dashboard.md | 3 +- .../commands/argocd_admin_export.md | 49 +- .../commands/argocd_admin_import.md | 56 +- .../commands/argocd_admin_initial-password.md | 3 +- .../commands/argocd_admin_notifications.md | 3 +- .../argocd_admin_notifications_template.md | 3 +- ...argocd_admin_notifications_template_get.md | 3 +- ...ocd_admin_notifications_template_notify.md | 3 +- .../argocd_admin_notifications_trigger.md | 3 +- .../argocd_admin_notifications_trigger_get.md | 3 +- .../argocd_admin_notifications_trigger_run.md | 3 +- docs/user-guide/commands/argocd_admin_proj.md | 3 +- .../argocd_admin_proj_generate-allow-list.md | 3 +- .../argocd_admin_proj_generate-spec.md | 3 +- .../argocd_admin_proj_update-role-policy.md | 3 +- .../argocd_admin_redis-initial-password.md | 3 +- docs/user-guide/commands/argocd_admin_repo.md | 3 +- .../argocd_admin_repo_generate-spec.md | 4 +- .../commands/argocd_admin_settings.md | 3 +- .../commands/argocd_admin_settings_rbac.md | 3 +- .../argocd_admin_settings_rbac_can.md | 3 +- .../argocd_admin_settings_rbac_validate.md | 7 +- ...rgocd_admin_settings_resource-overrides.md | 3 +- ...dmin_settings_resource-overrides_health.md | 3 +- ...s_resource-overrides_ignore-differences.md | 3 +- ...ource-overrides_ignore-resource-updates.md | 3 +- ...ettings_resource-overrides_list-actions.md | 3 +- ..._settings_resource-overrides_run-action.md | 3 +- .../argocd_admin_settings_validate.md | 3 +- docs/user-guide/commands/argocd_app.md | 5 +- .../user-guide/commands/argocd_app_actions.md | 3 +- .../commands/argocd_app_actions_list.md | 3 +- .../commands/argocd_app_actions_run.md | 3 +- .../commands/argocd_app_add-source.md | 115 - docs/user-guide/commands/argocd_app_create.md | 15 +- .../commands/argocd_app_delete-resource.md | 7 +- docs/user-guide/commands/argocd_app_delete.md | 5 +- docs/user-guide/commands/argocd_app_diff.md | 7 +- docs/user-guide/commands/argocd_app_edit.md | 6 +- docs/user-guide/commands/argocd_app_get.md | 20 +- .../user-guide/commands/argocd_app_history.md | 8 +- docs/user-guide/commands/argocd_app_list.md | 3 +- docs/user-guide/commands/argocd_app_logs.md | 3 +- .../commands/argocd_app_manifests.md | 28 +- .../commands/argocd_app_patch-resource.md | 3 +- docs/user-guide/commands/argocd_app_patch.md | 10 +- .../commands/argocd_app_remove-source.md | 58 - .../commands/argocd_app_resources.md | 3 +- .../commands/argocd_app_rollback.md | 12 +- docs/user-guide/commands/argocd_app_set.md | 21 +- docs/user-guide/commands/argocd_app_sync.md | 9 +- .../commands/argocd_app_terminate-op.md | 3 +- docs/user-guide/commands/argocd_app_unset.md | 11 +- docs/user-guide/commands/argocd_app_wait.md | 5 +- docs/user-guide/commands/argocd_appset.md | 4 +- .../commands/argocd_appset_create.md | 12 +- .../commands/argocd_appset_delete.md | 3 +- .../commands/argocd_appset_generate.md | 57 - docs/user-guide/commands/argocd_appset_get.md | 3 +- .../user-guide/commands/argocd_appset_list.md | 3 +- docs/user-guide/commands/argocd_cert.md | 3 +- .../commands/argocd_cert_add-ssh.md | 3 +- .../commands/argocd_cert_add-tls.md | 3 +- docs/user-guide/commands/argocd_cert_list.md | 3 +- docs/user-guide/commands/argocd_cert_rm.md | 3 +- docs/user-guide/commands/argocd_cluster.md | 3 +- .../user-guide/commands/argocd_cluster_add.md | 4 +- .../user-guide/commands/argocd_cluster_get.md | 3 +- .../commands/argocd_cluster_list.md | 5 +- docs/user-guide/commands/argocd_cluster_rm.md | 3 +- .../commands/argocd_cluster_rotate-auth.md | 3 +- .../user-guide/commands/argocd_cluster_set.md | 11 +- docs/user-guide/commands/argocd_completion.md | 12 +- docs/user-guide/commands/argocd_context.md | 3 +- docs/user-guide/commands/argocd_gpg.md | 3 +- docs/user-guide/commands/argocd_gpg_add.md | 3 +- docs/user-guide/commands/argocd_gpg_get.md | 3 +- docs/user-guide/commands/argocd_gpg_list.md | 3 +- docs/user-guide/commands/argocd_gpg_rm.md | 3 +- docs/user-guide/commands/argocd_login.md | 18 +- docs/user-guide/commands/argocd_logout.md | 3 +- docs/user-guide/commands/argocd_proj.md | 7 +- ...cd_proj_add-destination-service-account.md | 60 - .../commands/argocd_proj_add-destination.md | 3 +- .../argocd_proj_add-orphaned-ignore.md | 3 +- .../commands/argocd_proj_add-signature-key.md | 3 +- .../argocd_proj_add-source-namespace.md | 56 - .../commands/argocd_proj_add-source.md | 3 +- .../argocd_proj_allow-cluster-resource.md | 3 +- .../argocd_proj_allow-namespace-resource.md | 3 +- .../user-guide/commands/argocd_proj_create.md | 3 +- .../user-guide/commands/argocd_proj_delete.md | 3 +- .../argocd_proj_deny-cluster-resource.md | 3 +- .../argocd_proj_deny-namespace-resource.md | 3 +- docs/user-guide/commands/argocd_proj_edit.md | 3 +- docs/user-guide/commands/argocd_proj_get.md | 3 +- docs/user-guide/commands/argocd_proj_list.md | 3 +- ...proj_remove-destination-service-account.md | 56 - .../argocd_proj_remove-destination.md | 3 +- .../argocd_proj_remove-orphaned-ignore.md | 3 +- .../argocd_proj_remove-signature-key.md | 3 +- .../argocd_proj_remove-source-namespace.md | 56 - .../commands/argocd_proj_remove-source.md | 3 +- docs/user-guide/commands/argocd_proj_role.md | 3 +- .../commands/argocd_proj_role_add-group.md | 3 +- .../commands/argocd_proj_role_add-policy.md | 3 +- .../commands/argocd_proj_role_create-token.md | 3 +- .../commands/argocd_proj_role_create.md | 3 +- .../commands/argocd_proj_role_delete-token.md | 3 +- .../commands/argocd_proj_role_delete.md | 3 +- .../commands/argocd_proj_role_get.md | 3 +- .../commands/argocd_proj_role_list-tokens.md | 3 +- .../commands/argocd_proj_role_list.md | 3 +- .../commands/argocd_proj_role_remove-group.md | 3 +- .../argocd_proj_role_remove-policy.md | 3 +- docs/user-guide/commands/argocd_proj_set.md | 3 +- .../commands/argocd_proj_windows.md | 3 +- .../commands/argocd_proj_windows_add.md | 3 +- .../commands/argocd_proj_windows_delete.md | 3 +- ...argocd_proj_windows_disable-manual-sync.md | 5 +- .../argocd_proj_windows_enable-manual-sync.md | 3 +- .../commands/argocd_proj_windows_list.md | 3 +- .../commands/argocd_proj_windows_update.md | 3 +- docs/user-guide/commands/argocd_relogin.md | 10 +- docs/user-guide/commands/argocd_repo.md | 3 +- docs/user-guide/commands/argocd_repo_add.md | 10 +- docs/user-guide/commands/argocd_repo_get.md | 4 +- docs/user-guide/commands/argocd_repo_list.md | 3 +- docs/user-guide/commands/argocd_repo_rm.md | 6 +- docs/user-guide/commands/argocd_repocreds.md | 3 +- .../commands/argocd_repocreds_add.md | 4 +- .../commands/argocd_repocreds_list.md | 3 +- .../commands/argocd_repocreds_rm.md | 3 +- docs/user-guide/commands/argocd_version.md | 3 +- docs/user-guide/diff-strategies.md | 2 +- docs/user-guide/diffing.md | 5 +- docs/user-guide/gpg-verification.md | 13 +- docs/user-guide/helm.md | 58 +- docs/user-guide/kustomize.md | 30 +- docs/user-guide/multiple_sources.md | 32 +- docs/user-guide/projects.md | 111 +- docs/user-guide/resource_hooks.md | 1 - docs/user-guide/resource_tracking.md | 5 - docs/user-guide/status-badge.md | 49 +- docs/user-guide/sync-kubectl.md | 2 +- docs/user-guide/sync-options.md | 4 +- docs/user-guide/sync-waves.md | 10 +- docs/user-guide/tracking_strategies.md | 12 +- examples/dashboard.json | 2148 +++---- go.mod | 356 +- go.sum | 2089 +++++-- hack/dev-mounter/main.go | 4 +- hack/gen-catalog/main.go | 30 +- hack/gen-crd-spec/main.go | 20 +- hack/gen-docs/main.go | 12 +- hack/gen-resources/cmd/commands/cmd.go | 7 +- .../generators/cluster_generator.go | 18 +- .../generators/project_generator.go | 3 +- hack/gen-resources/util/gen_options_parser.go | 6 +- hack/generate-actions-list.sh | 1 - hack/generate-mock.sh | 18 - hack/generate-proto.sh | 50 +- .../checksums/add-helm-checksums.sh | 5 +- .../checksums/add-protoc-checksums.sh | 16 - .../helm-v3.14.2-darwin-amd64.tar.gz.sha256 | 1 - .../helm-v3.14.2-darwin-arm64.tar.gz.sha256 | 1 - .../helm-v3.15.2-darwin-amd64.tar.gz.sha256 | 1 - .../helm-v3.15.2-darwin-arm64.tar.gz.sha256 | 1 - .../helm-v3.15.2-linux-amd64.tar.gz.sha256 | 1 - .../helm-v3.15.2-linux-arm64.tar.gz.sha256 | 1 - .../helm-v3.15.2-linux-ppc64le.tar.gz.sha256 | 1 - .../helm-v3.15.2-linux-s390x.tar.gz.sha256 | 1 - ...kustomize_5.4.2_darwin_amd64.tar.gz.sha256 | 1 - ...kustomize_5.4.2_darwin_arm64.tar.gz.sha256 | 1 - .../kustomize_5.4.2_linux_amd64.tar.gz.sha256 | 1 - .../kustomize_5.4.2_linux_arm64.tar.gz.sha256 | 1 - ...ustomize_5.4.2_linux_ppc64le.tar.gz.sha256 | 1 - .../kustomize_5.4.2_linux_s390x.tar.gz.sha256 | 1 - ...kustomize_5.4.3_darwin_amd64.tar.gz.sha256 | 1 - ...kustomize_5.4.3_darwin_arm64.tar.gz.sha256 | 1 - .../kustomize_5.4.3_linux_amd64.tar.gz.sha256 | 1 - .../kustomize_5.4.3_linux_arm64.tar.gz.sha256 | 1 - ...ustomize_5.4.3_linux_ppc64le.tar.gz.sha256 | 1 - .../kustomize_5.4.3_linux_s390x.tar.gz.sha256 | 1 - .../protoc-27.2-linux-aarch_64.zip.sha256 | 1 - .../protoc-27.2-linux-ppcle_64.zip.sha256 | 1 - .../protoc-27.2-linux-s390_64.zip.sha256 | 1 - .../protoc-27.2-linux-x86_64.zip.sha256 | 1 - .../protoc-27.2-osx-aarch_64.zip.sha256 | 1 - .../protoc-27.2-osx-x86_64.zip.sha256 | 1 - hack/installers/install-codegen-go-tools.sh | 7 +- ...{install-helm.sh => install-helm-linux.sh} | 6 +- hack/installers/install-lint-tools.sh | 2 +- hack/installers/install-protoc.sh | 2 +- hack/known_types/main.go | 14 +- hack/snyk-report.sh | 6 +- hack/start-redis-with-password.sh | 30 - hack/test.sh | 4 +- hack/tool-versions.sh | 6 +- hack/update-codegen.sh | 22 +- hack/update-openapi.sh | 23 +- ...ocd-application-controller-deployment.yaml | 9 - ...cd-application-controller-statefulset.yaml | 15 - .../kustomization.yaml | 3 - .../kustomization.yaml | 7 - .../argocd-application-controller-role.yaml | 0 ...cd-application-controller-rolebinding.yaml | 0 .../argocd-application-controller-sa.yaml | 0 ...cd-application-controller-statefulset.yaml | 9 - .../application-controller/kustomization.yaml | 4 +- ...-applicationset-controller-deployment.yaml | 6 - ...argocd-applicationset-controller-role.yaml | 4 +- .../dex/argocd-dex-server-deployment.yaml | 14 +- manifests/base/kustomization.yaml | 2 +- ...d-notifications-controller-deployment.yaml | 6 - .../argocd-repo-server-deployment.yaml | 24 - .../base/server/argocd-server-deployment.yaml | 683 +-- ...applicationset-controller-clusterrole.yaml | 2 - manifests/core-install.yaml | 1873 +----- manifests/core-install/kustomization.yaml | 2 +- manifests/crds/application-crd.yaml | 686 +-- manifests/crds/applicationset-crd.yaml | 1053 ---- manifests/crds/appproject-crd.yaml | 83 +- .../controller-deployment/kustomization.yaml | 5 +- manifests/ha/base/kustomization.yaml | 2 +- .../ha/base/redis-ha/chart/upstream.yaml | 12 +- manifests/ha/base/redis-ha/chart/values.yaml | 4 +- manifests/ha/install.yaml | 1951 +----- manifests/ha/namespace-install.yaml | 127 +- manifests/install.yaml | 1940 +----- manifests/namespace-install.yaml | 116 +- mkdocs.yml | 6 +- .../controller/controller.go | 15 +- .../controller/controller_test.go | 8 +- notifications_catalog/install.yaml | 36 +- .../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 +- pkg/apiclient/apiclient.go | 14 +- pkg/apiclient/apiclient_test.go | 37 +- pkg/apiclient/application/application.pb.go | 722 +-- .../application/forwarder_overwrite.go | 9 +- .../applicationset/applicationset.pb.go | 812 +-- .../applicationset/applicationset.pb.gw.go | 200 - .../cluster/mocks/ClusterServiceServer.go | 117 +- pkg/apiclient/grpcproxy.go | 32 +- pkg/apiclient/repository/repository.pb.go | 286 +- .../session/mocks/SessionServiceClient.go | 71 +- .../session/mocks/SessionServiceServer.go | 67 +- pkg/apiclient/settings/settings.pb.go | 196 +- pkg/apis/api-rules/violation_exceptions.list | 132 + .../application/v1alpha1/app_project_types.go | 26 +- .../v1alpha1/applicationset_types.go | 51 +- .../v1alpha1/applicationset_types_test.go | 7 +- pkg/apis/application/v1alpha1/generated.pb.go | 5419 ++++++----------- pkg/apis/application/v1alpha1/generated.proto | 201 +- .../application/v1alpha1/openapi_generated.go | 332 +- .../application/v1alpha1/repository_types.go | 39 +- pkg/apis/application/v1alpha1/types.go | 223 +- pkg/apis/application/v1alpha1/types_test.go | 750 +-- pkg/apis/application/v1alpha1/values.go | 4 +- pkg/apis/application/v1alpha1/values_test.go | 7 +- .../v1alpha1/zz_generated.deepcopy.go | 57 - pkg/ratelimiter/ratelimiter.go | 16 +- reposerver/apiclient/clientset.go | 13 +- reposerver/apiclient/clientset_test.go | 13 +- .../mocks/RepoServerServiceClient.go | 103 +- ...Service_GenerateManifestWithFilesClient.go | 53 +- reposerver/apiclient/repository.pb.go | 1823 +----- reposerver/askpass/common.go | 11 +- reposerver/askpass/server.go | 26 +- reposerver/askpass/server_test.go | 4 +- reposerver/cache/cache.go | 202 +- reposerver/cache/cache_test.go | 527 +- reposerver/cache/mocks/reposervercache.go | 11 +- reposerver/gpgwatcher.go | 4 +- reposerver/metrics/githandlers_test.go | 122 - reposerver/metrics/metrics.go | 30 - reposerver/repository/chart.go | 3 +- reposerver/repository/chart_test.go | 21 +- reposerver/repository/repository.go | 555 +- reposerver/repository/repository.proto | 38 - reposerver/repository/repository_test.go | 1524 +---- .../helm-with-local-dependency/Chart.yaml | 7 - .../testdata/my-chart/test-file-param.yaml | 0 .../testdata/simple-chart/Chart.yaml | 2 - .../simple-chart/simple-chart-values.yaml | 0 .../simple-chart/templates/my-map.yaml | 4 - reposerver/repository/utils.go | 85 - reposerver/repository/utils_test.go | 46 - reposerver/server.go | 2 +- .../apps.kruise.io/DaemonSet/health.lua | 2 +- .../apps.kruise.io/DaemonSet/health_test.yaml | 4 - .../no-update-strategy-partition.yaml | 34 - .../astra.netapp.io/AppVault/health.lua | 13 - .../astra.netapp.io/AppVault/health_test.yaml | 13 - .../AppVault/testdata/degraded.yaml | 23 - .../AppVault/testdata/healthy.yaml | 21 - .../testdata/progressing_nostatus.yaml | 18 - .../astra.netapp.io/Application/health.lua | 17 - .../Application/health_test.yaml | 13 - .../Application/testdata/degraded.yaml | 26 - .../Application/testdata/healthy.yaml | 24 - .../Application/testdata/progressing.yaml | 16 - .../astra.netapp.io/Backup/health.lua | 16 - .../astra.netapp.io/Backup/health_test.yaml | 17 - .../Backup/testdata/degraded.yaml | 79 - .../Backup/testdata/healthy.yaml | 116 - .../Backup/testdata/progressing_nostatus.yaml | 26 - .../Backup/testdata/progressing_status.yaml | 76 - .../astra.netapp.io/ExecHook/health.lua | 13 - .../astra.netapp.io/ExecHook/health_test.yaml | 13 - .../ExecHook/testdata/healthy.yaml | 23 - .../testdata/progressing_nostatus.yaml | 22 - .../ExecHook/testdata/suspended.yaml | 23 - .../astra.netapp.io/ExecHooksRun/health.lua | 16 - .../ExecHooksRun/health_test.yaml | 17 - .../ExecHooksRun/testdata/degraded.yaml | 71 - .../ExecHooksRun/testdata/healthy.yaml | 71 - .../testdata/progressing_nostatus.yaml | 26 - .../testdata/progressing_status.yaml | 69 - .../astra.netapp.io/ResourceBackup/health.lua | 16 - .../ResourceBackup/health_test.yaml | 17 - .../ResourceBackup/testdata/degraded.yaml | 52 - .../ResourceBackup/testdata/healthy.yaml | 49 - .../testdata/progressing_nostatus.yaml | 24 - .../testdata/progressing_status.yaml | 48 - .../ResticVolumeBackup/health.lua | 16 - .../ResticVolumeBackup/health_test.yaml | 17 - .../ResticVolumeBackup/testdata/degraded.yaml | 99 - .../ResticVolumeBackup/testdata/healthy.yaml | 94 - .../testdata/progressing_nostatus.yaml | 49 - .../testdata/progressing_status.yaml | 92 - .../astra.netapp.io/Schedule/health.lua | 7 - .../astra.netapp.io/Schedule/health_test.yaml | 9 - .../Schedule/testdata/healthy_nostatus.yaml | 28 - .../Schedule/testdata/healthy_status.yaml | 30 - .../astra.netapp.io/Snapshot/health.lua | 16 - .../astra.netapp.io/Snapshot/health_test.yaml | 17 - .../Snapshot/testdata/degraded.yaml | 80 - .../Snapshot/testdata/healthy.yaml | 81 - .../testdata/progressing_nostatus.yaml | 24 - .../Snapshot/testdata/progressing_status.yaml | 73 - .../CronJob/actions/create-job/action.lua | 2 +- .../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 - .../camel.apache.org/Integration/health.lua | 24 - .../Integration/health_test.yaml | 13 - .../Integration/testdata/degraded.yaml | 58 - .../Integration/testdata/healthy.yaml | 58 - .../Integration/testdata/progressing.yaml | 39 - .../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 - .../cluster.x-k8s.io/MachinePool/health.lua | 48 - .../MachinePool/health_test.yaml | 13 - .../MachinePool/testdata/degraded_failed.yaml | 25 - .../testdata/healthy_provisioned.yaml | 57 - .../testdata/progressing_provisioning.yaml | 44 - .../AWSManagedControlPlane/health.lua | 47 - .../AWSManagedControlPlane/health_test.yaml | 17 - .../testdata/degraded.yaml | 50 - .../testdata/healthy.yaml | 46 - .../testdata/progressing_ready_false.yaml | 46 - .../testdata/progressing_ready_true.yaml | 46 - .../core.humio.com/HumioAction/health.lua | 30 - .../HumioAction/health_test.yaml | 21 - .../HumioAction/testdata/configerror.yaml | 23 - .../HumioAction/testdata/healthy.yaml | 23 - .../HumioAction/testdata/notfound.yaml | 23 - .../HumioAction/testdata/progressing.yaml | 21 - .../HumioAction/testdata/unknown.yaml | 23 - .../core.humio.com/HumioAlert/health.lua | 30 - .../HumioAlert/health_test.yaml | 21 - .../HumioAlert/testdata/configerror.yaml | 29 - .../HumioAlert/testdata/healthy.yaml | 29 - .../HumioAlert/testdata/notfound.yaml | 29 - .../HumioAlert/testdata/progressing.yaml | 27 - .../HumioAlert/testdata/unknown.yaml | 29 - .../core.humio.com/HumioCluster/health.lua | 67 - .../HumioCluster/health_test.yaml | 29 - .../HumioCluster/testdata/configerror.yaml | 30 - .../testdata/configerror_custom.yaml | 33 - .../HumioCluster/testdata/healthy.yaml | 30 - .../HumioCluster/testdata/pending.yaml | 30 - .../HumioCluster/testdata/progressing.yaml | 28 - .../HumioCluster/testdata/restarting.yaml | 30 - .../HumioCluster/testdata/unknown.yaml | 33 - .../HumioCluster/testdata/upgrading.yaml | 33 - .../HumioIngestToken/health.lua | 30 - .../HumioIngestToken/health_test.yaml | 21 - .../testdata/configerror.yaml | 20 - .../HumioIngestToken/testdata/healthy.yaml | 20 - .../HumioIngestToken/testdata/notfound.yaml | 20 - .../testdata/progressing.yaml | 18 - .../HumioIngestToken/testdata/unknown.yaml | 20 - .../core.humio.com/HumioParser/health.lua | 30 - .../HumioParser/health_test.yaml | 21 - .../HumioParser/testdata/configerror.yaml | 39 - .../HumioParser/testdata/healthy.yaml | 39 - .../HumioParser/testdata/notfound.yaml | 39 - .../HumioParser/testdata/progressing.yaml | 37 - .../HumioParser/testdata/unknown.yaml | 39 - .../core.humio.com/HumioRepository/health.lua | 30 - .../HumioRepository/health_test.yaml | 21 - .../HumioRepository/testdata/configerror.yaml | 24 - .../HumioRepository/testdata/healthy.yaml | 24 - .../HumioRepository/testdata/notfound.yaml | 24 - .../HumioRepository/testdata/progressing.yaml | 22 - .../HumioRepository/testdata/unknown.yaml | 24 - .../core.humio.com/HumioView/health.lua | 26 - .../core.humio.com/HumioView/health_test.yaml | 21 - .../HumioView/testdata/configerror.yaml | 24 - .../HumioView/testdata/healthy.yaml | 24 - .../HumioView/testdata/notfound.yaml | 24 - .../HumioView/testdata/progressing.yaml | 22 - .../HumioView/testdata/unknown.yaml | 24 - .../gateway.solo.io/Gateway/health.lua | 54 - .../gateway.solo.io/Gateway/health_test.yaml | 37 - .../Gateway/testdata/gloo-accepted.yaml | 14 - .../Gateway/testdata/gloo-no-status.yaml | 2 - .../Gateway/testdata/gloo-pending.yaml | 14 - .../Gateway/testdata/gloo-rejected.yaml | 15 - .../Gateway/testdata/gloo-warning.yaml | 15 - .../non-namespaced-gloo-accepted.yaml | 12 - .../testdata/non-namespaced-gloo-pending.yaml | 14 - .../non-namespaced-gloo-rejected.yaml | 13 - .../testdata/non-namespaced-gloo-warning.yaml | 13 - .../MatchableHttpGateway/health.lua | 54 - .../MatchableHttpGateway/health_test.yaml | 37 - .../testdata/gloo-accepted.yaml | 14 - .../testdata/gloo-no-status.yaml | 2 - .../testdata/gloo-pending.yaml | 14 - .../testdata/gloo-rejected.yaml | 15 - .../testdata/gloo-warning.yaml | 15 - .../non-namespaced-gloo-accepted.yaml | 12 - .../testdata/non-namespaced-gloo-pending.yaml | 14 - .../non-namespaced-gloo-rejected.yaml | 13 - .../testdata/non-namespaced-gloo-warning.yaml | 13 - .../gateway.solo.io/RouteOption/health.lua | 54 - .../RouteOption/health_test.yaml | 37 - .../RouteOption/testdata/gloo-accepted.yaml | 14 - .../RouteOption/testdata/gloo-no-status.yaml | 2 - .../RouteOption/testdata/gloo-pending.yaml | 14 - .../RouteOption/testdata/gloo-rejected.yaml | 15 - .../RouteOption/testdata/gloo-warning.yaml | 15 - .../non-namespaced-gloo-accepted.yaml | 12 - .../testdata/non-namespaced-gloo-pending.yaml | 14 - .../non-namespaced-gloo-rejected.yaml | 13 - .../testdata/non-namespaced-gloo-warning.yaml | 13 - .../gateway.solo.io/RouteTable/health.lua | 54 - .../RouteTable/health_test.yaml | 37 - .../RouteTable/testdata/gloo-accepted.yaml | 14 - .../RouteTable/testdata/gloo-no-status.yaml | 2 - .../RouteTable/testdata/gloo-pending.yaml | 14 - .../RouteTable/testdata/gloo-rejected.yaml | 15 - .../RouteTable/testdata/gloo-warning.yaml | 15 - .../non-namespaced-gloo-accepted.yaml | 12 - .../testdata/non-namespaced-gloo-pending.yaml | 14 - .../non-namespaced-gloo-rejected.yaml | 13 - .../testdata/non-namespaced-gloo-warning.yaml | 13 - .../VirtualHostOption/health.lua | 54 - .../VirtualHostOption/health_test.yaml | 37 - .../testdata/gloo-accepted.yaml | 14 - .../testdata/gloo-no-status.yaml | 2 - .../testdata/gloo-pending.yaml | 14 - .../testdata/gloo-rejected.yaml | 15 - .../testdata/gloo-warning.yaml | 15 - .../non-namespaced-gloo-accepted.yaml | 12 - .../testdata/non-namespaced-gloo-pending.yaml | 14 - .../non-namespaced-gloo-rejected.yaml | 13 - .../testdata/non-namespaced-gloo-warning.yaml | 13 - .../gateway.solo.io/VirtualService/health.lua | 54 - .../VirtualService/health_test.yaml | 37 - .../testdata/gloo-accepted.yaml | 14 - .../testdata/gloo-no-status.yaml | 2 - .../VirtualService/testdata/gloo-pending.yaml | 14 - .../testdata/gloo-rejected.yaml | 15 - .../VirtualService/testdata/gloo-warning.yaml | 15 - .../non-namespaced-gloo-accepted.yaml | 12 - .../testdata/non-namespaced-gloo-pending.yaml | 14 - .../non-namespaced-gloo-rejected.yaml | 13 - .../testdata/non-namespaced-gloo-warning.yaml | 13 - .../gloo.solo.io/Proxy/health.lua | 54 - .../gloo.solo.io/Proxy/health_test.yaml | 37 - .../Proxy/testdata/gloo-accepted.yaml | 14 - .../Proxy/testdata/gloo-no-status.yaml | 2 - .../Proxy/testdata/gloo-pending.yaml | 14 - .../Proxy/testdata/gloo-rejected.yaml | 15 - .../Proxy/testdata/gloo-warning.yaml | 15 - .../non-namespaced-gloo-accepted.yaml | 12 - .../testdata/non-namespaced-gloo-pending.yaml | 14 - .../non-namespaced-gloo-rejected.yaml | 13 - .../testdata/non-namespaced-gloo-warning.yaml | 13 - .../gloo.solo.io/Settings/health.lua | 54 - .../gloo.solo.io/Settings/health_test.yaml | 37 - .../Settings/testdata/gloo-accepted.yaml | 14 - .../Settings/testdata/gloo-no-status.yaml | 2 - .../Settings/testdata/gloo-pending.yaml | 14 - .../Settings/testdata/gloo-rejected.yaml | 15 - .../Settings/testdata/gloo-warning.yaml | 15 - .../non-namespaced-gloo-accepted.yaml | 12 - .../testdata/non-namespaced-gloo-pending.yaml | 14 - .../non-namespaced-gloo-rejected.yaml | 13 - .../testdata/non-namespaced-gloo-warning.yaml | 13 - .../gloo.solo.io/Upstream/health.lua | 54 - .../gloo.solo.io/Upstream/health_test.yaml | 37 - .../Upstream/testdata/gloo-accepted.yaml | 14 - .../Upstream/testdata/gloo-no-status.yaml | 2 - .../Upstream/testdata/gloo-pending.yaml | 14 - .../Upstream/testdata/gloo-rejected.yaml | 15 - .../Upstream/testdata/gloo-warning.yaml | 15 - .../non-namespaced-gloo-accepted.yaml | 12 - .../testdata/non-namespaced-gloo-pending.yaml | 14 - .../non-namespaced-gloo-rejected.yaml | 13 - .../testdata/non-namespaced-gloo-warning.yaml | 13 - .../gloo.solo.io/UpstreamGroup/health.lua | 54 - .../UpstreamGroup/health_test.yaml | 37 - .../UpstreamGroup/testdata/gloo-accepted.yaml | 14 - .../testdata/gloo-no-status.yaml | 2 - .../UpstreamGroup/testdata/gloo-pending.yaml | 14 - .../UpstreamGroup/testdata/gloo-rejected.yaml | 15 - .../UpstreamGroup/testdata/gloo-warning.yaml | 15 - .../non-namespaced-gloo-accepted.yaml | 12 - .../testdata/non-namespaced-gloo-pending.yaml | 14 - .../non-namespaced-gloo-rejected.yaml | 13 - .../testdata/non-namespaced-gloo-warning.yaml | 13 - .../HelmRelease/actions/action_test.yaml | 35 - .../HelmRelease/actions/discovery.lua | 18 - .../HelmRelease/actions/reconcile/action.lua | 7 - .../HelmRelease/actions/resume/action.lua | 5 - .../HelmRelease/actions/suspend/action.lua | 3 - .../actions/testdata/initial_helmrelease.yaml | 33 - .../testdata/reconciled_helmrelease.yaml | 35 - .../actions/testdata/resumed_helmrelease.yaml | 34 - .../testdata/suspended_helmrelease.yaml | 34 - .../HelmRelease/health.lua | 45 - .../HelmRelease/health_test.yaml | 13 - .../HelmRelease/testdata/degraded.yaml | 70 - .../HelmRelease/testdata/healthy.yaml | 49 - .../HelmRelease/testdata/progressing.yaml | 54 - .../iam.aws.crossplane.io/Policy/health.lua | 41 - .../Policy/health_test.yaml | 10 - .../Policy/testdata/ReconcileError.yaml | 39 - .../Policy/testdata/healthy.yaml | 45 - .../iam.aws.crossplane.io/Role/health.lua | 41 - .../Role/health_test.yaml | 10 - .../Role/testdata/ReconcileError.yaml | 54 - .../Role/testdata/healthy.yaml | 52 - .../RolePolicyAttachment/health.lua | 41 - .../RolePolicyAttachment/health_test.yaml | 10 - .../testdata/ReconcileError.yaml | 25 - .../testdata/healthy.yaml | 23 - .../ImagePolicy/health.lua | 38 - .../ImagePolicy/health_test.yaml | 13 - .../ImagePolicy/testdata/degraded.yaml | 26 - .../ImagePolicy/testdata/healthy.yaml | 19 - .../ImagePolicy/testdata/progressing.yaml | 13 - .../ImageRepository/actions/action_test.yaml | 35 - .../ImageRepository/actions/discovery.lua | 18 - .../actions/reconcile/action.lua | 7 - .../ImageRepository/actions/resume/action.lua | 5 - .../actions/suspend/action.lua | 3 - .../testdata/initial_imagerepository.yaml | 9 - .../testdata/reconciled_imagerepository.yaml | 11 - .../testdata/resumed_imagerepository.yaml | 10 - .../testdata/suspended_imagerepository.yaml | 10 - .../ImageRepository/health.lua | 43 - .../ImageRepository/health_test.yaml | 13 - .../ImageRepository/testdata/degraded.yaml | 25 - .../ImageRepository/testdata/healthy.yaml | 17 - .../ImageRepository/testdata/progressing.yaml | 11 - .../actions/action_test.yaml | 35 - .../actions/discovery.lua | 18 - .../actions/reconcile/action.lua | 7 - .../actions/resume/action.lua | 5 - .../actions/suspend/action.lua | 3 - .../initial_imageupdateautomation.yaml | 19 - .../reconciled_imageupdateautomation.yaml | 21 - .../resumed_imageupdateautomation.yaml | 20 - .../suspended_imageupdateautomation.yaml | 20 - .../ImageUpdateAutomation/health.lua | 41 - .../ImageUpdateAutomation/health_test.yaml | 13 - .../testdata/degraded.yaml | 27 - .../testdata/healthy.yaml | 26 - .../testdata/progressing.yaml | 21 - .../k8s.mariadb.com/Backup/health.lua | 25 - .../k8s.mariadb.com/Backup/health_test.yaml | 9 - .../Backup/testdata/failed.yaml | 30 - .../k8s.mariadb.com/Backup/testdata/ok.yaml | 41 - .../k8s.mariadb.com/Database/health.lua | 23 - .../k8s.mariadb.com/Database/health_test.yaml | 5 - .../Database/testdata/database-ready.yaml | 19 - .../k8s.mariadb.com/Grant/health.lua | 22 - .../k8s.mariadb.com/Grant/health_test.yaml | 6 - .../Grant/testdata/grant-ready.yaml | 26 - .../k8s.mariadb.com/MariaDB/health.lua | 25 - .../k8s.mariadb.com/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 - .../k8s.mariadb.com/SqlJob/health.lua | 21 - .../k8s.mariadb.com/SqlJob/health_test.yaml | 9 - .../SqlJob/testdata/sqljobs-failed.yaml | 24 - .../SqlJob/testdata/sqljobs-ok.yaml | 23 - .../k8s.mariadb.com/User/health.lua | 23 - .../k8s.mariadb.com/User/health_test.yaml | 5 - .../User/testdata/user-created.yaml | 37 - .../KafkaCluster/health.lua | 12 +- .../KafkaCluster/health_test.yaml | 6 +- .../KafkaCluster/testdata/rollingUpgrade.yaml | 48 - .../kafka.strimzi.io/KafkaBridge/health.lua | 21 - .../KafkaBridge/health_test.yaml | 12 - .../KafkaBridge/testdata/degraded.yaml | 54 - .../KafkaBridge/testdata/healthy.yaml | 53 - .../testdata/progressing_noStatus.yaml | 43 - .../KafkaConnector/health.lua | 21 - .../KafkaConnector/health_test.yaml | 12 - .../KafkaConnector/testdata/degraded.yaml | 51 - .../KafkaConnector/testdata/healthy.yaml | 43 - .../testdata/progressing_noStatus.yaml | 23 - .../keda.sh/ScaledObject/health.lua | 35 - .../keda.sh/ScaledObject/health_test.yaml | 21 - .../testdata/keda-degraded-1.yaml | 52 - .../ScaledObject/testdata/keda-degraded.yaml | 51 - .../ScaledObject/testdata/keda-healthy.yaml | 51 - .../testdata/keda-progressing.yaml | 30 - .../ScaledObject/testdata/keda-suspended.yaml | 51 - .../Kustomization/actions/action_test.yaml | 35 - .../Kustomization/actions/discovery.lua | 18 - .../actions/reconcile/action.lua | 7 - .../Kustomization/actions/resume/action.lua | 5 - .../Kustomization/actions/suspend/action.lua | 3 - .../testdata/initial_kustomization.yaml | 14 - .../testdata/reconciled_kustomization.yaml | 16 - .../testdata/resumed_kustomization.yaml | 15 - .../testdata/suspended_kustomization.yaml | 15 - .../Kustomization/health.lua | 41 - .../Kustomization/health_test.yaml | 13 - .../Kustomization/testdata/degraded.yaml | 23 - .../Kustomization/testdata/healthy.yaml | 30 - .../Kustomization/testdata/progressing.yaml | 35 - .../KeptnWorkloadVersion/health.lua | 14 - .../KeptnWorkloadVersion/health_test.yaml | 13 - .../testdata/degraded.yaml | 50 - .../testdata/healthy.yaml | 51 - .../testdata/progressing.yaml | 50 - .../metrics.keptn.sh/Analysis/health.lua | 19 - .../Analysis/health_test.yaml | 17 - .../Analysis/testdata/degraded.yaml | 24 - .../Analysis/testdata/healthy_pass.yaml | 24 - .../Analysis/testdata/healthy_warning.yaml | 24 - .../Analysis/testdata/progressing.yaml | 23 - .../metrics.keptn.sh/KeptnMetric/health.lua | 14 - .../KeptnMetric/health_test.yaml | 17 - .../KeptnMetric/testdata/degraded.yaml | 24 - .../KeptnMetric/testdata/healthy.yaml | 22 - .../testdata/healthy_empty_error.yaml | 23 - .../KeptnMetric/testdata/progressing.yaml | 21 - .../Alert/actions/action_test.yaml | 26 - .../Alert/actions/discovery.lua | 16 - .../Alert/actions/resume/action.lua | 5 - .../Alert/actions/suspend/action.lua | 3 - .../Alert/actions/testdata/initial_alert.yaml | 15 - .../Alert/actions/testdata/resumed_alert.yaml | 16 - .../actions/testdata/suspended_alert.yaml | 16 - .../Provider/actions/action_test.yaml | 26 - .../Provider/actions/discovery.lua | 16 - .../Provider/actions/resume/action.lua | 5 - .../Provider/actions/suspend/action.lua | 3 - .../actions/testdata/initial_provider.yaml | 11 - .../actions/testdata/resumed_provider.yaml | 12 - .../actions/testdata/suspended_provider.yaml | 12 - .../Receiver/actions/action_test.yaml | 35 - .../Receiver/actions/discovery.lua | 18 - .../Receiver/actions/reconcile/action.lua | 7 - .../Receiver/actions/resume/action.lua | 5 - .../Receiver/actions/suspend/action.lua | 3 - .../actions/testdata/initial_receiver.yaml | 16 - .../actions/testdata/reconciled_receiver.yaml | 18 - .../actions/testdata/resumed_receiver.yaml | 17 - .../actions/testdata/suspended_receiver.yaml | 17 - .../Receiver/health.lua | 41 - .../Receiver/health_test.yaml | 13 - .../Receiver/testdata/degraded.yaml | 31 - .../Receiver/testdata/healthy.yaml | 32 - .../Receiver/testdata/progressing.yaml | 18 - .../ISBServiceRollout/health.lua | 32 - .../ISBServiceRollout/health_test.yaml | 21 - .../ISBServiceRollout/testdata/degraded.yaml | 40 - .../ISBServiceRollout/testdata/healthy.yaml | 39 - .../testdata/progressing-nostatus.yaml | 23 - .../testdata/progressing-reason.yaml | 39 - .../testdata/progressing.yaml | 39 - .../MonoVertexRollout/health.lua | 32 - .../MonoVertexRollout/health_test.yaml | 17 - .../MonoVertexRollout/testdata/degraded.yaml | 47 - .../MonoVertexRollout/testdata/healthy.yaml | 47 - .../testdata/progressing-reason.yaml | 47 - .../testdata/progressing.yaml | 47 - .../NumaflowControllerRollout/health.lua | 32 - .../health_test.yaml | 17 - .../testdata/degraded.yaml | 35 - .../testdata/healthy.yaml | 36 - .../testdata/progressing-reason.yaml | 36 - .../testdata/progressing.yaml | 36 - .../PipelineRollout/health.lua | 40 - .../PipelineRollout/health_test.yaml | 21 - .../PipelineRollout/testdata/degraded.yaml | 59 - .../PipelineRollout/testdata/healthy.yaml | 59 - .../PipelineRollout/testdata/paused.yaml | 65 - .../testdata/progressing-reason.yaml | 59 - .../PipelineRollout/testdata/progressing.yaml | 61 - .../openfaas.com/Function/health.lua | 31 - .../openfaas.com/Function/health_test.yaml | 17 - .../Function/testdata/degraded_no_secret.yaml | 48 - .../Function/testdata/healthy.yaml | 36 - .../Function/testdata/progressing.yaml | 30 - .../testdata/suspended_zero_replicas.yaml | 35 - .../CertificatePolicy/health.lua | 15 - .../CertificatePolicy/health_test.yaml | 13 - .../CertificatePolicy/testdata/degraded.yaml | 34 - .../CertificatePolicy/testdata/healthy.yaml | 24 - .../testdata/progressing_no_status.yaml | 15 - .../ConfigurationPolicy/health.lua | 33 - .../ConfigurationPolicy/health_test.yaml | 27 - .../testdata/degraded.yaml | 61 - .../testdata/healthy_created.yaml | 67 - .../testdata/healthy_found.yaml | 67 - .../testdata/progressing.yaml | 61 - .../testdata/progressing_no_status.yaml | 25 - .../OperatorPolicy/health.lua | 26 - .../OperatorPolicy/health_test.yaml | 48 - .../OperatorPolicy/testdata/degraded.yaml | 69 - .../testdata/healthy_no_generation.yaml | 73 - .../testdata/healthy_with_generation.yaml | 74 - .../testdata/progressing_no_compliance.yaml | 61 - .../testdata/progressing_no_status.yaml | 26 - .../testdata/progressing_old_generation.yaml | 63 - .../Policy/health.lua | 38 - .../Policy/health_test.yaml | 17 - .../Policy/testdata/degraded_replicated.yaml | 80 - .../Policy/testdata/degraded_root.yaml | 68 - .../Policy/testdata/healthy_replicated.yaml | 91 - .../Policy/testdata/healthy_root.yaml | 68 - .../policy/PodDisruptionBudget/health.lua | 23 - .../PodDisruptionBudget/health_test.yaml | 13 - .../testdata/degraded.yaml | 23 - .../PodDisruptionBudget/testdata/healthy.yaml | 23 - .../testdata/progressing.yaml | 10 - .../rabbitmq.com/RabbitmqCluster/health.lua | 53 - .../RabbitmqCluster/health_test.yaml | 29 - .../testdata/degraded_badconfig.yaml | 31 - .../testdata/degraded_cluster_unknown.yaml | 44 - .../testdata/degraded_replicas_unknown.yaml | 44 - .../RabbitmqCluster/testdata/healthy.yaml | 42 - .../progressing_cluster_unavailable.yaml | 44 - .../testdata/progressing_no_status.yaml | 23 - .../testdata/progressing_pods_not_ready.yaml | 43 - .../DBCluster/health.lua | 41 - .../DBCluster/health_test.yaml | 18 - .../DBCluster/testdata/creating.yaml | 48 - .../DBCluster/testdata/degraded.yaml | 37 - .../DBCluster/testdata/healthy.yaml | 61 - .../DBCluster/testdata/suspended.yaml | 37 - .../DBInstance/health.lua | 41 - .../DBInstance/health_test.yaml | 14 - .../DBInstance/testdata/creating.yaml | 45 - .../DBInstance/testdata/degraded.yaml | 64 - .../DBInstance/testdata/healthy.yaml | 65 - .../ResourceRecordSet/health.lua | 41 - .../ResourceRecordSet/health_test.yaml | 25 - .../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 - .../s3.aws.crossplane.io/Bucket/health.lua | 41 - .../Bucket/health_test.yaml | 14 - .../Bucket/testdata/ReconcileError.yaml | 42 - .../Bucket/testdata/healthy.yaml | 36 - .../InferenceService/health.lua | 12 +- .../InferenceService/health_test.yaml | 4 - .../testdata/healthy_raw.yaml | 19 - .../Bucket/actions/action_test.yaml | 35 - .../Bucket/actions/discovery.lua | 18 - .../Bucket/actions/reconcile/action.lua | 7 - .../Bucket/actions/resume/action.lua | 5 - .../Bucket/actions/suspend/action.lua | 3 - .../actions/testdata/initial_bucket.yaml | 12 - .../actions/testdata/reconciled_bucket.yaml | 14 - .../actions/testdata/resumed_bucket.yaml | 13 - .../actions/testdata/suspended_bucket.yaml | 13 - .../Bucket/health.lua | 45 - .../Bucket/health_test.yaml | 13 - .../Bucket/testdata/degraded.yaml | 34 - .../Bucket/testdata/healthy.yaml | 26 - .../Bucket/testdata/progressing.yaml | 14 - .../GitRepository/actions/action_test.yaml | 35 - .../GitRepository/actions/discovery.lua | 18 - .../actions/reconcile/action.lua | 7 - .../GitRepository/actions/resume/action.lua | 5 - .../GitRepository/actions/suspend/action.lua | 3 - .../testdata/initial_gitrepository.yaml | 10 - .../testdata/reconciled_gitrepository.yaml | 12 - .../testdata/resumed_gitrepository.yaml | 11 - .../testdata/suspended_gitrepository.yaml | 11 - .../GitRepository/health.lua | 45 - .../GitRepository/health_test.yaml | 13 - .../GitRepository/testdata/degraded.yaml | 38 - .../GitRepository/testdata/healthy.yaml | 24 - .../GitRepository/testdata/progressing.yaml | 12 - .../HelmChart/actions/action_test.yaml | 35 - .../HelmChart/actions/discovery.lua | 18 - .../HelmChart/actions/reconcile/action.lua | 7 - .../HelmChart/actions/resume/action.lua | 5 - .../HelmChart/actions/suspend/action.lua | 3 - .../actions/testdata/initial_helmchart.yaml | 13 - .../testdata/reconciled_helmchart.yaml | 15 - .../actions/testdata/resumed_helmchart.yaml | 14 - .../actions/testdata/suspended_helmchart.yaml | 14 - .../HelmChart/health.lua | 45 - .../HelmChart/health_test.yaml | 13 - .../HelmChart/testdata/degraded.yaml | 41 - .../HelmChart/testdata/healthy.yaml | 27 - .../HelmChart/testdata/progressing.yaml | 16 - .../HelmRepository/actions/action_test.yaml | 35 - .../HelmRepository/actions/discovery.lua | 18 - .../actions/reconcile/action.lua | 7 - .../HelmRepository/actions/resume/action.lua | 5 - .../HelmRepository/actions/suspend/action.lua | 3 - .../testdata/initial_helmrepository.yaml | 8 - .../testdata/reconciled_helmrepository.yaml | 10 - .../testdata/resumed_helmrepository.yaml | 9 - .../testdata/suspended_helmrepository.yaml | 9 - .../HelmRepository/health.lua | 45 - .../HelmRepository/health_test.yaml | 13 - .../HelmRepository/testdata/degraded.yaml | 38 - .../HelmRepository/testdata/healthy.yaml | 22 - .../HelmRepository/testdata/progressing.yaml | 11 - .../OCIRepository/actions/action_test.yaml | 35 - .../OCIRepository/actions/discovery.lua | 18 - .../actions/reconcile/action.lua | 7 - .../OCIRepository/actions/resume/action.lua | 5 - .../OCIRepository/actions/suspend/action.lua | 3 - .../testdata/initial_ocirepository.yaml | 10 - .../testdata/reconciled_ocirepository.yaml | 12 - .../testdata/resumed_ocirepository.yaml | 11 - .../testdata/suspended_ocirepository.yaml | 11 - .../OCIRepository/health.lua | 45 - .../OCIRepository/health_test.yaml | 13 - .../OCIRepository/testdata/degraded.yaml | 38 - .../OCIRepository/testdata/healthy.yaml | 24 - .../OCIRepository/testdata/progressing.yaml | 12 - server/account/account.go | 36 +- server/account/account_test.go | 87 +- server/application/application.go | 509 +- server/application/application.proto | 8 - server/application/application_test.go | 1171 +--- server/application/broadcaster.go | 4 +- server/application/broadcaster_test.go | 1 + server/application/logs.go | 8 +- server/application/logs_test.go | 1 + server/application/mocks/Broadcaster.go | 21 +- server/application/terminal.go | 27 +- server/application/websocket.go | 31 +- server/application/websocket_test.go | 76 +- server/applicationset/applicationset.go | 210 +- server/applicationset/applicationset.proto | 33 +- server/applicationset/applicationset_test.go | 240 +- server/badge/badge.go | 110 +- server/badge/badge_test.go | 327 +- server/cache/cache.go | 15 +- server/cache/cache_test.go | 25 +- server/cluster/cluster.go | 9 +- server/cluster/cluster_test.go | 85 +- server/deeplinks/deeplinks.go | 52 +- server/deeplinks/deeplinks_test.go | 95 +- server/extension/extension.go | 147 +- server/extension/extension_test.go | 89 +- server/extension/mocks/ApplicationGetter.go | 20 +- .../mocks/ExtensionMetricsRegistry.go | 38 - server/extension/mocks/ProjectGetter.go | 29 +- server/extension/mocks/RbacEnforcer.go | 15 +- server/extension/mocks/SettingsGetter.go | 20 +- server/extension/mocks/UserGetter.go | 66 - server/gpgkey/gpgkey.go | 3 +- server/logout/logout.go | 5 +- server/logout/logout_test.go | 71 +- server/metrics/metrics.go | 50 +- server/notification/notification.go | 11 +- server/notification/notification_test.go | 24 +- server/project/project.go | 19 +- server/project/project_test.go | 153 +- server/rbacpolicy/rbacpolicy.go | 6 +- server/repocreds/repocreds.go | 4 +- server/repository/repository.go | 147 +- server/repository/repository.proto | 6 - server/repository/repository_test.go | 479 +- server/server.go | 196 +- server/server_norace_test.go | 35 +- server/server_test.go | 191 +- server/session/ratelimiter_test.go | 9 +- server/session/session.go | 1 + server/settings/settings.go | 1 - server/settings/settings.proto | 1 - sonar-project.properties | 5 +- test/container/Dockerfile | 34 +- test/container/Procfile | 2 +- test/e2e/accounts_test.go | 22 +- test/e2e/admin_test.go | 79 - test/e2e/app_autosync_ns_test.go | 3 +- test/e2e/app_autosync_test.go | 3 +- test/e2e/app_deletion_test.go | 15 - test/e2e/app_k8s_events_test.go | 65 - test/e2e/app_management_ns_test.go | 320 +- test/e2e/app_management_test.go | 471 +- test/e2e/app_multiple_sources_test.go | 11 +- test/e2e/app_namespaces_test.go | 3 +- test/e2e/applicationset_test.go | 1072 +--- test/e2e/cli_test.go | 7 +- test/e2e/cluster_generator_test.go | 41 +- test/e2e/cluster_objects_test.go | 5 +- test/e2e/cluster_test.go | 11 +- test/e2e/clusterdecisiongenerator_e2e_test.go | 42 +- test/e2e/custom_tool_test.go | 22 +- test/e2e/declarative_test.go | 11 +- test/e2e/deployment_test.go | 41 +- test/e2e/fixture/admin/actions.go | 67 - test/e2e/fixture/admin/consequences.go | 37 - test/e2e/fixture/admin/context.go | 41 - test/e2e/fixture/admin/fixture.go | 15 - test/e2e/fixture/admin/utils/backup.go | 48 - test/e2e/fixture/app/actions.go | 13 +- test/e2e/fixture/app/context.go | 2 +- test/e2e/fixture/app/expectation.go | 36 +- test/e2e/fixture/applicationsets/actions.go | 17 +- .../fixture/applicationsets/consequences.go | 3 + test/e2e/fixture/applicationsets/context.go | 21 +- .../fixture/applicationsets/expectation.go | 11 + .../fixture/applicationsets/utils/fixture.go | 16 +- test/e2e/fixture/certs/certs.go | 7 +- test/e2e/fixture/cluster/actions.go | 9 +- test/e2e/fixture/fixture.go | 32 +- test/e2e/fixture/gpgkeys/gpgkeys.go | 2 +- test/e2e/fixture/repos/repos.go | 7 +- test/e2e/git_test.go | 93 - test/e2e/helm_test.go | 61 +- test/e2e/hook_test.go | 8 +- test/e2e/jsonnet_test.go | 14 +- test/e2e/kustomize_test.go | 98 +- test/e2e/matrix_e2e_test.go | 29 +- test/e2e/merge_e2e_test.go | 23 +- test/e2e/multiarch-container/Dockerfile | 2 +- test/e2e/notification_test.go | 18 +- test/e2e/project_management_test.go | 158 +- test/e2e/repo_management_test.go | 49 +- test/e2e/scoped_repository_test.go | 20 +- test/e2e/sync_waves_test.go | 45 - test/e2e/sync_with_impersonate_test.go | 309 - .../e2e/testdata/helm-api-versions/Chart.yaml | 3 - .../templates/config-map.yaml | 7 - test/e2e/testdata/helm-namespace/Chart.yaml | 3 - test/e2e/testdata/helm-namespace/baz.yaml | 1 - .../helm-namespace/templates/config-map.yaml | 7 - test/e2e/testdata/helm-namespace/values.yaml | 0 .../helm-chart/Chart.yaml | 3 - .../helm-chart/templates/config-map.yaml | 7 - .../helm-chart/values.yaml | 0 .../kustomize-api-versions/kustomization.yaml | 7 - .../helm-chart/Chart.yaml | 3 - .../helm-chart/templates/config-map.yaml | 6 - .../helm-chart/values.yaml | 0 .../kustomize-kube-version/kustomization.yaml | 7 - .../testdata/syncwaves-prune-order/README.md | 15 - .../testdata/syncwaves-prune-order/pod.yaml | 41 - .../testdata/syncwaves-prune-order/rbac.yaml | 37 - test/e2e/user_info_test.go | 3 +- test/fixture/path/files.go | 1 + test/manifests_test.go | 5 +- test/remote/Dockerfile | 4 +- test/testutil.go | 11 - tools/cmd-docs/main.go | 1 + ui-test/Dockerfile | 2 +- ui-test/package.json | 22 +- ui-test/yarn.lock | 1304 ++-- ui/.nvmrc | 2 +- ui/.prettierrc | 3 +- ui/README.md | 23 +- ui/eslint.config.mjs | 37 - ui/package.json | 61 +- ui/src/app/app.tsx | 66 +- .../__snapshots__/utils.test.tsx.snap | 76 +- .../application-create-panel.tsx | 13 +- ...application-deployment-history-details.tsx | 125 - .../application-deployment-history.scss | 12 +- .../application-deployment-history.tsx | 45 +- .../initiated-by.tsx | 6 - .../revision-metadata-rows.tsx | 12 +- .../application-details-app-dropdown.tsx | 4 +- .../application-details.tsx | 411 +- .../application-resource-filter.tsx | 2 +- .../application-resource-list.scss | 13 - .../application-resource-list.tsx | 268 +- .../application-fullscreen-logs.scss | 7 +- .../application-fullscreen-logs.tsx | 2 - .../application-node-info.scss | 7 - .../application-node-info.tsx | 16 +- .../application-operation-state.tsx | 75 +- .../application-parameters-source.tsx | 116 - .../application-parameters.scss | 88 - .../application-parameters.tsx | 761 +-- .../kustomize-image.test.ts | 8 +- .../application-parameters/source-panel.scss | 18 - .../application-parameters/source-panel.tsx | 375 -- .../application-pod-view/pod-view.tsx | 120 +- .../application-resource-tree.scss | 6 +- .../application-resource-tree.test.tsx | 178 +- .../application-resource-tree.tsx | 204 +- .../application-resources-diff.scss | 7 - .../application-resources-diff.tsx | 4 +- .../individual-diff-section.tsx | 10 +- .../application-retry-options.tsx | 2 - .../application-retry-view.tsx | 2 +- .../application-status-panel.tsx | 60 +- .../revision-metadata-panel.tsx | 4 +- .../application-summary.scss | 15 - .../application-summary.tsx | 213 +- .../edit-notification-subscriptions.tsx | 26 +- .../edit-notification-subscriptsions.test.ts | 6 - .../components/application-urls.test.ts | 39 +- .../applications-status-bar.tsx | 2 +- .../applications-summary.tsx | 2 +- .../applications-list/applications-table.tsx | 20 +- .../applications-list/applications-tiles.tsx | 2 +- .../applications/components/filter/filter.tsx | 2 +- .../applications/components/label-selector.ts | 1 - .../pod-logs-viewer/container-selector.tsx | 2 +- .../pod-logs-viewer/fullscreen-button.tsx | 9 +- .../pod-logs-viewer/pod-logs-viewer.scss | 10 +- .../pod-logs-viewer/pod-logs-viewer.tsx | 48 +- .../resource-details/resource-details.tsx | 50 +- .../app/applications/components/resources.ts | 1 - .../revision-form-field.tsx | 3 +- ui/src/app/applications/components/utils.scss | 5 - .../applications/components/utils.test.tsx | 6 +- ui/src/app/applications/components/utils.tsx | 276 +- ui/src/app/login/components/login.scss | 6 - ui/src/app/login/components/pkce-verify.tsx | 3 +- ui/src/app/login/components/utils.ts | 22 +- .../components/certs-list/certs-list.tsx | 3 +- .../clusters-list/cluster-list.scss | 25 - .../clusters-list/clusters-list.tsx | 49 +- .../project-details/project-details.tsx | 105 +- .../project-details/resource-lists-panel.tsx | 43 +- .../project-sync-windows-edit.tsx | 2 +- .../components/repo-details/repo-details.tsx | 11 +- .../components/repos-list/repos-list.tsx | 47 +- .../components/badge-panel/badge-panel.tsx | 18 +- .../app/shared/components/clipboard-text.tsx | 4 +- .../editable-panel/editable-panel.scss | 35 - .../editable-panel/editable-panel.tsx | 207 +- .../editable-panel/editable-section.tsx | 182 - .../error-boundary/error-boundary.tsx | 2 +- .../components/expandable/expandable.scss | 2 +- .../app/shared/components/layout/layout.scss | 10 + .../app/shared/components/layout/layout.tsx | 3 +- .../app/shared/components/monaco-editor.tsx | 11 - ui/src/app/shared/components/page/page.tsx | 2 +- .../progress-popup.test.tsx.snap | 18 +- .../progress/progress-popup.test.tsx | 9 +- ui/src/app/shared/components/repo.tsx | 8 +- .../app/shared/components/revision.test.tsx | 37 +- .../components/tags-input/tags-input.scss | 3 +- ui/src/app/shared/components/urls.test.ts | 12 +- .../components/yaml-editor/yaml-editor.tsx | 4 +- ui/src/app/shared/context.ts | 2 +- ui/src/app/shared/models.ts | 20 +- .../shared/services/applications-service.ts | 28 +- .../app/shared/services/extensions-service.ts | 98 +- ui/src/app/shared/services/repo-service.ts | 32 +- .../app/shared/services/repocreds-service.ts | 12 +- ui/src/app/shared/utils.test.ts | 4 +- ui/src/app/typings.d.ts | 4 +- ui/src/app/ui-banner/ui-banner.tsx | 2 +- ui/src/app/webpack.config.js | 10 +- ui/tslint.json | 21 + ui/yarn.lock | 4576 +++++++------- util/app/discovery/discovery.go | 29 +- util/app/discovery/discovery_test.go | 29 +- util/app/path/path.go | 72 +- util/app/path/path_test.go | 164 +- util/argo/argo.go | 224 +- util/argo/argo_test.go | 342 +- util/argo/audit_logger.go | 96 +- util/argo/audit_logger_test.go | 50 +- util/argo/diff/diff.go | 17 +- util/argo/diff/diff_test.go | 17 +- util/argo/diff/ignore.go | 5 +- util/argo/diff/ignore_test.go | 1 + util/argo/diff/normalize_test.go | 6 +- util/argo/managedfields/managed_fields.go | 10 +- .../argo/managedfields/managed_fields_test.go | 36 +- util/argo/normalizers/corev1_known_types.go | 55 +- util/argo/normalizers/diff_normalizer.go | 4 +- util/argo/normalizers/diff_normalizer_test.go | 75 +- util/argo/normalizers/diffing_known_types.txt | 19 +- .../normalizers/knowntypes_normalizer_test.go | 104 +- util/argo/resource_tracking.go | 28 +- util/argo/resource_tracking_test.go | 145 +- .../buffered_context/buffered_context_test.go | 2 +- util/cache/appstate/cache.go | 49 +- util/cache/appstate/cache_test.go | 11 +- util/cache/cache.go | 191 +- util/cache/cache_test.go | 100 +- util/cache/client.go | 19 +- util/cache/client_test.go | 9 +- util/cache/inmemory.go | 22 +- util/cache/inmemory_test.go | 5 +- util/cache/mocks/cacheclient.go | 11 +- util/cache/redis.go | 24 +- util/cache/redis_hook_test.go | 4 +- util/cache/redis_test.go | 62 +- util/cache/twolevelclient.go | 8 - util/cert/cert.go | 4 +- util/cert/cert_test.go | 131 +- util/cli/cli.go | 4 +- util/clusterauth/clusterauth.go | 37 +- util/clusterauth/clusterauth_test.go | 102 +- util/cmp/stream.go | 19 +- util/cmp/stream_test.go | 2 +- util/config/env.go | 37 +- util/config/env_test.go | 59 +- util/config/reader.go | 2 +- util/config/reader_test.go | 5 +- util/db/certificate.go | 12 +- util/db/certificate_test.go | 105 +- util/db/cluster.go | 13 +- util/db/cluster_norace_test.go | 20 +- util/db/cluster_test.go | 39 +- util/db/db.go | 23 +- util/db/db_test.go | 177 +- util/db/gpgkeys.go | 2 +- util/db/gpgkeys_test.go | 69 +- util/db/helmrepository.go | 4 +- util/db/mocks/ArgoDB.go | 164 +- util/db/repository.go | 65 +- util/db/repository_legacy.go | 12 +- util/db/repository_secrets.go | 59 +- util/db/repository_secrets_test.go | 279 +- util/db/repository_test.go | 160 +- util/db/secrets.go | 7 +- util/dex/config.go | 55 +- util/dex/dex.go | 5 +- util/dex/dex_test.go | 138 +- util/env/env.go | 6 +- util/env/env_test.go | 16 +- util/errors/credentials.go | 6 +- util/exec/exec.go | 30 - util/exec/exec_test.go | 35 +- util/git/client.go | 181 +- util/git/client_test.go | 153 +- util/git/creds.go | 77 +- util/git/creds_test.go | 167 +- util/git/git.go | 4 +- util/git/git_test.go | 270 +- util/git/mocks/Client.go | 102 +- util/git/workaround.go | 32 +- util/github_app/repos_test.go | 5 +- util/glob/glob_test.go | 32 +- util/glob/list.go | 28 +- util/gpg/gpg.go | 30 +- util/gpg/gpg_test.go | 100 +- util/grpc/errors.go | 4 +- util/grpc/errors_test.go | 18 +- util/grpc/grpc.go | 12 +- util/grpc/logging.go | 2 +- util/grpc/logging_test.go | 7 +- util/grpc/sanitizer.go | 5 +- util/grpc/useragent.go | 2 +- util/healthz/healthz_test.go | 1 + util/helm/client.go | 125 +- util/helm/client_test.go | 77 +- util/helm/cmd.go | 182 +- util/helm/cmd_test.go | 36 +- util/helm/helm.go | 50 +- util/helm/helm_test.go | 103 +- util/helm/helmver.go | 56 + util/helm/index.go | 7 +- util/helm/index_test.go | 22 +- util/helm/mocks/Client.go | 100 +- util/helm/tags.go | 7 +- util/helm/tags_test.go | 7 +- util/http/http_test.go | 31 +- util/io/bytereadseeker_test.go | 19 +- util/io/closer.go | 8 +- util/io/files/tar.go | 15 +- util/io/files/tar_test.go | 22 +- util/io/files/util.go | 6 +- util/io/mocks/TempPaths.go | 44 +- util/io/path/resolved.go | 7 +- util/io/path/resolved_test.go | 36 +- util/io/paths.go | 18 +- util/io/paths_test.go | 12 - util/jwt/jwt_test.go | 17 +- util/kube/kube.go | 18 +- util/kube/kube_test.go | 111 +- util/kube/portforwarder.go | 2 +- util/kube/util.go | 1 + util/kube/util_test.go | 6 +- util/kustomize/kustomize.go | 113 +- util/kustomize/kustomize_test.go | 224 +- .../label_without_selector/deployment.yaml | 22 - .../label_without_selector/kustomization.yaml | 2 - util/localconfig/file_perm_unix.go | 2 +- util/localconfig/localconfig.go | 3 +- util/localconfig/localconfig_test.go | 20 +- util/log/logrus.go | 19 +- util/log/logrus_test.go | 13 - util/lua/custom_actions_test.go | 32 +- util/lua/health_test.go | 2 +- util/lua/lua.go | 116 +- util/lua/lua_test.go | 200 +- util/lua/oslib_safe.go | 41 +- util/manifeststream/stream.go | 12 +- util/manifeststream/stream_test.go | 11 +- util/metrics/metrics.go | 20 - util/metrics/metrics_test.go | 30 - util/notification/argocd/mocks/Service.go | 92 - util/notification/argocd/service.go | 17 +- util/notification/expression/repo/repo.go | 28 +- .../expression/shared/appdetail.go | 13 +- .../expression/shared/appdetail_test.go | 6 +- .../expression/strings/strings_test.go | 1 + util/notification/expression/time/time.go | 31 +- .../notification/expression/time/time_test.go | 45 +- util/notification/settings/legacy.go | 2 +- util/notification/settings/legacy_test.go | 19 +- util/notification/settings/settings.go | 20 - util/notification/settings/settings_test.go | 15 +- util/oidc/oidc.go | 51 +- util/oidc/oidc_test.go | 155 +- util/oidc/provider.go | 24 +- util/oidc/templates.go | 9 +- util/password/password.go | 6 +- util/profile/profile.go | 25 +- util/profile/profile_test.go | 48 - util/proxy/proxy.go | 24 +- util/proxy/proxy_test.go | 29 +- util/rbac/rbac.go | 5 +- util/rbac/rbac_norace_test.go | 5 +- util/rbac/rbac_test.go | 86 +- util/regex/regex.go | 20 - util/security/application_namespaces.go | 2 +- util/security/application_namespaces_test.go | 14 - util/security/jwt.go | 6 +- util/security/path_traversal_test.go | 9 +- util/session/sessionmanager.go | 16 +- util/session/sessionmanager_norace_test.go | 6 +- util/session/sessionmanager_test.go | 77 +- util/settings/accounts_test.go | 49 +- util/settings/resources_filter.go | 1 + util/settings/settings.go | 175 +- util/settings/settings_test.go | 422 +- util/test/testutil.go | 2 +- util/text/label/label_test.go | 9 +- util/tgzstream/stream.go | 5 +- util/tls/tls.go | 35 +- util/tls/tls_test.go | 65 +- util/util_test.go | 24 - util/webhook/webhook.go | 191 +- util/webhook/webhook_test.go | 168 +- 1739 files changed, 69322 insertions(+), 117301 deletions(-) delete mode 100644 .gitattributes delete mode 100644 .github/ISSUE_TEMPLATE/new_dev_tool.md delete mode 100644 .golangci.yaml delete mode 100644 .mockery.yaml rename .readthedocs.yaml => .readthedocs.yml (100%) delete mode 100644 applicationset/controllers/template/template.go delete mode 100644 applicationset/controllers/template/template_test.go rename applicationset/controllers/{template/patch.go => templatePatch.go} (98%) rename applicationset/controllers/{template/patch_test.go => templatePatch_test.go} (99%) delete mode 100644 applicationset/generators/mocks/Generator.go delete mode 100644 applicationset/generators/utils.go delete mode 100644 applicationset/metrics/fake.go delete mode 100644 applicationset/metrics/metrics.go delete mode 100644 applicationset/metrics/metrics_test.go create mode 100644 applicationset/services/mocks/RepositoryDB.go delete mode 100644 applicationset/status/resource_status.go delete mode 100644 applicationset/utils/applicationset_lister.go delete mode 100644 applicationset/utils/kubernetes.go delete mode 100644 applicationset/utils/kubernetes_test.go delete mode 100644 applicationset/utils/mocks/Renderer.go delete mode 100644 applicationset/webhook/testdata/github-commit-event-feature-branch.json delete mode 100644 cmd/argocd/commands/admin/admin_test.go delete mode 100644 cmd/argocd/commands/admin/backup_test.go create mode 100644 cmd/argocd/commands/admin/secrets_redactor_test.go delete mode 100644 controller/sharding/consistent/consistent.go delete mode 100644 controller/testdata/diff-cache.yaml create mode 100644 docs/assets/api-management.png create mode 100644 docs/assets/groups-claim.png create mode 100644 docs/assets/groups-scope.png delete mode 100644 docs/assets/okta-app.png delete mode 100644 docs/assets/okta-auth-policy-edit.png delete mode 100644 docs/assets/okta-auth-policy.png delete mode 100644 docs/assets/okta-auth-rule.png delete mode 100644 docs/assets/okta-create-oidc-app.png delete mode 100644 docs/assets/okta-groups-claim.png delete mode 100644 docs/assets/okta-groups-scope.png delete mode 100644 docs/developer-guide/docs-site.md create mode 100644 docs/developer-guide/site.md delete mode 100644 docs/operator-manual/app-sync-using-impersonation.md delete mode 100644 docs/operator-manual/resource_actions_builtin.md delete mode 100644 docs/operator-manual/upgrading/2.10-2.11.md delete mode 100644 docs/operator-manual/upgrading/2.11-2.12.md delete mode 100644 docs/operator-manual/upgrading/2.12-2.13.md delete mode 100644 docs/proposals/decouple-application-sync-user-using-impersonation.md delete mode 100644 docs/proposals/images/current-summary-tab.png delete mode 100644 docs/proposals/images/helm-parameter-list.png delete mode 100644 docs/proposals/images/history-and-rollback-button.png delete mode 100644 docs/proposals/images/history-rollback-contents.png delete mode 100644 docs/proposals/images/new-sources-tab.png delete mode 100644 docs/proposals/multiple-sources-for-applications-ui.md delete mode 100644 docs/proposals/project-scoped-repository-enhancements.md delete mode 100644 docs/proposals/resource-deletion-with-approval.md delete mode 100644 docs/proposals/sync-timeout.md rename docs/snyk/{v2.12.3/ghcr.io_dexidp_dex_v2.38.0.html => master/ghcr.io_dexidp_dex_v2.37.0.html} (56%) rename docs/snyk/{v2.13.0-rc2/ghcr.io_dexidp_dex_v2.41.1.html => master/haproxy_2.6.14-alpine.html} (70%) rename docs/snyk/{v2.13.0-rc2/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html => master/redis_7.0.11-alpine.html} (64%) delete mode 100644 docs/snyk/master/redis_7.0.15-alpine.html delete mode 100644 docs/snyk/v2.10.16/argocd-iac-install.html delete mode 100644 docs/snyk/v2.10.16/ghcr.io_dexidp_dex_v2.37.0.html delete mode 100644 docs/snyk/v2.10.16/haproxy_2.6.14-alpine.html delete mode 100644 docs/snyk/v2.10.16/redis_7.0.15-alpine.html delete mode 100644 docs/snyk/v2.11.8/argocd-iac-install.html delete mode 100644 docs/snyk/v2.11.8/haproxy_2.6.14-alpine.html delete mode 100644 docs/snyk/v2.11.8/redis_7.0.15-alpine.html delete mode 100644 docs/snyk/v2.12.3/argocd-iac-install.html delete mode 100644 docs/snyk/v2.12.3/argocd-test.html delete mode 100644 docs/snyk/v2.12.3/public.ecr.aws_docker_library_redis_7.0.15-alpine.html delete mode 100644 docs/snyk/v2.12.3/redis_7.0.15-alpine.html delete mode 100644 docs/snyk/v2.13.0-rc2/argocd-iac-install.html delete mode 100644 docs/snyk/v2.13.0-rc2/public.ecr.aws_docker_library_redis_7.0.15-alpine.html delete mode 100644 docs/snyk/v2.13.0-rc2/quay.io_argoproj_argocd_v2.13.0-rc2.html delete mode 100644 docs/snyk/v2.13.0-rc2/redis_7.0.15-alpine.html rename docs/snyk/{v2.11.8/argocd-iac-namespace-install.html => v2.6.15/argocd-iac-install.html} (90%) rename docs/snyk/{v2.10.16 => v2.6.15}/argocd-iac-namespace-install.html (91%) create mode 100644 docs/snyk/v2.6.15/argocd-test.html rename docs/snyk/{v2.11.8/ghcr.io_dexidp_dex_v2.38.0.html => v2.6.15/ghcr.io_dexidp_dex_v2.37.0.html} (56%) rename docs/snyk/{master/ghcr.io_dexidp_dex_v2.41.1.html => v2.6.15/haproxy_2.6.14-alpine.html} (70%) rename docs/snyk/{v2.10.16/quay.io_argoproj_argocd_v2.10.16.html => v2.6.15/quay.io_argoproj_argocd_v2.6.15.html} (59%) rename docs/snyk/{master/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html => v2.6.15/redis_7.0.11-alpine.html} (64%) rename docs/snyk/{v2.12.3/argocd-iac-namespace-install.html => v2.7.14/argocd-iac-install.html} (90%) rename docs/snyk/{v2.13.0-rc2 => v2.7.14}/argocd-iac-namespace-install.html (91%) rename docs/snyk/{v2.11.8 => v2.7.14}/argocd-test.html (80%) create mode 100644 docs/snyk/v2.7.14/ghcr.io_dexidp_dex_v2.37.0.html rename docs/snyk/{v2.13.0-rc2/argocd-test.html => v2.7.14/haproxy_2.6.14-alpine.html} (52%) rename docs/snyk/{v2.11.8/quay.io_argoproj_argocd_v2.11.8.html => v2.7.14/quay.io_argoproj_argocd_v2.7.14.html} (67%) rename docs/snyk/{v2.12.3/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html => v2.7.14/redis_7.0.11-alpine.html} (64%) create mode 100644 docs/snyk/v2.8.5/argocd-iac-install.html create mode 100644 docs/snyk/v2.8.5/argocd-iac-namespace-install.html create mode 100644 docs/snyk/v2.8.5/argocd-test.html create mode 100644 docs/snyk/v2.8.5/ghcr.io_dexidp_dex_v2.37.0.html rename docs/snyk/{master/public.ecr.aws_docker_library_redis_7.0.15-alpine.html => v2.8.5/haproxy_2.6.14-alpine.html} (51%) rename docs/snyk/{v2.12.3/quay.io_argoproj_argocd_v2.12.3.html => v2.8.5/quay.io_argoproj_argocd_v2.8.5.html} (57%) create mode 100644 docs/snyk/v2.8.5/redis_7.0.11-alpine.html create mode 100644 docs/snyk/v2.9.0-rc3/argocd-iac-install.html create mode 100644 docs/snyk/v2.9.0-rc3/argocd-iac-namespace-install.html rename docs/snyk/{v2.10.16 => v2.9.0-rc3}/argocd-test.html (76%) create mode 100644 docs/snyk/v2.9.0-rc3/ghcr.io_dexidp_dex_v2.37.0.html create mode 100644 docs/snyk/v2.9.0-rc3/haproxy_2.6.14-alpine.html create mode 100644 docs/snyk/v2.9.0-rc3/quay.io_argoproj_argocd_v2.9.0-rc3.html create mode 100644 docs/snyk/v2.9.0-rc3/redis_7.0.11-alpine.html delete mode 100644 docs/user-guide/commands/argocd_app_add-source.md delete mode 100644 docs/user-guide/commands/argocd_app_remove-source.md delete mode 100644 docs/user-guide/commands/argocd_appset_generate.md delete mode 100644 docs/user-guide/commands/argocd_proj_add-destination-service-account.md delete mode 100644 docs/user-guide/commands/argocd_proj_add-source-namespace.md delete mode 100644 docs/user-guide/commands/argocd_proj_remove-destination-service-account.md delete mode 100644 docs/user-guide/commands/argocd_proj_remove-source-namespace.md delete mode 100755 hack/generate-actions-list.sh delete mode 100755 hack/generate-mock.sh delete mode 100755 hack/installers/checksums/add-protoc-checksums.sh delete mode 100644 hack/installers/checksums/helm-v3.14.2-darwin-amd64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/helm-v3.14.2-darwin-arm64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/helm-v3.15.2-darwin-amd64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/helm-v3.15.2-darwin-arm64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/helm-v3.15.2-linux-amd64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/helm-v3.15.2-linux-arm64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/helm-v3.15.2-linux-ppc64le.tar.gz.sha256 delete mode 100644 hack/installers/checksums/helm-v3.15.2-linux-s390x.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.2_darwin_amd64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.2_darwin_arm64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.2_linux_amd64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.2_linux_arm64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.2_linux_ppc64le.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.2_linux_s390x.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.3_darwin_amd64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.3_darwin_arm64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.3_linux_amd64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.3_linux_arm64.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.3_linux_ppc64le.tar.gz.sha256 delete mode 100644 hack/installers/checksums/kustomize_5.4.3_linux_s390x.tar.gz.sha256 delete mode 100644 hack/installers/checksums/protoc-27.2-linux-aarch_64.zip.sha256 delete mode 100644 hack/installers/checksums/protoc-27.2-linux-ppcle_64.zip.sha256 delete mode 100644 hack/installers/checksums/protoc-27.2-linux-s390_64.zip.sha256 delete mode 100644 hack/installers/checksums/protoc-27.2-linux-x86_64.zip.sha256 delete mode 100644 hack/installers/checksums/protoc-27.2-osx-aarch_64.zip.sha256 delete mode 100644 hack/installers/checksums/protoc-27.2-osx-x86_64.zip.sha256 rename hack/installers/{install-helm.sh => install-helm-linux.sh} (63%) delete mode 100755 hack/start-redis-with-password.sh delete mode 100644 manifests/base/application-controller-deployment/argocd-application-controller-statefulset.yaml delete mode 100644 manifests/base/application-controller-roles/kustomization.yaml rename manifests/base/{application-controller-roles => application-controller}/argocd-application-controller-role.yaml (100%) rename manifests/base/{application-controller-roles => application-controller}/argocd-application-controller-rolebinding.yaml (100%) rename manifests/base/{application-controller-roles => application-controller}/argocd-application-controller-sa.yaml (100%) delete mode 100644 reposerver/metrics/githandlers_test.go delete mode 100644 reposerver/repository/testdata/helm-with-local-dependency/Chart.yaml delete mode 100644 reposerver/repository/testdata/my-chart/test-file-param.yaml delete mode 100644 reposerver/repository/testdata/simple-chart/Chart.yaml delete mode 100644 reposerver/repository/testdata/simple-chart/simple-chart-values.yaml delete mode 100644 reposerver/repository/testdata/simple-chart/templates/my-map.yaml delete mode 100644 reposerver/repository/utils.go delete mode 100644 reposerver/repository/utils_test.go delete mode 100644 resource_customizations/apps.kruise.io/DaemonSet/testdata/no-update-strategy-partition.yaml delete mode 100644 resource_customizations/astra.netapp.io/AppVault/health.lua delete mode 100644 resource_customizations/astra.netapp.io/AppVault/health_test.yaml delete mode 100644 resource_customizations/astra.netapp.io/AppVault/testdata/degraded.yaml delete mode 100644 resource_customizations/astra.netapp.io/AppVault/testdata/healthy.yaml delete mode 100644 resource_customizations/astra.netapp.io/AppVault/testdata/progressing_nostatus.yaml delete mode 100644 resource_customizations/astra.netapp.io/Application/health.lua delete mode 100644 resource_customizations/astra.netapp.io/Application/health_test.yaml delete mode 100644 resource_customizations/astra.netapp.io/Application/testdata/degraded.yaml delete mode 100644 resource_customizations/astra.netapp.io/Application/testdata/healthy.yaml delete mode 100644 resource_customizations/astra.netapp.io/Application/testdata/progressing.yaml delete mode 100644 resource_customizations/astra.netapp.io/Backup/health.lua delete mode 100644 resource_customizations/astra.netapp.io/Backup/health_test.yaml delete mode 100644 resource_customizations/astra.netapp.io/Backup/testdata/degraded.yaml delete mode 100644 resource_customizations/astra.netapp.io/Backup/testdata/healthy.yaml delete mode 100644 resource_customizations/astra.netapp.io/Backup/testdata/progressing_nostatus.yaml delete mode 100644 resource_customizations/astra.netapp.io/Backup/testdata/progressing_status.yaml delete mode 100644 resource_customizations/astra.netapp.io/ExecHook/health.lua delete mode 100644 resource_customizations/astra.netapp.io/ExecHook/health_test.yaml delete mode 100644 resource_customizations/astra.netapp.io/ExecHook/testdata/healthy.yaml delete mode 100644 resource_customizations/astra.netapp.io/ExecHook/testdata/progressing_nostatus.yaml delete mode 100644 resource_customizations/astra.netapp.io/ExecHook/testdata/suspended.yaml delete mode 100644 resource_customizations/astra.netapp.io/ExecHooksRun/health.lua delete mode 100644 resource_customizations/astra.netapp.io/ExecHooksRun/health_test.yaml delete mode 100644 resource_customizations/astra.netapp.io/ExecHooksRun/testdata/degraded.yaml delete mode 100644 resource_customizations/astra.netapp.io/ExecHooksRun/testdata/healthy.yaml delete mode 100644 resource_customizations/astra.netapp.io/ExecHooksRun/testdata/progressing_nostatus.yaml delete mode 100644 resource_customizations/astra.netapp.io/ExecHooksRun/testdata/progressing_status.yaml delete mode 100644 resource_customizations/astra.netapp.io/ResourceBackup/health.lua delete mode 100644 resource_customizations/astra.netapp.io/ResourceBackup/health_test.yaml delete mode 100644 resource_customizations/astra.netapp.io/ResourceBackup/testdata/degraded.yaml delete mode 100644 resource_customizations/astra.netapp.io/ResourceBackup/testdata/healthy.yaml delete mode 100644 resource_customizations/astra.netapp.io/ResourceBackup/testdata/progressing_nostatus.yaml delete mode 100644 resource_customizations/astra.netapp.io/ResourceBackup/testdata/progressing_status.yaml delete mode 100644 resource_customizations/astra.netapp.io/ResticVolumeBackup/health.lua delete mode 100644 resource_customizations/astra.netapp.io/ResticVolumeBackup/health_test.yaml delete mode 100644 resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/degraded.yaml delete mode 100644 resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/healthy.yaml delete mode 100644 resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/progressing_nostatus.yaml delete mode 100644 resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/progressing_status.yaml delete mode 100644 resource_customizations/astra.netapp.io/Schedule/health.lua delete mode 100644 resource_customizations/astra.netapp.io/Schedule/health_test.yaml delete mode 100644 resource_customizations/astra.netapp.io/Schedule/testdata/healthy_nostatus.yaml delete mode 100644 resource_customizations/astra.netapp.io/Schedule/testdata/healthy_status.yaml delete mode 100644 resource_customizations/astra.netapp.io/Snapshot/health.lua delete mode 100644 resource_customizations/astra.netapp.io/Snapshot/health_test.yaml delete mode 100644 resource_customizations/astra.netapp.io/Snapshot/testdata/degraded.yaml delete mode 100644 resource_customizations/astra.netapp.io/Snapshot/testdata/healthy.yaml delete mode 100644 resource_customizations/astra.netapp.io/Snapshot/testdata/progressing_nostatus.yaml delete mode 100644 resource_customizations/astra.netapp.io/Snapshot/testdata/progressing_status.yaml delete mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/health.lua delete mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/health_test.yaml delete mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/invalid.yaml delete mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/progressing.yaml delete mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_green.yaml delete mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_red.yaml delete mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow.yaml delete mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow_single_node.yaml delete mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/unknown.yaml delete mode 100644 resource_customizations/camel.apache.org/Integration/health.lua delete mode 100644 resource_customizations/camel.apache.org/Integration/health_test.yaml delete mode 100644 resource_customizations/camel.apache.org/Integration/testdata/degraded.yaml delete mode 100644 resource_customizations/camel.apache.org/Integration/testdata/healthy.yaml delete mode 100644 resource_customizations/camel.apache.org/Integration/testdata/progressing.yaml delete mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/health.lua delete mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/health_test.yaml delete mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/degraded_reconcileError.yaml delete mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/healthy.yaml delete mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing.yaml delete mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_creating.yaml delete mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noStatus.yaml delete mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noavailable.yaml delete mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/suspended.yaml delete mode 100644 resource_customizations/cluster.x-k8s.io/MachinePool/health.lua delete mode 100644 resource_customizations/cluster.x-k8s.io/MachinePool/health_test.yaml delete mode 100644 resource_customizations/cluster.x-k8s.io/MachinePool/testdata/degraded_failed.yaml delete mode 100644 resource_customizations/cluster.x-k8s.io/MachinePool/testdata/healthy_provisioned.yaml delete mode 100644 resource_customizations/cluster.x-k8s.io/MachinePool/testdata/progressing_provisioning.yaml delete mode 100644 resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/health.lua delete mode 100644 resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/health_test.yaml delete mode 100644 resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/degraded.yaml delete mode 100644 resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/healthy.yaml delete mode 100644 resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/progressing_ready_false.yaml delete mode 100644 resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/progressing_ready_true.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAction/health.lua delete mode 100644 resource_customizations/core.humio.com/HumioAction/health_test.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAction/testdata/configerror.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAction/testdata/healthy.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAction/testdata/notfound.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAction/testdata/progressing.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAction/testdata/unknown.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAlert/health.lua delete mode 100644 resource_customizations/core.humio.com/HumioAlert/health_test.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAlert/testdata/configerror.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAlert/testdata/healthy.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAlert/testdata/notfound.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAlert/testdata/progressing.yaml delete mode 100644 resource_customizations/core.humio.com/HumioAlert/testdata/unknown.yaml delete mode 100644 resource_customizations/core.humio.com/HumioCluster/health.lua delete mode 100644 resource_customizations/core.humio.com/HumioCluster/health_test.yaml delete mode 100644 resource_customizations/core.humio.com/HumioCluster/testdata/configerror.yaml delete mode 100644 resource_customizations/core.humio.com/HumioCluster/testdata/configerror_custom.yaml delete mode 100644 resource_customizations/core.humio.com/HumioCluster/testdata/healthy.yaml delete mode 100644 resource_customizations/core.humio.com/HumioCluster/testdata/pending.yaml delete mode 100644 resource_customizations/core.humio.com/HumioCluster/testdata/progressing.yaml delete mode 100644 resource_customizations/core.humio.com/HumioCluster/testdata/restarting.yaml delete mode 100644 resource_customizations/core.humio.com/HumioCluster/testdata/unknown.yaml delete mode 100644 resource_customizations/core.humio.com/HumioCluster/testdata/upgrading.yaml delete mode 100644 resource_customizations/core.humio.com/HumioIngestToken/health.lua delete mode 100644 resource_customizations/core.humio.com/HumioIngestToken/health_test.yaml delete mode 100644 resource_customizations/core.humio.com/HumioIngestToken/testdata/configerror.yaml delete mode 100644 resource_customizations/core.humio.com/HumioIngestToken/testdata/healthy.yaml delete mode 100644 resource_customizations/core.humio.com/HumioIngestToken/testdata/notfound.yaml delete mode 100644 resource_customizations/core.humio.com/HumioIngestToken/testdata/progressing.yaml delete mode 100644 resource_customizations/core.humio.com/HumioIngestToken/testdata/unknown.yaml delete mode 100644 resource_customizations/core.humio.com/HumioParser/health.lua delete mode 100644 resource_customizations/core.humio.com/HumioParser/health_test.yaml delete mode 100644 resource_customizations/core.humio.com/HumioParser/testdata/configerror.yaml delete mode 100644 resource_customizations/core.humio.com/HumioParser/testdata/healthy.yaml delete mode 100644 resource_customizations/core.humio.com/HumioParser/testdata/notfound.yaml delete mode 100644 resource_customizations/core.humio.com/HumioParser/testdata/progressing.yaml delete mode 100644 resource_customizations/core.humio.com/HumioParser/testdata/unknown.yaml delete mode 100644 resource_customizations/core.humio.com/HumioRepository/health.lua delete mode 100644 resource_customizations/core.humio.com/HumioRepository/health_test.yaml delete mode 100644 resource_customizations/core.humio.com/HumioRepository/testdata/configerror.yaml delete mode 100644 resource_customizations/core.humio.com/HumioRepository/testdata/healthy.yaml delete mode 100644 resource_customizations/core.humio.com/HumioRepository/testdata/notfound.yaml delete mode 100644 resource_customizations/core.humio.com/HumioRepository/testdata/progressing.yaml delete mode 100644 resource_customizations/core.humio.com/HumioRepository/testdata/unknown.yaml delete mode 100644 resource_customizations/core.humio.com/HumioView/health.lua delete mode 100644 resource_customizations/core.humio.com/HumioView/health_test.yaml delete mode 100644 resource_customizations/core.humio.com/HumioView/testdata/configerror.yaml delete mode 100644 resource_customizations/core.humio.com/HumioView/testdata/healthy.yaml delete mode 100644 resource_customizations/core.humio.com/HumioView/testdata/notfound.yaml delete mode 100644 resource_customizations/core.humio.com/HumioView/testdata/progressing.yaml delete mode 100644 resource_customizations/core.humio.com/HumioView/testdata/unknown.yaml delete mode 100644 resource_customizations/gateway.solo.io/Gateway/health.lua delete mode 100644 resource_customizations/gateway.solo.io/Gateway/health_test.yaml delete mode 100644 resource_customizations/gateway.solo.io/Gateway/testdata/gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/Gateway/testdata/gloo-no-status.yaml delete mode 100644 resource_customizations/gateway.solo.io/Gateway/testdata/gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/Gateway/testdata/gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/Gateway/testdata/gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/health.lua delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/health_test.yaml delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-no-status.yaml delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/health.lua delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/health_test.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-no-status.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/health.lua delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/health_test.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-no-status.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/health.lua delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/health_test.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-no-status.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/health.lua delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/health_test.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-no-status.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-warning.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-accepted.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-pending.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-rejected.yaml delete mode 100644 resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-warning.yaml delete mode 100644 resource_customizations/gloo.solo.io/Proxy/health.lua delete mode 100644 resource_customizations/gloo.solo.io/Proxy/health_test.yaml delete mode 100644 resource_customizations/gloo.solo.io/Proxy/testdata/gloo-accepted.yaml delete mode 100644 resource_customizations/gloo.solo.io/Proxy/testdata/gloo-no-status.yaml delete mode 100644 resource_customizations/gloo.solo.io/Proxy/testdata/gloo-pending.yaml delete mode 100644 resource_customizations/gloo.solo.io/Proxy/testdata/gloo-rejected.yaml delete mode 100644 resource_customizations/gloo.solo.io/Proxy/testdata/gloo-warning.yaml delete mode 100644 resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-accepted.yaml delete mode 100644 resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-pending.yaml delete mode 100644 resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-rejected.yaml delete mode 100644 resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-warning.yaml delete mode 100644 resource_customizations/gloo.solo.io/Settings/health.lua delete mode 100644 resource_customizations/gloo.solo.io/Settings/health_test.yaml delete mode 100644 resource_customizations/gloo.solo.io/Settings/testdata/gloo-accepted.yaml delete mode 100644 resource_customizations/gloo.solo.io/Settings/testdata/gloo-no-status.yaml delete mode 100644 resource_customizations/gloo.solo.io/Settings/testdata/gloo-pending.yaml delete mode 100644 resource_customizations/gloo.solo.io/Settings/testdata/gloo-rejected.yaml delete mode 100644 resource_customizations/gloo.solo.io/Settings/testdata/gloo-warning.yaml delete mode 100644 resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-accepted.yaml delete mode 100644 resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-pending.yaml delete mode 100644 resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-rejected.yaml delete mode 100644 resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-warning.yaml delete mode 100644 resource_customizations/gloo.solo.io/Upstream/health.lua delete mode 100644 resource_customizations/gloo.solo.io/Upstream/health_test.yaml delete mode 100644 resource_customizations/gloo.solo.io/Upstream/testdata/gloo-accepted.yaml delete mode 100644 resource_customizations/gloo.solo.io/Upstream/testdata/gloo-no-status.yaml delete mode 100644 resource_customizations/gloo.solo.io/Upstream/testdata/gloo-pending.yaml delete mode 100644 resource_customizations/gloo.solo.io/Upstream/testdata/gloo-rejected.yaml delete mode 100644 resource_customizations/gloo.solo.io/Upstream/testdata/gloo-warning.yaml delete mode 100644 resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-accepted.yaml delete mode 100644 resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-pending.yaml delete mode 100644 resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-rejected.yaml delete mode 100644 resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-warning.yaml delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/health.lua delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/health_test.yaml delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-accepted.yaml delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-no-status.yaml delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-pending.yaml delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-rejected.yaml delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-warning.yaml delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-accepted.yaml delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-pending.yaml delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-rejected.yaml delete mode 100644 resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-warning.yaml delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/action_test.yaml delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/discovery.lua delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/reconcile/action.lua delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/resume/action.lua delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/suspend/action.lua delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/initial_helmrelease.yaml delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/reconciled_helmrelease.yaml delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/resumed_helmrelease.yaml delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/suspended_helmrelease.yaml delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/health.lua delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/health_test.yaml delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/degraded.yaml delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/healthy.yaml delete mode 100644 resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/progressing.yaml delete mode 100644 resource_customizations/iam.aws.crossplane.io/Policy/health.lua delete mode 100644 resource_customizations/iam.aws.crossplane.io/Policy/health_test.yaml delete mode 100644 resource_customizations/iam.aws.crossplane.io/Policy/testdata/ReconcileError.yaml delete mode 100644 resource_customizations/iam.aws.crossplane.io/Policy/testdata/healthy.yaml delete mode 100644 resource_customizations/iam.aws.crossplane.io/Role/health.lua delete mode 100644 resource_customizations/iam.aws.crossplane.io/Role/health_test.yaml delete mode 100644 resource_customizations/iam.aws.crossplane.io/Role/testdata/ReconcileError.yaml delete mode 100644 resource_customizations/iam.aws.crossplane.io/Role/testdata/healthy.yaml delete mode 100644 resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/health.lua delete mode 100644 resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/health_test.yaml delete mode 100644 resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/testdata/ReconcileError.yaml delete mode 100644 resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/testdata/healthy.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/health.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/health_test.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/degraded.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/healthy.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/progressing.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/action_test.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/discovery.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/reconcile/action.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/resume/action.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/suspend/action.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/initial_imagerepository.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/reconciled_imagerepository.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/resumed_imagerepository.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/suspended_imagerepository.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/health.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/health_test.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/degraded.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/healthy.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/progressing.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/action_test.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/discovery.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/reconcile/action.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/resume/action.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/suspend/action.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/initial_imageupdateautomation.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/reconciled_imageupdateautomation.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/resumed_imageupdateautomation.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/suspended_imageupdateautomation.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/health.lua delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/health_test.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/degraded.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/healthy.yaml delete mode 100644 resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/progressing.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/Backup/health.lua delete mode 100644 resource_customizations/k8s.mariadb.com/Backup/health_test.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/Backup/testdata/failed.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/Backup/testdata/ok.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/Database/health.lua delete mode 100644 resource_customizations/k8s.mariadb.com/Database/health_test.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/Database/testdata/database-ready.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/Grant/health.lua delete mode 100644 resource_customizations/k8s.mariadb.com/Grant/health_test.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/Grant/testdata/grant-ready.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/MariaDB/health.lua delete mode 100644 resource_customizations/k8s.mariadb.com/MariaDB/health_test.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/MariaDB/testdata/mariadb_error.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/MariaDB/testdata/no_status.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/MariaDB/testdata/restore_complete.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/MariaDB/testdata/restore_not_complete.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/MariaDB/testdata/statefulset_not_ready.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/MariaDB/testdata/statefulset_ready.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/SqlJob/health.lua delete mode 100644 resource_customizations/k8s.mariadb.com/SqlJob/health_test.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/SqlJob/testdata/sqljobs-failed.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/SqlJob/testdata/sqljobs-ok.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/User/health.lua delete mode 100644 resource_customizations/k8s.mariadb.com/User/health_test.yaml delete mode 100644 resource_customizations/k8s.mariadb.com/User/testdata/user-created.yaml delete mode 100644 resource_customizations/kafka.banzaicloud.io/KafkaCluster/testdata/rollingUpgrade.yaml delete mode 100644 resource_customizations/kafka.strimzi.io/KafkaBridge/health.lua delete mode 100644 resource_customizations/kafka.strimzi.io/KafkaBridge/health_test.yaml delete mode 100644 resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/degraded.yaml delete mode 100644 resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/healthy.yaml delete mode 100644 resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/progressing_noStatus.yaml delete mode 100644 resource_customizations/kafka.strimzi.io/KafkaConnector/health.lua delete mode 100644 resource_customizations/kafka.strimzi.io/KafkaConnector/health_test.yaml delete mode 100644 resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/degraded.yaml delete mode 100644 resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/healthy.yaml delete mode 100644 resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/progressing_noStatus.yaml delete mode 100644 resource_customizations/keda.sh/ScaledObject/health.lua delete mode 100644 resource_customizations/keda.sh/ScaledObject/health_test.yaml delete mode 100644 resource_customizations/keda.sh/ScaledObject/testdata/keda-degraded-1.yaml delete mode 100644 resource_customizations/keda.sh/ScaledObject/testdata/keda-degraded.yaml delete mode 100644 resource_customizations/keda.sh/ScaledObject/testdata/keda-healthy.yaml delete mode 100644 resource_customizations/keda.sh/ScaledObject/testdata/keda-progressing.yaml delete mode 100644 resource_customizations/keda.sh/ScaledObject/testdata/keda-suspended.yaml delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/action_test.yaml delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/discovery.lua delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/reconcile/action.lua delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/resume/action.lua delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/suspend/action.lua delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/initial_kustomization.yaml delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/reconciled_kustomization.yaml delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/resumed_kustomization.yaml delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/suspended_kustomization.yaml delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/health.lua delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/health_test.yaml delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/degraded.yaml delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/healthy.yaml delete mode 100644 resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/progressing.yaml delete mode 100644 resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/health.lua delete mode 100644 resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/health_test.yaml delete mode 100644 resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/degraded.yaml delete mode 100644 resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/healthy.yaml delete mode 100644 resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/progressing.yaml delete mode 100644 resource_customizations/metrics.keptn.sh/Analysis/health.lua delete mode 100644 resource_customizations/metrics.keptn.sh/Analysis/health_test.yaml delete mode 100644 resource_customizations/metrics.keptn.sh/Analysis/testdata/degraded.yaml delete mode 100644 resource_customizations/metrics.keptn.sh/Analysis/testdata/healthy_pass.yaml delete mode 100644 resource_customizations/metrics.keptn.sh/Analysis/testdata/healthy_warning.yaml delete mode 100644 resource_customizations/metrics.keptn.sh/Analysis/testdata/progressing.yaml delete mode 100644 resource_customizations/metrics.keptn.sh/KeptnMetric/health.lua delete mode 100644 resource_customizations/metrics.keptn.sh/KeptnMetric/health_test.yaml delete mode 100644 resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/degraded.yaml delete mode 100644 resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/healthy.yaml delete mode 100644 resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/healthy_empty_error.yaml delete mode 100644 resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/progressing.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/action_test.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/discovery.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/resume/action.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/suspend/action.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/initial_alert.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/resumed_alert.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/suspended_alert.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/action_test.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/discovery.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/resume/action.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/suspend/action.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/initial_provider.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/resumed_provider.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/suspended_provider.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/action_test.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/discovery.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/reconcile/action.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/resume/action.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/suspend/action.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/initial_receiver.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/reconciled_receiver.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/resumed_receiver.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/suspended_receiver.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/health.lua delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/health_test.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/degraded.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/healthy.yaml delete mode 100644 resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/progressing.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/ISBServiceRollout/health.lua delete mode 100644 resource_customizations/numaplane.numaproj.io/ISBServiceRollout/health_test.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/degraded.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/healthy.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing-nostatus.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing-reason.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/MonoVertexRollout/health.lua delete mode 100644 resource_customizations/numaplane.numaproj.io/MonoVertexRollout/health_test.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/degraded.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/healthy.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/progressing-reason.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/progressing.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/health.lua delete mode 100644 resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/health_test.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/degraded.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/healthy.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/progressing-reason.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/progressing.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/PipelineRollout/health.lua delete mode 100644 resource_customizations/numaplane.numaproj.io/PipelineRollout/health_test.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/degraded.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/healthy.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/paused.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/progressing-reason.yaml delete mode 100644 resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/progressing.yaml delete mode 100644 resource_customizations/openfaas.com/Function/health.lua delete mode 100644 resource_customizations/openfaas.com/Function/health_test.yaml delete mode 100644 resource_customizations/openfaas.com/Function/testdata/degraded_no_secret.yaml delete mode 100644 resource_customizations/openfaas.com/Function/testdata/healthy.yaml delete mode 100644 resource_customizations/openfaas.com/Function/testdata/progressing.yaml delete mode 100644 resource_customizations/openfaas.com/Function/testdata/suspended_zero_replicas.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/CertificatePolicy/health.lua delete mode 100644 resource_customizations/policy.open-cluster-management.io/CertificatePolicy/health_test.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/degraded.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/healthy.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/progressing_no_status.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/health.lua delete mode 100644 resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/health_test.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/degraded.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/healthy_created.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/healthy_found.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/progressing.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/progressing_no_status.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/OperatorPolicy/health.lua delete mode 100644 resource_customizations/policy.open-cluster-management.io/OperatorPolicy/health_test.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/degraded.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/healthy_no_generation.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/healthy_with_generation.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_no_compliance.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_no_status.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_old_generation.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/Policy/health.lua delete mode 100644 resource_customizations/policy.open-cluster-management.io/Policy/health_test.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/Policy/testdata/degraded_replicated.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/Policy/testdata/degraded_root.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/Policy/testdata/healthy_replicated.yaml delete mode 100644 resource_customizations/policy.open-cluster-management.io/Policy/testdata/healthy_root.yaml delete mode 100644 resource_customizations/policy/PodDisruptionBudget/health.lua delete mode 100644 resource_customizations/policy/PodDisruptionBudget/health_test.yaml delete mode 100644 resource_customizations/policy/PodDisruptionBudget/testdata/degraded.yaml delete mode 100644 resource_customizations/policy/PodDisruptionBudget/testdata/healthy.yaml delete mode 100644 resource_customizations/policy/PodDisruptionBudget/testdata/progressing.yaml delete mode 100644 resource_customizations/rabbitmq.com/RabbitmqCluster/health.lua delete mode 100644 resource_customizations/rabbitmq.com/RabbitmqCluster/health_test.yaml delete mode 100644 resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_badconfig.yaml delete mode 100644 resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_cluster_unknown.yaml delete mode 100644 resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_replicas_unknown.yaml delete mode 100644 resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/healthy.yaml delete mode 100644 resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_cluster_unavailable.yaml delete mode 100644 resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_no_status.yaml delete mode 100644 resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_pods_not_ready.yaml delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBCluster/health.lua delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBCluster/health_test.yaml delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/creating.yaml delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/degraded.yaml delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/healthy.yaml delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/suspended.yaml delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBInstance/health.lua delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBInstance/health_test.yaml delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/creating.yaml delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/degraded.yaml delete mode 100644 resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/healthy.yaml delete mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health.lua delete mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health_test.yaml delete mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/degraded_reconcileError.yaml delete mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/healthy.yaml delete mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_creating.yaml delete mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_noStatus.yaml delete mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/suspended_reconcilePaused.yaml delete mode 100644 resource_customizations/s3.aws.crossplane.io/Bucket/health.lua delete mode 100644 resource_customizations/s3.aws.crossplane.io/Bucket/health_test.yaml delete mode 100644 resource_customizations/s3.aws.crossplane.io/Bucket/testdata/ReconcileError.yaml delete mode 100644 resource_customizations/s3.aws.crossplane.io/Bucket/testdata/healthy.yaml delete mode 100644 resource_customizations/serving.kserve.io/InferenceService/testdata/healthy_raw.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/action_test.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/discovery.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/reconcile/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/resume/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/suspend/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/initial_bucket.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/reconciled_bucket.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/resumed_bucket.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/suspended_bucket.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/health.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/health_test.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/degraded.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/healthy.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/progressing.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/action_test.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/discovery.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/reconcile/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/resume/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/suspend/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/initial_gitrepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/reconciled_gitrepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/resumed_gitrepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/suspended_gitrepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/health.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/health_test.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/degraded.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/healthy.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/progressing.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/action_test.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/discovery.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/reconcile/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/resume/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/suspend/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/initial_helmchart.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/reconciled_helmchart.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/resumed_helmchart.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/suspended_helmchart.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/health.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/health_test.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/degraded.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/healthy.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/progressing.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/action_test.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/discovery.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/reconcile/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/resume/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/suspend/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/initial_helmrepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/reconciled_helmrepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/resumed_helmrepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/suspended_helmrepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/health.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/health_test.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/degraded.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/healthy.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/progressing.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/action_test.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/discovery.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/reconcile/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/resume/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/suspend/action.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/initial_ocirepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/reconciled_ocirepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/resumed_ocirepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/suspended_ocirepository.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/health.lua delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/health_test.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/degraded.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/healthy.yaml delete mode 100644 resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/progressing.yaml delete mode 100644 server/extension/mocks/ExtensionMetricsRegistry.go delete mode 100644 server/extension/mocks/UserGetter.go delete mode 100644 test/e2e/admin_test.go delete mode 100644 test/e2e/app_k8s_events_test.go delete mode 100644 test/e2e/fixture/admin/actions.go delete mode 100644 test/e2e/fixture/admin/consequences.go delete mode 100644 test/e2e/fixture/admin/context.go delete mode 100644 test/e2e/fixture/admin/fixture.go delete mode 100644 test/e2e/fixture/admin/utils/backup.go delete mode 100644 test/e2e/git_test.go delete mode 100644 test/e2e/sync_with_impersonate_test.go delete mode 100644 test/e2e/testdata/helm-api-versions/Chart.yaml delete mode 100644 test/e2e/testdata/helm-api-versions/templates/config-map.yaml delete mode 100644 test/e2e/testdata/helm-namespace/Chart.yaml delete mode 100644 test/e2e/testdata/helm-namespace/baz.yaml delete mode 100644 test/e2e/testdata/helm-namespace/templates/config-map.yaml delete mode 100644 test/e2e/testdata/helm-namespace/values.yaml delete mode 100644 test/e2e/testdata/kustomize-api-versions/helm-chart/Chart.yaml delete mode 100644 test/e2e/testdata/kustomize-api-versions/helm-chart/templates/config-map.yaml delete mode 100644 test/e2e/testdata/kustomize-api-versions/helm-chart/values.yaml delete mode 100644 test/e2e/testdata/kustomize-api-versions/kustomization.yaml delete mode 100644 test/e2e/testdata/kustomize-kube-version/helm-chart/Chart.yaml delete mode 100644 test/e2e/testdata/kustomize-kube-version/helm-chart/templates/config-map.yaml delete mode 100644 test/e2e/testdata/kustomize-kube-version/helm-chart/values.yaml delete mode 100644 test/e2e/testdata/kustomize-kube-version/kustomization.yaml delete mode 100644 test/e2e/testdata/syncwaves-prune-order/README.md delete mode 100644 test/e2e/testdata/syncwaves-prune-order/pod.yaml delete mode 100644 test/e2e/testdata/syncwaves-prune-order/rbac.yaml delete mode 100644 ui/eslint.config.mjs delete mode 100644 ui/src/app/applications/components/application-deployment-history/application-deployment-history-details.tsx delete mode 100644 ui/src/app/applications/components/application-deployment-history/initiated-by.tsx delete mode 100644 ui/src/app/applications/components/application-details/application-resource-list.scss delete mode 100644 ui/src/app/applications/components/application-parameters/application-parameters-source.tsx delete mode 100644 ui/src/app/applications/components/application-parameters/application-parameters.scss delete mode 100644 ui/src/app/applications/components/application-parameters/source-panel.scss delete mode 100644 ui/src/app/applications/components/application-parameters/source-panel.tsx delete mode 100644 ui/src/app/applications/components/application-summary/edit-notification-subscriptsions.test.ts delete mode 100644 ui/src/app/settings/components/clusters-list/cluster-list.scss delete mode 100644 ui/src/app/shared/components/editable-panel/editable-section.tsx create mode 100644 ui/tslint.json create mode 100644 util/helm/helmver.go delete mode 100644 util/kustomize/testdata/label_without_selector/deployment.yaml delete mode 100644 util/kustomize/testdata/label_without_selector/kustomization.yaml delete mode 100644 util/metrics/metrics.go delete mode 100644 util/metrics/metrics_test.go delete mode 100644 util/notification/argocd/mocks/Service.go delete mode 100644 util/profile/profile_test.go delete mode 100644 util/regex/regex.go diff --git a/.codecov.yml b/.codecov.yml index 0d91db029a28b..6700ed0f05e74 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -8,7 +8,6 @@ ignore: - "pkg/client/.*" - "vendor/.*" - "test/.*" -- "**/mocks/*" coverage: status: # we've found this not to be useful diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 3c4605c943bdb..0000000000000 --- a/.gitattributes +++ /dev/null @@ -1,13 +0,0 @@ -**/*.pb.go linguist-generated=true -**/mocks/*.go linguist-generated=true -assets/swagger.json linguist-generated=true -docs/operator-manual/resource_actions_builtin.md linguist-generated=true -docs/operator-manual/server-commands/argocd-*.md linguist-generated=true -docs/user-guide/commands/argocd_*.md linguist-generated=true -manifests/core-install.yaml linguist-generated=true -manifests/crds/*-crd.yaml linguist-generated=true -manifests/ha/install.yaml linguist-generated=true -manifests/ha/namespace-install.yaml linguist-generated=true -manifests/install.yaml linguist-generated=true -manifests/namespace-install.yaml linguist-generated=true -pkg/apis/api-rules/violation_exceptions.list linguist-generated=true diff --git a/.github/ISSUE_TEMPLATE/new_dev_tool.md b/.github/ISSUE_TEMPLATE/new_dev_tool.md deleted file mode 100644 index 6100922376b9d..0000000000000 --- a/.github/ISSUE_TEMPLATE/new_dev_tool.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -name: New Dev Tool Request -about: This is a request for adding a new tool for setting up a dev environment. -title: '' -labels: '' -assignees: '' ---- - -Checklist: - -* [ ] I am willing to maintain this tool, or have another Argo CD maintainer who is. -* [ ] I have another Argo CD maintainer who is willing to help maintain this tool (there needs to be at least two maintainers willing to maintain this tool) -* [ ] I have a lead sponsor who is a core Argo CD maintainer -* [ ] There is a PR which adds said tool - this is so that the maintainers can assess the impact of having this in the tree -* [ ] I have given a motivation why this should be added - -### The proposer - -<-- The username(s) of the person(s) proposing the tool --> - -### The proposed tool - - - -### Motivation - - - -### Link to PR (Optional) - - - -### Lead Sponsor(s) - -Final approval requires sponsorship from at least one core maintainer. - -- @ - -### Co-sponsors - -These will be the co-maintainers of the specified tool. - -- @ diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6205c1098d1f9..5540fb7fd93e6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,13 +4,8 @@ updates: directory: "/" schedule: interval: "daily" - open-pull-requests-limit: 20 ignore: - dependency-name: k8s.io/* - groups: - otel: - patterns: - - "^go.opentelemetry.io/.*" - package-ecosystem: "github-actions" directory: "/" @@ -22,11 +17,6 @@ updates: schedule: interval: "daily" - - package-ecosystem: "npm" - directory: "/ui-test/" - schedule: - interval: "daily" - - package-ecosystem: "docker" directory: "/" schedule: diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 2e876c1095f73..6d4302d2b540c 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -6,10 +6,9 @@ | 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 | -| init-release.yaml | Build manifests and version then create a PR for release branch| | 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 | -| scorecard.yaml | Generate scorecard for supply-chain security | | update-snyk.yaml | Scheduled snyk reports | # Reusable workflows diff --git a/.github/workflows/ci-build.yaml b/.github/workflows/ci-build.yaml index 5fe31bf6ca9f9..41b39659a5aed 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' @@ -13,7 +13,7 @@ on: env: # Golang version to use across CI steps - GOLANG_VERSION: '1.22' + GOLANG_VERSION: '1.21' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -23,65 +23,36 @@ permissions: contents: read jobs: - changes: - runs-on: ubuntu-latest - outputs: - backend: ${{ steps.filter.outputs.backend_any_changed }} - frontend: ${{ steps.filter.outputs.frontend_any_changed }} - docs: ${{ steps.filter.outputs.docs_any_changed }} - steps: - - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - - uses: tj-actions/changed-files@48d8f15b2aaa3d255ca5af3eba4870f807ce6b3c # v45.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 - docs: - - 'docs/**' 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@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: Setup Golang - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + 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 tidiness of go.mod and go.sum + - name: Check for tidyness 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@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: Setup Golang - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Restore go build cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ~/.cache/go-build key: ${{ runner.os }}-go-build-v1-${{ github.run_id }} @@ -96,42 +67,37 @@ 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@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: Setup Golang - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Run golangci-lint - uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0 + uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 with: - version: v1.58.2 - args: --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@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # 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@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Install required packages @@ -151,7 +117,7 @@ jobs: run: | echo "/usr/local/bin" >> $GITHUB_PATH - name: Restore go build cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ~/.cache/go-build key: ${{ runner.os }}-go-build-v1-${{ github.run_id }} @@ -171,31 +137,34 @@ jobs: go mod download - name: Run all unit tests run: make test-local + - name: Generate code coverage artifacts + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + with: + name: code-coverage + path: coverage.out - name: Generate test results artifacts - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: test-results - path: test-results + path: test-results/ test-go-race: 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@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # 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@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Install required packages @@ -215,7 +184,7 @@ jobs: run: | echo "/usr/local/bin" >> $GITHUB_PATH - name: Restore go build cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ~/.cache/go-build key: ${{ runner.os }}-go-build-v1-${{ github.run_id }} @@ -236,22 +205,19 @@ jobs: - name: Run all unit tests run: make test-race-local - name: Generate test results artifacts - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + 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' || needs.changes.outputs.docs == 'true'}} runs-on: ubuntu-22.04 - needs: - - changes steps: - name: Checkout code - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: Setup Golang - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Create symlink in GOPATH @@ -294,21 +260,17 @@ jobs: build-ui: name: Build, test & lint UI code - # We run UI logic for backend changes so that we have a complete set of coverage documents to send to codecov. - if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }} runs-on: ubuntu-22.04 - needs: - - changes steps: - name: Checkout code - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: Setup NodeJS - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 + uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1 with: - node-version: '21.6.1' + node-version: '20.7.0' - name: Restore node dependency cache id: cache-dependencies - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ui/node_modules key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }} @@ -323,8 +285,6 @@ jobs: NODE_ENV: production NODE_ONLINE_ENV: online HOST_ARCH: amd64 - # If we're on the master branch, set the codecov token so that we upload bundle analysis - CODECOV_TOKEN: ${{ github.ref == 'refs/heads/master' && secrets.CODECOV_TOKEN || '' }} working-directory: ui/ - name: Run ESLint run: yarn lint @@ -332,86 +292,79 @@ 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 - - test-e2e env: sonar_secret: ${{ secrets.SONAR_TOKEN }} steps: - name: Checkout code - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 with: fetch-depth: 0 - name: Restore node dependency cache id: cache-dependencies - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ui/node_modules key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }} - name: Remove other node_modules directory run: | rm -rf ui/node_modules/argo-ui/node_modules - - name: Get e2e code coverage - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + - name: Create test-results directory + run: | + mkdir -p test-results + - name: Get code coverage artifiact + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: e2e-code-coverage - path: e2e-code-coverage - - name: Get unit test code coverage - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + name: code-coverage + - name: Get test result artifact + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: test-results path: test-results - - name: combine-go-coverage - # We generate coverage reports for all Argo CD components, but only the applicationset-controller, - # app-controller, and repo-server report contain coverage data. The other components currently don't shut down - # gracefully, so no coverage data is produced. Once those components are fixed, we can add references to their - # coverage output directories. - run: | - go tool covdata percent -i=test-results,e2e-code-coverage/applicationset-controller,e2e-code-coverage/repo-server,e2e-code-coverage/app-controller -o test-results/full-coverage.out - name: Upload code coverage information to codecov.io - uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4 with: - file: test-results/full-coverage.out - fail_ci_if_error: true - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - name: Upload test results to Codecov - if: github.ref == 'refs/heads/master' && github.event_name == 'push' && github.repository == 'argoproj/argo-cd' - uses: codecov/test-results-action@1b5b448b98e58ba90d1a1a1d9fcb72ca2263be46 # v1.0.0 - with: - file: test-results/junit.xml - fail_ci_if_error: true - token: ${{ secrets.CODECOV_TOKEN }} + file: coverage.out - name: Perform static code analysis using SonarCloud env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - uses: SonarSource/sonarqube-scan-action@aecaf43ae57e412bd97d70ef9ce6076e672fe0a9 # v2.2 + SCANNER_VERSION: 4.2.0.1873 + 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 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.30.2 - # We designate the latest version because we only collect code coverage for that version. - latest: true - - version: v1.29.6 - latest: false - - version: v1.28.11 - latest: false - - version: v1.27.15 - latest: false - needs: + k3s-version: [v1.28.2, v1.27.6, v1.26.9, v1.25.14] + needs: - build-go - - changes env: GOPATH: /home/runner/go ARGOCD_FAKE_IN_CLUSTER: "true" @@ -424,12 +377,12 @@ jobs: 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@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: Setup Golang - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: GH actions workaround - Kill XSP4 process @@ -437,7 +390,7 @@ jobs: sudo pkill mono || true - name: Install K3S env: - INSTALL_K3S_VERSION: ${{ matrix.k3s.version }}+k3s1 + INSTALL_K3S_VERSION: ${{ matrix.k3s-version }}+k3s1 run: | set -x curl -sfL https://get.k3s.io | sh - @@ -448,7 +401,7 @@ jobs: sudo chmod go-r $HOME/.kube/config kubectl version - name: Restore go build cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ~/.cache/go-build key: ${{ runner.os }}-go-build-v1-${{ github.run_id }} @@ -474,7 +427,7 @@ 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.41.1 + docker pull ghcr.io/dexidp/dex:v2.37.0 docker pull argoproj/argo-cd-ci-builder:v1.0.0 docker pull redis:7.0.15-alpine - name: Create target directory for binaries in the build-process @@ -489,7 +442,7 @@ jobs: # port 8080 which is not visible in netstat -tulpen, but still there # with a HTTP listener. We have API server listening on port 8088 # instead. - make start-e2e-local COVERAGE_ENABLED=true 2>&1 | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g" > /tmp/e2e-server.log & + make start-e2e-local 2>&1 | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g" > /tmp/e2e-server.log & count=1 until curl -f http://127.0.0.1:8088/healthz; do sleep 10; @@ -503,40 +456,9 @@ jobs: run: | set -x make test-e2e-local - goreman run stop-all || echo "goreman trouble" - sleep 30 - - name: Upload e2e coverage report - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 - with: - name: e2e-code-coverage - path: /tmp/coverage - if: ${{ matrix.k3s.latest }} - name: Upload e2e-server logs - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: - name: e2e-server-k8s${{ matrix.k3s.version }}.log + 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 530340127708c..2311d43925bb7 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,23 +23,23 @@ jobs: actions: read # for github/codeql-action/init to get workflow details contents: read # for actions/checkout to fetch code security-events: write # for github/codeql-action/autobuild to send a status report - if: github.repository == 'argoproj/argo-cd' || vars.enable_codeql + if: github.repository == 'argoproj/argo-cd' # CodeQL runs on ubuntu-latest and windows-latest runs-on: ubuntu-22.04 steps: - name: Checkout repository - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 # Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087 - name: Setup Golang - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version-file: go.mod # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2 + uses: github/codeql-action/init@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java @@ -47,7 +47,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2 + uses: github/codeql-action/autobuild@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -61,4 +61,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2 + uses: github/codeql-action/analyze@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33 diff --git a/.github/workflows/image-reuse.yaml b/.github/workflows/image-reuse.yaml index f4b7a851816a8..9cdfbc181d766 100644 --- a/.github/workflows/image-reuse.yaml +++ b/.github/workflows/image-reuse.yaml @@ -58,26 +58,26 @@ jobs: image-digest: ${{ steps.image.outputs.digest }} steps: - name: Checkout code - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # 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 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 if: ${{ github.ref_type != 'tag'}} - name: Setup Golang - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version: ${{ inputs.go-version }} - name: Install cosign - uses: sigstore/cosign-installer@4959ce089c160fddf62f7b42464195ba1a56d382 # v3.6.0 + uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0 - - uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 - - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + - 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: | @@ -104,7 +104,7 @@ jobs: echo 'EOF' >> $GITHUB_ENV - name: Login to Quay.io - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0 with: registry: quay.io username: ${{ secrets.quay_username }} @@ -112,7 +112,7 @@ jobs: if: ${{ inputs.quay_image_name && inputs.push }} - name: Login to GitHub Container Registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0 with: registry: ghcr.io username: ${{ secrets.ghcr_username }} @@ -120,7 +120,7 @@ jobs: if: ${{ inputs.ghcr_image_name && inputs.push }} - name: Login to dockerhub Container Registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0 with: username: ${{ secrets.docker_username }} password: ${{ secrets.docker_password }} @@ -134,7 +134,7 @@ jobs: 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@54081f138730dfa15788a46383842cd2f914a1be + uses: jlumbroso/free-disk-space@4d9e71b726748f254fe64fa44d273194bd18ec91 with: large-packages: false docker-images: false @@ -143,7 +143,7 @@ jobs: - name: Build and push container image id: image - uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 #v6.7.0 + uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 #v5.1.0 with: context: . platforms: ${{ inputs.platforms }} diff --git a/.github/workflows/image.yaml b/.github/workflows/image.yaml index 3102e8361aa06..57fe5f8ecc27c 100644 --- a/.github/workflows/image.yaml +++ b/.github/workflows/image.yaml @@ -25,7 +25,7 @@ jobs: image-tag: ${{ steps.image.outputs.tag}} platforms: ${{ steps.platforms.outputs.platforms }} steps: - - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: Set image tag for ghcr run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT @@ -52,7 +52,7 @@ jobs: 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.22 + go-version: 1.21 platforms: ${{ needs.set-vars.outputs.platforms }} push: false @@ -68,7 +68,7 @@ jobs: 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.22 + go-version: 1.21 platforms: ${{ needs.set-vars.outputs.platforms }} push: true secrets: @@ -86,7 +86,7 @@ jobs: 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@v2.0.0 + 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 }} @@ -104,7 +104,7 @@ jobs: if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }} runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments" env: TOKEN: ${{ secrets.TOKEN }} diff --git a/.github/workflows/init-release.yaml b/.github/workflows/init-release.yaml index 8353e8a67ae84..2cd8111bd87c1 100644 --- a/.github/workflows/init-release.yaml +++ b/.github/workflows/init-release.yaml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout code - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} @@ -64,7 +64,7 @@ jobs: git stash pop - name: Create pull request - uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5 + 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" diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml index 61c38548cf6ba..020535d7b8afa 100644 --- a/.github/workflows/pr-title-check.yml +++ b/.github/workflows/pr-title-check.yml @@ -23,7 +23,7 @@ jobs: name: Validate PR Title runs-on: ubuntu-latest steps: - - uses: thehanimo/pr-title-checker@1d8cd483a2b73118406a187f54dca8a9415f1375 # v1.4.2 + - uses: thehanimo/pr-title-checker@0cf5902181e78341bb97bb06646396e5bd354b3f # v1.4.0 with: 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 a30e44ec0ec7a..e391cb3250a99 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,7 +10,7 @@ on: permissions: {} env: - GOLANG_VERSION: '1.22' # Note: go-version must also be set in job argocd-image.with.go-version + GOLANG_VERSION: '1.21' # Note: go-version must also be set in job argocd-image.with.go-version jobs: argocd-image: @@ -23,7 +23,7 @@ jobs: 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.22 + go-version: 1.21 platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le push: true secrets: @@ -38,7 +38,7 @@ jobs: 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@v2.0.0 + 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 }} @@ -59,7 +59,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} @@ -77,7 +77,7 @@ jobs: fi - name: Setup Golang - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} @@ -88,7 +88,7 @@ jobs: 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@54081f138730dfa15788a46383842cd2f914a1be + uses: jlumbroso/free-disk-space@4d9e71b726748f254fe64fa44d273194bd18ec91 with: large-packages: false docker-images: false @@ -96,7 +96,7 @@ jobs: tool-cache: false - name: Run GoReleaser - uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0 + uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0 id: run-goreleaser with: version: latest @@ -128,7 +128,7 @@ jobs: 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@v2.0.0 + 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" @@ -147,13 +147,13 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout code - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Golang - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version: ${{ env.GOLANG_VERSION }} @@ -197,7 +197,7 @@ jobs: echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT" - name: Upload SBOM - uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -212,7 +212,7 @@ jobs: 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@v2.0.0 + 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" @@ -230,7 +230,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout code - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} @@ -291,11 +291,11 @@ jobs: # 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.COMMIT_HASH }}/" 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@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5 + uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2 with: commit-message: Bump version in master title: "chore: Bump version in master" diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 6975868f4a78a..e6abc5adc3c0c 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -30,12 +30,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0 with: results_file: results.sarif results_format: sarif @@ -54,7 +54,7 @@ jobs: # 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@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: SARIF file path: results.sarif @@ -62,6 +62,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2 + uses: github/codeql-action/upload-sarif@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2.2.1 with: sarif_file: results.sarif diff --git a/.github/workflows/update-snyk.yaml b/.github/workflows/update-snyk.yaml index b4d98134e84ad..62655b433d9e4 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@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} - name: Build reports diff --git a/.gitignore b/.gitignore index cc5a439491dbb..ab17deb0db139 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ node_modules/ ./test/cmp/*.sock .envrc.remote .*.swp -rerunreport.txt # ignore built binaries cmd/argocd/argocd diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index ec47f2553d19d..d105f49fde2b1 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -1,4 +1,4 @@ -FROM gitpod/workspace-full@sha256:230285e0b949e6d728d384b2029a4111db7b9c87c182f22f32a0be9e36b225df +FROM gitpod/workspace-full@sha256:511cecde4dc129ca9eb4cc4c479d61f95e5485ebe320a07f5b902f11899956a3 USER root diff --git a/.golangci.yaml b/.golangci.yaml deleted file mode 100644 index 2140cf08a7bd9..0000000000000 --- a/.golangci.yaml +++ /dev/null @@ -1,45 +0,0 @@ -issues: - exclude: - - SA5011 - max-issues-per-linter: 0 - max-same-issues: 0 - exclude-rules: - - path: '(.+)_test\.go' - linters: - - unparam -linters: - enable: - - errcheck - - errorlint - - gocritic - - gofumpt - - goimports - - gosimple - - govet - - ineffassign - - misspell - - staticcheck - - testifylint - - unparam - - unused - - whitespace -linters-settings: - gocritic: - disabled-checks: - - appendAssign - - assignOp # Keep it disabled for readability - - badCond - - commentFormatting - - exitAfterDefer - - ifElseChain - - mapKey - - singleCaseSwitch - - typeSwitchVar - goimports: - local-prefixes: github.com/argoproj/argo-cd/v2 - testifylint: - enable-all: true - disable: - - go-require -run: - timeout: 50m diff --git a/.goreleaser.yaml b/.goreleaser.yaml index ad23b852597fb..c156d37b19081 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,5 +1,3 @@ -version: 2 - project_name: argocd before: diff --git a/.mockery.yaml b/.mockery.yaml deleted file mode 100644 index 3a8b437ef347d..0000000000000 --- a/.mockery.yaml +++ /dev/null @@ -1,69 +0,0 @@ -# global config -filename: "{{.InterfaceName}}.go" -dir: "{{.InterfaceDir}}/mocks" -outpkg: "mocks" -mockname: "{{.InterfaceName}}" -with-expecter: false -# individual interface config -packages: - github.com/argoproj/argo-cd/v2/applicationset/generators: - interfaces: - Generator: - github.com/argoproj/argo-cd/v2/applicationset/services: - interfaces: - Repos: - github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider: - config: - dir: "applicationset/services/scm_provider/aws_codecommit/mocks" - interfaces: - AWSCodeCommitClient: - AWSTaggingClient: - github.com/microsoft/azure-devops-go-api/azuredevops/git: - config: - dir: "applicationset/services/scm_provider/azure_devops/git/mocks" - interfaces: - Client: - github.com/argoproj/argo-cd/v2/applicationset/utils: - interfaces: - Renderer: - github.com/argoproj/argo-cd/v2/controller/cache: - interfaces: - LiveStateCache: - github.com/argoproj/argo-cd/v2/reposerver/apiclient: - interfaces: - RepoServerServiceClient: - RepoServerService_GenerateManifestWithFilesClient: - github.com/argoproj/argo-cd/v2/server/application: - interfaces: - Broadcaster: - github.com/argoproj/argo-cd/v2/server/extension: - interfaces: - ApplicationGetter: - ExtensionMetricsRegistry: - ProjectGetter: - RbacEnforcer: - SettingsGetter: - UserGetter: - github.com/argoproj/argo-cd/v2/util/db: - interfaces: - ArgoDB: - github.com/argoproj/argo-cd/v2/util/git: - interfaces: - Client: - github.com/argoproj/argo-cd/v2/util/helm: - interfaces: - Client: - github.com/argoproj/argo-cd/v2/util/io: - interfaces: - TempPaths: - github.com/argoproj/argo-cd/v2/util/notification/argocd: - interfaces: - Service: - # These mocks are not currently used, but they are part of the public API of this package. - github.com/argoproj/argo-cd/v2/pkg/apiclient/session: - interfaces: - SessionServiceServer: - SessionServiceClient: - github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster: - interfaces: - ClusterServiceServer: diff --git a/.readthedocs.yaml b/.readthedocs.yml similarity index 100% rename from .readthedocs.yaml rename to .readthedocs.yml diff --git a/CODEOWNERS b/CODEOWNERS index 20ff6cd449af7..ec72eccbf416e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -2,13 +2,10 @@ ** @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 +/docs/** @argoproj/argocd-approvers @argoproj/argocd-approvers-docs +/USERS.md @argoproj/argocd-approvers @argoproj/argocd-approvers-docs +/mkdocs.yml @argoproj/argocd-approvers @argoproj/argocd-approvers-docs # CI -/.codecov.yml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci -/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci -/.goreleaser.yaml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci -/sonar-project.properties @argoproj/argocd-approvers @argoproj/argocd-approvers-ci +/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci +/.goreleaser.yaml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci diff --git a/Dockerfile b/Dockerfile index bc4e6debbfaa1..019d39ad5b30d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,10 @@ -ARG BASE_IMAGE=docker.io/library/ubuntu:24.04@sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15 +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.23.1@sha256:2fe82a3f3e006b4f2a316c6a21f62b66e1330ae211d039bb8d1128e12ed57bf1 AS builder +FROM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca AS builder RUN echo 'deb http://archive.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 && \ +RUN ./install.sh helm-linux && \ INSTALL_PATH=/usr/local/bin ./install.sh kustomize #################################################################################################### @@ -51,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 connect-proxy && \ + git git-lfs tini gpg tzdata && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* @@ -83,7 +83,7 @@ WORKDIR /home/argocd #################################################################################################### # Argo CD UI stage #################################################################################################### -FROM --platform=$BUILDPLATFORM docker.io/library/node:22.9.0@sha256:cbe2d5f94110cea9817dd8c5809d05df49b4bd1aac5203f3594d88665ad37988 AS argocd-ui +FROM --platform=$BUILDPLATFORM docker.io/library/node:20.6.1@sha256:14bd39208dbc0eb171cbfb26ccb9ac09fa1b2eba04ccd528ab5d12983fd9ee24 AS argocd-ui WORKDIR /src COPY ["ui/package.json", "ui/yarn.lock", "./"] @@ -101,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.23.1@sha256:2fe82a3f3e006b4f2a316c6a21f62b66e1330ae211d039bb8d1128e12ed57bf1 AS argocd-build +FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca AS argocd-build WORKDIR /go/src/github.com/argoproj/argo-cd diff --git a/Makefile b/Makefile index d6f8cdf62d5d8..f639e3498ae15 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,6 @@ 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 @@ -23,21 +22,14 @@ KUBECTL_VERSION=$(shell go list -m k8s.io/client-go | head -n 1 | rev | cut -d' 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 -# pointing to python 3.7 to match https://github.com/argoproj/argo-cd/blob/master/.readthedocs.yml -MKDOCS_DOCKER_IMAGE?=python:3.7-alpine +# 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 MKDOCS_RUN_ARGS?= # Configuration for building argocd-test-tools image @@ -84,7 +76,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) \ @@ -109,14 +101,13 @@ 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 \ @@ -131,14 +122,13 @@ 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 @@ -153,13 +143,6 @@ DEV_IMAGE?=false ARGOCD_GPG_ENABLED?=true ARGOCD_E2E_APISERVER_PORT?=8080 -ifeq (${COVERAGE_ENABLED}, true) -# We use this in the cli-local target to enable code coverage for e2e tests. -COVERAGE_FLAG=-cover -else -COVERAGE_FLAG= -endif - override LDFLAGS += \ -X ${PACKAGE}.version=${VERSION} \ -X ${PACKAGE}.buildDate=${BUILD_DATE} \ @@ -192,25 +175,29 @@ endif .PHONY: all all: cli image -.PHONY: mockgen -mockgen: - ./hack/generate-mock.sh +# We have some legacy requirements for being checked out within $GOPATH. +# The ensure-gopath target can be used as dependency to ensure we are running +# within these boundaries. +.PHONY: ensure-gopath +ensure-gopath: +ifneq ("$(PWD)","$(LEGACY_PATH)") + @echo "Due to legacy requirements for codegen, repository needs to be checked out within \$$GOPATH" + @echo "Location of this repo should be '$(LEGACY_PATH)' but is '$(PWD)'" + @exit 1 +endif .PHONY: gogen -gogen: +gogen: ensure-gopath export GO111MODULE=off - go generate ./... + go generate ./util/argo/... .PHONY: protogen -protogen: mod-vendor-local protogen-fast - -.PHONY: protogen-fast -protogen-fast: +protogen: ensure-gopath mod-vendor-local export GO111MODULE=off ./hack/generate-proto.sh .PHONY: openapigen -openapigen: +openapigen: ensure-gopath export GO111MODULE=off ./hack/update-openapi.sh @@ -225,25 +212,19 @@ notification-docs: .PHONY: clientgen -clientgen: +clientgen: ensure-gopath export GO111MODULE=off ./hack/update-codegen.sh .PHONY: clidocsgen -clidocsgen: +clidocsgen: ensure-gopath go run tools/cmd-docs/main.go -.PHONY: actionsdocsgen -actionsdocsgen: - hack/generate-actions-list.sh .PHONY: codegen-local -codegen-local: mod-vendor-local mockgen gogen protogen clientgen openapigen clidocsgen actionsdocsgen manifests-local notification-docs notification-catalog +codegen-local: ensure-gopath mod-vendor-local gogen protogen clientgen openapigen clidocsgen manifests-local notification-docs notification-catalog rm -rf vendor/ -.PHONY: codegen-local-fast -codegen-local-fast: mockgen 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) @@ -254,11 +235,11 @@ cli: test-tools-image .PHONY: cli-local cli-local: clean-debug - CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -gcflags="all=-N -l" $(COVERAGE_FLAG) -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd + CGO_ENABLED=0 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=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${GEN_RESOURCES_CLI_NAME} ./hack/gen-resources/cmd + CGO_ENABLED=0 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 @@ -273,8 +254,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 @@ -288,25 +269,25 @@ manifests: test-tools-image # consolidated binary for cli, util, server, repo-server, controller .PHONY: argocd-all argocd-all: clean-debug - CGO_ENABLED=${CGO_FLAG} GOOS=${GOOS} GOARCH=${GOARCH} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd + CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd .PHONY: server server: clean-debug - CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd + CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd .PHONY: repo-server repo-server: - CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd + CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd .PHONY: controller controller: - CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd + CGO_ENABLED=0 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 --platform=$(TARGET_ARCH) --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) @@ -315,29 +296,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=$(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 + DOCKER_BUILDKIT=1 docker build --platform=$(TARGET_ARCH) -t argocd-base --target argocd-base . + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd 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=$(TARGET_ARCH) -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) --platform=$(TARGET_ARCH) . + 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 @@ -371,7 +352,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 + 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 @@ -405,9 +386,9 @@ test: test-tools-image .PHONY: test-local test-local: if test "$(TEST_MODULE)" = ""; then \ - DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -args -test.gocoverdir="$(PWD)/test-results"; \ + DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -coverprofile=coverage.out; \ else \ - DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -args -test.gocoverdir="$(PWD)/test-results" "$(TEST_MODULE)"; \ + DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \ fi .PHONY: test-race @@ -419,9 +400,9 @@ test-race: test-tools-image .PHONY: test-race-local test-race-local: if test "$(TEST_MODULE)" = ""; then \ - DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -race -args -test.gocoverdir="$(PWD)/test-results"; \ + DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -race -coverprofile=coverage.out; \ else \ - DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -race -args -test.gocoverdir="$(PWD)/test-results"; \ + 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 @@ -435,7 +416,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 - 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 -args -test.gocoverdir="$(PWD)/test-results" + 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 @@ -448,7 +429,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) @@ -466,12 +447,6 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local mkdir -p /tmp/argo-e2e/app/config/gpg/keys && chmod 0700 /tmp/argo-e2e/app/config/gpg/keys mkdir -p /tmp/argo-e2e/app/config/gpg/source && chmod 0700 /tmp/argo-e2e/app/config/gpg/source mkdir -p /tmp/argo-e2e/app/config/plugin && chmod 0700 /tmp/argo-e2e/app/config/plugin - # create folders to hold go coverage results for each component - mkdir -p /tmp/coverage/app-controller - mkdir -p /tmp/coverage/api-server - mkdir -p /tmp/coverage/repo-server - mkdir -p /tmp/coverage/applicationset-controller - mkdir -p /tmp/coverage/notification # set paths for locally managed ssh known hosts and tls certs data ARGOCD_SSH_DATA_PATH=/tmp/argo-e2e/app/config/ssh \ ARGOCD_TLS_DATA_PATH=/tmp/argo-e2e/app/config/tls \ @@ -489,7 +464,6 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local 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} - ls -lrt /tmp/coverage # Cleans VSCode debug.test files from sub-dirs to prevent them from being included in by golang embed .PHONY: clean-debug @@ -502,7 +476,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 @@ -515,7 +489,6 @@ start-local: mod-vendor-local dep-ui-local cli-local mkdir -p /tmp/argocd-local mkdir -p /tmp/argocd-local/gpg/keys && chmod 0700 /tmp/argocd-local/gpg/keys mkdir -p /tmp/argocd-local/gpg/source - REDIS_PASSWORD=$(shell kubectl get secret argocd-redis -o jsonpath='{.data.auth}' | base64 -d) \ ARGOCD_ZJWT_FEATURE_FLAG=always \ ARGOCD_IN_CI=false \ ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \ @@ -553,7 +526,7 @@ build-docs-local: .PHONY: build-docs build-docs: - $(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install mkdocs; pip install $$(mkdocs get-deps); mkdocs build' + 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' .PHONY: serve-docs-local serve-docs-local: @@ -561,7 +534,8 @@ serve-docs-local: .PHONY: serve-docs serve-docs: - $(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install mkdocs; pip install $$(mkdocs get-deps); mkdocs serve -a $$(ip route get 1 | awk '\''{print $$7}'\''):8000' + 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 + # Verify that kubectl can connect to your K8s cluster from Docker .PHONY: verify-kube-connect @@ -584,7 +558,7 @@ 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 + ./hack/install.sh helm-linux ./hack/install.sh gotestsum # Installs all tools required for running codegen (Linux packages) @@ -613,7 +587,7 @@ list: .PHONY: applicationset-controller applicationset-controller: - GODEBUG="tarinsecurepath=0,zipinsecurepath=0" CGO_ENABLED=${CGO_FLAG} go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-applicationset-controller ./cmd + GODEBUG="tarinsecurepath=0,zipinsecurepath=0" CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-applicationset-controller ./cmd .PHONY: checksums checksums: diff --git a/OWNERS b/OWNERS index ca6588fd3d6c8..56e037e282a0a 100644 --- a/OWNERS +++ b/OWNERS @@ -1,6 +1,5 @@ owners: - alexmt -- crenshaw-dev - jessesuen approvers: diff --git a/Procfile b/Procfile index fd955a39ac416..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 "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/app-controller} 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 "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/api-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}" +controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "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: hack/start-redis-with-password.sh -repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/repo-server} 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}" +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 "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/applicationset-controller} 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 "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/notification} 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'}" \ No newline at end of file +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 b369043821010..ef5664de5b5b7 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,12 @@ [![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://scorecard.dev/viewer/?uri=github.com/argoproj/argo-cd) +[![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 @@ -56,7 +56,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://akuity.io/blog/secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argocd/) +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) @@ -85,5 +85,4 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h 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 index 74236df3b1a27..8ac4bc36b04ae 100644 --- a/SECURITY-INSIGHTS.yml +++ b/SECURITY-INSIGHTS.yml @@ -3,9 +3,9 @@ header: 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: 74a367d10e7110209610ba3ec225539ebe5f7522 + commit-hash: b71277c6beb949d0199d647a582bc25822b88838 project-url: https://github.com/argoproj/argo-cd - project-release: v2.14.0 + 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: diff --git a/USERS.md b/USERS.md index ab5dbc8c745c1..2c4bc85d2b220 100644 --- a/USERS.md +++ b/USERS.md @@ -11,7 +11,6 @@ Currently, the following organizations are **officially** using Argo CD: 1. [7shifts](https://www.7shifts.com/) 1. [Adevinta](https://www.adevinta.com/) 1. [Adfinis](https://adfinis.com) -1. [Adobe](https://www.adobe.com/) 1. [Adventure](https://jp.adventurekk.com/) 1. [Adyen](https://www.adyen.com) 1. [AirQo](https://airqo.net/) @@ -19,18 +18,13 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Albert Heijn](https://ah.nl/) 1. [Alibaba Group](https://www.alibabagroup.com/) 1. [Allianz Direct](https://www.allianzdirect.de/) -1. [AlphaSense](https://www.alpha-sense.com/) 1. [Amadeus IT Group](https://amadeus.com/) 1. [Ambassador Labs](https://www.getambassador.io/) -1. [Ancestry](https://www.ancestry.com/) -1. [Andgo Systems](https://www.andgosystems.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. [Augury](https://www.augury.com/) 1. [Autodesk](https://www.autodesk.com) 1. [Axians ACSP](https://www.axians.fr) 1. [Axual B.V.](https://axual.com) @@ -39,43 +33,35 @@ Currently, the following organizations are **officially** using Argo CD: 1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform) 1. [Beat](https://thebeat.co/en/) 1. [Beez Innovation Labs](https://www.beezlabs.com/) -1. [Bedag Informatik AG](https://www.bedag.ch/) 1. [Beleza Na Web](https://www.belezanaweb.com.br/) 1. [BigPanda](https://bigpanda.io) 1. [BioBox Analytics](https://biobox.io) 1. [BMW Group](https://www.bmwgroup.com/) 1. [Boozt](https://www.booztgroup.com/) -1. [Bosch](https://www.bosch.com/) 1. [Boticario](https://www.boticario.com.br/) -1. [Broker Consulting, a.s.](https://www.bcas.cz/en/) 1. [Bulder Bank](https://bulderbank.no) -1. [Cabify](https://cabify.com/en) 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 Europe](https://www.carfax.eu) 1. [CARFAX](https://www.carfax.com) +1. [CARFAX Europe](https://www.carfax.eu) 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. [Chainnodes](https://chainnodes.org) 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. [CloudScript](https://www.cloudscript.com.br/) -1. [CloudGeometry](https://www.cloudgeometry.io/) 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. [Cognizant](https://www.cognizant.com/) 1. [Commonbond](https://commonbond.co/) -1. [Contlo](https://contlo.com/) 1. [Coralogix](https://coralogix.com/) 1. [Crédit Agricole CIB](https://www.ca-cib.com) 1. [CROZ d.o.o.](https://croz.net/) @@ -108,18 +94,15 @@ Currently, the following organizations are **officially** using Argo CD: 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/) 1. [G DATA CyberDefense AG](https://www.gdata-software.com/) -1. [G-Research](https://www.gresearch.com/teams/open-source-software/) 1. [Garner](https://www.garnercorp.com) 1. [Generali Deutschland AG](https://www.generali.de/) 1. [Gepardec](https://gepardec.com/) -1. [Getir](https://getir.com) 1. [GetYourGuide](https://www.getyourguide.com/) 1. [Gitpod](https://www.gitpod.io) 1. [Gllue](https://gllue.com) @@ -129,14 +112,13 @@ Currently, the following organizations are **officially** using Argo CD: 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. [GoTo Financial](https://gotofinancial.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. [Hazelcast](https://hazelcast.com/) 1. [Healy](https://www.healyworld.net) 1. [Helio](https://helio.exchange) 1. [Hetki](https://hetki.ai) @@ -144,18 +126,14 @@ Currently, the following organizations are **officially** using Argo CD: 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. [IFS](https://www.ifs.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. [Instruqt](https://www.instruqt.com) 1. [Intuit](https://www.intuit.com/) 1. [Jellysmack](https://www.jellysmack.com) 1. [Joblift](https://joblift.com/) @@ -165,7 +143,6 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Karrot](https://www.daangn.com/) 1. [KarrotPay](https://www.daangnpay.com/) 1. [Kasa](https://kasa.co.kr/) -1. [Kave Home](https://kavehome.com) 1. [Keeeb](https://www.keeeb.com/) 1. [KelkooGroup](https://www.kelkoogroup.com) 1. [Keptn](https://keptn.sh) @@ -177,9 +154,6 @@ Currently, the following organizations are **officially** using Argo CD: 1. [KubeSphere](https://github.com/kubesphere) 1. [Kurly](https://www.kurly.com/) 1. [Kvist](https://kvistsolutions.com) -1. [Kyriba](https://www.kyriba.com/) -1. [LeFigaro](https://www.lefigaro.fr/) -1. [Lely](https://www.lely.com/) 1. [LexisNexis](https://www.lexisnexis.com/) 1. [Lian Chu Securities](https://lczq.com) 1. [Liatrio](https://www.liatrio.com) @@ -200,7 +174,6 @@ Currently, the following organizations are **officially** using Argo CD: 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. [Metacore Games](https://metacoregames.com/) 1. [Metanet](http://www.metanet.co.kr/en/) 1. [MindSpore](https://mindspore.cn) 1. [Mirantis](https://mirantis.com/) @@ -209,28 +182,20 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Moengage](https://www.moengage.com/) 1. [Money Forward](https://corp.moneyforward.com/en/) 1. [MOO Print](https://www.moo.com/) -1. [Mozilla](https://www.mozilla.org) 1. [MTN Group](https://www.mtn.com/) -1. [Municipality of The Hague](https://www.denhaag.nl/) -1. [My Job Glasses](https://myjobglasses.com) 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. [Next Fit Sistemas](https://nextfit.com.br/) 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. [Octopus Deploy](https://octopus.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. [Open Analytics](https://openanalytics.eu) 1. [openEuler](https://openeuler.org) 1. [openGauss](https://opengauss.org/) 1. [OpenGov](https://opengov.com) @@ -247,23 +212,18 @@ Currently, the following organizations are **officially** using Argo CD: 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. [Pipedrive](https://www.pipedrive.com/) 1. [Pipefy](https://www.pipefy.com/) -1. [Pipekit](https://pipekit.io/) 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. [Priceline](https://priceline.com) 1. [Procore](https://www.procore.com) 1. [Productboard](https://www.productboard.com/) 1. [Prudential](https://prudential.com.sg) @@ -275,18 +235,15 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Quipper](https://www.quipper.com/) 1. [RapidAPI](https://www.rapidapi.com/) 1. [rebuy](https://www.rebuy.de/) +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. [Relex Solutions](https://www.relexsolutions.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. [Rogo](https://rogodata.com) 1. [Rubin Observatory](https://www.lsst.org) 1. [Saildrone](https://www.saildrone.com/) 1. [Salad Technologies](https://salad.com/) @@ -296,17 +253,12 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Schwarz IT](https://jobs.schwarz/it-mission) 1. [SCRM Lidl International Hub](https://scrm.lidl) 1. [SEEK](https://seek.com.au) -1. [SEKAI](https://www.sekai.io/) 1. [Semgrep](https://semgrep.com) -1. [Shield](https://shield.com) 1. [SI Analytics](https://si-analytics.ai) -1. [Sidewalk Entertainment](https://sidewalkplay.com/) 1. [Skit](https://skit.ai/) -1. [Skribble](https://skribble.com) 1. [Skyscanner](https://www.skyscanner.net/) 1. [Smart Pension](https://www.smartpension.co.uk/) 1. [Smilee.io](https://smilee.io) -1. [Smilegate Stove](https://www.onstove.com/) 1. [Smood.ch](https://www.smood.ch/) 1. [Snapp](https://snapp.ir/) 1. [Snyk](https://snyk.io/) @@ -317,7 +269,6 @@ Currently, the following organizations are **officially** using Argo CD: 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/) @@ -326,13 +277,11 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Swisscom](https://www.swisscom.ch) 1. [Swissquote](https://github.com/swissquote) 1. [Syncier](https://syncier.com/) -1. [Syself](https://syself.com) 1. [TableCheck](https://tablecheck.com/) 1. [Tailor Brands](https://www.tailorbrands.com) 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/) @@ -356,7 +305,6 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Urbantz](https://urbantz.com/) 1. [Vectra](https://www.vectra.ai) 1. [Veepee](https://www.veepee.com) -1. [Verkada](https://www.verkada.com) 1. [Viaduct](https://www.viaduct.ai/) 1. [VietMoney](https://vietmoney.vn/) 1. [Vinted](https://vinted.com/) diff --git a/VERSION b/VERSION index edcfe40d1984a..cf686df885477 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.14.0 +2.10.16 diff --git a/applicationset/controllers/applicationset_controller.go b/applicationset/controllers/applicationset_controller.go index d3911f1e0c7c4..a5f5c971d2910 100644 --- a/applicationset/controllers/applicationset_controller.go +++ b/applicationset/controllers/applicationset_controller.go @@ -18,12 +18,8 @@ import ( "context" "fmt" "reflect" - "sort" - "strings" "time" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" log "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" apierr "k8s.io/apimachinery/pkg/api/errors" @@ -32,24 +28,24 @@ 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" - "k8s.io/client-go/util/retry" 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/controllers/template" "github.com/argoproj/argo-cd/v2/applicationset/generators" - "github.com/argoproj/argo-cd/v2/applicationset/metrics" - "github.com/argoproj/argo-cd/v2/applicationset/status" "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" @@ -67,10 +63,12 @@ const ( ReconcileRequeueOnValidationError = time.Minute * 3 ) -var defaultPreservedAnnotations = []string{ - NotifiedAnnotationKey, - argov1alpha1.AnnotationKeyRefresh, -} +var ( + defaultPreservedAnnotations = []string{ + NotifiedAnnotationKey, + argov1alpha1.AnnotationKeyRefresh, + } +) // ApplicationSetReconciler reconciles a ApplicationSet object type ApplicationSetReconciler struct { @@ -90,7 +88,7 @@ type ApplicationSetReconciler struct { SCMRootCAPath string GlobalPreservedAnnotations []string GlobalPreservedLabels []string - Metrics *metrics.ApplicationsetMetrics + Cache cache.Cache } // +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets,verbs=get;list;watch;create;update;patch;delete @@ -101,7 +99,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque var applicationSetInfo argov1alpha1.ApplicationSet parametersGenerated := false - startTime := time.Now() + if err := r.Get(ctx, req.NamespacedName, &applicationSetInfo); err != nil { if client.IgnoreNotFound(err) != nil { logCtx.WithError(err).Infof("unable to get ApplicationSet: '%v' ", err) @@ -109,38 +107,25 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, client.IgnoreNotFound(err) } - defer func() { - r.Metrics.ObserveReconcile(&applicationSetInfo, time.Since(startTime)) - }() - // Do not attempt to further reconcile the ApplicationSet if it is being deleted. if applicationSetInfo.ObjectMeta.DeletionTimestamp != nil { - appsetName := applicationSetInfo.ObjectMeta.Name - logCtx.Debugf("DeletionTimestamp is set on %s", appsetName) deleteAllowed := utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete() if !deleteAllowed { - logCtx.Debugf("ApplicationSet policy does not allow to delete") if err := r.removeOwnerReferencesOnDeleteAppSet(ctx, applicationSetInfo); err != nil { return ctrl.Result{}, err } - logCtx.Debugf("ownerReferences referring %s is deleted from generated applications", appsetName) - } - controllerutil.RemoveFinalizer(&applicationSetInfo, argov1alpha1.ResourcesFinalizerName) - if err := r.Update(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 } - if err := r.migrateStatus(ctx, &applicationSetInfo); err != nil { - logCtx.Errorf("failed to migrate status subresource %v", err) - return ctrl.Result{}, err - } - // 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 := template.GenerateApplications(logCtx, applicationSetInfo, r.Generators, r.Renderer, r.Client) + desiredApplications, applicationSetReason, err := r.generateApplications(logCtx, applicationSetInfo) if err != nil { _ = r.setApplicationSetStatusCondition(ctx, &applicationSetInfo, @@ -151,7 +136,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque Status: argov1alpha1.ApplicationSetConditionStatusTrue, }, parametersGenerated, ) - return ctrl.Result{RequeueAfter: ReconcileRequeueOnValidationError}, err + return ctrl.Result{}, err } parametersGenerated = true @@ -179,16 +164,6 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{RequeueAfter: ReconcileRequeueOnValidationError}, nil } - currentApplications, err := r.getCurrentApplications(ctx, applicationSetInfo) - if err != nil { - return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err) - } - - err = r.updateResourcesStatus(ctx, logCtx, &applicationSetInfo, currentApplications) - if err != nil { - return ctrl.Result{}, fmt.Errorf("failed to get update resources status for application set: %w", err) - } - // appMap is a name->app collection of Applications in this ApplicationSet. appMap := map[string]argov1alpha1.Application{} // appSyncMap tracks which apps will be synced during this reconciliation. @@ -205,11 +180,16 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque } } else if applicationSetInfo.Spec.Strategy != nil { // appset uses progressive sync - for _, app := range currentApplications { + applications, err := r.getCurrentApplications(ctx, applicationSetInfo) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err) + } + + for _, app := range applications { appMap[app.Name] = app } - appSyncMap, err = r.performProgressiveSyncs(ctx, logCtx, applicationSetInfo, currentApplications, desiredApplications, appMap) + 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) } @@ -246,8 +226,21 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque if r.EnableProgressiveSyncs { // trigger appropriate application syncs if RollingSync strategy is enabled - if progressiveSyncsRollingSyncStrategyEnabled(&applicationSetInfo) { - validApps = r.syncValidApplications(logCtx, &applicationSetInfo, appSyncMap, appMap, validApps) + if progressiveSyncsStrategyEnabled(&applicationSetInfo, "RollingSync") { + validApps, err = r.syncValidApplications(logCtx, &applicationSetInfo, appSyncMap, appMap, validApps) + + if err != nil { + _ = r.setApplicationSetStatusCondition(ctx, + &applicationSetInfo, + argov1alpha1.ApplicationSetCondition{ + Type: argov1alpha1.ApplicationSetConditionErrorOccurred, + Message: err.Error(), + Reason: argov1alpha1.ApplicationSetReasonSyncApplicationError, + Status: argov1alpha1.ApplicationSetConditionStatusTrue, + }, parametersGenerated, + ) + return ctrl.Result{}, err + } } } @@ -401,21 +394,8 @@ func (r *ApplicationSetReconciler) setApplicationSetStatusCondition(ctx context. paramtersGeneratedCondition := getParametersGeneratedCondition(paramtersGenerated, condition.Message) resourceUpToDateCondition := getResourceUpToDateCondition(errOccurred, condition.Message, condition.Reason) - evaluatedTypes := map[argov1alpha1.ApplicationSetConditionType]bool{ - argov1alpha1.ApplicationSetConditionErrorOccurred: true, - argov1alpha1.ApplicationSetConditionParametersGenerated: true, - argov1alpha1.ApplicationSetConditionResourcesUpToDate: true, - } newConditions := []argov1alpha1.ApplicationSetCondition{errOccurredCondition, paramtersGeneratedCondition, resourceUpToDateCondition} - if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) { - evaluatedTypes[argov1alpha1.ApplicationSetConditionRolloutProgressing] = true - - if condition.Type == argov1alpha1.ApplicationSetConditionRolloutProgressing { - newConditions = append(newConditions, condition) - } - } - needToUpdateConditions := false for _, condition := range newConditions { // do nothing if appset already has same condition @@ -426,34 +406,30 @@ func (r *ApplicationSetReconciler) setApplicationSetStatusCondition(ctx context. } } } + evaluatedTypes := map[argov1alpha1.ApplicationSetConditionType]bool{ + argov1alpha1.ApplicationSetConditionErrorOccurred: true, + argov1alpha1.ApplicationSetConditionParametersGenerated: true, + argov1alpha1.ApplicationSetConditionResourcesUpToDate: true, + } - if needToUpdateConditions || len(applicationSet.Status.Conditions) < len(newConditions) { + if needToUpdateConditions || len(applicationSet.Status.Conditions) < 3 { // fetch updated Application Set object before updating it - // DefaultRetry will retry 5 times with a backoff factor of 1, jitter of 0.1 and a duration of 10ms - err := retry.RetryOnConflict(retry.DefaultRetry, func() error { - namespacedName := types.NamespacedName{Namespace: applicationSet.Namespace, Name: applicationSet.Name} - updatedAppset := &argov1alpha1.ApplicationSet{} - if err := r.Get(ctx, namespacedName, updatedAppset); err != nil { - if client.IgnoreNotFound(err) != nil { - return nil - } - return fmt.Errorf("error fetching updated application set: %w", err) + 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) + } - updatedAppset.Status.SetConditions( - newConditions, evaluatedTypes, - ) + applicationSet.Status.SetConditions( + newConditions, evaluatedTypes, + ) - // Update the newly fetched object with new set of conditions - err := r.Client.Status().Update(ctx, updatedAppset) - if err != nil { - return err - } - updatedAppset.DeepCopyInto(applicationSet) - return nil - }) + // Update the newly fetched object with new set of conditions + err := r.Client.Status().Update(ctx, applicationSet) if err != nil && !apierr.IsNotFound(err) { - return fmt.Errorf("unable to set application set condition: %w", err) + return fmt.Errorf("unable to set application set condition: %v", err) } } @@ -466,6 +442,7 @@ func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Con errorsByIndex := map[int]error{} namesSet := map[string]bool{} for i, app := range desiredApplications { + if !namesSet[app.Name] { namesSet[app.Name] = true } else { @@ -485,6 +462,7 @@ func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Con errorsByIndex[i] = fmt.Errorf("application destination spec is invalid: %s", err.Error()) continue } + } return errorsByIndex, nil @@ -493,6 +471,7 @@ func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Con func (r *ApplicationSetReconciler) getMinRequeueAfter(applicationSetInfo *argov1alpha1.ApplicationSet) time.Duration { var res time.Duration for _, requestedGenerator := range applicationSetInfo.Spec.Generators { + relevantGenerators := generators.GetRelevantGenerators(&requestedGenerator, r.Generators) for _, g := range relevantGenerators { @@ -509,10 +488,95 @@ func (r *ApplicationSetReconciler) getMinRequeueAfter(applicationSetInfo *argov1 return res } +func getTempApplication(applicationSetTemplate argov1alpha1.ApplicationSetTemplate) *argov1alpha1.Application { + var tmplApplication argov1alpha1.Application + tmplApplication.Annotations = applicationSetTemplate.Annotations + tmplApplication.Labels = applicationSetTemplate.Labels + tmplApplication.Namespace = applicationSetTemplate.Namespace + tmplApplication.Name = applicationSetTemplate.Name + tmplApplication.Spec = applicationSetTemplate.Spec + tmplApplication.Finalizers = applicationSetTemplate.Finalizers + + return &tmplApplication +} + +func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, applicationSetInfo argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) { + var res []argov1alpha1.Application + + var firstError error + var applicationSetReason argov1alpha1.ApplicationSetReasonType + + for _, requestedGenerator := range applicationSetInfo.Spec.Generators { + t, err := generators.Transform(requestedGenerator, r.Generators, applicationSetInfo.Spec.Template, &applicationSetInfo, map[string]interface{}{}) + if err != nil { + logCtx.WithError(err).WithField("generator", requestedGenerator). + Error("error generating application from params") + if firstError == nil { + firstError = err + applicationSetReason = argov1alpha1.ApplicationSetReasonApplicationParamsGenerationError + } + continue + } + + for _, a := range t { + tmplApplication := getTempApplication(a.Template) + + for _, p := range a.Params { + app, err := r.Renderer.RenderTemplateParams(tmplApplication, applicationSetInfo.Spec.SyncPolicy, p, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions) + + if err != nil { + logCtx.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator). + Error("error generating application from params") + + if firstError == nil { + firstError = err + applicationSetReason = argov1alpha1.ApplicationSetReasonRenderTemplateParamsError + } + 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) + } + } + + 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) 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 utils.IsNamespaceAllowed(namespaces, e.Object.GetNamespace()) + return glob.MatchStringInList(namespaces, e.Object.GetNamespace(), false) }, } } @@ -546,7 +610,7 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProg Owns(&argov1alpha1.Application{}, builder.WithPredicates(ownsHandler)). WithEventFilter(ignoreNotAllowedNamespaces(r.ApplicationSetNamespaces)). Watches( - &corev1.Secret{}, + &source.Kind{Type: &corev1.Secret{}}, &clusterSecretEventHandler{ Client: mgr.GetClient(), Log: log.WithField("type", "createSecretEventHandler"), @@ -555,14 +619,38 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProg 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, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error { + var firstError error // Creates or updates the application in appList for _, generatedApp := range desiredApplications { + // 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. @@ -627,17 +715,6 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, } } - // Preserve post-delete finalizers: - // https://github.com/argoproj/argo-cd/issues/17181 - for _, finalizer := range found.ObjectMeta.Finalizers { - if strings.HasPrefix(finalizer, argov1alpha1.PostDeleteFinalizerName) { - if generatedApp.Finalizers == nil { - generatedApp.Finalizers = []string{} - } - generatedApp.Finalizers = append(generatedApp.Finalizers, finalizer) - } - } - found.ObjectMeta.Annotations = generatedApp.Annotations found.ObjectMeta.Finalizers = generatedApp.Finalizers @@ -645,6 +722,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, return controllerutil.SetControllerReference(&applicationSet, found, r.Scheme) }) + if err != nil { appLog.WithError(err).WithField("action", action).Errorf("failed to %s Application", action) if firstError == nil { @@ -652,6 +730,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, } continue } + r.updateCache(ctx, found, appLog) if action != controllerutil.OperationResultNone { // Don't pollute etcd with "unchanged Application" events @@ -669,6 +748,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, // 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, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error { + var createApps []argov1alpha1.Application current, err := r.getCurrentApplications(ctx, applicationSet) if err != nil { @@ -696,6 +776,7 @@ func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, logCtx * func (r *ApplicationSetReconciler) getCurrentApplications(ctx context.Context, applicationSet argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) { var current argov1alpha1.ApplicationList err := r.Client.List(ctx, ¤t, client.MatchingFields{".metadata.controller": applicationSet.Name}, client.InNamespace(applicationSet.Namespace)) + if err != nil { return nil, fmt.Errorf("error retrieving applications: %w", err) } @@ -733,6 +814,7 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx * _, 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, logCtx) if err != nil { @@ -760,6 +842,7 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx * // removeFinalizerOnInvalidDestination removes the Argo CD resources finalizer if the application contains an invalid target (eg missing cluster) func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, app *argov1alpha1.Application, clusterList *argov1alpha1.ClusterList, appLog *log.Entry) error { + // Only check if the finalizers need to be removed IF there are finalizers to remove if len(app.Finalizers) == 0 { return nil @@ -772,10 +855,12 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte appLog.Warnf("The destination cluster for %s couldn't be found: %v", app.Name, err) validDestination = false } else { + // Detect if the destination's server field does not match an existing cluster matchingCluster := false for _, cluster := range clusterList.Items { + // Server fields must match. Note that ValidateDestination ensures that the server field is set, if applicable. if app.Spec.Destination.Server != cluster.Server { continue @@ -799,6 +884,7 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte // If the destination is invalid (for example the cluster is no longer defined), then remove // the application finalizers to avoid triggering Argo CD bug #5817 if !validDestination { + // Filter out the Argo CD finalizer from the finalizer list var newFinalizers []string for _, existingFinalizer := range app.Finalizers { @@ -818,6 +904,7 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte 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) @@ -832,14 +919,14 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte func (r *ApplicationSetReconciler) removeOwnerReferencesOnDeleteAppSet(ctx context.Context, applicationSet argov1alpha1.ApplicationSet) error { applications, err := r.getCurrentApplications(ctx, applicationSet) if err != nil { - return fmt.Errorf("error getting current applications for ApplicationSet: %w", err) + return err } for _, app := range applications { app.SetOwnerReferences([]metav1.OwnerReference{}) err := r.Client.Update(ctx, &app) if err != nil { - return fmt.Errorf("error updating application: %w", err) + return err } } @@ -847,9 +934,13 @@ func (r *ApplicationSetReconciler) removeOwnerReferencesOnDeleteAppSet(ctx conte } 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) { - appDependencyList, appStepMap := r.buildAppDependencyList(logCtx, appset, desiredApplications) - _, err := r.updateApplicationSetApplicationStatus(ctx, logCtx, &appset, applications, appStepMap) + appDependencyList, appStepMap, err := r.buildAppDependencyList(logCtx, appset, desiredApplications) + if err != nil { + return nil, fmt.Errorf("failed to build app dependency list: %w", err) + } + + _, err = r.updateApplicationSetApplicationStatus(ctx, logCtx, &appset, applications, appStepMap) if err != nil { return nil, fmt.Errorf("failed to update applicationset app status: %w", err) } @@ -859,27 +950,35 @@ func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context, logCtx.Infof("step %v: %+v", i+1, step) } - appSyncMap := r.buildAppSyncMap(appset, appDependencyList, appMap) + appSyncMap, err := r.buildAppSyncMap(ctx, appset, appDependencyList, appMap) + if err != nil { + return nil, fmt.Errorf("failed to build app sync map: %w", err) + } + logCtx.Infof("Application allowed to sync before maxUpdate?: %+v", appSyncMap) - _, err = r.updateApplicationSetApplicationStatusProgress(ctx, logCtx, &appset, appSyncMap, appStepMap) + _, 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) } - _ = r.updateApplicationSetApplicationStatusConditions(ctx, &appset) + _, err = r.updateApplicationSetApplicationStatusConditions(ctx, &appset) + if err != nil { + return nil, fmt.Errorf("failed to update applicationset application status conditions: %w", err) + } return appSyncMap, nil } // this list tracks which Applications belong to each RollingUpdate step -func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([][]string, map[string]int) { +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{} + return [][]string{}, map[string]int{}, nil } steps := []argov1alpha1.ApplicationSetRolloutStep{} - if progressiveSyncsRollingSyncStrategyEnabled(&applicationSet) { + if progressiveSyncsStrategyEnabled(&applicationSet, "RollingSync") { steps = applicationSet.Spec.Strategy.RollingSync.Steps } @@ -893,9 +992,11 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, app // use applicationLabelSelectors to filter generated Applications into steps and status by name for _, app := range applications { for i, step := range steps { + selected := true // default to true, assuming the current Application is a match for the given step matchExpression for _, matchExpression := range step.MatchExpressions { + if val, ok := app.Labels[matchExpression.Key]; ok { valueMatched := labelMatchedExpression(logCtx, val, matchExpression) @@ -920,7 +1021,7 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, app } } - return appDependencyList, appStepMap + return appDependencyList, appStepMap, nil } func labelMatchedExpression(logCtx *log.Entry, val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool { @@ -944,7 +1045,7 @@ func labelMatchedExpression(logCtx *log.Entry, val string, matchExpression argov } // this map is used to determine which stage of Applications are ready to be updated in the reconciler loop -func (r *ApplicationSetReconciler) buildAppSyncMap(applicationSet argov1alpha1.ApplicationSet, appDependencyList [][]string, appMap map[string]argov1alpha1.Application) map[string]bool { +func (r *ApplicationSetReconciler) buildAppSyncMap(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, appDependencyList [][]string, appMap map[string]argov1alpha1.Application) (map[string]bool, error) { appSyncMap := map[string]bool{} syncEnabled := true @@ -959,6 +1060,7 @@ func (r *ApplicationSetReconciler) buildAppSyncMap(applicationSet argov1alpha1.A // detect if we need to halt before progressing to the next step for _, appName := range appDependencyList[i] { + idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, appName) if idx == -1 { // no Application status found, likely because the Application is being newly created @@ -969,6 +1071,7 @@ func (r *ApplicationSetReconciler) buildAppSyncMap(applicationSet argov1alpha1.A appStatus := applicationSet.Status.ApplicationStatus[idx] if app, ok := appMap[appName]; ok { + syncEnabled = appSyncEnabledForNextStep(&applicationSet, app, appStatus) if !syncEnabled { break @@ -981,11 +1084,12 @@ func (r *ApplicationSetReconciler) buildAppSyncMap(applicationSet argov1alpha1.A } } - return appSyncMap + return appSyncMap, nil } func appSyncEnabledForNextStep(appset *argov1alpha1.ApplicationSet, app argov1alpha1.Application, appStatus argov1alpha1.ApplicationSetApplicationStatus) bool { - if progressiveSyncsRollingSyncStrategyEnabled(appset) { + + 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" } @@ -993,8 +1097,16 @@ func appSyncEnabledForNextStep(appset *argov1alpha1.ApplicationSet, app argov1al return true } -func progressiveSyncsRollingSyncStrategyEnabled(appset *argov1alpha1.ApplicationSet) bool { - return appset.Spec.Strategy != nil && appset.Spec.Strategy.RollingSync != nil && appset.Spec.Strategy.Type == "RollingSync" +func progressiveSyncsStrategyEnabled(appset *argov1alpha1.ApplicationSet, strategyType string) bool { + if appset.Spec.Strategy == nil || appset.Spec.Strategy.Type != strategyType { + return false + } + + if strategyType == "RollingSync" && appset.Spec.Strategy.RollingSync == nil { + return false + } + + return true } func isApplicationHealthy(app argov1alpha1.Application) bool { @@ -1019,10 +1131,12 @@ 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, 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)) for _, app := range applications { + healthStatusString, syncStatusString, operationPhaseString := statusStrings(app) idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, app.Name) @@ -1037,22 +1151,14 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con Message: "No Application status found, defaulting status to Waiting.", Status: "Waiting", Step: fmt.Sprint(appStepMap[app.Name] + 1), - TargetRevisions: app.Status.GetRevisions(), } } else { // we have an existing AppStatus currentAppStatus = applicationSet.Status.ApplicationStatus[idx] - - // upgrade any existing AppStatus that might have been set by an older argo-cd version - // note: currentAppStatus.TargetRevisions may be set to empty list earlier during migrations, - // to prevent other usage of r.Client.Status().Update to fail before reaching here. - if len(currentAppStatus.TargetRevisions) == 0 { - currentAppStatus.TargetRevisions = app.Status.GetRevisions() - } } appOutdated := false - if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) { + if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") { appOutdated = syncStatusString == "OutOfSync" } @@ -1062,25 +1168,20 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con currentAppStatus.Status = "Waiting" currentAppStatus.Message = "Application has pending changes, setting status to Waiting." currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1) - currentAppStatus.TargetRevisions = app.Status.GetRevisions() } if currentAppStatus.Status == "Pending" { - if operationPhaseString == "Succeeded" { - revisions := []string{} - if len(app.Status.OperationState.SyncResult.Revisions) > 0 { - revisions = app.Status.OperationState.SyncResult.Revisions - } else if app.Status.OperationState.SyncResult.Revision != "" { - revisions = append(revisions, app.Status.OperationState.SyncResult.Revision) - } - - if reflect.DeepEqual(currentAppStatus.TargetRevisions, revisions) { - 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) + // 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 @@ -1118,7 +1219,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, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appStepMap map[string]int) ([]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)) @@ -1129,7 +1230,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress totalCountMap := []int{} length := 0 - if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) { + if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") { length = len(applicationSet.Spec.Strategy.RollingSync.Steps) } for s := 0; s < length; s++ { @@ -1141,7 +1242,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress for _, appStatus := range applicationSet.Status.ApplicationStatus { totalCountMap[appStepMap[appStatus.Application]] += 1 - if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) { + if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") { if appStatus.Status == "Pending" || appStatus.Status == "Progressing" { updateCountMap[appStepMap[appStatus.Application]] += 1 } @@ -1149,9 +1250,10 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress } for _, appStatus := range applicationSet.Status.ApplicationStatus { + maxUpdateAllowed := true maxUpdate := &intstr.IntOrString{} - if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) { + if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") { maxUpdate = applicationSet.Spec.Strategy.RollingSync.Steps[appStepMap[appStatus.Application]].MaxUpdate } @@ -1171,6 +1273,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress maxUpdateAllowed = false 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 { @@ -1195,7 +1298,8 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress return appStatuses, nil } -func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditions(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet) []argov1alpha1.ApplicationSetCondition { +func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditions(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet) ([]argov1alpha1.ApplicationSetCondition, error) { + appSetProgressing := false for _, appStatus := range applicationSet.Status.ApplicationStatus { if appStatus.Status != "Healthy" { @@ -1220,7 +1324,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditio Message: "ApplicationSet Rollout Rollout started", Reason: argov1alpha1.ApplicationSetReasonApplicationSetModified, Status: argov1alpha1.ApplicationSetConditionStatusTrue, - }, true, + }, false, ) } else if !appSetProgressing && appSetConditionProgressing { _ = r.setApplicationSetStatusCondition(ctx, @@ -1230,11 +1334,11 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditio Message: "ApplicationSet Rollout Rollout complete", Reason: argov1alpha1.ApplicationSetReasonApplicationSetRolloutComplete, Status: argov1alpha1.ApplicationSetConditionStatusFalse, - }, true, + }, false, ) } - return applicationSet.Status.Conditions + return applicationSet.Status.Conditions, nil } func findApplicationStatusIndex(appStatuses []argov1alpha1.ApplicationSetApplicationStatus, application string) int { @@ -1246,89 +1350,7 @@ func findApplicationStatusIndex(appStatuses []argov1alpha1.ApplicationSetApplica return -1 } -// migrateStatus run migrations on the status subresource of ApplicationSet early during the run of ApplicationSetReconciler.Reconcile -// this handles any defaulting of values - which would otherwise cause the references to r.Client.Status().Update to fail given missing required fields. -func (r *ApplicationSetReconciler) migrateStatus(ctx context.Context, appset *argov1alpha1.ApplicationSet) error { - update := false - if statusList := appset.Status.ApplicationStatus; statusList != nil { - for idx := range statusList { - if statusList[idx].TargetRevisions == nil { - statusList[idx].TargetRevisions = []string{} - update = true - } - } - } - - if update { - // DefaultRetry will retry 5 times with a backoff factor of 1, jitter of 0.1 and a duration of 10ms - err := retry.RetryOnConflict(retry.DefaultRetry, func() error { - namespacedName := types.NamespacedName{Namespace: appset.Namespace, Name: appset.Name} - updatedAppset := &argov1alpha1.ApplicationSet{} - if err := r.Get(ctx, namespacedName, updatedAppset); err != nil { - if client.IgnoreNotFound(err) != nil { - return nil - } - return fmt.Errorf("error fetching updated application set: %w", err) - } - - updatedAppset.Status.ApplicationStatus = appset.Status.ApplicationStatus - - // Update the newly fetched object with new set of ApplicationStatus - err := r.Client.Status().Update(ctx, updatedAppset) - if err != nil { - return err - } - updatedAppset.DeepCopyInto(appset) - return nil - }) - if err != nil && !apierr.IsNotFound(err) { - return fmt.Errorf("unable to set application set condition: %w", err) - } - } - return nil -} - -func (r *ApplicationSetReconciler) updateResourcesStatus(ctx context.Context, logCtx *log.Entry, appset *argov1alpha1.ApplicationSet, apps []argov1alpha1.Application) error { - statusMap := status.GetResourceStatusMap(appset) - statusMap = status.BuildResourceStatus(statusMap, apps) - - statuses := []argov1alpha1.ResourceStatus{} - for _, status := range statusMap { - statuses = append(statuses, status) - } - sort.Slice(statuses, func(i, j int) bool { - return statuses[i].Name < statuses[j].Name - }) - appset.Status.Resources = statuses - // DefaultRetry will retry 5 times with a backoff factor of 1, jitter of 0.1 and a duration of 10ms - err := retry.RetryOnConflict(retry.DefaultRetry, func() error { - namespacedName := types.NamespacedName{Namespace: appset.Namespace, Name: appset.Name} - updatedAppset := &argov1alpha1.ApplicationSet{} - if err := r.Get(ctx, namespacedName, updatedAppset); err != nil { - if client.IgnoreNotFound(err) != nil { - return nil - } - return fmt.Errorf("error fetching updated application set: %w", err) - } - - updatedAppset.Status.Resources = appset.Status.Resources - - // Update the newly fetched object with new status resources - err := r.Client.Status().Update(ctx, updatedAppset) - if err != nil { - return err - } - updatedAppset.DeepCopyInto(appset) - return nil - }) - if err != nil { - logCtx.Errorf("unable to set application set status: %v", err) - return fmt.Errorf("unable to set application set status: %w", err) - } - return nil -} - -// setAppSetApplicationStatus updates the ApplicationSet's status field +// setApplicationSetApplicationStatus updates the ApplicatonSet's status field // with any new/changed Application statuses. func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, applicationStatuses []argov1alpha1.ApplicationSetApplicationStatus) error { needToUpdateStatus := false @@ -1359,36 +1381,27 @@ func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Contex for i := range applicationStatuses { applicationSet.Status.SetApplicationStatus(applicationStatuses[i]) } - // DefaultRetry will retry 5 times with a backoff factor of 1, jitter of 0.1 and a duration of 10ms - err := retry.RetryOnConflict(retry.DefaultRetry, func() error { - updatedAppset := &argov1alpha1.ApplicationSet{} - if err := r.Get(ctx, namespacedName, updatedAppset); err != nil { - if client.IgnoreNotFound(err) != nil { - return nil - } - return fmt.Errorf("error fetching updated application set: %w", err) - } - - updatedAppset.Status.ApplicationStatus = applicationSet.Status.ApplicationStatus - // Update the newly fetched object with new set of ApplicationStatus - err := r.Client.Status().Update(ctx, updatedAppset) - if err != nil { - return err - } - updatedAppset.DeepCopyInto(applicationSet) - return nil - }) + // Update the newly fetched object with new set of ApplicationStatus + err := r.Client.Status().Update(ctx, applicationSet) if err != nil { + logCtx.Errorf("unable to set application set status: %v", err) - return fmt.Errorf("unable to set application set status: %w", err) + return fmt.Errorf("unable to set application set status: %v", err) + } + + 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) } } return nil } -func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appMap map[string]argov1alpha1.Application, validApps []argov1alpha1.Application) []argov1alpha1.Application { +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 @@ -1409,15 +1422,16 @@ func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, appl // 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 { logCtx.Infof("triggering sync for application: %v, prune enabled: %v", validApps[i].Name, pruneEnabled) - validApps[i] = syncApplication(validApps[i], pruneEnabled) + validApps[i], _ = syncApplication(validApps[i], pruneEnabled) } rolloutApps = append(rolloutApps, validApps[i]) } - return rolloutApps + return rolloutApps, nil } // 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 { +func syncApplication(application argov1alpha1.Application, prune bool) (argov1alpha1.Application, error) { + operation := argov1alpha1.Operation{ InitiatedBy: argov1alpha1.OperationInitiator{ Username: "applicationset-controller", @@ -1443,7 +1457,7 @@ func syncApplication(application argov1alpha1.Application, prune bool) argov1alp } application.Operation = &operation - return application + return application, nil } func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs { @@ -1513,13 +1527,10 @@ func shouldRequeueApplicationSet(appOld *argov1alpha1.Application, appNew *argov } // the applicationset controller owns the application spec, labels, annotations, and finalizers on the applications - // reflect.DeepEqual considers nil slices/maps not equal to empty slices/maps - // https://pkg.go.dev/reflect#DeepEqual - // ApplicationDestination has an unexported field so we can just use the == for comparison - if !cmp.Equal(appOld.Spec, appNew.Spec, cmpopts.EquateEmpty(), cmpopts.EquateComparable(argov1alpha1.ApplicationDestination{})) || - !cmp.Equal(appOld.ObjectMeta.GetAnnotations(), appNew.ObjectMeta.GetAnnotations(), cmpopts.EquateEmpty()) || - !cmp.Equal(appOld.ObjectMeta.GetLabels(), appNew.ObjectMeta.GetLabels(), cmpopts.EquateEmpty()) || - !cmp.Equal(appOld.ObjectMeta.GetFinalizers(), appNew.ObjectMeta.GetFinalizers(), cmpopts.EquateEmpty()) { + 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 } diff --git a/applicationset/controllers/applicationset_controller_test.go b/applicationset/controllers/applicationset_controller_test.go index 02608175245b4..81fbad95ac50b 100644 --- a/applicationset/controllers/applicationset_controller_test.go +++ b/applicationset/controllers/applicationset_controller_test.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "reflect" - "strconv" "strings" "testing" "time" @@ -13,7 +12,6 @@ import ( log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -21,8 +19,10 @@ 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" @@ -32,24 +32,343 @@ import ( "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/argoproj/argo-cd/v2/applicationset/generators" - "github.com/argoproj/argo-cd/v2/applicationset/generators/mocks" "github.com/argoproj/argo-cd/v2/applicationset/utils" - appsetmetrics "github.com/argoproj/argo-cd/v2/applicationset/metrics" "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 *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate { + args := g.Called(appSetGenerator) + + return args.Get(0).(*v1alpha1.ApplicationSetTemplate) +} + +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 *v1alpha1.ApplicationSetGenerator) time.Duration { + args := g.Called(appSetGenerator) + + return args.Get(0).(time.Duration) +} + +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).(*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 := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + for _, c := range []struct { + name string + params []map[string]interface{} + template v1alpha1.ApplicationSetTemplate + generateParamsError error + rendererError error + expectErr bool + expectedReason v1alpha1.ApplicationSetReasonType + }{ + { + name: "Generate two applications", + params: []map[string]interface{}{{"name": "app1"}, {"name": "app2"}}, + template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{"label_name": "label_value"}, + }, + Spec: v1alpha1.ApplicationSpec{}, + }, + expectedReason: "", + }, + { + name: "Handles error from the generator", + generateParamsError: fmt.Errorf("error"), + expectErr: true, + expectedReason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError, + }, + { + name: "Handles error from the render", + params: []map[string]interface{}{{"name": "app1"}, {"name": "app2"}}, + template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{"label_name": "label_value"}, + }, + Spec: v1alpha1.ApplicationSpec{}, + }, + rendererError: fmt.Errorf("error"), + expectErr: true, + expectedReason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError, + }, + } { + cc := c + app := v1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + } + + t.Run(cc.name, func(t *testing.T) { + + appSet := &v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + }, + } + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(appSet).Build() + + generatorMock := generatorMock{} + generator := v1alpha1.ApplicationSetGenerator{ + List: &v1alpha1.ListGenerator{}, + } + + generatorMock.On("GenerateParams", &generator). + Return(cc.params, cc.generateParamsError) + + generatorMock.On("GetTemplate", &generator). + Return(&v1alpha1.ApplicationSetTemplate{}) + + rendererMock := rendererMock{} + + 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, []string(nil)). + Return(nil, cc.rendererError) + } else { + rendererMock.On("RenderTemplateParams", getTempApplication(cc.template), p, false, []string(nil)). + Return(&app, nil) + expectedApps = append(expectedApps, app) + } + } + } + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(1), + Generators: map[string]generators.Generator{ + "List": &generatorMock, + }, + Renderer: &rendererMock, + KubeClientset: kubefake.NewSimpleClientset(), + Cache: &fakeCache{}, + } + + got, reason, err := r.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{generator}, + Template: cc.template, + }, + }) + + if cc.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, expectedApps, got) + assert.Equal(t, cc.expectedReason, reason) + generatorMock.AssertNumberOfCalls(t, "GenerateParams", 1) + + if cc.generateParamsError == nil { + rendererMock.AssertNumberOfCalls(t, "RenderTemplateParams", len(cc.params)) + } + + }) + } + +} + +func TestMergeTemplateApplications(t *testing.T) { + scheme := runtime.NewScheme() + _ = v1alpha1.AddToScheme(scheme) + _ = v1alpha1.AddToScheme(scheme) + + client := fake.NewClientBuilder().WithScheme(scheme).Build() + + for _, c := range []struct { + name string + params []map[string]interface{} + template v1alpha1.ApplicationSetTemplate + overrideTemplate v1alpha1.ApplicationSetTemplate + expectedMerged v1alpha1.ApplicationSetTemplate + expectedApps []v1alpha1.Application + }{ + { + name: "Generate app", + params: []map[string]interface{}{{"name": "app1"}}, + template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{"label_name": "label_value"}, + }, + Spec: v1alpha1.ApplicationSpec{}, + }, + overrideTemplate: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ + Name: "test", + Labels: map[string]string{"foo": "bar"}, + }, + Spec: v1alpha1.ApplicationSpec{}, + }, + expectedMerged: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ + Name: "test", + Namespace: "namespace", + Labels: map[string]string{"label_name": "label_value", "foo": "bar"}, + }, + Spec: v1alpha1.ApplicationSpec{}, + }, + expectedApps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + Labels: map[string]string{"foo": "bar"}, + }, + Spec: v1alpha1.ApplicationSpec{}, + }, + }, + }, + } { + cc := c + + t.Run(cc.name, func(t *testing.T) { + + generatorMock := generatorMock{} + generator := v1alpha1.ApplicationSetGenerator{ + List: &v1alpha1.ListGenerator{}, + } + + generatorMock.On("GenerateParams", &generator). + Return(cc.params, nil) + + generatorMock.On("GetTemplate", &generator). + Return(&cc.overrideTemplate) + + rendererMock := rendererMock{} + + rendererMock.On("RenderTemplateParams", getTempApplication(cc.expectedMerged), cc.params[0], false, []string(nil)). + Return(&cc.expectedApps[0], nil) + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(1), + Generators: map[string]generators.Generator{ + "List": &generatorMock, + }, + Renderer: &rendererMock, + KubeClientset: kubefake.NewSimpleClientset(), + } + + got, _, _ := r.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{generator}, + Template: cc.template, + }, + }, + ) + + assert.Equal(t, cc.expectedApps, got) + }) + } + +} + func TestCreateOrUpdateInCluster(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) for _, c := range []struct { // name is human-readable test name @@ -75,10 +394,8 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, - Spec: v1alpha1.ApplicationSpec{Project: "default"}, }, }, expected: []v1alpha1.Application{ @@ -130,8 +447,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -189,8 +505,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app2", - Namespace: "namespace", + Name: "app2", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -249,7 +564,6 @@ func TestCreateOrUpdateInCluster(t *testing.T) { { ObjectMeta: metav1.ObjectMeta{ Name: "app1", - Namespace: "namespace", Labels: map[string]string{"label-key": "label-value"}, Annotations: map[string]string{"annot-key": "annot-value"}, }, @@ -313,8 +627,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -380,8 +693,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -454,7 +766,6 @@ func TestCreateOrUpdateInCluster(t *testing.T) { { ObjectMeta: metav1.ObjectMeta{ Name: "app1", - Namespace: "namespace", Labels: map[string]string{"label-key": "label-value"}, Annotations: map[string]string{"annot-key": "annot-value"}, }, @@ -532,8 +843,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -560,8 +870,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, }, }, - }, - { + }, { name: "Ensure that configured preserved annotations are preserved from an existing app", appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -602,8 +911,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -629,8 +937,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, }, }, - }, - { + }, { name: "Ensure that the app spec is normalized before applying", appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -653,8 +960,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -685,8 +991,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, }, }, - }, - { + }, { // 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{ @@ -732,8 +1037,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -778,8 +1082,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, }, }, - }, - { + }, { // 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{ @@ -836,8 +1139,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -881,8 +1183,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, }, }, - }, - { + }, { 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{ @@ -938,8 +1239,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -982,95 +1282,29 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, }, }, - { - name: "Ensure that argocd post-delete finalizers are preserved from an existing app", - 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: "app1", - Namespace: "namespace", - ResourceVersion: "2", - Finalizers: []string{ - v1alpha1.PostDeleteFinalizerName, - v1alpha1.PostDeleteFinalizerName + "/mystage", - }, - }, - Spec: v1alpha1.ApplicationSpec{ - Project: "project", - }, - }, - }, - desiredApps: []v1alpha1.Application{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", - }, - 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", - Finalizers: []string{ - v1alpha1.PostDeleteFinalizerName, - v1alpha1.PostDeleteFinalizerName + "/mystage", - }, - }, - 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) - require.NoError(t, err) + assert.Nil(t, err) initObjs = append(initObjs, &a) } client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) r := ApplicationSetReconciler{ Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), - Metrics: metrics, + Cache: &fakeCache{}, } err = r.createOrUpdateInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps) - require.NoError(t, err) + assert.NoError(t, err) for _, obj := range c.expected { got := &v1alpha1.Application{} @@ -1087,12 +1321,13 @@ func TestCreateOrUpdateInCluster(t *testing.T) { } func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) for _, c := range []struct { // name is human-readable test name @@ -1122,6 +1357,7 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) { }, } { t.Run(c.name, func(t *testing.T) { + appSet := v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", @@ -1171,31 +1407,30 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) { objects := append([]runtime.Object{}, secret) kubeclientset := kubefake.NewSimpleClientset(objects...) - metrics := appsetmetrics.NewFakeAppsetMetrics(client) r := ApplicationSetReconciler{ Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(10), KubeClientset: kubeclientset, - Metrics: metrics, + Cache: &fakeCache{}, } - // settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "namespace") - // argoDB := db.NewDB("namespace", settingsMgr, r.KubeClientset) - // clusterList, err := argoDB.ListClusters(context.Background()) + //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") - require.NoError(t, err, "Unexpected error") + 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) - require.NoError(t, err, "Unexpected error") + assert.NoError(t, err, "Unexpected error") retrievedApp := v1alpha1.Application{} err = client.Get(context.Background(), crtclient.ObjectKeyFromObject(&app), &retrievedApp) - require.NoError(t, err, "Unexpected error") + assert.NoError(t, err, "Unexpected error") // App on the cluster should have the expected finalizers assert.ElementsMatch(t, c.expectedFinalizers, retrievedApp.Finalizers) @@ -1205,17 +1440,19 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) { bytes, _ := json.MarshalIndent(retrievedApp, "", " ") t.Log("Contents of app after call:", string(bytes)) + }) } } func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) for _, c := range []struct { // name is human-readable test name @@ -1281,7 +1518,9 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) { expectFinalizerRemoved: false, }, } { + t.Run(c.name, func(t *testing.T) { + appSet := v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", @@ -1330,38 +1569,38 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) { objects := append([]runtime.Object{}, secret) kubeclientset := kubefake.NewSimpleClientset(objects...) - metrics := appsetmetrics.NewFakeAppsetMetrics(client) r := ApplicationSetReconciler{ Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(10), KubeClientset: kubeclientset, - Metrics: metrics, + 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") - require.NoError(t, err, "Unexpected error") + 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) - require.NoError(t, err, "Unexpected error") + assert.NoError(t, err, "Unexpected error") retrievedApp := v1alpha1.Application{} err = client.Get(context.Background(), crtclient.ObjectKeyFromObject(&app), &retrievedApp) - require.NoError(t, err, "Unexpected error") + assert.NoError(t, err, "Unexpected error") finalizerRemoved := len(retrievedApp.Finalizers) == 0 - assert.Equal(t, c.expectFinalizerRemoved, finalizerRemoved) + assert.True(t, c.expectFinalizerRemoved == finalizerRemoved) bytes, _ := json.MarshalIndent(retrievedApp, "", " ") t.Log("Contents of app after call:", string(bytes)) + }) } } @@ -1369,10 +1608,10 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) { func TestRemoveOwnerReferencesOnDeleteAppSet(t *testing.T) { scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) for _, c := range []struct { // name is human-readable test name @@ -1414,27 +1653,26 @@ func TestRemoveOwnerReferencesOnDeleteAppSet(t *testing.T) { } err := controllerutil.SetControllerReference(&appSet, &app, scheme) - require.NoError(t, err, "Unexpected error") + 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() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) r := ApplicationSetReconciler{ Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(10), KubeClientset: nil, - Metrics: metrics, + Cache: &fakeCache{}, } err = r.removeOwnerReferencesOnDeleteAppSet(context.Background(), appSet) - require.NoError(t, err, "Unexpected error") + assert.NoError(t, err, "Unexpected error") retrievedApp := v1alpha1.Application{} err = client.Get(context.Background(), crtclient.ObjectKeyFromObject(&app), &retrievedApp) - require.NoError(t, err, "Unexpected error") + assert.NoError(t, err, "Unexpected error") ownerReferencesRemoved := len(retrievedApp.OwnerReferences) == 0 assert.True(t, ownerReferencesRemoved) @@ -1443,12 +1681,13 @@ func TestRemoveOwnerReferencesOnDeleteAppSet(t *testing.T) { } func TestCreateApplications(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) testCases := []struct { name string @@ -1469,8 +1708,7 @@ func TestCreateApplications(t *testing.T) { apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, }, }, @@ -1525,8 +1763,7 @@ func TestCreateApplications(t *testing.T) { apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -1584,8 +1821,7 @@ func TestCreateApplications(t *testing.T) { apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app2", - Namespace: "namespace", + Name: "app2", }, Spec: v1alpha1.ApplicationSpec{ Project: "project", @@ -1616,22 +1852,21 @@ func TestCreateApplications(t *testing.T) { initObjs := []crtclient.Object{&c.appSet} for _, a := range c.existsApps { err = controllerutil.SetControllerReference(&c.appSet, &a, scheme) - require.NoError(t, err) + assert.Nil(t, err) initObjs = append(initObjs, &a) } client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) r := ApplicationSetReconciler{ Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), - Metrics: metrics, + Cache: &fakeCache{}, } err = r.createInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.apps) - require.NoError(t, err) + assert.Nil(t, err) for _, obj := range c.expected { got := &v1alpha1.Application{} @@ -1641,7 +1876,7 @@ func TestCreateApplications(t *testing.T) { }, got) err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme) - require.NoError(t, err) + assert.Nil(t, err) assert.Equal(t, obj, *got) } @@ -1650,11 +1885,12 @@ func TestCreateApplications(t *testing.T) { } func TestDeleteInCluster(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) for _, c := range []struct { // appSet is the application set on which the delete function is called @@ -1760,23 +1996,21 @@ func TestDeleteInCluster(t *testing.T) { for _, a := range c.existingApps { temp := a err = controllerutil.SetControllerReference(&c.appSet, &temp, scheme) - require.NoError(t, err) + assert.Nil(t, err) initObjs = append(initObjs, &temp) } client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) r := ApplicationSetReconciler{ Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), KubeClientset: kubefake.NewSimpleClientset(), - Metrics: metrics, } err = r.deleteInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps) - require.NoError(t, err) + assert.Nil(t, err) // For each of the expected objects, verify they exist on the cluster for _, obj := range c.expected { @@ -1787,7 +2021,7 @@ func TestDeleteInCluster(t *testing.T) { }, got) err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme) - require.NoError(t, err) + assert.Nil(t, err) assert.Equal(t, obj, *got) } @@ -1808,12 +2042,11 @@ func TestDeleteInCluster(t *testing.T) { func TestGetMinRequeueAfter(t *testing.T) { scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) client := fake.NewClientBuilder().WithScheme(scheme).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) generator := v1alpha1.ApplicationSetGenerator{ List: &v1alpha1.ListGenerator{}, @@ -1821,15 +2054,15 @@ func TestGetMinRequeueAfter(t *testing.T) { Clusters: &v1alpha1.ClusterGenerator{}, } - generatorMock0 := mocks.Generator{} + generatorMock0 := generatorMock{} generatorMock0.On("GetRequeueAfter", &generator). Return(generators.NoRequeueAfter) - generatorMock1 := mocks.Generator{} + generatorMock1 := generatorMock{} generatorMock1.On("GetRequeueAfter", &generator). Return(time.Duration(1) * time.Second) - generatorMock10 := mocks.Generator{} + generatorMock10 := generatorMock{} generatorMock10.On("GetRequeueAfter", &generator). Return(time.Duration(10) * time.Second) @@ -1837,7 +2070,7 @@ func TestGetMinRequeueAfter(t *testing.T) { Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(0), - Metrics: metrics, + Cache: &fakeCache{}, Generators: map[string]generators.Generator{ "List": &generatorMock10, "Git": &generatorMock1, @@ -1854,70 +2087,16 @@ func TestGetMinRequeueAfter(t *testing.T) { assert.Equal(t, time.Duration(1)*time.Second, got) } -func TestRequeueGeneratorFails(t *testing.T) { - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - - appSet := v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "argocd", - }, - Spec: v1alpha1.ApplicationSetSpec{ - Generators: []v1alpha1.ApplicationSetGenerator{{ - PullRequest: &v1alpha1.PullRequestGenerator{}, - }}, - }, - } - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build() - - generator := v1alpha1.ApplicationSetGenerator{ - PullRequest: &v1alpha1.PullRequestGenerator{}, - } - - generatorMock := mocks.Generator{} - generatorMock.On("GetTemplate", &generator). - Return(&v1alpha1.ApplicationSetTemplate{}) - generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything). - Return([]map[string]interface{}{}, fmt.Errorf("Simulated error generating params that could be related to an external service/API call")) - - metrics := appsetmetrics.NewFakeAppsetMetrics(client) - - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(0), - Generators: map[string]generators.Generator{ - "PullRequest": &generatorMock, - }, - Metrics: metrics, - } - - req := ctrl.Request{ - NamespacedName: types.NamespacedName{ - Namespace: "argocd", - Name: "name", - }, - } - - res, err := r.Reconcile(context.Background(), req) - require.Error(t, err) - assert.Equal(t, ReconcileRequeueOnValidationError, res.RequeueAfter) -} - func TestValidateGeneratedApplications(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) client := fake.NewClientBuilder().WithScheme(scheme).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) // Valid cluster myCluster := v1alpha1.Cluster{ @@ -2069,7 +2248,9 @@ func TestValidateGeneratedApplications(t *testing.T) { 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", @@ -2103,12 +2284,12 @@ func TestValidateGeneratedApplications(t *testing.T) { 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, - Metrics: metrics, } appSetInfo := v1alpha1.ApplicationSet{} @@ -2120,7 +2301,7 @@ func TestValidateGeneratedApplications(t *testing.T) { } if len(errorMessages) == 0 { - assert.Empty(t, cc.expectedErrors, "Expected errors but none were seen") + assert.Equal(t, len(cc.expectedErrors), 0, "Expected errors but none were seen") } else { // An error was returned: it should be expected matched := false @@ -2147,11 +2328,12 @@ func TestValidateGeneratedApplications(t *testing.T) { } func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) project := v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{Name: "good-project", Namespace: "argocd"}, @@ -2192,8 +2374,7 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) { argoDBMock := dbmocks.ArgoDB{} argoObjs := []runtime.Object{&project} - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) + 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) @@ -2207,6 +2388,7 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) { Scheme: scheme, Renderer: &utils.Render{}, Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{ "List": generators.NewListGenerator(), }, @@ -2215,7 +2397,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) { KubeClientset: kubeclientset, Policy: v1alpha1.ApplicationsSyncPolicySync, ArgoCDNamespace: "argocd", - Metrics: metrics, } req := ctrl.Request{ @@ -2227,209 +2408,85 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) { // Verify that on validation error, no error is returned, but the object is requeued res, err := r.Reconcile(context.Background(), req) - require.NoError(t, err) - assert.Equal(t, ReconcileRequeueOnValidationError, res.RequeueAfter) + 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) - require.NoError(t, err) - assert.Equal(t, "good-project", app.Name) + 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) - require.Error(t, err) + assert.Error(t, err) } func TestSetApplicationSetStatusCondition(t *testing.T) { scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) - testCases := []struct { - appset v1alpha1.ApplicationSet - conditions []v1alpha1.ApplicationSetCondition - testfunc func(t *testing.T, appset v1alpha1.ApplicationSet) - }{ - { - 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{}, - }, - }, - conditions: []v1alpha1.ApplicationSetCondition{ - { - Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, - Message: "All applications have been generated successfully", - Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - }, - }, - testfunc: func(t *testing.T, appset v1alpha1.ApplicationSet) { - assert.Len(t, appset.Status.Conditions, 3) - }, + appSet := v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", }, - { - 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{}, - }, - }, - conditions: []v1alpha1.ApplicationSetCondition{ - { - Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, - Message: "All applications have been generated successfully", - Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - }, - { - Type: v1alpha1.ApplicationSetConditionRolloutProgressing, - Message: "ApplicationSet Rollout Rollout started", - Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - }, - }, - testfunc: func(t *testing.T, appset v1alpha1.ApplicationSet) { - assert.Len(t, appset.Status.Conditions, 3) - - isProgressingCondition := false - - for _, condition := range appset.Status.Conditions { - if condition.Type == v1alpha1.ApplicationSetConditionRolloutProgressing { - isProgressingCondition = true - break - } - } - - assert.False(t, isProgressingCondition, "no RolloutProgressing should be set for applicationsets that don't have rolling strategy") + 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{}, }, - { - 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{}, - Strategy: &v1alpha1.ApplicationSetStrategy{ - Type: "RollingSync", - RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ - Steps: []v1alpha1.ApplicationSetRolloutStep{ - { - MatchExpressions: []v1alpha1.ApplicationMatchExpression{ - { - Key: "test", - Operator: "In", - Values: []string{"test"}, - }, - }, - }, - }, - }, - }, - }, - }, - conditions: []v1alpha1.ApplicationSetCondition{ - { - Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, - Message: "All applications have been generated successfully", - Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - }, - { - Type: v1alpha1.ApplicationSetConditionRolloutProgressing, - Message: "ApplicationSet Rollout Rollout started", - Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - }, - }, - testfunc: func(t *testing.T, appset v1alpha1.ApplicationSet) { - assert.Len(t, appset.Status.Conditions, 4) - - isProgressingCondition := false - - for _, condition := range appset.Status.Conditions { - if condition.Type == v1alpha1.ApplicationSetConditionRolloutProgressing { - isProgressingCondition = true - break - } - } + } - assert.True(t, isProgressingCondition, "RolloutProgressing should be set for rollout strategy appset") - }, - }, + 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{} - for _, testCase := range testCases { - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&testCase.appset).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).WithStatusSubresource(&testCase.appset).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) + 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), - Generators: map[string]generators.Generator{ - "List": generators.NewListGenerator(), - }, - ArgoDB: &argoDBMock, - ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), - KubeClientset: kubeclientset, - Metrics: metrics, - } + 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, + } - for _, condition := range testCase.conditions { - err = r.setApplicationSetStatusCondition(context.TODO(), &testCase.appset, condition, true) - require.NoError(t, err) - } + err = r.setApplicationSetStatusCondition(context.TODO(), &appSet, appCondition, true) + assert.Nil(t, err) - testCase.testfunc(t, testCase.appset) - } + 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) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) defaultProject := v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"}, @@ -2471,8 +2528,7 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp argoDBMock := dbmocks.ArgoDB{} argoObjs := []runtime.Object{&defaultProject} - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) + 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{ @@ -2484,6 +2540,7 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp Scheme: scheme, Renderer: &utils.Render{}, Recorder: record.NewFakeRecorder(recordBuffer), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{ "List": generators.NewListGenerator(), }, @@ -2493,7 +2550,6 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp KubeClientset: kubeclientset, Policy: v1alpha1.ApplicationsSyncPolicySync, EnablePolicyOverride: allowPolicyOverride, - Metrics: metrics, } req := ctrl.Request{ @@ -2505,20 +2561,20 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp // Verify that on validation error, no error is returned, but the object is requeued resCreate, err := r.Reconcile(context.Background(), req) - require.NoError(t, err) - assert.Equal(t, time.Duration(0), resCreate.RequeueAfter) + 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) - require.NoError(t, err) - assert.Equal(t, "good-cluster", app.Name) + 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) - require.NoError(t, err) + 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"} @@ -2528,20 +2584,21 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp } err = r.Client.Update(context.TODO(), &retrievedApplicationSet) - require.NoError(t, err) + assert.Nil(t, err) resUpdate, err := r.Reconcile(context.Background(), req) - require.NoError(t, err) + assert.Nil(t, err) err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "good-cluster"}, &app) - require.NoError(t, err) - assert.Equal(t, time.Duration(0), resUpdate.RequeueAfter) - assert.Equal(t, "good-cluster", app.Name) + 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) @@ -2551,6 +2608,7 @@ func TestUpdateNotPerformedWithSyncPolicyCreateOnly(t *testing.T) { } func TestUpdateNotPerformedWithSyncPolicyCreateDelete(t *testing.T) { + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateDelete app := applicationsUpdateSyncPolicyTest(t, applicationsSyncPolicy, 1, true) @@ -2560,6 +2618,7 @@ func TestUpdateNotPerformedWithSyncPolicyCreateDelete(t *testing.T) { } func TestUpdatePerformedWithSyncPolicyCreateUpdate(t *testing.T) { + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateUpdate app := applicationsUpdateSyncPolicyTest(t, applicationsSyncPolicy, 2, true) @@ -2570,6 +2629,7 @@ func TestUpdatePerformedWithSyncPolicyCreateUpdate(t *testing.T) { } func TestUpdatePerformedWithSyncPolicySync(t *testing.T) { + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicySync app := applicationsUpdateSyncPolicyTest(t, applicationsSyncPolicy, 2, true) @@ -2580,6 +2640,7 @@ func TestUpdatePerformedWithSyncPolicySync(t *testing.T) { } func TestUpdatePerformedWithSyncPolicyCreateOnlyAndAllowPolicyOverrideFalse(t *testing.T) { + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateOnly app := applicationsUpdateSyncPolicyTest(t, applicationsSyncPolicy, 2, false) @@ -2590,11 +2651,12 @@ func TestUpdatePerformedWithSyncPolicyCreateOnlyAndAllowPolicyOverrideFalse(t *t } func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alpha1.ApplicationsSyncPolicy, recordBuffer int, allowPolicyOverride bool) v1alpha1.ApplicationList { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) defaultProject := v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"}, @@ -2636,8 +2698,7 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp argoDBMock := dbmocks.ArgoDB{} argoObjs := []runtime.Object{&defaultProject} - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) + 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{ @@ -2649,6 +2710,7 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp Scheme: scheme, Renderer: &utils.Render{}, Recorder: record.NewFakeRecorder(recordBuffer), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{ "List": generators.NewListGenerator(), }, @@ -2658,7 +2720,6 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp KubeClientset: kubeclientset, Policy: v1alpha1.ApplicationsSyncPolicySync, EnablePolicyOverride: allowPolicyOverride, - Metrics: metrics, } req := ctrl.Request{ @@ -2670,20 +2731,20 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp // Verify that on validation error, no error is returned, but the object is requeued resCreate, err := r.Reconcile(context.Background(), req) - require.NoError(t, err) - assert.Equal(t, time.Duration(0), resCreate.RequeueAfter) + 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) - require.NoError(t, err) - assert.Equal(t, "good-cluster", app.Name) + 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) - require.NoError(t, err) + assert.Nil(t, err) retrievedApplicationSet.Spec.Generators = []v1alpha1.ApplicationSetGenerator{ { List: &v1alpha1.ListGenerator{ @@ -2693,21 +2754,22 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp } err = r.Client.Update(context.TODO(), &retrievedApplicationSet) - require.NoError(t, err) + assert.Nil(t, err) resUpdate, err := r.Reconcile(context.Background(), req) - require.NoError(t, err) + assert.Nil(t, err) var apps v1alpha1.ApplicationList err = r.Client.List(context.TODO(), &apps) - require.NoError(t, err) - assert.Equal(t, time.Duration(0), resUpdate.RequeueAfter) + assert.Nil(t, err) + assert.True(t, resUpdate.RequeueAfter == 0) return apps } func TestDeleteNotPerformedWithSyncPolicyCreateOnly(t *testing.T) { + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateOnly apps := applicationsDeleteSyncPolicyTest(t, applicationsSyncPolicy, 1, true) @@ -2716,6 +2778,7 @@ func TestDeleteNotPerformedWithSyncPolicyCreateOnly(t *testing.T) { } func TestDeleteNotPerformedWithSyncPolicyCreateUpdate(t *testing.T) { + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateUpdate apps := applicationsDeleteSyncPolicyTest(t, applicationsSyncPolicy, 2, true) @@ -2724,55 +2787,172 @@ func TestDeleteNotPerformedWithSyncPolicyCreateUpdate(t *testing.T) { } func TestDeletePerformedWithSyncPolicyCreateDelete(t *testing.T) { + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateDelete apps := applicationsDeleteSyncPolicyTest(t, applicationsSyncPolicy, 3, true) - assert.Empty(t, apps.Items) + assert.Equal(t, 0, len(apps.Items)) } func TestDeletePerformedWithSyncPolicySync(t *testing.T) { + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicySync apps := applicationsDeleteSyncPolicyTest(t, applicationsSyncPolicy, 3, true) - assert.Empty(t, apps.Items) + assert.Equal(t, 0, len(apps.Items)) } func TestDeletePerformedWithSyncPolicyCreateOnlyAndAllowPolicyOverrideFalse(t *testing.T) { + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateOnly apps := applicationsDeleteSyncPolicyTest(t, applicationsSyncPolicy, 3, false) - assert.Empty(t, apps.Items) + assert.Equal(t, 0, len(apps.Items)) } -func TestPolicies(t *testing.T) { +// Test app generation from a go template application set using a pull request generator +func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) { scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + client := fake.NewClientBuilder().WithScheme(scheme).Build() - err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + for _, cases := range []struct { + name string + params []map[string]interface{} + 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", + "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}}", + "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: v1alpha1.ApplicationSpec{ + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://testurl/testRepo", + TargetRevision: "{{.head_short_sha}}", + }, + Destination: v1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "AppSet-{{.branch_slug}}-{{.head_sha}}", + }, + }, + }, + expectedApp: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "AppSet-branch1-1", + Labels: map[string]string{ + "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{ + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://testurl/testRepo", + TargetRevision: "089d92cb", + }, + Destination: v1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "AppSet-branchSlug1-089d92cbf9ff857a39e6feccd32798ca700fb958", + }, + }, + }, + }, + }, + } { - defaultProject := v1alpha1.AppProject{ - ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"}, - Spec: v1alpha1.AppProjectSpec{SourceRepos: []string{"*"}, Destinations: []v1alpha1.ApplicationDestination{{Namespace: "*", Server: "https://kubernetes.default.svc"}}}, - } - myCluster := v1alpha1.Cluster{ - Server: "https://kubernetes.default.svc", - Name: "my-cluster", - } + t.Run(cases.name, func(t *testing.T) { - kubeclientset := kubefake.NewSimpleClientset() - argoDBMock := dbmocks.ArgoDB{} - argoDBMock.On("GetCluster", mock.Anything, "https://kubernetes.default.svc").Return(&myCluster, nil) - argoObjs := []runtime.Object{&defaultProject} + generatorMock := generatorMock{} + generator := v1alpha1.ApplicationSetGenerator{ + PullRequest: &v1alpha1.PullRequestGenerator{}, + } - for _, c := range []struct { - name string - policyName string - allowedUpdate bool + generatorMock.On("GenerateParams", &generator). + Return(cases.params, nil) + + generatorMock.On("GetTemplate", &generator). + Return(&cases.template, nil) + + appSetReconciler := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, + Generators: map[string]generators.Generator{ + "PullRequest": &generatorMock, + }, + Renderer: &utils.Render{}, + KubeClientset: kubefake.NewSimpleClientset(), + } + + gotApp, _, _ := appSetReconciler.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{ + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Generators: []v1alpha1.ApplicationSetGenerator{{ + PullRequest: &v1alpha1.PullRequestGenerator{}, + }}, + Template: cases.template, + }, + }, + ) + assert.EqualValues(t, cases.expectedApp[0].ObjectMeta.Name, gotApp[0].ObjectMeta.Name) + assert.EqualValues(t, cases.expectedApp[0].Spec.Source.TargetRevision, gotApp[0].Spec.Source.TargetRevision) + assert.EqualValues(t, cases.expectedApp[0].Spec.Destination.Namespace, gotApp[0].Spec.Destination.Namespace) + assert.True(t, collections.StringMapsEqual(cases.expectedApp[0].ObjectMeta.Labels, gotApp[0].ObjectMeta.Labels)) + }) + } +} + +func TestPolicies(t *testing.T) { + 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://kubernetes.default.svc"}}}, + } + myCluster := v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Name: "my-cluster", + } + + kubeclientset := kubefake.NewSimpleClientset() + argoDBMock := dbmocks.ArgoDB{} + argoDBMock.On("GetCluster", mock.Anything, "https://kubernetes.default.svc").Return(&myCluster, nil) + argoObjs := []runtime.Object{&defaultProject} + + for _, c := range []struct { + name string + policyName string + allowedUpdate bool allowedDelete bool }{ { @@ -2839,14 +3019,14 @@ func TestPolicies(t *testing.T) { }, } - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) + 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(), }, @@ -2855,7 +3035,6 @@ func TestPolicies(t *testing.T) { ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), KubeClientset: kubeclientset, Policy: policy, - Metrics: metrics, } req := ctrl.Request{ @@ -2867,49 +3046,49 @@ func TestPolicies(t *testing.T) { // Check if Application is created res, err := r.Reconcile(context.Background(), req) - require.NoError(t, err) - assert.Equal(t, time.Duration(0), res.RequeueAfter) + assert.Nil(t, err) + assert.True(t, res.RequeueAfter == 0) var app v1alpha1.Application err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app) - require.NoError(t, err) - assert.Equal(t, "value", app.Annotations["key"]) + assert.NoError(t, err) + assert.Equal(t, app.Annotations["key"], "value") // Check if Application is updated app.Annotations["key"] = "edited" err = r.Client.Update(context.TODO(), &app) - require.NoError(t, err) + assert.NoError(t, err) res, err = r.Reconcile(context.Background(), req) - require.NoError(t, err) - assert.Equal(t, time.Duration(0), res.RequeueAfter) + assert.Nil(t, err) + assert.True(t, res.RequeueAfter == 0) err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app) - require.NoError(t, err) + assert.NoError(t, err) if c.allowedUpdate { - assert.Equal(t, "value", app.Annotations["key"]) + assert.Equal(t, app.Annotations["key"], "value") } else { - assert.Equal(t, "edited", app.Annotations["key"]) + assert.Equal(t, app.Annotations["key"], "edited") } // Check if Application is deleted err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "name"}, &appSet) - require.NoError(t, err) + assert.NoError(t, err) appSet.Spec.Generators[0] = v1alpha1.ApplicationSetGenerator{ List: &v1alpha1.ListGenerator{ Elements: []apiextensionsv1.JSON{}, }, } err = r.Client.Update(context.TODO(), &appSet) - require.NoError(t, err) + assert.NoError(t, err) res, err = r.Reconcile(context.Background(), req) - require.NoError(t, err) - assert.Equal(t, time.Duration(0), res.RequeueAfter) + assert.Nil(t, err) + assert.True(t, res.RequeueAfter == 0) err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app) - require.NoError(t, err) + assert.NoError(t, err) if c.allowedDelete { assert.NotNil(t, app.DeletionTimestamp) } else { @@ -2922,9 +3101,9 @@ func TestPolicies(t *testing.T) { func TestSetApplicationSetApplicationStatus(t *testing.T) { scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) argoDBMock := dbmocks.ArgoDB{} @@ -3000,26 +3179,27 @@ func TestSetApplicationSetApplicationStatus(t *testing.T) { expectedAppStatuses: nil, }, } { + t.Run(cc.name, func(t *testing.T) { - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).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, - Metrics: metrics, } err = r.setAppSetApplicationStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appStatuses) - require.NoError(t, err) + assert.Nil(t, err) assert.Equal(t, cc.expectedAppStatuses, cc.appSet.Status.ApplicationStatus) }) @@ -3027,15 +3207,15 @@ func TestSetApplicationSetApplicationStatus(t *testing.T) { } func TestBuildAppDependencyList(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) client := fake.NewClientBuilder().WithScheme(scheme).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) for _, cc := range []struct { name string @@ -3763,7 +3943,9 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, } { + t.Run(cc.name, func(t *testing.T) { + kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) argoDBMock := dbmocks.ArgoDB{} argoObjs := []runtime.Object{} @@ -3772,14 +3954,15 @@ 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, - Metrics: metrics, } - appDependencyList, appStepMap := r.buildAppDependencyList(log.NewEntry(log.StandardLogger()), 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") }) @@ -3787,15 +3970,15 @@ func TestBuildAppDependencyList(t *testing.T) { } func TestBuildAppSyncMap(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) client := fake.NewClientBuilder().WithScheme(scheme).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) for _, cc := range []struct { name string @@ -4354,7 +4537,9 @@ func TestBuildAppSyncMap(t *testing.T) { }, }, } { + t.Run(cc.name, func(t *testing.T) { + kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) argoDBMock := dbmocks.ArgoDB{} argoObjs := []runtime.Object{} @@ -4363,26 +4548,28 @@ 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...), KubeClientset: kubeclientset, - Metrics: metrics, } - appSyncMap := r.buildAppSyncMap(cc.appSet, cc.appDependencyList, cc.appMap) + appSyncMap, err := r.buildAppSyncMap(context.TODO(), cc.appSet, cc.appDependencyList, cc.appMap) + assert.Equal(t, err, nil, "expected no errors, but errors occured") assert.Equal(t, cc.expectedMap, appSyncMap, "expected appSyncMap did not match actual") }) } } func TestUpdateApplicationSetApplicationStatus(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) for _, cc := range []struct { name string @@ -4442,11 +4629,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { }, expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", - Status: "Healthy", - Step: "1", - TargetRevisions: []string{}, + Application: "app1", + Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", + Status: "Healthy", + Step: "1", }, }, }, @@ -4485,16 +4671,15 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { }, expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", - Status: "Healthy", - Step: "1", - TargetRevisions: []string{}, + Application: "app1", + Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", + Status: "Healthy", + Step: "1", }, }, }, { - name: "handles an outdated list of statuses with a healthy application, setting required variables", + name: "progresses an OutOfSync RollingSync application to waiting", appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", @@ -4510,7 +4695,7 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", - Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", + Message: "", Status: "Healthy", Step: "1", }, @@ -4522,99 +4707,19 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { 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", - TargetRevisions: []string{}, - }, - }, - }, - { - name: "progresses an OutOfSync RollingSync application to waiting", - 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: "", - Status: "Healthy", - Step: "1", - TargetRevisions: []string{"Previous"}, - }, - { - Application: "app2-multisource", - Message: "", - Status: "Healthy", - Step: "1", - TargetRevisions: []string{"Previous", "OtherPrevious"}, - }, - }, - }, - }, - apps: []v1alpha1.Application{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - }, - Status: v1alpha1.ApplicationStatus{ - Sync: v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeOutOfSync, - Revision: "Next", - }, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: "app2-multisource", - }, Status: v1alpha1.ApplicationStatus{ Sync: v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeOutOfSync, - Revisions: []string{"Next", "OtherNext"}, + Status: v1alpha1.SyncStatusCodeOutOfSync, }, }, }, }, expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "Application has pending changes, setting status to Waiting.", - Status: "Waiting", - Step: "1", - TargetRevisions: []string{"Next"}, - }, - { - Application: "app2-multisource", - Message: "Application has pending changes, setting status to Waiting.", - Status: "Waiting", - Step: "1", - TargetRevisions: []string{"Next", "OtherNext"}, + Application: "app1", + Message: "Application has pending changes, setting status to Waiting.", + Status: "Waiting", + Step: "1", }, }, }, @@ -4634,11 +4739,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { Status: v1alpha1.ApplicationSetStatus{ ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "", - Status: "Pending", - Step: "1", - TargetRevisions: []string{"Next"}, + Application: "app1", + Message: "", + Status: "Pending", + Step: "1", }, }, }, @@ -4657,16 +4761,15 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { }, expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "Application resource became Progressing, updating status from Pending to Progressing.", - Status: "Progressing", - Step: "1", - TargetRevisions: []string{"Next"}, + Application: "app1", + Message: "Application resource became Progressing, updating status from Pending to Progressing.", + Status: "Progressing", + Step: "1", }, }, }, { - name: "progresses a pending synced application to progressing", + name: "progresses a pending syncing application to progressing", appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", @@ -4681,11 +4784,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { Status: v1alpha1.ApplicationSetStatus{ ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "", - Status: "Pending", - Step: "1", - TargetRevisions: []string{"Current"}, + Application: "app1", + Message: "", + Status: "Pending", + Step: "1", }, }, }, @@ -4710,11 +4812,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { }, expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "Application resource became Progressing, updating status from Pending to Progressing.", - Status: "Progressing", - Step: "1", - TargetRevisions: []string{"Current"}, + Application: "app1", + Message: "Application resource became Progressing, updating status from Pending to Progressing.", + Status: "Progressing", + Step: "1", }, }, }, @@ -4734,11 +4835,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { Status: v1alpha1.ApplicationSetStatus{ ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "", - Status: "Progressing", - Step: "1", - TargetRevisions: []string{"Next"}, + Application: "app1", + Message: "", + Status: "Progressing", + Step: "1", }, }, }, @@ -4763,11 +4863,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { }, expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "Application resource became Healthy, updating status from Progressing to Healthy.", - Status: "Healthy", - Step: "1", - TargetRevisions: []string{"Next"}, + Application: "app1", + Message: "Application resource became Healthy, updating status from Progressing to Healthy.", + Status: "Healthy", + Step: "1", }, }, }, @@ -4787,11 +4886,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { Status: v1alpha1.ApplicationSetStatus{ ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "", - Status: "Waiting", - Step: "1", - TargetRevisions: []string{"Current"}, + Application: "app1", + Message: "", + Status: "Waiting", + Step: "1", }, }, }, @@ -4816,11 +4914,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { }, expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", - Status: "Healthy", - Step: "1", - TargetRevisions: []string{"Current"}, + Application: "app1", + Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", + Status: "Healthy", + Step: "1", }, }, }, @@ -4849,13 +4946,9 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { }, OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, - SyncResult: &v1alpha1.SyncOperationResult{ - Revision: "Previous", - }, }, Sync: v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeOutOfSync, - Revision: "Next", + Status: v1alpha1.SyncStatusCodeOutOfSync, }, }, }, @@ -4866,16 +4959,15 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { }, expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "No Application status found, defaulting status to Waiting.", - Status: "Waiting", - Step: "2", - TargetRevisions: []string{"Next"}, + Application: "app1", + Message: "No Application status found, defaulting status to Waiting.", + Status: "Waiting", + Step: "2", }, }, }, { - name: "progresses a pending application with a successful sync triggered by controller to progressing", + name: "progresses a pending application with a successful sync to progressing", appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", @@ -4894,10 +4986,9 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { LastTransitionTime: &metav1.Time{ Time: time.Now().Add(time.Duration(-1) * time.Minute), }, - Message: "", - Status: "Pending", - Step: "1", - TargetRevisions: []string{"Next"}, + Message: "", + Status: "Pending", + Step: "1", }, }, }, @@ -4916,35 +5007,24 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { StartedAt: metav1.Time{ Time: time.Now(), }, - Operation: v1alpha1.Operation{ - InitiatedBy: v1alpha1.OperationInitiator{ - Username: "applicationset-controller", - Automated: true, - }, - }, - SyncResult: &v1alpha1.SyncOperationResult{ - Revision: "Next", - }, }, Sync: v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeSynced, - Revision: "Next", + 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", - TargetRevisions: []string{"Next"}, + 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 trigger by applicationset-controller <1s ago to progressing", + name: "progresses a pending application with a successful sync <1s ago to progressing", appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", @@ -4963,10 +5043,9 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { LastTransitionTime: &metav1.Time{ Time: time.Now(), }, - Message: "", - Status: "Pending", - Step: "1", - TargetRevisions: []string{"Next"}, + Message: "", + Status: "Pending", + Step: "1", }, }, }, @@ -4985,35 +5064,24 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { StartedAt: metav1.Time{ Time: time.Now().Add(time.Duration(-1) * time.Second), }, - Operation: v1alpha1.Operation{ - InitiatedBy: v1alpha1.OperationInitiator{ - Username: "applicationset-controller", - Automated: true, - }, - }, - SyncResult: &v1alpha1.SyncOperationResult{ - Revision: "Next", - }, }, Sync: v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeSynced, - Revision: "Next", + 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", - TargetRevisions: []string{"Next"}, + 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 a successful sync triggered by controller with invalid revision to progressing", + name: "does not progresses a pending application with an old successful sync to progressing", appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", @@ -5030,12 +5098,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { { Application: "app1", LastTransitionTime: &metav1.Time{ - Time: time.Now().Add(time.Duration(-1) * time.Minute), + Time: time.Now(), }, - Message: "", - Status: "Pending", - Step: "1", - TargetRevisions: []string{"Next"}, + Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", + Status: "Pending", + Step: "1", }, }, }, @@ -5052,16 +5119,7 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, StartedAt: metav1.Time{ - Time: time.Now(), - }, - Operation: v1alpha1.Operation{ - InitiatedBy: v1alpha1.OperationInitiator{ - Username: "applicationset-controller", - Automated: true, - }, - }, - SyncResult: &v1alpha1.SyncOperationResult{ - Revision: "Previous", + Time: time.Now().Add(time.Duration(-11) * time.Second), }, }, Sync: v1alpha1.SyncStatus{ @@ -5072,11 +5130,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { }, expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "", - Status: "Pending", - Step: "1", - TargetRevisions: []string{"Next"}, + Application: "app1", + Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", + Status: "Pending", + Step: "1", }, }, }, @@ -5096,18 +5153,16 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { Status: v1alpha1.ApplicationSetStatus{ ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "Application has pending changes, setting status to Waiting.", - Status: "Waiting", - Step: "1", - TargetRevisions: []string{"Current"}, + 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", - TargetRevisions: []string{"Current"}, + Application: "app2", + Message: "Application has pending changes, setting status to Waiting.", + Status: "Waiting", + Step: "1", }, }, }, @@ -5132,32 +5187,32 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { }, expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { - Application: "app1", - Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", - Status: "Healthy", - Step: "1", - TargetRevisions: []string{"Current"}, + Application: "app1", + Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", + Status: "Healthy", + Step: "1", }, }, }, } { + t.Run(cc.name, func(t *testing.T) { + kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) argoDBMock := dbmocks.ArgoDB{} argoObjs := []runtime.Object{} - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).Build() r := ApplicationSetReconciler{ Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{}, ArgoDB: &argoDBMock, ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), KubeClientset: kubeclientset, - Metrics: metrics, } appStatuses, err := r.updateApplicationSetApplicationStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps, cc.appStepMap) @@ -5167,19 +5222,20 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { appStatuses[i].LastTransitionTime = nil } - require.NoError(t, err, "expected no errors, but errors occurred") + assert.Equal(t, err, nil, "expected no errors, but errors occured") assert.Equal(t, cc.expectedAppStatus, appStatuses, "expected appStatuses did not match actual") }) } } func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { + scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) for _, cc := range []struct { name string @@ -5299,10 +5355,9 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { 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", - TargetRevisions: []string{"Next"}, + Application: "app1", + Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", + Status: "Waiting", }, }, }, @@ -5320,7 +5375,6 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", Status: "Pending", Step: "1", - TargetRevisions: []string{"Next"}, }, }, }, @@ -5895,347 +5949,39 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { }, }, } { + t.Run(cc.name, func(t *testing.T) { + kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) argoDBMock := dbmocks.ArgoDB{} argoObjs := []runtime.Object{} - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).Build() r := ApplicationSetReconciler{ Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{}, ArgoDB: &argoDBMock, ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), KubeClientset: kubeclientset, - Metrics: metrics, } - appStatuses, err := r.updateApplicationSetApplicationStatusProgress(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appSyncMap, cc.appStepMap) + 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 { appStatuses[i].LastTransitionTime = nil } - require.NoError(t, err, "expected no errors, but errors occurred") + assert.Equal(t, err, nil, "expected no errors, but errors occured") assert.Equal(t, cc.expectedAppStatus, appStatuses, "expected appStatuses did not match actual") }) } } -func TestUpdateResourceStatus(t *testing.T) { - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - - err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - - for _, cc := range []struct { - name string - appSet v1alpha1.ApplicationSet - apps []v1alpha1.Application - expectedResources []v1alpha1.ResourceStatus - }{ - { - name: "handles an empty application list", - appSet: v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "argocd", - }, - Status: v1alpha1.ApplicationSetStatus{ - Resources: []v1alpha1.ResourceStatus{}, - }, - }, - apps: []v1alpha1.Application{}, - expectedResources: nil, - }, - { - name: "adds status if no existing statuses", - appSet: v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "argocd", - }, - Status: v1alpha1.ApplicationSetStatus{ - ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{}, - }, - }, - apps: []v1alpha1.Application{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - }, - Status: v1alpha1.ApplicationStatus{ - Sync: v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeSynced, - }, - Health: v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "OK", - }, - }, - }, - }, - expectedResources: []v1alpha1.ResourceStatus{ - { - Name: "app1", - Status: v1alpha1.SyncStatusCodeSynced, - Health: &v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "OK", - }, - }, - }, - }, - { - name: "handles an applicationset with existing and up-to-date status", - appSet: v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "argocd", - }, - Status: v1alpha1.ApplicationSetStatus{ - Resources: []v1alpha1.ResourceStatus{ - { - Name: "app1", - Status: v1alpha1.SyncStatusCodeSynced, - Health: &v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "OK", - }, - }, - }, - }, - }, - apps: []v1alpha1.Application{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - }, - Status: v1alpha1.ApplicationStatus{ - Sync: v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeSynced, - }, - Health: v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "OK", - }, - }, - }, - }, - expectedResources: []v1alpha1.ResourceStatus{ - { - Name: "app1", - Status: v1alpha1.SyncStatusCodeSynced, - Health: &v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "OK", - }, - }, - }, - }, - { - name: "updates an applicationset with existing and out of date status", - appSet: v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "argocd", - }, - Status: v1alpha1.ApplicationSetStatus{ - Resources: []v1alpha1.ResourceStatus{ - { - Name: "app1", - Status: v1alpha1.SyncStatusCodeOutOfSync, - Health: &v1alpha1.HealthStatus{ - Status: health.HealthStatusProgressing, - Message: "Progressing", - }, - }, - }, - }, - }, - apps: []v1alpha1.Application{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - }, - Status: v1alpha1.ApplicationStatus{ - Sync: v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeSynced, - }, - Health: v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "OK", - }, - }, - }, - }, - expectedResources: []v1alpha1.ResourceStatus{ - { - Name: "app1", - Status: v1alpha1.SyncStatusCodeSynced, - Health: &v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "OK", - }, - }, - }, - }, - { - name: "deletes an applicationset status if the application no longer exists", - appSet: v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "argocd", - }, - Status: v1alpha1.ApplicationSetStatus{ - Resources: []v1alpha1.ResourceStatus{ - { - Name: "app1", - Status: v1alpha1.SyncStatusCodeSynced, - Health: &v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "OK", - }, - }, - }, - }, - }, - apps: []v1alpha1.Application{}, - expectedResources: nil, - }, - } { - t.Run(cc.name, func(t *testing.T) { - kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) - argoDBMock := dbmocks.ArgoDB{} - argoObjs := []runtime.Object{} - - client := fake.NewClientBuilder().WithScheme(scheme).WithStatusSubresource(&cc.appSet).WithObjects(&cc.appSet).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) - - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(1), - Generators: map[string]generators.Generator{}, - ArgoDB: &argoDBMock, - ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), - KubeClientset: kubeclientset, - Metrics: metrics, - } - - err := r.updateResourcesStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps) - - require.NoError(t, err, "expected no errors, but errors occurred") - assert.Equal(t, cc.expectedResources, cc.appSet.Status.Resources, "expected resources did not match actual") - }) - } -} - -func generateNAppResourceStatuses(n int) []v1alpha1.ResourceStatus { - var r []v1alpha1.ResourceStatus - for i := 0; i < n; i++ { - r = append(r, v1alpha1.ResourceStatus{ - Name: "app" + strconv.Itoa(i), - Status: v1alpha1.SyncStatusCodeSynced, - Health: &v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "OK", - }, - }, - ) - } - return r -} - -func generateNHealthyApps(n int) []v1alpha1.Application { - var r []v1alpha1.Application - for i := 0; i < n; i++ { - r = append(r, v1alpha1.Application{ - ObjectMeta: metav1.ObjectMeta{ - Name: "app" + strconv.Itoa(i), - }, - Status: v1alpha1.ApplicationStatus{ - Sync: v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeSynced, - }, - Health: v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "OK", - }, - }, - }) - } - return r -} - -func TestResourceStatusAreOrdered(t *testing.T) { - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - - err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - for _, cc := range []struct { - name string - appSet v1alpha1.ApplicationSet - apps []v1alpha1.Application - expectedResources []v1alpha1.ResourceStatus - }{ - { - name: "Ensures AppSet is always ordered", - appSet: v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "argocd", - }, - Status: v1alpha1.ApplicationSetStatus{ - Resources: []v1alpha1.ResourceStatus{}, - }, - }, - apps: generateNHealthyApps(10), - expectedResources: generateNAppResourceStatuses(10), - }, - } { - t.Run(cc.name, func(t *testing.T) { - kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) - argoDBMock := dbmocks.ArgoDB{} - argoObjs := []runtime.Object{} - - client := fake.NewClientBuilder().WithScheme(scheme).WithStatusSubresource(&cc.appSet).WithObjects(&cc.appSet).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) - - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(1), - Generators: map[string]generators.Generator{}, - ArgoDB: &argoDBMock, - ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), - KubeClientset: kubeclientset, - Metrics: metrics, - } - - err := r.updateResourcesStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps) - require.NoError(t, err, "expected no errors, but errors occurred") - - err = r.updateResourcesStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps) - require.NoError(t, err, "expected no errors, but errors occurred") - - err = r.updateResourcesStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps) - require.NoError(t, err, "expected no errors, but errors occurred") - - assert.Equal(t, cc.expectedResources, cc.appSet.Status.Resources, "expected resources did not match actual") - }) - } -} - func TestOwnsHandler(t *testing.T) { // progressive syncs do not affect create, delete, or generic ownsHandler := getOwnsHandlerPredicates(true) @@ -6269,64 +6015,60 @@ func TestOwnsHandler(t *testing.T) { 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", - }, - }}, - }, + {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", - }, - }}, - }, + {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", - }, - }}, - }, + {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)), - }, - }}, - }, + {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{ @@ -6345,72 +6087,14 @@ func TestOwnsHandler(t *testing.T) { 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: "DifferentApplicationLabelsNil", args: args{e: event.UpdateEvent{ - ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}}, - ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: nil}}, - }}, want: false}, {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: "DifferentApplicationAnnotationsNil", args: args{e: event.UpdateEvent{ - ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, - ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: nil}}, - }}, want: false}, {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: "DifferentApplicationFinalizersNil", args: args{e: event.UpdateEvent{ - ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}}, - ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Finalizers: nil}}, - }}, want: false}, - {name: "ApplicationDestinationSame", args: args{ - e: event.UpdateEvent{ - ObjectOld: &v1alpha1.Application{ - Spec: v1alpha1.ApplicationSpec{ - Destination: v1alpha1.ApplicationDestination{ - Server: "server", - Namespace: "ns", - Name: "name", - }, - }, - }, - ObjectNew: &v1alpha1.Application{ - Spec: v1alpha1.ApplicationSpec{ - Destination: v1alpha1.ApplicationDestination{ - Server: "server", - Namespace: "ns", - Name: "name", - }, - }, - }, - }, - enableProgressiveSyncs: true, - }, want: false}, - {name: "ApplicationDestinationDiff", args: args{ - e: event.UpdateEvent{ - ObjectOld: &v1alpha1.Application{ - Spec: v1alpha1.ApplicationSpec{ - Destination: v1alpha1.ApplicationDestination{ - Server: "server", - Namespace: "ns", - Name: "name", - }, - }, - }, - ObjectNew: &v1alpha1.Application{ - Spec: v1alpha1.ApplicationSpec{ - Destination: v1alpha1.ApplicationDestination{ - Server: "notSameServer", - Namespace: "ns", - Name: "name", - }, - }, - }, - }, - enableProgressiveSyncs: true, - }, want: true}, {name: "NotAnAppOld", args: args{e: event.UpdateEvent{ ObjectOld: &v1alpha1.AppProject{}, ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"bar": "foo"}}}, @@ -6427,74 +6111,3 @@ func TestOwnsHandler(t *testing.T) { }) } } - -func TestMigrateStatus(t *testing.T) { - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - - err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - - for _, tc := range []struct { - name string - appset v1alpha1.ApplicationSet - expectedStatus v1alpha1.ApplicationSetStatus - }{ - { - name: "status without applicationstatus target revisions set will default to empty list", - appset: v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test", - }, - Status: v1alpha1.ApplicationSetStatus{ - ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ - {}, - }, - }, - }, - expectedStatus: v1alpha1.ApplicationSetStatus{ - ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ - { - TargetRevisions: []string{}, - }, - }, - }, - }, - { - name: "status with applicationstatus target revisions set will do nothing", - appset: v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test", - }, - Status: v1alpha1.ApplicationSetStatus{ - ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ - { - TargetRevisions: []string{"Current"}, - }, - }, - }, - }, - expectedStatus: v1alpha1.ApplicationSetStatus{ - ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ - { - TargetRevisions: []string{"Current"}, - }, - }, - }, - }, - } { - t.Run(tc.name, func(t *testing.T) { - client := fake.NewClientBuilder().WithScheme(scheme).WithStatusSubresource(&tc.appset).WithObjects(&tc.appset).Build() - r := ApplicationSetReconciler{ - Client: client, - } - - err := r.migrateStatus(context.Background(), &tc.appset) - require.NoError(t, err) - assert.Equal(t, tc.expectedStatus, tc.appset.Status) - }) - } -} diff --git a/applicationset/controllers/clustereventhandler.go b/applicationset/controllers/clustereventhandler.go index 66fdebca66a21..951da0cb6bc44 100644 --- a/applicationset/controllers/clustereventhandler.go +++ b/applicationset/controllers/clustereventhandler.go @@ -4,8 +4,6 @@ import ( "context" "fmt" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/types" @@ -21,34 +19,34 @@ import ( // clusterSecretEventHandler is used when watching Secrets to check if they are ArgoCD Cluster Secrets, and if so // requeue any related ApplicationSets. type clusterSecretEventHandler struct { - // handler.EnqueueRequestForOwner + //handler.EnqueueRequestForOwner Log log.FieldLogger Client client.Client } -func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) { - h.queueRelatedAppGenerators(ctx, q, e.Object) +func (h *clusterSecretEventHandler) Create(e event.CreateEvent, q workqueue.RateLimitingInterface) { + h.queueRelatedAppGenerators(q, e.Object) } -func (h *clusterSecretEventHandler) Update(ctx context.Context, e event.UpdateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) { - h.queueRelatedAppGenerators(ctx, q, e.ObjectNew) +func (h *clusterSecretEventHandler) Update(e event.UpdateEvent, q workqueue.RateLimitingInterface) { + h.queueRelatedAppGenerators(q, e.ObjectNew) } -func (h *clusterSecretEventHandler) Delete(ctx context.Context, e event.DeleteEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) { - h.queueRelatedAppGenerators(ctx, q, e.Object) +func (h *clusterSecretEventHandler) Delete(e event.DeleteEvent, q workqueue.RateLimitingInterface) { + h.queueRelatedAppGenerators(q, e.Object) } -func (h *clusterSecretEventHandler) Generic(ctx context.Context, e event.GenericEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) { - h.queueRelatedAppGenerators(ctx, q, e.Object) +func (h *clusterSecretEventHandler) Generic(e event.GenericEvent, q workqueue.RateLimitingInterface) { + h.queueRelatedAppGenerators(q, e.Object) } // addRateLimitingInterface defines the Add method of workqueue.RateLimitingInterface, allow us to easily mock // it for testing purposes. -type addRateLimitingInterface[T comparable] interface { - Add(item T) +type addRateLimitingInterface interface { + Add(item interface{}) } -func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Context, q addRateLimitingInterface[reconcile.Request], object client.Object) { +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 @@ -60,7 +58,7 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Contex }).Info("processing event for cluster secret") appSetList := &argoprojiov1alpha1.ApplicationSetList{} - err := h.Client.List(ctx, appSetList) + err := h.Client.List(context.Background(), appSetList) if err != nil { h.Log.WithError(err).Error("unable to list ApplicationSets") return @@ -68,6 +66,7 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Contex h.Log.WithField("count", len(appSetList.Items)).Info("listed ApplicationSets") for _, appSet := range appSetList.Items { + foundClusterGenerator := false for _, generator := range appSet.Spec.Generators { if generator.Clusters != nil { @@ -110,6 +109,7 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Contex } } if foundClusterGenerator { + // TODO: only queue the AppGenerator if the labels match this cluster req := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: appSet.Namespace, Name: appSet.Name}} q.Add(req) diff --git a/applicationset/controllers/clustereventhandler_test.go b/applicationset/controllers/clustereventhandler_test.go index 1f73ab36746f2..7e850fc44c66d 100644 --- a/applicationset/controllers/clustereventhandler_test.go +++ b/applicationset/controllers/clustereventhandler_test.go @@ -1,12 +1,10 @@ package controllers import ( - "context" "testing" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -21,12 +19,13 @@ import ( ) func TestClusterEventHandler(t *testing.T) { + scheme := runtime.NewScheme() err := argov1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = argov1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) tests := []struct { name string @@ -535,7 +534,9 @@ func TestClusterEventHandler(t *testing.T) { } for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + appSetList := argov1alpha1.ApplicationSetList{ Items: test.items, } @@ -549,20 +550,28 @@ func TestClusterEventHandler(t *testing.T) { mockAddRateLimitingInterface := mockAddRateLimitingInterface{} - handler.queueRelatedAppGenerators(context.Background(), &mockAddRateLimitingInterface, &test.secret) + handler.queueRelatedAppGenerators(&mockAddRateLimitingInterface, &test.secret) + assert.False(t, mockAddRateLimitingInterface.errorOccurred) assert.ElementsMatch(t, mockAddRateLimitingInterface.addedItems, test.expectedRequests) + }) } + } // Add checks the type, and adds it to the internal list of received additions -func (obj *mockAddRateLimitingInterface) Add(item reconcile.Request) { - obj.addedItems = append(obj.addedItems, item) +func (obj *mockAddRateLimitingInterface) Add(item interface{}) { + if req, ok := item.(ctrl.Request); ok { + obj.addedItems = append(obj.addedItems, req) + } else { + obj.errorOccurred = true + } } type mockAddRateLimitingInterface struct { - addedItems []reconcile.Request + errorOccurred bool + addedItems []ctrl.Request } func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) { @@ -572,7 +581,7 @@ func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested) - require.NoError(t, err) + assert.Nil(t, err) assert.True(t, hasClusterGenerator) } @@ -599,7 +608,7 @@ func TestNestedGeneratorHasClusterGenerator_NestedMergeGenerator(t *testing.T) { hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested) - require.NoError(t, err) + assert.Nil(t, err) assert.True(t, hasClusterGenerator) } @@ -626,6 +635,6 @@ func TestNestedGeneratorHasClusterGenerator_NestedMergeGeneratorWithInvalidJSON( hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested) - require.Error(t, err) + assert.NotNil(t, err) assert.False(t, hasClusterGenerator) } diff --git a/applicationset/controllers/requeue_after_test.go b/applicationset/controllers/requeue_after_test.go index fd922f53566a5..6db6145af5348 100644 --- a/applicationset/controllers/requeue_after_test.go +++ b/applicationset/controllers/requeue_after_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -17,7 +16,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/argoproj/argo-cd/v2/applicationset/generators" - appsetmetrics "github.com/argoproj/argo-cd/v2/applicationset/metrics" "github.com/argoproj/argo-cd/v2/applicationset/services/mocks" argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -27,7 +25,7 @@ func TestRequeueAfter(t *testing.T) { ctx := context.Background() scheme := runtime.NewScheme() err := argov1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) gvrToListKind := map[schema.GroupVersionResource]string{{ Group: "mallard.io", Version: "v1", @@ -57,14 +55,14 @@ func TestRequeueAfter(t *testing.T) { }, } fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, duckType) - scmConfig := generators.NewSCMConfig("", []string{""}, true, nil) + terminalGenerators := map[string]generators.Generator{ "List": generators.NewListGenerator(), "Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"), - "Git": generators.NewGitGenerator(mockServer, "namespace"), - "SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), scmConfig), + "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, scmConfig), + "PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}, true), } nestedGenerators := map[string]generators.Generator{ @@ -90,13 +88,11 @@ func TestRequeueAfter(t *testing.T) { } client := fake.NewClientBuilder().WithScheme(scheme).Build() - metrics := appsetmetrics.NewFakeAppsetMetrics(client) r := ApplicationSetReconciler{ Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(0), Generators: topLevelGenerators, - Metrics: metrics, } type args struct { diff --git a/applicationset/controllers/template/template.go b/applicationset/controllers/template/template.go deleted file mode 100644 index bb4bc155d4e59..0000000000000 --- a/applicationset/controllers/template/template.go +++ /dev/null @@ -1,99 +0,0 @@ -package template - -import ( - "fmt" - - "sigs.k8s.io/controller-runtime/pkg/client" - - log "github.com/sirupsen/logrus" - - "github.com/argoproj/argo-cd/v2/applicationset/generators" - "github.com/argoproj/argo-cd/v2/applicationset/utils" - - argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" -) - -func GenerateApplications(logCtx *log.Entry, applicationSetInfo argov1alpha1.ApplicationSet, g map[string]generators.Generator, renderer utils.Renderer, client client.Client) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) { - var res []argov1alpha1.Application - - var firstError error - var applicationSetReason argov1alpha1.ApplicationSetReasonType - - for _, requestedGenerator := range applicationSetInfo.Spec.Generators { - t, err := generators.Transform(requestedGenerator, g, applicationSetInfo.Spec.Template, &applicationSetInfo, map[string]interface{}{}, client) - if err != nil { - logCtx.WithError(err).WithField("generator", requestedGenerator). - Error("error generating application from params") - if firstError == nil { - firstError = err - applicationSetReason = argov1alpha1.ApplicationSetReasonApplicationParamsGenerationError - } - continue - } - - for _, a := range t { - tmplApplication := GetTempApplication(a.Template) - - for _, p := range a.Params { - app, err := renderer.RenderTemplateParams(tmplApplication, applicationSetInfo.Spec.SyncPolicy, p, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions) - if err != nil { - logCtx.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator). - Error("error generating application from params") - - if firstError == nil { - firstError = err - applicationSetReason = argov1alpha1.ApplicationSetReasonRenderTemplateParamsError - } - continue - } - - if applicationSetInfo.Spec.TemplatePatch != nil { - patchedApplication, err := renderTemplatePatch(renderer, 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 - } - - // The app's namespace must be the same as the AppSet's namespace to preserve the appsets-in-any-namespace - // security boundary. - app.Namespace = applicationSetInfo.Namespace - res = append(res, *app) - } - } - - 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 renderTemplatePatch(r utils.Renderer, app *argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet, params map[string]interface{}) (*argov1alpha1.Application, error) { - replacedTemplate, err := r.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 GetTempApplication(applicationSetTemplate argov1alpha1.ApplicationSetTemplate) *argov1alpha1.Application { - var tmplApplication argov1alpha1.Application - tmplApplication.Annotations = applicationSetTemplate.Annotations - tmplApplication.Labels = applicationSetTemplate.Labels - tmplApplication.Namespace = applicationSetTemplate.Namespace - tmplApplication.Name = applicationSetTemplate.Name - tmplApplication.Spec = applicationSetTemplate.Spec - tmplApplication.Finalizers = applicationSetTemplate.Finalizers - - return &tmplApplication -} diff --git a/applicationset/controllers/template/template_test.go b/applicationset/controllers/template/template_test.go deleted file mode 100644 index c765e9c1c67a4..0000000000000 --- a/applicationset/controllers/template/template_test.go +++ /dev/null @@ -1,350 +0,0 @@ -package template - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - - log "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/argoproj/argo-cd/v2/applicationset/generators" - genmock "github.com/argoproj/argo-cd/v2/applicationset/generators/mocks" - "github.com/argoproj/argo-cd/v2/applicationset/utils" - rendmock "github.com/argoproj/argo-cd/v2/applicationset/utils/mocks" - "github.com/argoproj/argo-cd/v2/pkg/apis/application" - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - "github.com/argoproj/argo-cd/v2/util/collections" -) - -func TestGenerateApplications(t *testing.T) { - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - - err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - - for _, c := range []struct { - name string - params []map[string]interface{} - template v1alpha1.ApplicationSetTemplate - generateParamsError error - rendererError error - expectErr bool - expectedReason v1alpha1.ApplicationSetReasonType - }{ - { - name: "Generate two applications", - params: []map[string]interface{}{{"name": "app1"}, {"name": "app2"}}, - template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ - Name: "name", - Namespace: "namespace", - Labels: map[string]string{"label_name": "label_value"}, - }, - Spec: v1alpha1.ApplicationSpec{}, - }, - expectedReason: "", - }, - { - name: "Handles error from the generator", - generateParamsError: fmt.Errorf("error"), - expectErr: true, - expectedReason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError, - }, - { - name: "Handles error from the render", - params: []map[string]interface{}{{"name": "app1"}, {"name": "app2"}}, - template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ - Name: "name", - Namespace: "namespace", - Labels: map[string]string{"label_name": "label_value"}, - }, - Spec: v1alpha1.ApplicationSpec{}, - }, - rendererError: fmt.Errorf("error"), - expectErr: true, - expectedReason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError, - }, - } { - cc := c - app := v1alpha1.Application{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "namespace", - }, - TypeMeta: metav1.TypeMeta{ - Kind: application.ApplicationKind, - APIVersion: "argoproj.io/v1alpha1", - }, - } - - t.Run(cc.name, func(t *testing.T) { - generatorMock := genmock.Generator{} - generator := v1alpha1.ApplicationSetGenerator{ - List: &v1alpha1.ListGenerator{}, - } - - generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything). - Return(cc.params, cc.generateParamsError) - - generatorMock.On("GetTemplate", &generator). - Return(&v1alpha1.ApplicationSetTemplate{}) - - rendererMock := rendmock.Renderer{} - - var expectedApps []v1alpha1.Application - - if cc.generateParamsError == nil { - for _, p := range cc.params { - if cc.rendererError != nil { - rendererMock.On("RenderTemplateParams", GetTempApplication(cc.template), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), p, false, []string(nil)). - Return(nil, cc.rendererError) - } else { - rendererMock.On("RenderTemplateParams", GetTempApplication(cc.template), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), p, false, []string(nil)). - Return(&app, nil) - expectedApps = append(expectedApps, app) - } - } - } - - generators := map[string]generators.Generator{ - "List": &generatorMock, - } - renderer := &rendererMock - - got, reason, err := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - }, - Spec: v1alpha1.ApplicationSetSpec{ - Generators: []v1alpha1.ApplicationSetGenerator{generator}, - Template: cc.template, - }, - }, - generators, - renderer, - nil, - ) - - if cc.expectErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - assert.Equal(t, expectedApps, got) - assert.Equal(t, cc.expectedReason, reason) - generatorMock.AssertNumberOfCalls(t, "GenerateParams", 1) - - if cc.generateParamsError == nil { - rendererMock.AssertNumberOfCalls(t, "RenderTemplateParams", len(cc.params)) - } - }) - } -} - -func TestMergeTemplateApplications(t *testing.T) { - for _, c := range []struct { - name string - params []map[string]interface{} - template v1alpha1.ApplicationSetTemplate - overrideTemplate v1alpha1.ApplicationSetTemplate - expectedMerged v1alpha1.ApplicationSetTemplate - expectedApps []v1alpha1.Application - }{ - { - name: "Generate app", - params: []map[string]interface{}{{"name": "app1"}}, - template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ - Name: "name", - Namespace: "namespace", - Labels: map[string]string{"label_name": "label_value"}, - }, - Spec: v1alpha1.ApplicationSpec{}, - }, - overrideTemplate: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ - Name: "test", - Labels: map[string]string{"foo": "bar"}, - }, - Spec: v1alpha1.ApplicationSpec{}, - }, - expectedMerged: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ - Name: "test", - Namespace: "namespace", - Labels: map[string]string{"label_name": "label_value", "foo": "bar"}, - }, - Spec: v1alpha1.ApplicationSpec{}, - }, - expectedApps: []v1alpha1.Application{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test", - Labels: map[string]string{"foo": "bar"}, - }, - Spec: v1alpha1.ApplicationSpec{}, - }, - }, - }, - } { - cc := c - - t.Run(cc.name, func(t *testing.T) { - generatorMock := genmock.Generator{} - generator := v1alpha1.ApplicationSetGenerator{ - List: &v1alpha1.ListGenerator{}, - } - - generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything). - Return(cc.params, nil) - - generatorMock.On("GetTemplate", &generator). - Return(&cc.overrideTemplate) - - rendererMock := rendmock.Renderer{} - - rendererMock.On("RenderTemplateParams", GetTempApplication(cc.expectedMerged), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), cc.params[0], false, []string(nil)). - Return(&cc.expectedApps[0], nil) - - generators := map[string]generators.Generator{ - "List": &generatorMock, - } - renderer := &rendererMock - - got, _, _ := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - }, - Spec: v1alpha1.ApplicationSetSpec{ - Generators: []v1alpha1.ApplicationSetGenerator{generator}, - Template: cc.template, - }, - }, - generators, - renderer, - nil, - ) - - assert.Equal(t, cc.expectedApps, got) - }) - } -} - -// Test app generation from a go template application set using a pull request generator -func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) { - for _, cases := range []struct { - name string - params []map[string]interface{} - 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", - "title": "title1", - "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}}", - "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: v1alpha1.ApplicationSpec{ - Source: &v1alpha1.ApplicationSource{ - RepoURL: "https://testurl/testRepo", - TargetRevision: "{{.head_short_sha}}", - }, - Destination: v1alpha1.ApplicationDestination{ - Server: "https://kubernetes.default.svc", - Namespace: "AppSet-{{.branch_slug}}-{{.head_sha}}", - }, - }, - }, - expectedApp: []v1alpha1.Application{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "AppSet-branch1-1", - Labels: map[string]string{ - "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{ - Source: &v1alpha1.ApplicationSource{ - RepoURL: "https://testurl/testRepo", - TargetRevision: "089d92cb", - }, - Destination: v1alpha1.ApplicationDestination{ - Server: "https://kubernetes.default.svc", - Namespace: "AppSet-branchSlug1-089d92cbf9ff857a39e6feccd32798ca700fb958", - }, - }, - }, - }, - }, - } { - t.Run(cases.name, func(t *testing.T) { - generatorMock := genmock.Generator{} - generator := v1alpha1.ApplicationSetGenerator{ - PullRequest: &v1alpha1.PullRequestGenerator{}, - } - - generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything). - Return(cases.params, nil) - - generatorMock.On("GetTemplate", &generator). - Return(&cases.template, nil) - - generators := map[string]generators.Generator{ - "PullRequest": &generatorMock, - } - renderer := &utils.Render{} - - gotApp, _, _ := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{ - Spec: v1alpha1.ApplicationSetSpec{ - GoTemplate: true, - Generators: []v1alpha1.ApplicationSetGenerator{{ - PullRequest: &v1alpha1.PullRequestGenerator{}, - }}, - Template: cases.template, - }, - }, - generators, - renderer, - nil, - ) - assert.EqualValues(t, cases.expectedApp[0].ObjectMeta.Name, gotApp[0].ObjectMeta.Name) - assert.EqualValues(t, cases.expectedApp[0].Spec.Source.TargetRevision, gotApp[0].Spec.Source.TargetRevision) - assert.EqualValues(t, cases.expectedApp[0].Spec.Destination.Namespace, gotApp[0].Spec.Destination.Namespace) - assert.True(t, collections.StringMapsEqual(cases.expectedApp[0].ObjectMeta.Labels, gotApp[0].ObjectMeta.Labels)) - }) - } -} diff --git a/applicationset/controllers/template/patch.go b/applicationset/controllers/templatePatch.go similarity index 98% rename from applicationset/controllers/template/patch.go rename to applicationset/controllers/templatePatch.go index b9d1166f1f237..f8efd9f376996 100644 --- a/applicationset/controllers/template/patch.go +++ b/applicationset/controllers/templatePatch.go @@ -1,4 +1,4 @@ -package template +package controllers import ( "encoding/json" @@ -11,12 +11,14 @@ import ( ) 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) } @@ -26,6 +28,7 @@ func applyTemplatePatch(app *appv1.Application, templatePatch string) (*appv1.Ap } 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) } diff --git a/applicationset/controllers/template/patch_test.go b/applicationset/controllers/templatePatch_test.go similarity index 99% rename from applicationset/controllers/template/patch_test.go rename to applicationset/controllers/templatePatch_test.go index 456fe445994c8..c1a794077c8ee 100644 --- a/applicationset/controllers/template/patch_test.go +++ b/applicationset/controllers/templatePatch_test.go @@ -1,4 +1,4 @@ -package template +package controllers import ( "testing" diff --git a/applicationset/generators/cluster.go b/applicationset/generators/cluster.go index eafb3de1fabb6..d8647d78d3a5c 100644 --- a/applicationset/generators/cluster.go +++ b/applicationset/generators/cluster.go @@ -38,6 +38,7 @@ type ClusterGenerator struct { var render = &utils.Render{} func NewClusterGenerator(c client.Client, ctx context.Context, clientset kubernetes.Interface, namespace string) Generator { + settingsManager := settings.NewSettingsManager(ctx, clientset, namespace) g := &ClusterGenerator{ @@ -60,7 +61,8 @@ func (g *ClusterGenerator) GetTemplate(appSetGenerator *argoappsetv1alpha1.Appli return &appSetGenerator.Clusters.Template } -func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) { +func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet) ([]map[string]interface{}, error) { + if appSetGenerator == nil { return nil, EmptyAppSetGeneratorError } @@ -85,7 +87,7 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap clusterSecrets, err := g.getSecretsByClusterName(appSetGenerator) if err != nil { - return nil, fmt.Errorf("error getting cluster secrets: %w", err) + return nil, err } res := []map[string]interface{}{} @@ -93,10 +95,12 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap secretsFound := []corev1.Secret{} for _, cluster := range clustersFromArgoCD.Items { + // If there is a secret for this cluster, then it's a non-local cluster, so it will be // handled by the next step. if secretForCluster, exists := clusterSecrets[cluster.Name]; exists { secretsFound = append(secretsFound, secretForCluster) + } else if !ignoreLocalClusters { // If there is no secret for the cluster, it's the local cluster, so handle it here. params := map[string]interface{}{} @@ -106,7 +110,7 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) if err != nil { - return nil, fmt.Errorf("error appending templated values for local cluster: %w", err) + return nil, err } res = append(res, params) @@ -146,7 +150,7 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) if err != nil { - return nil, fmt.Errorf("error appending templated values for cluster: %w", err) + return nil, err } res = append(res, params) @@ -164,7 +168,7 @@ func (g *ClusterGenerator) getSecretsByClusterName(appSetGenerator *argoappsetv1 selector := metav1.AddLabelToSelector(&appSetGenerator.Clusters.Selector, ArgoCDSecretTypeLabel, ArgoCDSecretTypeCluster) secretSelector, err := metav1.LabelSelectorAsSelector(selector) if err != nil { - return nil, fmt.Errorf("error converting label selector: %w", err) + return nil, err } if err := g.Client.List(context.Background(), clusterSecretList, client.MatchingLabelsSelector{Selector: secretSelector}); err != nil { @@ -181,4 +185,5 @@ func (g *ClusterGenerator) getSecretsByClusterName(appSetGenerator *argoappsetv1 } return res, nil + } diff --git a/applicationset/generators/cluster_test.go b/applicationset/generators/cluster_test.go index f319081c09218..0abc9399149d2 100644 --- a/applicationset/generators/cluster_test.go +++ b/applicationset/generators/cluster_test.go @@ -17,7 +17,6 @@ import ( argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) type possiblyErroringFakeCtrlRuntimeClient struct { @@ -105,15 +104,11 @@ func TestGenerateParams(t *testing.T) { "aaa": "{{ server }}", "no-op": "{{ this-does-not-exist }}", }, expected: []map[string]interface{}{ - { - "values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "production", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "production", "values.aaa": "https://production-01.example.com", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar", - "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", - }, + {"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "production", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "production", "values.aaa": "https://production-01.example.com", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar", + "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"}, - { - "values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "staging", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "staging", "values.aaa": "https://staging-01.example.com", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo", - "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", - }, + {"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "staging", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "staging", "values.aaa": "https://staging-01.example.com", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo", + "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"}, {"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "{{ metadata.annotations.foo.argoproj.io }}", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "{{ metadata.labels.environment }}", "values.aaa": "https://kubernetes.default.svc", "nameNormalized": "in-cluster", "name": "in-cluster", "server": "https://kubernetes.default.svc"}, }, @@ -129,15 +124,11 @@ func TestGenerateParams(t *testing.T) { }, values: nil, expected: []map[string]interface{}{ - { - "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar", - "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", - }, + {"name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar", + "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"}, - { - "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo", - "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", - }, + {"name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo", + "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"}, }, clientError: false, expectedError: nil, @@ -153,10 +144,8 @@ func TestGenerateParams(t *testing.T) { "foo": "bar", }, expected: []map[string]interface{}{ - { - "values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar", - "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", - }, + {"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar", + "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"}, }, clientError: false, expectedError: nil, @@ -179,14 +168,10 @@ func TestGenerateParams(t *testing.T) { "foo": "bar", }, expected: []map[string]interface{}{ - { - "values.foo": "bar", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo", - "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", - }, - { - "values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar", - "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", - }, + {"values.foo": "bar", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo", + "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"}, + {"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar", + "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"}, }, clientError: false, expectedError: nil, @@ -212,10 +197,8 @@ func TestGenerateParams(t *testing.T) { "name": "baz", }, expected: []map[string]interface{}{ - { - "values.name": "baz", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo", - "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", - }, + {"values.name": "baz", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo", + "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"}, }, clientError: false, expectedError: nil, @@ -226,7 +209,7 @@ func TestGenerateParams(t *testing.T) { values: nil, expected: nil, clientError: true, - expectedError: fmt.Errorf("error getting cluster secrets: could not list Secrets"), + expectedError: fmt.Errorf("could not list Secrets"), }, } @@ -237,7 +220,9 @@ func TestGenerateParams(t *testing.T) { } for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + appClientset := kubefake.NewSimpleClientset(runtimeClusters...) fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build() @@ -246,7 +231,7 @@ func TestGenerateParams(t *testing.T) { testCase.clientError, } - clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace") + var clusterGenerator = NewClusterGenerator(cl, context.Background(), appClientset, "namespace") applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -260,14 +245,15 @@ func TestGenerateParams(t *testing.T) { Selector: testCase.selector, Values: testCase.values, }, - }, &applicationSetInfo, nil) + }, &applicationSetInfo) if testCase.expectedError != nil { - require.EqualError(t, err, testCase.expectedError.Error()) + assert.EqualError(t, err, testCase.expectedError.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCase.expected, got) } + }) } } @@ -597,7 +583,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) { values: nil, expected: nil, clientError: true, - expectedError: fmt.Errorf("error getting cluster secrets: could not list Secrets"), + expectedError: fmt.Errorf("could not list Secrets"), }, } @@ -608,7 +594,9 @@ func TestGenerateParamsGoTemplate(t *testing.T) { } for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + appClientset := kubefake.NewSimpleClientset(runtimeClusters...) fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build() @@ -617,7 +605,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) { testCase.clientError, } - clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace") + var clusterGenerator = NewClusterGenerator(cl, context.Background(), appClientset, "namespace") applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -633,14 +621,15 @@ func TestGenerateParamsGoTemplate(t *testing.T) { Selector: testCase.selector, Values: testCase.values, }, - }, &applicationSetInfo, nil) + }, &applicationSetInfo) if testCase.expectedError != nil { - require.EqualError(t, err, testCase.expectedError.Error()) + assert.EqualError(t, err, testCase.expectedError.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCase.expected, got) } + }) } } diff --git a/applicationset/generators/duck_type.go b/applicationset/generators/duck_type.go index d7ceafd31de3b..f98afd0e01381 100644 --- a/applicationset/generators/duck_type.go +++ b/applicationset/generators/duck_type.go @@ -7,7 +7,6 @@ import ( "time" log "github.com/sirupsen/logrus" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/argoproj/argo-cd/v2/util/settings" @@ -33,6 +32,7 @@ type DuckTypeGenerator struct { } func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clientset kubernetes.Interface, namespace string) Generator { + settingsManager := settings.NewSettingsManager(ctx, clientset, namespace) g := &DuckTypeGenerator{ @@ -46,6 +46,7 @@ func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clie } func (g *DuckTypeGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration { + // Return a requeue default of 3 minutes, if no override is specified. if appSetGenerator.ClusterDecisionResource.RequeueAfterSeconds != nil { @@ -59,7 +60,8 @@ func (g *DuckTypeGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Appl return &appSetGenerator.ClusterDecisionResource.Template } -func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) { +func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { + if appSetGenerator == nil { return nil, EmptyAppSetGeneratorError } @@ -81,6 +83,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A // Read the configMapRef cm, err := g.clientset.CoreV1().ConfigMaps(g.namespace).Get(g.ctx, appSetGenerator.ClusterDecisionResource.ConfigMapRef, metav1.GetOptions{}) + if err != nil { return nil, fmt.Errorf("error reading configMapRef: %w", err) } @@ -101,6 +104,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A if (resourceName == "" && labelSelector.MatchLabels == nil && labelSelector.MatchExpressions == nil) || (resourceName != "" && (labelSelector.MatchExpressions != nil || labelSelector.MatchLabels != nil)) { + log.Warningf("You must choose either resourceName=%v, labelSelector.matchLabels=%v or labelSelect.matchExpressions=%v", resourceName, labelSelector.MatchLabels, labelSelector.MatchExpressions) return nil, fmt.Errorf("There is a problem with the definition of the ClusterDecisionResource generator") } @@ -118,14 +122,15 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A log.WithField("listOptions.LabelSelector", listOptions.LabelSelector).Info("selection type") } else { listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", resourceName).String() - // metav1.Convert_fields_Selector_To_string(fields.).Sprintf("metadata.name=%s", resourceName) + //metav1.Convert_fields_Selector_To_string(fields.).Sprintf("metadata.name=%s", resourceName) log.WithField("listOptions.FieldSelector", listOptions.FieldSelector).Info("selection type") } duckResources, err := g.dynClient.Resource(duckGVR).Namespace(g.namespace).List(g.ctx, listOptions) + if err != nil { log.WithField("GVK", duckGVR).Warning("resources were not found") - return nil, fmt.Errorf("failed to get dynamic resources: %w", err) + return nil, err } if len(duckResources.Items) == 0 { @@ -144,6 +149,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A if matchKey == "" { log.WithField("matchKey", matchKey).Warning("matchKey not found in " + cm.Name) return nil, nil + } res := []map[string]interface{}{} @@ -161,6 +167,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A log.WithField("duckResourceStatus", duckResource.Object["status"]).Debug("found resource") clusterDecisions = append(clusterDecisions, duckResource.Object["status"].(map[string]interface{})[statusListKey].([]interface{})...) + } log.Infof("Number of decisions found: %v", len(clusterDecisions)) @@ -169,6 +176,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A if len(clusterDecisions) > 0 { for _, cluster := range clusterDecisions { + // generated instance of cluster params params := map[string]interface{}{} @@ -186,6 +194,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A for _, argoCluster := range argoClusters { if argoCluster.Name == strMatchValue { + log.WithField(matchKey, argoCluster.Name).Info("matched cluster in ArgoCD") params["name"] = argoCluster.Name params["server"] = argoCluster.Server @@ -193,6 +202,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A found = true break // Stop looking } + } if !found { @@ -218,7 +228,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A res = append(res, params) } } else { - log.Warningf("clusterDecisionResource status.%s missing", statusListKey) + log.Warningf("clusterDecisionResource status." + statusListKey + " missing") return nil, nil } diff --git a/applicationset/generators/duck_type_test.go b/applicationset/generators/duck_type_test.go index d2cfdbc59d6bc..788457b27559c 100644 --- a/applicationset/generators/duck_type_test.go +++ b/applicationset/generators/duck_type_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -19,11 +18,9 @@ import ( argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) -const ( - resourceApiVersion = "mallard.io/v1" - resourceKind = "ducks" - resourceName = "quak" -) +const resourceApiVersion = "mallard.io/v1" +const resourceKind = "ducks" +const resourceName = "quak" func TestGenerateParamsForDuckType(t *testing.T) { clusters := []client.Object{ @@ -282,7 +279,9 @@ func TestGenerateParamsForDuckType(t *testing.T) { } for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + appClientset := kubefake.NewSimpleClientset(append(runtimeClusters, configMap)...) gvrToListKind := map[schema.GroupVersionResource]string{{ @@ -293,7 +292,7 @@ func TestGenerateParamsForDuckType(t *testing.T) { fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource) - duckTypeGenerator := NewDuckTypeGenerator(context.Background(), fakeDynClient, appClientset, "namespace") + var duckTypeGenerator = NewDuckTypeGenerator(context.Background(), fakeDynClient, appClientset, "namespace") applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -309,12 +308,12 @@ func TestGenerateParamsForDuckType(t *testing.T) { LabelSelector: testCase.labelSelector, Values: testCase.values, }, - }, &applicationSetInfo, nil) + }, &applicationSetInfo) if testCase.expectedError != nil { - require.EqualError(t, err, testCase.expectedError.Error()) + assert.EqualError(t, err, testCase.expectedError.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCase.expected, got) } }) @@ -578,7 +577,9 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) { } for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + appClientset := kubefake.NewSimpleClientset(append(runtimeClusters, configMap)...) gvrToListKind := map[schema.GroupVersionResource]string{{ @@ -589,7 +590,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) { fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource) - duckTypeGenerator := NewDuckTypeGenerator(context.Background(), fakeDynClient, appClientset, "namespace") + var duckTypeGenerator = NewDuckTypeGenerator(context.Background(), fakeDynClient, appClientset, "namespace") applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -607,12 +608,12 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) { LabelSelector: testCase.labelSelector, Values: testCase.values, }, - }, &applicationSetInfo, nil) + }, &applicationSetInfo) if testCase.expectedError != nil { - require.EqualError(t, err, testCase.expectedError.Error()) + assert.EqualError(t, err, testCase.expectedError.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCase.expected, got) } }) diff --git a/applicationset/generators/generator_spec_processor.go b/applicationset/generators/generator_spec_processor.go index 25e6b138da749..494b2e8d9a37d 100644 --- a/applicationset/generators/generator_spec_processor.go +++ b/applicationset/generators/generator_spec_processor.go @@ -5,7 +5,6 @@ import ( "reflect" "github.com/jeremywohl/flatten" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/argoproj/argo-cd/v2/applicationset/utils" @@ -27,7 +26,7 @@ type TransformResult struct { } // 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{}, client client.Client) ([]TransformResult, error) { +func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}) ([]TransformResult, error) { // This is a custom version of the `LabelSelectorAsSelector` that is in k8s.io/apimachinery. This has been copied // verbatim from that package, with the difference that we do not have any restrictions on label values. This is done // so that, among other things, we can match on cluster urls. @@ -65,7 +64,7 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al continue } } - params, err = g.GenerateParams(interpolatedGenerator, appSet, client) + params, err = g.GenerateParams(interpolatedGenerator, appSet) if err != nil { log.WithError(err).WithField("generator", g). Error("error generating params") diff --git a/applicationset/generators/generator_spec_processor_test.go b/applicationset/generators/generator_spec_processor_test.go index a02ea5e3312c1..b5838e7af7cbe 100644 --- a/applicationset/generators/generator_spec_processor_test.go +++ b/applicationset/generators/generator_spec_processor_test.go @@ -65,8 +65,8 @@ func TestMatchValues(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - listGenerator := NewListGenerator() - data := map[string]Generator{ + var listGenerator = NewListGenerator() + var data = map[string]Generator{ "List": listGenerator, } @@ -84,13 +84,12 @@ func TestMatchValues(t *testing.T) { List: &argov1alpha1.ListGenerator{ Elements: testCase.elements, Template: emptyTemplate(), - }, - }, + }}, data, emptyTemplate(), - &applicationSetInfo, nil, nil) + &applicationSetInfo, nil) - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCase.expected, results[0].Params) }) } @@ -149,8 +148,8 @@ func TestMatchValuesGoTemplate(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - listGenerator := NewListGenerator() - data := map[string]Generator{ + var listGenerator = NewListGenerator() + var data = map[string]Generator{ "List": listGenerator, } @@ -168,13 +167,12 @@ func TestMatchValuesGoTemplate(t *testing.T) { List: &argov1alpha1.ListGenerator{ Elements: testCase.elements, Template: emptyTemplate(), - }, - }, + }}, data, emptyTemplate(), - &applicationSetInfo, nil, nil) + &applicationSetInfo, nil) - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCase.expected, results[0].Params) }) } @@ -238,13 +236,12 @@ func TestTransForm(t *testing.T) { Selector: metav1.LabelSelector{}, Template: argov1alpha1.ApplicationSetTemplate{}, Values: nil, - }, - }, + }}, testGenerators, emptyTemplate(), - &applicationSetInfo, nil, nil) + &applicationSetInfo, nil) - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCase.expected, results[0].Params) }) } @@ -346,11 +343,12 @@ func getMockClusterGenerator() Generator { func getMockGitGenerator() Generator { argoCDServiceMock := mocks.Repos{} argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil) - gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace") + var gitGenerator = NewGitGenerator(&argoCDServiceMock) return gitGenerator } func TestGetRelevantGenerators(t *testing.T) { + testGenerators := map[string]Generator{ "Clusters": getMockClusterGenerator(), "Git": getMockGitGenerator(), @@ -363,8 +361,7 @@ func TestGetRelevantGenerators(t *testing.T) { requestedGenerator := &argov1alpha1.ApplicationSetGenerator{ List: &argov1alpha1.ListGenerator{ Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}}, - }, - } + }} relevantGenerators := GetRelevantGenerators(requestedGenerator, testGenerators) assert.Len(t, relevantGenerators, 1) @@ -407,8 +404,7 @@ func TestInterpolateGenerator(t *testing.T) { "path-basename": "{{path.basename}}", "path-zero": "{{path[0]}}", "path-full": "{{path}}", - }, - }, + }}, }, } gitGeneratorParams := map[string]interface{}{ @@ -462,8 +458,7 @@ func TestInterpolateGenerator_go(t *testing.T) { "path-zero": "{{index .path.segments 0}}", "path-full": "{{.path.path}}", "kubernetes.io/environment": `{{default "foo" .my_label}}`, - }, - }, + }}, }, } gitGeneratorParams := map[string]interface{}{ @@ -555,7 +550,7 @@ func TestInterpolateGeneratorError(t *testing.T) { 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 != "" { - require.EqualError(t, err, tt.expectedErrStr) + assert.EqualError(t, err, tt.expectedErrStr) } else { require.NoError(t, err) } diff --git a/applicationset/generators/git.go b/applicationset/generators/git.go index 74fe02044b473..57fe2835b8df0 100644 --- a/applicationset/generators/git.go +++ b/applicationset/generators/git.go @@ -11,29 +11,23 @@ import ( "github.com/jeremywohl/flatten" log "github.com/sirupsen/logrus" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/applicationset/services" "github.com/argoproj/argo-cd/v2/applicationset/utils" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - "github.com/argoproj/argo-cd/v2/util/gpg" ) var _ Generator = (*GitGenerator)(nil) type GitGenerator struct { - repos services.Repos - namespace string + repos services.Repos } -func NewGitGenerator(repos services.Repos, namespace string) Generator { +func NewGitGenerator(repos services.Repos) Generator { g := &GitGenerator{ - repos: repos, - namespace: namespace, + repos: repos, } - return g } @@ -42,6 +36,7 @@ func (g *GitGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applicati } func (g *GitGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration { + // Return a requeue default of 3 minutes, if no default is specified. if appSetGenerator.Git.RequeueAfterSeconds != nil { @@ -51,7 +46,8 @@ func (g *GitGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Appli return DefaultRequeueAfterSeconds } -func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) { +func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { + if appSetGenerator == nil { return nil, EmptyAppSetGeneratorError } @@ -62,31 +58,12 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic noRevisionCache := appSet.RefreshRequired() - verifyCommit := false - - // When the project field is templated, the contents of the git repo are required to run the git generator and get the templated value, - // but git generator cannot be called without verifying the commit signature. - // In this case, we skip the signature verification. - if !strings.Contains(appSet.Spec.Template.Spec.Project, "{{") { - project := appSet.Spec.Template.Spec.Project - appProject := &argoprojiov1alpha1.AppProject{} - namespace := g.namespace - if namespace == "" { - namespace = appSet.Namespace - } - if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: namespace}, appProject); err != nil { - return nil, fmt.Errorf("error getting project %s: %w", project, err) - } - // we need to verify the signature on the Git revision if GPG is enabled - verifyCommit = len(appProject.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled() - } - var err error var res []map[string]interface{} if len(appSetGenerator.Git.Directories) != 0 { - res, err = g.generateParamsForGitDirectories(appSetGenerator, noRevisionCache, verifyCommit, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) + 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, verifyCommit, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) + res, err = g.generateParamsForGitFiles(appSetGenerator, noRevisionCache, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) } else { return nil, EmptyAppSetGeneratorError } @@ -97,9 +74,10 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic return res, nil } -func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit bool, useGoTemplate bool, goTemplateOptions []string) ([]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, noRevisionCache, verifyCommit) + allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, noRevisionCache) if err != nil { return nil, fmt.Errorf("error getting directories from repo: %w", err) } @@ -122,11 +100,12 @@ func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoproj return res, nil } -func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit bool, useGoTemplate bool, goTemplateOptions []string) ([]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, noRevisionCache, verifyCommit) + files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, requestedPath.Path, noRevisionCache) if err != nil { return nil, err } @@ -146,10 +125,11 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al // Generate params from each path, and return res := []map[string]interface{}{} 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], appSetGenerator.Git.Values, useGoTemplate, goTemplateOptions, appSetGenerator.Git.PathParamPrefix) if err != nil { - return nil, fmt.Errorf("unable to process file '%s': %w", path, err) + return nil, fmt.Errorf("unable to process file '%s': %v", path, err) } res = append(res, paramsArray...) @@ -167,7 +147,7 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent [] singleObj := make(map[string]interface{}) err = yaml.Unmarshal(fileContent, &singleObj) if err != nil { - return nil, fmt.Errorf("unable to parse file: %w", err) + return nil, fmt.Errorf("unable to parse file: %v", err) } objectsFound = append(objectsFound, singleObj) } else if len(objectsFound) == 0 { @@ -178,6 +158,7 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent [] res := []map[string]interface{}{} for _, objectFound := range objectsFound { + params := map[string]interface{}{} if useGoTemplate { @@ -233,13 +214,13 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent [] return res, nil } -func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryGeneratorItem, allPaths []string) []string { +func (g *GitGenerator) filterApps(Directories []argoprojiov1alpha1.GitDirectoryGeneratorItem, allPaths []string) []string { res := []string{} for _, appPath := range allPaths { appInclude := false appExclude := false // Iterating over each appPath and check whether directories object has requestedPath that matches the appPath - for _, requestedPath := range directories { + for _, requestedPath := range Directories { match, err := path.Match(requestedPath.Path, appPath) if err != nil { log.WithError(err).WithField("requestedPath", requestedPath). @@ -264,6 +245,7 @@ func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryG 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 { + params := make(map[string]interface{}, 5) if useGoTemplate { diff --git a/applicationset/generators/git_test.go b/applicationset/generators/git_test.go index cd0c14d8443f0..d3fd4965057f8 100644 --- a/applicationset/generators/git_test.go +++ b/applicationset/generators/git_test.go @@ -4,16 +4,11 @@ import ( "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" - "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - - "github.com/argoproj/argo-cd/v2/applicationset/services/mocks" - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) func Test_generateParamsFromGitFile(t *testing.T) { @@ -179,6 +174,7 @@ foo: } func TestGitGenerateParamsFromDirectories(t *testing.T) { + cases := []struct { name string directories []argoprojiov1alpha1.GitDirectoryGeneratorItem @@ -321,9 +317,9 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) { argoCDServiceMock := mocks.Repos{} - argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, 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) - gitGenerator := NewGitGenerator(&argoCDServiceMock, "") + var gitGenerator = NewGitGenerator(&argoCDServiceMock) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", @@ -341,19 +337,12 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) { }, } - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - appProject := argoprojiov1alpha1.AppProject{} - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build() - - got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, client) + got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) if testCaseCopy.expectedError != nil { - require.EqualError(t, err, testCaseCopy.expectedError.Error()) + assert.EqualError(t, err, testCaseCopy.expectedError.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCaseCopy.expected, got) } @@ -363,6 +352,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) { } func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) { + cases := []struct { name string directories []argoprojiov1alpha1.GitDirectoryGeneratorItem @@ -562,6 +552,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) { }, repoError: nil, expected: []map[string]interface{}{ + { "path": map[string]interface{}{ "path": "app1", @@ -622,9 +613,9 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) { argoCDServiceMock := mocks.Repos{} - argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, 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) - gitGenerator := NewGitGenerator(&argoCDServiceMock, "") + var gitGenerator = NewGitGenerator(&argoCDServiceMock) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", @@ -642,28 +633,23 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) { }, } - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - appProject := argoprojiov1alpha1.AppProject{} - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build() - - got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, client) + got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) if testCaseCopy.expectedError != nil { - require.EqualError(t, err, testCaseCopy.expectedError.Error()) + assert.EqualError(t, err, testCaseCopy.expectedError.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCaseCopy.expected, got) } argoCDServiceMock.AssertExpectations(t) }) } + } func TestGitGenerateParamsFromFiles(t *testing.T) { + cases := []struct { name string // files is the list of paths/globs to match @@ -986,10 +972,10 @@ cluster: t.Parallel() argoCDServiceMock := mocks.Repos{} - argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError) - gitGenerator := NewGitGenerator(&argoCDServiceMock, "") + var gitGenerator = NewGitGenerator(&argoCDServiceMock) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", @@ -1006,20 +992,13 @@ cluster: }, } - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - appProject := argoprojiov1alpha1.AppProject{} - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build() - - got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, client) + got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) fmt.Println(got, err) if testCaseCopy.expectedError != nil { - require.EqualError(t, err, testCaseCopy.expectedError.Error()) + assert.EqualError(t, err, testCaseCopy.expectedError.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCaseCopy.expected, got) } @@ -1029,6 +1008,7 @@ cluster: } func TestGitGenerateParamsFromFilesGoTemplate(t *testing.T) { + cases := []struct { name string // files is the list of paths/globs to match @@ -1342,10 +1322,10 @@ cluster: t.Parallel() argoCDServiceMock := mocks.Repos{} - argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError) - gitGenerator := NewGitGenerator(&argoCDServiceMock, "") + var gitGenerator = NewGitGenerator(&argoCDServiceMock) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", @@ -1362,20 +1342,13 @@ cluster: }, } - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - appProject := argoprojiov1alpha1.AppProject{} - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build() - - got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, client) + got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) fmt.Println(got, err) if testCaseCopy.expectedError != nil { - require.EqualError(t, err, testCaseCopy.expectedError.Error()) + assert.EqualError(t, err, testCaseCopy.expectedError.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCaseCopy.expected, got) } @@ -1383,114 +1356,3 @@ cluster: }) } } - -func TestGitGenerator_GenerateParams(t *testing.T) { - cases := []struct { - name string - directories []argoprojiov1alpha1.GitDirectoryGeneratorItem - pathParamPrefix string - repoApps []string - repoPathsError error - repoFileContents map[string][]byte - values map[string]string - expected []map[string]interface{} - expectedError error - appset argoprojiov1alpha1.ApplicationSet - callGetDirectories bool - }{ - { - name: "Signature Verification - ignores templated project field", - repoApps: []string{ - "app1", - }, - repoPathsError: nil, - appset: argoprojiov1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "set", - Namespace: "namespace", - }, - Spec: argoprojiov1alpha1.ApplicationSetSpec{ - Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{ - Git: &argoprojiov1alpha1.GitGenerator{ - RepoURL: "RepoURL", - Revision: "Revision", - Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}}, - PathParamPrefix: "", - Values: map[string]string{ - "foo": "bar", - }, - }, - }}, - Template: argoprojiov1alpha1.ApplicationSetTemplate{ - Spec: argoprojiov1alpha1.ApplicationSpec{ - Project: "{{.project}}", - }, - }, - }, - }, - callGetDirectories: true, - expected: []map[string]interface{}{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}}, - expectedError: nil, - }, - { - name: "Signature Verification - Checks for non-templated project field", - repoApps: []string{ - "app1", - }, - repoPathsError: nil, - appset: argoprojiov1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "set", - Namespace: "namespace", - }, - Spec: argoprojiov1alpha1.ApplicationSetSpec{ - Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{ - Git: &argoprojiov1alpha1.GitGenerator{ - RepoURL: "RepoURL", - Revision: "Revision", - Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}}, - PathParamPrefix: "", - Values: map[string]string{ - "foo": "bar", - }, - }, - }}, - Template: argoprojiov1alpha1.ApplicationSetTemplate{ - Spec: argoprojiov1alpha1.ApplicationSpec{ - Project: "project", - }, - }, - }, - }, - callGetDirectories: false, - expected: []map[string]interface{}{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}}, - expectedError: fmt.Errorf("error getting project project: appprojects.argoproj.io \"project\" not found"), - }, - } - for _, testCase := range cases { - argoCDServiceMock := mocks.Repos{} - - if testCase.callGetDirectories { - argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCase.repoApps, testCase.repoPathsError) - } - gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace") - - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - appProject := argoprojiov1alpha1.AppProject{} - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build() - - got, err := gitGenerator.GenerateParams(&testCase.appset.Spec.Generators[0], &testCase.appset, client) - - if testCase.expectedError != nil { - require.EqualError(t, err, testCase.expectedError.Error()) - } else { - require.NoError(t, err) - assert.Equal(t, testCase.expected, got) - } - - argoCDServiceMock.AssertExpectations(t) - } -} diff --git a/applicationset/generators/interface.go b/applicationset/generators/interface.go index ea105c7842279..abb4830cf3fbe 100644 --- a/applicationset/generators/interface.go +++ b/applicationset/generators/interface.go @@ -4,8 +4,6 @@ import ( "fmt" "time" - "sigs.k8s.io/controller-runtime/pkg/client" - argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -14,7 +12,7 @@ type Generator interface { // GenerateParams interprets the ApplicationSet and generates all relevant parameters for the application template. // The expected / desired list of parameters is returned, it then will be render and reconciled // against the current state of the Applications in the cluster. - GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) + GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) // GetRequeueAfter is the generator can controller the next reconciled loop // In case there is more then one generator the time will be the minimum of the times. @@ -25,10 +23,8 @@ type Generator interface { GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate } -var ( - EmptyAppSetGeneratorError = fmt.Errorf("ApplicationSet is empty") - NoRequeueAfter time.Duration -) +var EmptyAppSetGeneratorError = fmt.Errorf("ApplicationSet is empty") +var NoRequeueAfter time.Duration // DefaultRequeueAfterSeconds is used when GetRequeueAfter is not specified, it is the default time to wait before the next reconcile loop const ( diff --git a/applicationset/generators/list.go b/applicationset/generators/list.go index fad6a6af5c6d9..b3afabe6dac7d 100644 --- a/applicationset/generators/list.go +++ b/applicationset/generators/list.go @@ -5,7 +5,6 @@ import ( "fmt" "time" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/yaml" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -13,7 +12,8 @@ import ( var _ Generator = (*ListGenerator)(nil) -type ListGenerator struct{} +type ListGenerator struct { +} func NewListGenerator() Generator { g := &ListGenerator{} @@ -28,7 +28,7 @@ func (g *ListGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applicat return &appSetGenerator.List.Template } -func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) { +func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { if appSetGenerator == nil { return nil, EmptyAppSetGeneratorError } @@ -44,7 +44,7 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli var element map[string]interface{} err := json.Unmarshal(tmpItem.Raw, &element) if err != nil { - return nil, fmt.Errorf("error unmarshling list element %w", err) + return nil, fmt.Errorf("error unmarshling list element %v", err) } if appSet.Spec.GoTemplate { @@ -59,14 +59,14 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli for k, v := range values { value, ok := v.(string) if !ok { - return nil, fmt.Errorf("error parsing value as string %w", err) + return nil, fmt.Errorf("error parsing value as string %v", err) } params[fmt.Sprintf("values.%s", k)] = value } } else { v, ok := value.(string) if !ok { - return nil, fmt.Errorf("error parsing value as string %w", err) + return nil, fmt.Errorf("error parsing value as string %v", err) } params[key] = v } @@ -77,10 +77,11 @@ 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 %w", err) + return nil, fmt.Errorf("error unmarshling decoded ElementsYaml %v", err) } res = append(res, yamlElements...) } diff --git a/applicationset/generators/list_test.go b/applicationset/generators/list_test.go index 5a3b1d88dd4f4..39bdb06c06dd7 100644 --- a/applicationset/generators/list_test.go +++ b/applicationset/generators/list_test.go @@ -4,7 +4,6 @@ import ( "testing" "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" @@ -26,7 +25,8 @@ func TestGenerateListParams(t *testing.T) { } for _, testCase := range testCases { - listGenerator := NewListGenerator() + + var listGenerator = NewListGenerator() applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -38,11 +38,11 @@ func TestGenerateListParams(t *testing.T) { got, err := listGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{ List: &argoprojiov1alpha1.ListGenerator{ Elements: testCase.elements, - }, - }, &applicationSetInfo, nil) + }}, &applicationSetInfo) - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCase.expected, got) + } } @@ -61,7 +61,8 @@ func TestGenerateListParamsGoTemplate(t *testing.T) { } for _, testCase := range testCases { - listGenerator := NewListGenerator() + + var listGenerator = NewListGenerator() applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -75,10 +76,9 @@ func TestGenerateListParamsGoTemplate(t *testing.T) { got, err := listGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{ List: &argoprojiov1alpha1.ListGenerator{ Elements: testCase.elements, - }, - }, &applicationSetInfo, nil) + }}, &applicationSetInfo) - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, testCase.expected, got) } } diff --git a/applicationset/generators/matrix.go b/applicationset/generators/matrix.go index 2a44d97b71ac5..3edac086a4b3c 100644 --- a/applicationset/generators/matrix.go +++ b/applicationset/generators/matrix.go @@ -5,7 +5,6 @@ import ( "time" "github.com/imdario/mergo" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/argoproj/argo-cd/v2/applicationset/utils" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -33,7 +32,8 @@ func NewMatrixGenerator(supportedGenerators map[string]Generator) Generator { return m } -func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) { +func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { + if appSetGenerator.Matrix == nil { return nil, EmptyAppSetGeneratorError } @@ -48,16 +48,17 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App res := []map[string]interface{}{} - g0, err := m.getParams(appSetGenerator.Matrix.Generators[0], appSet, nil, client) + g0, err := m.getParams(appSetGenerator.Matrix.Generators[0], appSet, nil) if err != nil { 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, client) + g1, err := m.getParams(appSetGenerator.Matrix.Generators[1], appSet, a) if err != nil { return nil, fmt.Errorf("failed to get params for second generator in the matrix generator: %w", err) } for _, b := range g1 { + if appSet.Spec.GoTemplate { tmp := map[string]interface{}{} if err := mergo.Merge(&tmp, b, mergo.WithOverride); err != nil { @@ -80,7 +81,7 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App return res, nil } -func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, params map[string]interface{}, client client.Client) ([]map[string]interface{}, error) { +func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, params map[string]interface{}) ([]map[string]interface{}, error) { matrixGen, err := getMatrixGenerator(appSetBaseGenerator) if err != nil { return nil, err @@ -118,10 +119,10 @@ func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Appli m.supportedGenerators, argoprojiov1alpha1.ApplicationSetTemplate{}, appSet, - params, - client) + params) + if err != nil { - return nil, fmt.Errorf("child generator returned an error on parameter generation: %w", err) + return nil, fmt.Errorf("child generator returned an error on parameter generation: %v", err) } if len(t) == 0 { @@ -171,6 +172,7 @@ func (m *MatrixGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap } else { return NoRequeueAfter } + } func getMatrixGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MatrixGenerator, error) { diff --git a/applicationset/generators/matrix_test.go b/applicationset/generators/matrix_test.go index 3a961bb0fe877..21e88710ae618 100644 --- a/applicationset/generators/matrix_test.go +++ b/applicationset/generators/matrix_test.go @@ -19,11 +19,11 @@ import ( "github.com/stretchr/testify/mock" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) func TestMatrixGenerate(t *testing.T) { + gitGenerator := &argoprojiov1alpha1.GitGenerator{ RepoURL: "RepoURL", Revision: "Revision", @@ -147,11 +147,12 @@ func TestMatrixGenerate(t *testing.T) { } for _, g := range testCaseCopy.baseGenerators { + gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{ Git: g.Git, List: g.List, } - genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]interface{}{ + genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]interface{}{ { "path": "app1", "path.basename": "app1", @@ -168,7 +169,7 @@ func TestMatrixGenerate(t *testing.T) { Return(&argoprojiov1alpha1.ApplicationSetTemplate{}) } - matrixGenerator := NewMatrixGenerator( + var matrixGenerator = NewMatrixGenerator( map[string]Generator{ "Git": genMock, "List": &ListGenerator{}, @@ -180,19 +181,22 @@ func TestMatrixGenerate(t *testing.T) { Generators: testCaseCopy.baseGenerators, Template: argoprojiov1alpha1.ApplicationSetTemplate{}, }, - }, appSet, nil) + }, appSet) if testCaseCopy.expectedErr != nil { - require.ErrorIs(t, err, testCaseCopy.expectedErr) + assert.ErrorIs(t, err, testCaseCopy.expectedErr) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCaseCopy.expected, got) } + }) + } } func TestMatrixGenerateGoTemplate(t *testing.T) { + gitGenerator := &argoprojiov1alpha1.GitGenerator{ RepoURL: "RepoURL", Revision: "Revision", @@ -356,11 +360,12 @@ func TestMatrixGenerateGoTemplate(t *testing.T) { } for _, g := range testCaseCopy.baseGenerators { + gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{ Git: g.Git, List: g.List, } - genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]interface{}{ + genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]interface{}{ { "path": map[string]string{ "path": "app1", @@ -381,7 +386,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) { Return(&argoprojiov1alpha1.ApplicationSetTemplate{}) } - matrixGenerator := NewMatrixGenerator( + var matrixGenerator = NewMatrixGenerator( map[string]Generator{ "Git": genMock, "List": &ListGenerator{}, @@ -393,19 +398,22 @@ func TestMatrixGenerateGoTemplate(t *testing.T) { Generators: testCaseCopy.baseGenerators, Template: argoprojiov1alpha1.ApplicationSetTemplate{}, }, - }, appSet, nil) + }, appSet) if testCaseCopy.expectedErr != nil { - require.ErrorIs(t, err, testCaseCopy.expectedErr) + assert.ErrorIs(t, err, testCaseCopy.expectedErr) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCaseCopy.expected, got) } + }) + } } func TestMatrixGetRequeueAfter(t *testing.T) { + gitGenerator := &argoprojiov1alpha1.GitGenerator{ RepoURL: "RepoURL", Revision: "Revision", @@ -522,7 +530,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) { mock.On("GetRequeueAfter", &gitGeneratorSpec).Return(testCaseCopy.gitGetRequeueAfter, nil) } - matrixGenerator := NewMatrixGenerator( + var matrixGenerator = NewMatrixGenerator( map[string]Generator{ "Git": mock, "List": &ListGenerator{}, @@ -540,7 +548,9 @@ func TestMatrixGetRequeueAfter(t *testing.T) { }) assert.Equal(t, testCaseCopy.expected, got) + }) + } } @@ -645,9 +655,10 @@ func TestInterpolatedMatrixGenerate(t *testing.T) { fakeClient, testCase.clientError, } - clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace") + var clusterGenerator = NewClusterGenerator(cl, context.Background(), appClientset, "namespace") for _, g := range testCaseCopy.baseGenerators { + gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{ Git: g.Git, Clusters: g.Clusters, @@ -667,7 +678,7 @@ func TestInterpolatedMatrixGenerate(t *testing.T) { genMock.On("GetTemplate", &gitGeneratorSpec). Return(&argoprojiov1alpha1.ApplicationSetTemplate{}) } - matrixGenerator := NewMatrixGenerator( + var matrixGenerator = NewMatrixGenerator( map[string]Generator{ "Git": genMock, "Clusters": clusterGenerator, @@ -679,14 +690,15 @@ func TestInterpolatedMatrixGenerate(t *testing.T) { Generators: testCaseCopy.baseGenerators, Template: argoprojiov1alpha1.ApplicationSetTemplate{}, }, - }, appSet, nil) + }, appSet) if testCaseCopy.expectedErr != nil { - require.ErrorIs(t, err, testCaseCopy.expectedErr) + assert.ErrorIs(t, err, testCaseCopy.expectedErr) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCaseCopy.expected, got) } + }) } } @@ -826,14 +838,16 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) { fakeClient, testCase.clientError, } - clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace") + var clusterGenerator = NewClusterGenerator(cl, context.Background(), appClientset, "namespace") for _, g := range testCaseCopy.baseGenerators { + gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{ Git: g.Git, Clusters: g.Clusters, } genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]interface{}{ + { "path": map[string]string{ "path": "examples/git-generator-files-discovery/cluster-config/dev/config.json", @@ -852,7 +866,7 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) { genMock.On("GetTemplate", &gitGeneratorSpec). Return(&argoprojiov1alpha1.ApplicationSetTemplate{}) } - matrixGenerator := NewMatrixGenerator( + var matrixGenerator = NewMatrixGenerator( map[string]Generator{ "Git": genMock, "Clusters": clusterGenerator, @@ -864,19 +878,22 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) { Generators: testCaseCopy.baseGenerators, Template: argoprojiov1alpha1.ApplicationSetTemplate{}, }, - }, appSet, nil) + }, appSet) if testCaseCopy.expectedErr != nil { - require.ErrorIs(t, err, testCaseCopy.expectedErr) + assert.ErrorIs(t, err, testCaseCopy.expectedErr) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCaseCopy.expected, got) } + }) + } } func TestMatrixGenerateListElementsYaml(t *testing.T) { + gitGenerator := &argoprojiov1alpha1.GitGenerator{ RepoURL: "RepoURL", Revision: "Revision", @@ -980,6 +997,7 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) { } for _, g := range testCaseCopy.baseGenerators { + gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{ Git: g.Git, List: g.List, @@ -1011,9 +1029,10 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) { }}, nil) genMock.On("GetTemplate", &gitGeneratorSpec). Return(&argoprojiov1alpha1.ApplicationSetTemplate{}) + } - matrixGenerator := NewMatrixGenerator( + var matrixGenerator = NewMatrixGenerator( map[string]Generator{ "Git": genMock, "List": &ListGenerator{}, @@ -1025,15 +1044,17 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) { Generators: testCaseCopy.baseGenerators, Template: argoprojiov1alpha1.ApplicationSetTemplate{}, }, - }, appSet, nil) + }, appSet) if testCaseCopy.expectedErr != nil { - require.ErrorIs(t, err, testCaseCopy.expectedErr) + assert.ErrorIs(t, err, testCaseCopy.expectedErr) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCaseCopy.expected, got) } + }) + } } @@ -1047,7 +1068,7 @@ func (g *generatorMock) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applicat return args.Get(0).(*argoprojiov1alpha1.ApplicationSetTemplate) } -func (g *generatorMock) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) { +func (g *generatorMock) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { args := g.Called(appSetGenerator, appSet) return args.Get(0).([]map[string]interface{}), args.Error(1) @@ -1057,6 +1078,7 @@ func (g *generatorMock) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Appl args := g.Called(appSetGenerator) return args.Get(0).(time.Duration) + } func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) { @@ -1073,7 +1095,7 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) { // of that bug. listGeneratorMock := &generatorMock{} - listGeneratorMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).Return([]map[string]interface{}{ + 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{}) @@ -1086,10 +1108,10 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) { } repoServiceMock := &mocks.Repos{} - repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{ + 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, "") + gitGenerator := NewGitGenerator(repoServiceMock) matrixGenerator := NewMatrixGenerator(map[string]Generator{ "List": listGeneratorMock, @@ -1112,17 +1134,9 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) { }, }, } - - scheme := runtime.NewScheme() - err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) - appProject := argoprojiov1alpha1.AppProject{} - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build() - params, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{ Matrix: matrixGeneratorSpec, - }, &argoprojiov1alpha1.ApplicationSet{}, client) + }, &argoprojiov1alpha1.ApplicationSet{}) require.NoError(t, err) assert.Equal(t, []map[string]interface{}{{ "path": "some", diff --git a/applicationset/generators/merge.go b/applicationset/generators/merge.go index e9af81fadae4e..ebda7180df70f 100644 --- a/applicationset/generators/merge.go +++ b/applicationset/generators/merge.go @@ -6,7 +6,6 @@ import ( "time" "github.com/imdario/mergo" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/argoproj/argo-cd/v2/applicationset/utils" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -37,10 +36,10 @@ func NewMergeGenerator(supportedGenerators map[string]Generator) Generator { // getParamSetsForAllGenerators generates params for each child generator in a MergeGenerator. Param sets are returned // in slices ordered according to the order of the given generators. -func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([][]map[string]interface{}, error) { +func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([][]map[string]interface{}, error) { var paramSets [][]map[string]interface{} for i, generator := range generators { - generatorParamSets, err := m.getParams(generator, appSet, client) + generatorParamSets, err := m.getParams(generator, appSet) if err != nil { return nil, fmt.Errorf("error getting params from generator %d of %d: %w", i+1, len(generators), err) } @@ -51,7 +50,7 @@ func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1a } // GenerateParams gets the params produced by the MergeGenerator. -func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) { +func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { if appSetGenerator.Merge == nil { return nil, EmptyAppSetGeneratorError } @@ -60,7 +59,7 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl return nil, ErrLessThanTwoGeneratorsInMerge } - paramSetsFromGenerators, err := m.getParamSetsForAllGenerators(appSetGenerator.Merge.Generators, appSet, client) + paramSetsFromGenerators, err := m.getParamSetsForAllGenerators(appSetGenerator.Merge.Generators, appSet) if err != nil { return nil, fmt.Errorf("error getting param sets from generators: %w", err) } @@ -78,6 +77,7 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl for mergeKeyValue, baseParamSet := range baseParamSetsByMergeKey { if overrideParamSet, exists := paramSetsByMergeKey[mergeKeyValue]; exists { + if appSet.Spec.GoTemplate { if err := mergo.Merge(&baseParamSet, overrideParamSet, mergo.WithOverride); err != nil { return nil, fmt.Errorf("error merging base param set with override param set: %w", err) @@ -95,7 +95,7 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl } mergedParamSets := make([]map[string]interface{}, len(baseParamSetsByMergeKey)) - i := 0 + var i = 0 for _, mergedParamSet := range baseParamSetsByMergeKey { mergedParamSets[i] = mergedParamSet i += 1 @@ -138,7 +138,7 @@ 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, client client.Client) ([]map[string]interface{}, error) { +func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { matrixGen, err := getMatrixGenerator(appSetBaseGenerator) if err != nil { return nil, err @@ -176,9 +176,10 @@ func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Applic m.supportedGenerators, argoprojiov1alpha1.ApplicationSetTemplate{}, appSet, - map[string]interface{}{}, client) + map[string]interface{}{}) + if err != nil { - return nil, fmt.Errorf("child generator returned an error on parameter generation: %w", err) + return nil, fmt.Errorf("child generator returned an error on parameter generation: %v", err) } if len(t) == 0 { @@ -226,6 +227,7 @@ func (m *MergeGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.App } else { return NoRequeueAfter } + } func getMergeGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MergeGenerator, error) { diff --git a/applicationset/generators/merge_test.go b/applicationset/generators/merge_test.go index 005e5c2c32905..454b1884190a3 100644 --- a/applicationset/generators/merge_test.go +++ b/applicationset/generators/merge_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -50,6 +49,7 @@ func listOfMapsToSet(maps []map[string]interface{}) (map[string]bool, error) { } func TestMergeGenerate(t *testing.T) { + testCases := []struct { name string baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator @@ -156,7 +156,7 @@ func TestMergeGenerate(t *testing.T) { appSet := &argoprojiov1alpha1.ApplicationSet{} - mergeGenerator := NewMergeGenerator( + var mergeGenerator = NewMergeGenerator( map[string]Generator{ "List": &ListGenerator{}, "Matrix": &MatrixGenerator{ @@ -178,18 +178,18 @@ func TestMergeGenerate(t *testing.T) { MergeKeys: testCaseCopy.mergeKeys, Template: argoprojiov1alpha1.ApplicationSetTemplate{}, }, - }, appSet, nil) + }, appSet) if testCaseCopy.expectedErr != nil { - require.EqualError(t, err, testCaseCopy.expectedErr.Error()) + assert.EqualError(t, err, testCaseCopy.expectedErr.Error()) } else { expectedSet, err := listOfMapsToSet(testCaseCopy.expected) - require.NoError(t, err) + assert.NoError(t, err) actualSet, err := listOfMapsToSet(got) - require.NoError(t, err) + assert.NoError(t, err) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, expectedSet, actualSet) } }) @@ -197,6 +197,7 @@ func TestMergeGenerate(t *testing.T) { } func toAPIExtensionsJSON(t *testing.T, g interface{}) *apiextensionsv1.JSON { + resVal, err := json.Marshal(g) if err != nil { t.Error("unable to unmarshal json", g) @@ -338,11 +339,13 @@ func TestParamSetsAreUniqueByMergeKeys(t *testing.T) { got, err := getParamSetsByMergeKey(testCaseCopy.mergeKeys, testCaseCopy.paramSets) if testCaseCopy.expectedErr != nil { - require.EqualError(t, err, testCaseCopy.expectedErr.Error()) + assert.EqualError(t, err, testCaseCopy.expectedErr.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCaseCopy.expected, got) } + }) + } } diff --git a/applicationset/generators/mocks/Generator.go b/applicationset/generators/mocks/Generator.go deleted file mode 100644 index dc6197f892866..0000000000000 --- a/applicationset/generators/mocks/Generator.go +++ /dev/null @@ -1,100 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package mocks - -import ( - client "sigs.k8s.io/controller-runtime/pkg/client" - - mock "github.com/stretchr/testify/mock" - - time "time" - - v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" -) - -// Generator is an autogenerated mock type for the Generator type -type Generator struct { - mock.Mock -} - -// GenerateParams provides a mock function with given fields: appSetGenerator, applicationSetInfo, _a2 -func (_m *Generator) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, _a2 client.Client) ([]map[string]interface{}, error) { - ret := _m.Called(appSetGenerator, applicationSetInfo, _a2) - - if len(ret) == 0 { - panic("no return value specified for GenerateParams") - } - - var r0 []map[string]interface{} - var r1 error - if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) ([]map[string]interface{}, error)); ok { - return rf(appSetGenerator, applicationSetInfo, _a2) - } - if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) []map[string]interface{}); ok { - r0 = rf(appSetGenerator, applicationSetInfo, _a2) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]map[string]interface{}) - } - } - - if rf, ok := ret.Get(1).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) error); ok { - r1 = rf(appSetGenerator, applicationSetInfo, _a2) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetRequeueAfter provides a mock function with given fields: appSetGenerator -func (_m *Generator) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration { - ret := _m.Called(appSetGenerator) - - if len(ret) == 0 { - panic("no return value specified for GetRequeueAfter") - } - - var r0 time.Duration - if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) time.Duration); ok { - r0 = rf(appSetGenerator) - } else { - r0 = ret.Get(0).(time.Duration) - } - - return r0 -} - -// GetTemplate provides a mock function with given fields: appSetGenerator -func (_m *Generator) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate { - ret := _m.Called(appSetGenerator) - - if len(ret) == 0 { - panic("no return value specified for GetTemplate") - } - - var r0 *v1alpha1.ApplicationSetTemplate - if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate); ok { - r0 = rf(appSetGenerator) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.ApplicationSetTemplate) - } - } - - return r0 -} - -// NewGenerator creates a new instance of Generator. 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 NewGenerator(t interface { - mock.TestingT - Cleanup(func()) -}) *Generator { - mock := &Generator{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/applicationset/generators/plugin.go b/applicationset/generators/plugin.go index 6b6ba1ec4027e..e0acca0622cdc 100644 --- a/applicationset/generators/plugin.go +++ b/applicationset/generators/plugin.go @@ -55,7 +55,8 @@ func (g *PluginGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applic return &appSetGenerator.Plugin.Template } -func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) { +func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { + if appSetGenerator == nil { return nil, EmptyAppSetGeneratorError } @@ -93,7 +94,7 @@ func (g *PluginGenerator) getPluginFromGenerator(ctx context.Context, appSetName } token, err := g.getToken(ctx, cm["token"]) if err != nil { - return nil, fmt.Errorf("error fetching Secret token: %w", err) + return nil, fmt.Errorf("error fetching Secret token: %v", err) } var requestTimeout int @@ -116,6 +117,7 @@ func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.App res := []map[string]interface{}{} for _, objectFound := range objectsFound { + params := map[string]interface{}{} if useGoTemplate { @@ -150,6 +152,7 @@ func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.App } 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) } @@ -164,8 +167,9 @@ func (g *PluginGenerator) getToken(ctx context.Context, tokenRef string) (string Namespace: g.namespace, }, secret) + if err != nil { - return "", fmt.Errorf("error fetching secret %s/%s: %w", g.namespace, secretName, err) + return "", fmt.Errorf("error fetching secret %s/%s: %v", g.namespace, secretName, err) } secretValues := make(map[string]string, len(secret.Data)) @@ -188,6 +192,7 @@ func (g *PluginGenerator) getConfigMap(ctx context.Context, configMapRef string) Namespace: g.namespace, }, cm) + if err != nil { return nil, err } diff --git a/applicationset/generators/plugin_test.go b/applicationset/generators/plugin_test.go index 55ebcfd5c7820..9611a2cbf14c1 100644 --- a/applicationset/generators/plugin_test.go +++ b/applicationset/generators/plugin_test.go @@ -631,7 +631,9 @@ func TestPluginGenerateParams(t *testing.T) { 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}, @@ -643,9 +645,10 @@ func TestPluginGenerateParams(t *testing.T) { } 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.ReplaceAll(tokenKey, "$", "")] + expectedToken := testCase.secret.Data[strings.Replace(tokenKey, "$", "", -1)] if authHeader != "Bearer "+string(expectedToken) { w.WriteHeader(http.StatusUnauthorized) return @@ -654,7 +657,7 @@ func TestPluginGenerateParams(t *testing.T) { w.Header().Set("Content-Type", "application/json") _, err := w.Write(testCase.content) if err != nil { - require.NoError(t, fmt.Errorf("Error Write %w", err)) + assert.NoError(t, fmt.Errorf("Error Write %v", err)) } }) @@ -670,7 +673,7 @@ func TestPluginGenerateParams(t *testing.T) { fakeClientWithCache := fake.NewClientBuilder().WithObjects([]client.Object{testCase.configmap, testCase.secret}...).Build() - pluginGenerator := NewPluginGenerator(fakeClientWithCache, ctx, fakeClient, "default") + var pluginGenerator = NewPluginGenerator(fakeClientWithCache, ctx, fakeClient, "default") applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -681,15 +684,16 @@ func TestPluginGenerateParams(t *testing.T) { }, } - got, err := pluginGenerator.GenerateParams(&generatorConfig, &applicationSetInfo, nil) + got, err := pluginGenerator.GenerateParams(&generatorConfig, &applicationSetInfo) + if err != nil { fmt.Println(err) } if testCase.expectedError != nil { - require.EqualError(t, err, testCase.expectedError.Error()) + assert.EqualError(t, err, testCase.expectedError.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) expectedJson, err := json.Marshal(testCase.expected) require.NoError(t, err) gotJson, err := json.Marshal(got) diff --git a/applicationset/generators/pull_request.go b/applicationset/generators/pull_request.go index 209e09950e581..c1dfd5ed978e9 100644 --- a/applicationset/generators/pull_request.go +++ b/applicationset/generators/pull_request.go @@ -6,12 +6,12 @@ import ( "strconv" "time" + corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/gosimple/slug" pullrequest "github.com/argoproj/argo-cd/v2/applicationset/services/pull_request" - "github.com/argoproj/argo-cd/v2/applicationset/utils" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -24,13 +24,19 @@ const ( type PullRequestGenerator struct { client client.Client selectServiceProviderFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) - SCMConfig + auth SCMAuthProviders + scmRootCAPath string + allowedSCMProviders []string + enableSCMProviders bool } -func NewPullRequestGenerator(client client.Client, scmConfig SCMConfig) Generator { +func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string, enableSCMProviders bool) Generator { g := &PullRequestGenerator{ - client: client, - SCMConfig: scmConfig, + client: client, + auth: auth, + scmRootCAPath: scmRootCAPath, + allowedSCMProviders: allowedScmProviders, + enableSCMProviders: enableSCMProviders, } g.selectServiceProviderFunc = g.selectServiceProvider return g @@ -50,7 +56,7 @@ func (g *PullRequestGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.A return &appSetGenerator.PullRequest.Template } -func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) { +func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { if appSetGenerator == nil { return nil, EmptyAppSetGeneratorError } @@ -67,7 +73,7 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters) if err != nil { - return nil, fmt.Errorf("error listing repos: %w", err) + return nil, fmt.Errorf("error listing repos: %v", err) } params := make([]map[string]interface{}, 0, len(pulls)) @@ -97,7 +103,6 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha paramMap := map[string]interface{}{ "number": strconv.Itoa(pull.Number), - "title": pull.Title, "branch": pull.Branch, "branch_slug": slug.Make(pull.Branch), "target_branch": pull.TargetBranch, @@ -105,7 +110,6 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha "head_sha": pull.HeadSHA, "head_short_sha": pull.HeadSHA[:shortSHALength], "head_short_sha_7": pull.HeadSHA[:shortSHALength7], - "author": pull.Author, } // PR lables will only be supported for Go Template appsets, since fasttemplate will be deprecated. @@ -131,66 +135,44 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera } if generatorConfig.GitLab != nil { providerConfig := generatorConfig.GitLab - var caCerts []byte - var prErr error - if providerConfig.CARef != nil { - caCerts, prErr = utils.GetConfigMapData(ctx, g.client, providerConfig.CARef, applicationSetInfo.Namespace) - if prErr != nil { - return nil, fmt.Errorf("error fetching CA certificates from ConfigMap: %w", prErr) - } - } - token, err := utils.GetSecretRef(ctx, g.client, providerConfig.TokenRef, applicationSetInfo.Namespace) + token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Secret token: %w", err) + return nil, fmt.Errorf("error fetching Secret token: %v", err) } - return pullrequest.NewGitLabService(ctx, token, providerConfig.API, providerConfig.Project, providerConfig.Labels, providerConfig.PullRequestState, g.scmRootCAPath, providerConfig.Insecure, caCerts) + return pullrequest.NewGitLabService(ctx, token, providerConfig.API, providerConfig.Project, providerConfig.Labels, providerConfig.PullRequestState, g.scmRootCAPath, providerConfig.Insecure) } if generatorConfig.Gitea != nil { providerConfig := generatorConfig.Gitea - token, err := utils.GetSecretRef(ctx, g.client, providerConfig.TokenRef, applicationSetInfo.Namespace) + token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Secret token: %w", err) + return nil, fmt.Errorf("error fetching Secret token: %v", err) } return pullrequest.NewGiteaService(ctx, token, providerConfig.API, providerConfig.Owner, providerConfig.Repo, providerConfig.Insecure) } if generatorConfig.BitbucketServer != nil { providerConfig := generatorConfig.BitbucketServer - var caCerts []byte - var prErr error - if providerConfig.CARef != nil { - caCerts, prErr = utils.GetConfigMapData(ctx, g.client, providerConfig.CARef, applicationSetInfo.Namespace) - if prErr != nil { - return nil, fmt.Errorf("error fetching CA certificates from ConfigMap: %w", prErr) - } - } - if providerConfig.BearerToken != nil { - appToken, err := utils.GetSecretRef(ctx, g.client, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace) - if err != nil { - return nil, fmt.Errorf("error fetching Secret Bearer token: %w", err) - } - return pullrequest.NewBitbucketServiceBearerToken(ctx, providerConfig.API, appToken, providerConfig.Project, providerConfig.Repo, g.scmRootCAPath, providerConfig.Insecure, caCerts) - } else if providerConfig.BasicAuth != nil { - password, err := utils.GetSecretRef(ctx, g.client, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace) + 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: %w", err) + return nil, fmt.Errorf("error fetching Secret token: %v", err) } - return pullrequest.NewBitbucketServiceBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.Repo, g.scmRootCAPath, providerConfig.Insecure, caCerts) + return pullrequest.NewBitbucketServiceBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.Repo) } else { - return pullrequest.NewBitbucketServiceNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.Repo, g.scmRootCAPath, providerConfig.Insecure, caCerts) + return pullrequest.NewBitbucketServiceNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.Repo) } } if generatorConfig.Bitbucket != nil { providerConfig := generatorConfig.Bitbucket if providerConfig.BearerToken != nil { - appToken, err := utils.GetSecretRef(ctx, g.client, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace) + appToken, err := g.getSecretRef(ctx, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Secret Bearer token: %w", err) + 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 := utils.GetSecretRef(ctx, g.client, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace) + password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Secret token: %w", err) + return nil, fmt.Errorf("error fetching Secret token: %v", err) } return pullrequest.NewBitbucketCloudServiceBasicAuth(providerConfig.API, providerConfig.BasicAuth.Username, password, providerConfig.Owner, providerConfig.Repo) } else { @@ -199,9 +181,9 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera } if generatorConfig.AzureDevOps != nil { providerConfig := generatorConfig.AzureDevOps - token, err := utils.GetSecretRef(ctx, g.client, providerConfig.TokenRef, applicationSetInfo.Namespace) + token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Secret token: %w", err) + 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) } @@ -211,17 +193,41 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera func (g *PullRequestGenerator) github(ctx context.Context, cfg *argoprojiov1alpha1.PullRequestGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) { // use an app if it was configured if cfg.AppSecretName != "" { - auth, err := g.GitHubApps.GetAuthSecret(ctx, cfg.AppSecretName) + auth, err := g.auth.GitHubApps.GetAuthSecret(ctx, cfg.AppSecretName) if err != nil { - return nil, fmt.Errorf("error getting GitHub App secret: %w", err) + return nil, fmt.Errorf("error getting GitHub App secret: %v", err) } return pullrequest.NewGithubAppService(*auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels) } // always default to token, even if not set (public access) - token, err := utils.GetSecretRef(ctx, g.client, cfg.TokenRef, applicationSetInfo.Namespace) + token, err := g.getSecretRef(ctx, cfg.TokenRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Secret token: %w", err) + return nil, fmt.Errorf("error fetching Secret token: %v", err) } return pullrequest.NewGithubService(ctx, token, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels) } + +// getSecretRef gets the value of the key for the specified Secret resource. +func (g *PullRequestGenerator) getSecretRef(ctx context.Context, ref *argoprojiov1alpha1.SecretRef, namespace string) (string, error) { + if ref == nil { + return "", nil + } + + secret := &corev1.Secret{} + err := g.client.Get( + ctx, + client.ObjectKey{ + Name: ref.SecretName, + Namespace: namespace, + }, + secret) + if err != nil { + return "", fmt.Errorf("error fetching secret %s/%s: %v", namespace, ref.SecretName, err) + } + tokenBytes, ok := secret.Data[ref.Key] + if !ok { + return "", fmt.Errorf("key %q in secret %s/%s not found", ref.Key, namespace, ref.SecretName) + } + return string(tokenBytes), nil +} diff --git a/applicationset/generators/pull_request_test.go b/applicationset/generators/pull_request_test.go index e02e7312b350f..9f4d3d0a9b693 100644 --- a/applicationset/generators/pull_request_test.go +++ b/applicationset/generators/pull_request_test.go @@ -6,8 +6,9 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" pullrequest "github.com/argoproj/argo-cd/v2/applicationset/services/pull_request" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -28,11 +29,9 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { []*pullrequest.PullRequest{ { Number: 1, - Title: "title1", Branch: "branch1", TargetBranch: "master", HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "testName", }, }, nil, @@ -41,7 +40,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { expected: []map[string]interface{}{ { "number": "1", - "title": "title1", "branch": "branch1", "branch_slug": "branch1", "target_branch": "master", @@ -49,7 +47,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { "head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958", "head_short_sha": "089d92cb", "head_short_sha_7": "089d92c", - "author": "testName", }, }, expectedErr: nil, @@ -61,11 +58,9 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { []*pullrequest.PullRequest{ { Number: 2, - Title: "title2", 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", - Author: "testName", }, }, nil, @@ -74,7 +69,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { expected: []map[string]interface{}{ { "number": "2", - "title": "title2", "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", @@ -82,7 +76,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { "head_sha": "9b34ff5bd418e57d58891eb0aa0728043ca1e8be", "head_short_sha": "9b34ff5b", "head_short_sha_7": "9b34ff5", - "author": "testName", }, }, expectedErr: nil, @@ -94,11 +87,9 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { []*pullrequest.PullRequest{ { Number: 1, - Title: "title1", Branch: "a-very-short-sha", TargetBranch: "master", HeadSHA: "abcd", - Author: "testName", }, }, nil, @@ -107,7 +98,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { expected: []map[string]interface{}{ { "number": "1", - "title": "title1", "branch": "a-very-short-sha", "branch_slug": "a-very-short-sha", "target_branch": "master", @@ -115,7 +105,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { "head_sha": "abcd", "head_short_sha": "abcd", "head_short_sha_7": "abcd", - "author": "testName", }, }, expectedErr: nil, @@ -138,12 +127,10 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { []*pullrequest.PullRequest{ { Number: 1, - Title: "title1", Branch: "branch1", TargetBranch: "master", HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", Labels: []string{"preview"}, - Author: "testName", }, }, nil, @@ -152,7 +139,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { expected: []map[string]interface{}{ { "number": "1", - "title": "title1", "branch": "branch1", "branch_slug": "branch1", "target_branch": "master", @@ -161,7 +147,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { "head_short_sha": "089d92cb", "head_short_sha_7": "089d92c", "labels": []string{"preview"}, - "author": "testName", }, }, expectedErr: nil, @@ -179,12 +164,10 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { []*pullrequest.PullRequest{ { Number: 1, - Title: "title1", Branch: "branch1", TargetBranch: "master", HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", Labels: []string{"preview"}, - Author: "testName", }, }, nil, @@ -193,7 +176,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { expected: []map[string]interface{}{ { "number": "1", - "title": "title1", "branch": "branch1", "branch_slug": "branch1", "target_branch": "master", @@ -201,7 +183,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { "head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958", "head_short_sha": "089d92cb", "head_short_sha_7": "089d92c", - "author": "testName", }, }, expectedErr: nil, @@ -222,20 +203,82 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { PullRequest: &argoprojiov1alpha1.PullRequestGenerator{}, } - got, gotErr := gen.GenerateParams(&generatorConfig, &c.applicationSet, nil) - if c.expectedErr != nil { - assert.Equal(t, c.expectedErr.Error(), gotErr.Error()) - } else { - require.NoError(t, gotErr) - } + got, gotErr := gen.GenerateParams(&generatorConfig, &c.applicationSet) + assert.Equal(t, c.expectedErr, gotErr) assert.ElementsMatch(t, c.expected, got) } } +func TestPullRequestGetSecretRef(t *testing.T) { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test-secret", Namespace: "test"}, + Data: map[string][]byte{ + "my-token": []byte("secret"), + }, + } + gen := &PullRequestGenerator{client: fake.NewClientBuilder().WithObjects(secret).Build()} + ctx := context.Background() + + cases := []struct { + name, namespace, token string + ref *argoprojiov1alpha1.SecretRef + hasError bool + }{ + { + name: "valid ref", + ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "my-token"}, + namespace: "test", + token: "secret", + hasError: false, + }, + { + name: "nil ref", + ref: nil, + namespace: "test", + token: "", + hasError: false, + }, + { + name: "wrong name", + ref: &argoprojiov1alpha1.SecretRef{SecretName: "other", Key: "my-token"}, + namespace: "test", + token: "", + hasError: true, + }, + { + name: "wrong key", + ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "other-token"}, + namespace: "test", + token: "", + hasError: true, + }, + { + name: "wrong namespace", + ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "my-token"}, + namespace: "other", + token: "", + hasError: true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + token, err := gen.getSecretRef(ctx, c.ref, c.namespace) + if c.hasError { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + } + assert.Equal(t, c.token, token) + }) + } +} + func TestAllowedSCMProviderPullRequest(t *testing.T) { cases := []struct { name string providerConfig *argoprojiov1alpha1.PullRequestGenerator + expectedError error }{ { name: "Error Github", @@ -244,6 +287,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Gitlab", @@ -252,6 +296,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Gitea", @@ -260,6 +305,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Bitbucket", @@ -268,6 +314,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, + expectedError: &ErrDisallowedSCMProvider{}, }, } @@ -277,13 +324,13 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { t.Run(testCaseCopy.name, func(t *testing.T) { t.Parallel() - pullRequestGenerator := NewPullRequestGenerator(nil, NewSCMConfig("", []string{ + pullRequestGenerator := NewPullRequestGenerator(nil, SCMAuthProviders{}, "", []string{ "github.myorg.com", "gitlab.myorg.com", "gitea.myorg.com", "bitbucket.myorg.com", "azuredevops.myorg.com", - }, true, nil)) + }, true) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -296,17 +343,16 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { }, } - _, err := pullRequestGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil) + _, err := pullRequestGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) - require.Error(t, err, "Must return an error") - var expectedError ErrDisallowedSCMProvider - assert.ErrorAs(t, err, &expectedError) + assert.Error(t, err, "Must return an error") + assert.ErrorAs(t, err, testCaseCopy.expectedError) }) } } func TestSCMProviderDisabled_PRGenerator(t *testing.T) { - generator := NewPullRequestGenerator(nil, NewSCMConfig("", []string{}, false, nil)) + generator := NewPullRequestGenerator(nil, SCMAuthProviders{}, "", []string{}, false) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -323,6 +369,6 @@ func TestSCMProviderDisabled_PRGenerator(t *testing.T) { }, } - _, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil) + _, 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 85a2550ae21f9..42b7789be67f0 100644 --- a/applicationset/generators/scm_provider.go +++ b/applicationset/generators/scm_provider.go @@ -7,6 +7,7 @@ import ( "strings" "time" + corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" log "github.com/sirupsen/logrus" @@ -28,36 +29,29 @@ type SCMProviderGenerator struct { client client.Client // Testing hooks. overrideProvider scm_provider.SCMProviderService - SCMConfig -} -type SCMConfig struct { + SCMAuthProviders scmRootCAPath string allowedSCMProviders []string enableSCMProviders bool - GitHubApps github_app_auth.Credentials } -func NewSCMConfig(scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool, gitHubApps github_app_auth.Credentials) SCMConfig { - return SCMConfig{ - scmRootCAPath: scmRootCAPath, - allowedSCMProviders: allowedSCMProviders, - enableSCMProviders: enableSCMProviders, - GitHubApps: gitHubApps, - } +type SCMAuthProviders struct { + GitHubApps github_app_auth.Credentials } -func NewSCMProviderGenerator(client client.Client, scmConfig SCMConfig) Generator { +func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool) Generator { return &SCMProviderGenerator{ - client: client, - SCMConfig: scmConfig, + client: client, + SCMAuthProviders: providers, + scmRootCAPath: scmRootCAPath, + allowedSCMProviders: allowedSCMProviders, + enableSCMProviders: enableSCMProviders, } } // Testing generator func NewTestSCMProviderGenerator(overrideProvider scm_provider.SCMProviderService) Generator { - return &SCMProviderGenerator{overrideProvider: overrideProvider, SCMConfig: SCMConfig{ - enableSCMProviders: true, - }} + return &SCMProviderGenerator{overrideProvider: overrideProvider, enableSCMProviders: true} } func (g *SCMProviderGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration { @@ -114,7 +108,7 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, g return NewErrDisallowedSCMProvider(url, allowedScmProviders) } -func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) { +func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { if appSetGenerator == nil { return nil, EmptyAppSetGeneratorError } @@ -145,83 +139,61 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha return nil, fmt.Errorf("scm provider: %w", err) } } else if providerConfig.Gitlab != nil { - providerConfig := providerConfig.Gitlab - var caCerts []byte - var scmError error - if providerConfig.CARef != nil { - caCerts, scmError = utils.GetConfigMapData(ctx, g.client, providerConfig.CARef, applicationSetInfo.Namespace) - if scmError != nil { - return nil, fmt.Errorf("error fetching CA certificates from ConfigMap: %w", scmError) - } - } - token, err := utils.GetSecretRef(ctx, g.client, providerConfig.TokenRef, applicationSetInfo.Namespace) + token, err := g.getSecretRef(ctx, providerConfig.Gitlab.TokenRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Gitlab token: %w", err) + return nil, fmt.Errorf("error fetching Gitlab token: %v", err) } - provider, err = scm_provider.NewGitlabProvider(ctx, providerConfig.Group, token, providerConfig.API, providerConfig.AllBranches, providerConfig.IncludeSubgroups, providerConfig.WillIncludeSharedProjects(), providerConfig.Insecure, g.scmRootCAPath, providerConfig.Topic, caCerts) + 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: %w", err) + return nil, fmt.Errorf("error initializing Gitlab service: %v", err) } } else if providerConfig.Gitea != nil { - token, err := utils.GetSecretRef(ctx, g.client, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace) + token, err := g.getSecretRef(ctx, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Gitea token: %w", err) + return nil, fmt.Errorf("error fetching Gitea token: %v", err) } provider, err = scm_provider.NewGiteaProvider(ctx, providerConfig.Gitea.Owner, token, providerConfig.Gitea.API, providerConfig.Gitea.AllBranches, providerConfig.Gitea.Insecure) if err != nil { - return nil, fmt.Errorf("error initializing Gitea service: %w", err) + return nil, fmt.Errorf("error initializing Gitea service: %v", err) } } else if providerConfig.BitbucketServer != nil { providerConfig := providerConfig.BitbucketServer - var caCerts []byte var scmError error - if providerConfig.CARef != nil { - caCerts, scmError = utils.GetConfigMapData(ctx, g.client, providerConfig.CARef, applicationSetInfo.Namespace) - if scmError != nil { - return nil, fmt.Errorf("error fetching CA certificates from ConfigMap: %w", scmError) - } - } - if providerConfig.BearerToken != nil { - appToken, err := utils.GetSecretRef(ctx, g.client, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace) - if err != nil { - return nil, fmt.Errorf("error fetching Secret Bearer token: %w", err) - } - provider, scmError = scm_provider.NewBitbucketServerProviderBearerToken(ctx, appToken, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts) - } else if providerConfig.BasicAuth != nil { - password, err := utils.GetSecretRef(ctx, g.client, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace) + 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: %w", err) + return nil, fmt.Errorf("error fetching Secret token: %v", err) } - provider, scmError = scm_provider.NewBitbucketServerProviderBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts) + provider, scmError = scm_provider.NewBitbucketServerProviderBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.AllBranches) } else { - provider, scmError = scm_provider.NewBitbucketServerProviderNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts) + provider, scmError = scm_provider.NewBitbucketServerProviderNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.AllBranches) } if scmError != nil { - return nil, fmt.Errorf("error initializing Bitbucket Server service: %w", scmError) + return nil, fmt.Errorf("error initializing Bitbucket Server service: %v", scmError) } } else if providerConfig.AzureDevOps != nil { - token, err := utils.GetSecretRef(ctx, g.client, providerConfig.AzureDevOps.AccessTokenRef, applicationSetInfo.Namespace) + token, err := g.getSecretRef(ctx, providerConfig.AzureDevOps.AccessTokenRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Azure Devops access token: %w", err) + return nil, fmt.Errorf("error fetching Azure Devops access token: %v", err) } provider, err = scm_provider.NewAzureDevOpsProvider(ctx, token, providerConfig.AzureDevOps.Organization, providerConfig.AzureDevOps.API, providerConfig.AzureDevOps.TeamProject, providerConfig.AzureDevOps.AllBranches) if err != nil { - return nil, fmt.Errorf("error initializing Azure Devops service: %w", err) + return nil, fmt.Errorf("error initializing Azure Devops service: %v", err) } } else if providerConfig.Bitbucket != nil { - appPassword, err := utils.GetSecretRef(ctx, g.client, providerConfig.Bitbucket.AppPasswordRef, applicationSetInfo.Namespace) + appPassword, err := g.getSecretRef(ctx, providerConfig.Bitbucket.AppPasswordRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Bitbucket cloud appPassword: %w", err) + return nil, fmt.Errorf("error fetching Bitbucket cloud appPassword: %v", err) } provider, err = scm_provider.NewBitBucketCloudProvider(ctx, providerConfig.Bitbucket.Owner, providerConfig.Bitbucket.User, appPassword, providerConfig.Bitbucket.AllBranches) if err != nil { - return nil, fmt.Errorf("error initializing Bitbucket cloud service: %w", err) + 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: %w", awsErr) + return nil, fmt.Errorf("error initializing AWS codecommit service: %v", awsErr) } } else { return nil, fmt.Errorf("no SCM provider implementation configured") @@ -230,7 +202,7 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha // Find all the available repos. repos, err := scm_provider.ListRepos(ctx, provider, providerConfig.Filters, providerConfig.CloneProtocol) if err != nil { - return nil, fmt.Errorf("error listing repos: %w", err) + return nil, fmt.Errorf("error listing repos: %v", err) } paramsArray := make([]map[string]interface{}, 0, len(repos)) var shortSHALength int @@ -268,11 +240,34 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha return paramsArray, nil } +func (g *SCMProviderGenerator) getSecretRef(ctx context.Context, ref *argoprojiov1alpha1.SecretRef, namespace string) (string, error) { + if ref == nil { + return "", nil + } + + secret := &corev1.Secret{} + err := g.client.Get( + ctx, + client.ObjectKey{ + Name: ref.SecretName, + Namespace: namespace, + }, + secret) + if err != nil { + return "", fmt.Errorf("error fetching secret %s/%s: %v", namespace, ref.SecretName, err) + } + tokenBytes, ok := secret.Data[ref.Key] + if !ok { + return "", fmt.Errorf("key %q in secret %s/%s not found", ref.Key, namespace, ref.SecretName) + } + return string(tokenBytes), nil +} + func (g *SCMProviderGenerator) githubProvider(ctx context.Context, github *argoprojiov1alpha1.SCMProviderGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (scm_provider.SCMProviderService, error) { if github.AppSecretName != "" { auth, err := g.GitHubApps.GetAuthSecret(ctx, github.AppSecretName) if err != nil { - return nil, fmt.Errorf("error fetching Github app secret: %w", err) + return nil, fmt.Errorf("error fetching Github app secret: %v", err) } return scm_provider.NewGithubAppProviderFor( @@ -283,9 +278,9 @@ func (g *SCMProviderGenerator) githubProvider(ctx context.Context, github *argop ) } - token, err := utils.GetSecretRef(ctx, g.client, github.TokenRef, applicationSetInfo.Namespace) + token, err := g.getSecretRef(ctx, github.TokenRef, applicationSetInfo.Namespace) if err != nil { - return nil, fmt.Errorf("error fetching Github token: %w", err) + return nil, fmt.Errorf("error fetching Github token: %v", err) } return scm_provider.NewGithubProvider(ctx, github.Organization, token, github.API, github.AllBranches) } diff --git a/applicationset/generators/scm_provider_test.go b/applicationset/generators/scm_provider_test.go index a52f7e8159b86..c438aa8f646fe 100644 --- a/applicationset/generators/scm_provider_test.go +++ b/applicationset/generators/scm_provider_test.go @@ -1,16 +1,84 @@ package generators import ( + "context" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) +func TestSCMProviderGetSecretRef(t *testing.T) { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test-secret", Namespace: "test"}, + Data: map[string][]byte{ + "my-token": []byte("secret"), + }, + } + gen := &SCMProviderGenerator{client: fake.NewClientBuilder().WithObjects(secret).Build()} + ctx := context.Background() + + cases := []struct { + name, namespace, token string + ref *argoprojiov1alpha1.SecretRef + hasError bool + }{ + { + name: "valid ref", + ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "my-token"}, + namespace: "test", + token: "secret", + hasError: false, + }, + { + name: "nil ref", + ref: nil, + namespace: "test", + token: "", + hasError: false, + }, + { + name: "wrong name", + ref: &argoprojiov1alpha1.SecretRef{SecretName: "other", Key: "my-token"}, + namespace: "test", + token: "", + hasError: true, + }, + { + name: "wrong key", + ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "other-token"}, + namespace: "test", + token: "", + hasError: true, + }, + { + name: "wrong namespace", + ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "my-token"}, + namespace: "other", + token: "", + hasError: true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + token, err := gen.getSecretRef(ctx, c.ref, c.namespace) + if c.hasError { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + } + assert.Equal(t, c.token, token) + + }) + } +} + func TestSCMProviderGenerateParams(t *testing.T) { cases := []struct { name string @@ -106,7 +174,7 @@ func TestSCMProviderGenerateParams(t *testing.T) { mockProvider := &scm_provider.MockProvider{ Repos: testCaseCopy.repos, } - scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider, SCMConfig: SCMConfig{enableSCMProviders: true}} + scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider, enableSCMProviders: true} applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", @@ -120,14 +188,15 @@ func TestSCMProviderGenerateParams(t *testing.T) { }, } - got, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil) + got, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) if testCaseCopy.expectedError != nil { assert.EqualError(t, err, testCaseCopy.expectedError.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCaseCopy.expected, got) } + }) } } @@ -136,6 +205,7 @@ func TestAllowedSCMProvider(t *testing.T) { cases := []struct { name string providerConfig *argoprojiov1alpha1.SCMProviderGenerator + expectedError error }{ { name: "Error Github", @@ -144,6 +214,7 @@ func TestAllowedSCMProvider(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Gitlab", @@ -152,6 +223,7 @@ func TestAllowedSCMProvider(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Gitea", @@ -160,6 +232,7 @@ func TestAllowedSCMProvider(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Bitbucket", @@ -168,6 +241,7 @@ func TestAllowedSCMProvider(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error AzureDevops", @@ -176,6 +250,7 @@ func TestAllowedSCMProvider(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, + expectedError: &ErrDisallowedSCMProvider{}, }, } @@ -186,16 +261,14 @@ func TestAllowedSCMProvider(t *testing.T) { t.Parallel() scmGenerator := &SCMProviderGenerator{ - SCMConfig: SCMConfig{ - allowedSCMProviders: []string{ - "github.myorg.com", - "gitlab.myorg.com", - "gitea.myorg.com", - "bitbucket.myorg.com", - "azuredevops.myorg.com", - }, - enableSCMProviders: true, + allowedSCMProviders: []string{ + "github.myorg.com", + "gitlab.myorg.com", + "gitea.myorg.com", + "bitbucket.myorg.com", + "azuredevops.myorg.com", }, + enableSCMProviders: true, } applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ @@ -209,17 +282,16 @@ func TestAllowedSCMProvider(t *testing.T) { }, } - _, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil) + _, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) - require.Error(t, err, "Must return an error") - var expectedError ErrDisallowedSCMProvider - assert.ErrorAs(t, err, &expectedError) + assert.Error(t, err, "Must return an error") + assert.ErrorAs(t, err, testCaseCopy.expectedError) }) } } func TestSCMProviderDisabled_SCMGenerator(t *testing.T) { - generator := &SCMProviderGenerator{SCMConfig: SCMConfig{enableSCMProviders: false}} + generator := &SCMProviderGenerator{enableSCMProviders: false} applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -236,6 +308,6 @@ func TestSCMProviderDisabled_SCMGenerator(t *testing.T) { }, } - _, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil) + _, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) assert.ErrorIs(t, err, ErrSCMProvidersDisabled) } diff --git a/applicationset/generators/utils.go b/applicationset/generators/utils.go deleted file mode 100644 index 84bdda6101006..0000000000000 --- a/applicationset/generators/utils.go +++ /dev/null @@ -1,49 +0,0 @@ -package generators - -import ( - "context" - - "k8s.io/client-go/dynamic" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/argoproj/argo-cd/v2/applicationset/services" -) - -func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, namespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator { - terminalGenerators := map[string]Generator{ - "List": NewListGenerator(), - "Clusters": NewClusterGenerator(c, ctx, k8sClient, namespace), - "Git": NewGitGenerator(argoCDService, namespace), - "SCMProvider": NewSCMProviderGenerator(c, scmConfig), - "ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace), - "PullRequest": NewPullRequestGenerator(c, scmConfig), - "Plugin": NewPluginGenerator(c, ctx, k8sClient, namespace), - } - - nestedGenerators := map[string]Generator{ - "List": terminalGenerators["List"], - "Clusters": terminalGenerators["Clusters"], - "Git": terminalGenerators["Git"], - "SCMProvider": terminalGenerators["SCMProvider"], - "ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"], - "PullRequest": terminalGenerators["PullRequest"], - "Plugin": terminalGenerators["Plugin"], - "Matrix": NewMatrixGenerator(terminalGenerators), - "Merge": NewMergeGenerator(terminalGenerators), - } - - topLevelGenerators := map[string]Generator{ - "List": terminalGenerators["List"], - "Clusters": terminalGenerators["Clusters"], - "Git": terminalGenerators["Git"], - "SCMProvider": terminalGenerators["SCMProvider"], - "ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"], - "PullRequest": terminalGenerators["PullRequest"], - "Plugin": terminalGenerators["Plugin"], - "Matrix": NewMatrixGenerator(nestedGenerators), - "Merge": NewMergeGenerator(nestedGenerators), - } - - return topLevelGenerators -} diff --git a/applicationset/generators/value_interpolation.go b/applicationset/generators/value_interpolation.go index 814843e3d899d..05a078d42f782 100644 --- a/applicationset/generators/value_interpolation.go +++ b/applicationset/generators/value_interpolation.go @@ -12,6 +12,7 @@ func appendTemplatedValues(values map[string]string, params 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) } diff --git a/applicationset/generators/value_interpolation_test.go b/applicationset/generators/value_interpolation_test.go index 5b490233d5d7e..8aa57dc0c0e65 100644 --- a/applicationset/generators/value_interpolation_test.go +++ b/applicationset/generators/value_interpolation_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestValueInterpolation(t *testing.T) { @@ -54,9 +53,10 @@ func TestValueInterpolation(t *testing.T) { } for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { err := appendTemplatedValues(testCase.values, testCase.params, false, nil) - require.NoError(t, err) + assert.NoError(t, err) assert.EqualValues(t, testCase.expected, testCase.params) }) } @@ -115,9 +115,10 @@ func TestValueInterpolationWithGoTemplating(t *testing.T) { } for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { err := appendTemplatedValues(testCase.values, testCase.params, true, nil) - require.NoError(t, err) + assert.NoError(t, err) assert.EqualValues(t, testCase.expected, testCase.params) }) } diff --git a/applicationset/metrics/fake.go b/applicationset/metrics/fake.go deleted file mode 100644 index 9c1d55d2f24d2..0000000000000 --- a/applicationset/metrics/fake.go +++ /dev/null @@ -1,22 +0,0 @@ -package metrics - -import ( - "github.com/prometheus/client_golang/prometheus" - ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" -) - -// Fake implementation for testing -func NewFakeAppsetMetrics(client ctrlclient.WithWatch) *ApplicationsetMetrics { - reconcileHistogram := prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "argocd_appset_reconcile", - Help: "Application reconciliation performance in seconds.", - // Buckets can be set later on after observing median time - }, - []string{"name", "namespace"}, - ) - - return &ApplicationsetMetrics{ - reconcileHistogram: reconcileHistogram, - } -} diff --git a/applicationset/metrics/metrics.go b/applicationset/metrics/metrics.go deleted file mode 100644 index 5b5c1cd82c4b3..0000000000000 --- a/applicationset/metrics/metrics.go +++ /dev/null @@ -1,131 +0,0 @@ -package metrics - -import ( - "time" - - "github.com/prometheus/client_golang/prometheus" - "k8s.io/apimachinery/pkg/labels" - "sigs.k8s.io/controller-runtime/pkg/metrics" - - argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" - metricsutil "github.com/argoproj/argo-cd/v2/util/metrics" -) - -var ( - descAppsetLabels *prometheus.Desc - descAppsetDefaultLabels = []string{"namespace", "name"} - descAppsetInfo = prometheus.NewDesc( - "argocd_appset_info", - "Information about applicationset", - append(descAppsetDefaultLabels, "resource_update_status"), - nil, - ) - - descAppsetGeneratedApps = prometheus.NewDesc( - "argocd_appset_owned_applications", - "Number of applications owned by the applicationset", - descAppsetDefaultLabels, - nil, - ) -) - -type ApplicationsetMetrics struct { - reconcileHistogram *prometheus.HistogramVec -} - -type appsetCollector struct { - lister applisters.ApplicationSetLister - // appsClientSet appclientset.Interface - labels []string - filter func(appset *argoappv1.ApplicationSet) bool -} - -func NewApplicationsetMetrics(appsetLister applisters.ApplicationSetLister, appsetLabels []string, appsetFilter func(appset *argoappv1.ApplicationSet) bool) ApplicationsetMetrics { - reconcileHistogram := prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "argocd_appset_reconcile", - Help: "Application reconciliation performance in seconds.", - // Buckets can be set later on after observing median time - }, - descAppsetDefaultLabels, - ) - - appsetCollector := newAppsetCollector(appsetLister, appsetLabels, appsetFilter) - - // Register collectors and metrics - metrics.Registry.MustRegister(reconcileHistogram) - metrics.Registry.MustRegister(appsetCollector) - - return ApplicationsetMetrics{ - reconcileHistogram: reconcileHistogram, - } -} - -func (m *ApplicationsetMetrics) ObserveReconcile(appset *argoappv1.ApplicationSet, duration time.Duration) { - m.reconcileHistogram.WithLabelValues(appset.Namespace, appset.Name).Observe(duration.Seconds()) -} - -func newAppsetCollector(lister applisters.ApplicationSetLister, labels []string, filter func(appset *argoappv1.ApplicationSet) bool) *appsetCollector { - descAppsetDefaultLabels = []string{"namespace", "name"} - - if len(labels) > 0 { - descAppsetLabels = prometheus.NewDesc( - "argocd_appset_labels", - "Applicationset labels translated to Prometheus labels", - append(descAppsetDefaultLabels, metricsutil.NormalizeLabels("label", labels)...), - nil, - ) - } - - return &appsetCollector{ - lister: lister, - labels: labels, - filter: filter, - } -} - -// Describe implements the prometheus.Collector interface -func (c *appsetCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- descAppsetInfo - ch <- descAppsetGeneratedApps - - if len(c.labels) > 0 { - ch <- descAppsetLabels - } -} - -// Collect implements the prometheus.Collector interface -func (c *appsetCollector) Collect(ch chan<- prometheus.Metric) { - appsets, _ := c.lister.List(labels.NewSelector()) - - for _, appset := range appsets { - if c.filter(appset) { - collectAppset(appset, c.labels, ch) - } - } -} - -func collectAppset(appset *argoappv1.ApplicationSet, labelsToCollect []string, ch chan<- prometheus.Metric) { - labelValues := make([]string, 0) - commonLabelValues := []string{appset.Namespace, appset.Name} - - for _, label := range labelsToCollect { - labelValues = append(labelValues, appset.GetLabels()[label]) - } - - resourceUpdateStatus := "Unknown" - - for _, condition := range appset.Status.Conditions { - if condition.Type == argoappv1.ApplicationSetConditionResourcesUpToDate { - resourceUpdateStatus = condition.Reason - } - } - - if len(labelsToCollect) > 0 { - ch <- prometheus.MustNewConstMetric(descAppsetLabels, prometheus.GaugeValue, 1, append(commonLabelValues, labelValues...)...) - } - - ch <- prometheus.MustNewConstMetric(descAppsetInfo, prometheus.GaugeValue, 1, appset.Namespace, appset.Name, resourceUpdateStatus) - ch <- prometheus.MustNewConstMetric(descAppsetGeneratedApps, prometheus.GaugeValue, float64(len(appset.Status.Resources)), appset.Namespace, appset.Name) -} diff --git a/applicationset/metrics/metrics_test.go b/applicationset/metrics/metrics_test.go deleted file mode 100644 index b9ed0ae6ec57a..0000000000000 --- a/applicationset/metrics/metrics_test.go +++ /dev/null @@ -1,256 +0,0 @@ -package metrics - -import ( - "net/http" - "net/http/httptest" - "strings" - "testing" - "time" - - "github.com/argoproj/argo-cd/v2/applicationset/utils" - argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/runtime" - ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" - fake "sigs.k8s.io/controller-runtime/pkg/client/fake" - - prometheus "github.com/prometheus/client_golang/prometheus" - - metricsutil "github.com/argoproj/argo-cd/v2/util/metrics" - - "sigs.k8s.io/controller-runtime/pkg/metrics" - - "sigs.k8s.io/yaml" -) - -var ( - applicationsetNamespaces = []string{"argocd", "test-namespace1"} - - filter = func(appset *argoappv1.ApplicationSet) bool { - return utils.IsNamespaceAllowed(applicationsetNamespaces, appset.Namespace) - } - - collectedLabels = []string{"included/test"} -) - -const fakeAppsetList = ` -apiVersion: argoproj.io/v1alpha1 -kind: ApplicationSet -metadata: - name: test1 - namespace: argocd - labels: - included/test: test - not-included.label/test: test -spec: - generators: - - git: - directories: - - path: test/* - repoURL: https://github.com/test/test.git - revision: HEAD - template: - metadata: - name: '{{.path.basename}}' - spec: - destination: - namespace: '{{.path.basename}}' - server: https://kubernetes.default.svc - project: default - source: - path: '{{.path.path}}' - repoURL: https://github.com/test/test.git - targetRevision: HEAD -status: - resources: - - group: argoproj.io - health: - status: Missing - kind: Application - name: test-app1 - namespace: argocd - status: OutOfSync - version: v1alpha1 - - group: argoproj.io - health: - status: Missing - kind: Application - name: test-app2 - namespace: argocd - status: OutOfSync - version: v1alpha1 - conditions: - - lastTransitionTime: "2024-01-01T00:00:00Z" - message: Successfully generated parameters for all Applications - reason: ApplicationSetUpToDate - status: "False" - type: ErrorOccurred - - lastTransitionTime: "2024-01-01T00:00:00Z" - message: Successfully generated parameters for all Applications - reason: ParametersGenerated - status: "True" - type: ParametersGenerated - - lastTransitionTime: "2024-01-01T00:00:00Z" - message: ApplicationSet up to date - reason: ApplicationSetUpToDate - status: "True" - type: ResourcesUpToDate ---- -apiVersion: argoproj.io/v1alpha1 -kind: ApplicationSet -metadata: - name: test2 - namespace: argocd - labels: - not-included.label/test: test -spec: - generators: - - git: - directories: - - path: test/* - repoURL: https://github.com/test/test.git - revision: HEAD - template: - metadata: - name: '{{.path.basename}}' - spec: - destination: - namespace: '{{.path.basename}}' - server: https://kubernetes.default.svc - project: default - source: - path: '{{.path.path}}' - repoURL: https://github.com/test/test.git - targetRevision: HEAD ---- -apiVersion: argoproj.io/v1alpha1 -kind: ApplicationSet -metadata: - name: should-be-filtered-out - namespace: not-allowed -spec: - generators: - - git: - directories: - - path: test/* - repoURL: https://github.com/test/test.git - revision: HEAD - template: - metadata: - name: '{{.path.basename}}' - spec: - destination: - namespace: '{{.path.basename}}' - server: https://kubernetes.default.svc - project: default - source: - path: '{{.path.path}}' - repoURL: https://github.com/test/test.git - targetRevision: HEAD -` - -func newFakeAppsets(fakeAppsetYAML string) []argoappv1.ApplicationSet { - var results []argoappv1.ApplicationSet - - appsetRawYamls := strings.Split(fakeAppsetYAML, "---") - - for _, appsetRawYaml := range appsetRawYamls { - var appset argoappv1.ApplicationSet - err := yaml.Unmarshal([]byte(appsetRawYaml), &appset) - if err != nil { - panic(err) - } - - results = append(results, appset) - } - - return results -} - -func TestApplicationsetCollector(t *testing.T) { - appsetList := newFakeAppsets(fakeAppsetList) - client := initializeClient(appsetList) - metrics.Registry = prometheus.NewRegistry() - - appsetCollector := newAppsetCollector(utils.NewAppsetLister(client), collectedLabels, filter) - - metrics.Registry.MustRegister(appsetCollector) - req, err := http.NewRequest("GET", "/metrics", nil) - require.NoError(t, err) - rr := httptest.NewRecorder() - handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{}) - handler.ServeHTTP(rr, req) - - assert.Equal(t, http.StatusOK, rr.Code) - // Test correct appset_info and owned applications - assert.Contains(t, rr.Body.String(), ` -argocd_appset_info{name="test1",namespace="argocd",resource_update_status="ApplicationSetUpToDate"} 1 -`) - assert.Contains(t, rr.Body.String(), ` -argocd_appset_owned_applications{name="test1",namespace="argocd"} 2 -`) - // Test labels collection - should not include labels not included in the list of collected labels and include the ones that do. - assert.Contains(t, rr.Body.String(), ` -argocd_appset_labels{label_included_test="test",name="test1",namespace="argocd"} 1 -`) - assert.NotContains(t, rr.Body.String(), normalizeLabel("not-included.label/test")) - // If collected label is not present on the applicationset the value should be empty - assert.Contains(t, rr.Body.String(), ` -argocd_appset_labels{label_included_test="",name="test2",namespace="argocd"} 1 -`) - // If ResourcesUpToDate condition is not present on the applicationset the status should be reported as 'Unknown' - assert.Contains(t, rr.Body.String(), ` -argocd_appset_info{name="test2",namespace="argocd",resource_update_status="Unknown"} 1 -`) - // If there are no resources on the applicationset the owned application gague should return 0 - assert.Contains(t, rr.Body.String(), ` -argocd_appset_owned_applications{name="test2",namespace="argocd"} 0 -`) - // Test that filter is working - assert.NotContains(t, rr.Body.String(), `name="should-be-filtered-out"`) -} - -func TestObserveReconcile(t *testing.T) { - appsetList := newFakeAppsets(fakeAppsetList) - client := initializeClient(appsetList) - metrics.Registry = prometheus.NewRegistry() - - appsetMetrics := NewApplicationsetMetrics(utils.NewAppsetLister(client), collectedLabels, filter) - - req, err := http.NewRequest("GET", "/metrics", nil) - require.NoError(t, err) - rr := httptest.NewRecorder() - handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{}) - appsetMetrics.ObserveReconcile(&appsetList[0], 5*time.Second) - handler.ServeHTTP(rr, req) - assert.Contains(t, rr.Body.String(), ` -argocd_appset_reconcile_sum{name="test1",namespace="argocd"} 5 -`) - // If there are no resources on the applicationset the owned application gague should return 0 - assert.Contains(t, rr.Body.String(), ` -argocd_appset_reconcile_count{name="test1",namespace="argocd"} 1 -`) -} - -func initializeClient(appsets []argoappv1.ApplicationSet) ctrlclient.WithWatch { - scheme := runtime.NewScheme() - err := argoappv1.AddToScheme(scheme) - if err != nil { - panic(err) - } - - var clientObjects []ctrlclient.Object - - for _, appset := range appsets { - clientObjects = append(clientObjects, appset.DeepCopy()) - } - - return fake.NewClientBuilder().WithScheme(scheme).WithObjects(clientObjects...).Build() -} - -func normalizeLabel(label string) string { - return metricsutil.NormalizeLabels("label", []string{label})[0] -} diff --git a/applicationset/services/internal/github_app/client.go b/applicationset/services/internal/github_app/client.go index 742b2bc001383..bad6e828aa5c6 100644 --- a/applicationset/services/internal/github_app/client.go +++ b/applicationset/services/internal/github_app/client.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/bradleyfalzon/ghinstallation/v2" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v35/github" "github.com/argoproj/argo-cd/v2/applicationset/services/github_app_auth" ) @@ -26,7 +26,7 @@ func Client(g github_app_auth.Authentication, url string) (*github.Client, error } else { rt.BaseURL = url httpClient := http.Client{Transport: rt} - client, err = github.NewClient(&httpClient).WithEnterpriseURLs(url, url) + client, err = github.NewEnterpriseClient(url, url, &httpClient) if err != nil { return nil, fmt.Errorf("failed to create github enterprise client: %w", err) } diff --git a/applicationset/services/internal/http/client.go b/applicationset/services/internal/http/client.go index df43d89f873bb..00bcf32f3204f 100644 --- a/applicationset/services/internal/http/client.go +++ b/applicationset/services/internal/http/client.go @@ -66,6 +66,7 @@ func newClient(baseURL string, options ...ClientOptionFunc) (*Client, error) { } 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 += "/" @@ -134,13 +135,14 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*htt // 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: %w", resp.StatusCode, err) + return fmt.Errorf("API error with status code %d: %v", resp.StatusCode, err) } var raw map[string]interface{} diff --git a/applicationset/services/internal/http/client_test.go b/applicationset/services/internal/http/client_test.go index 9235ce5ab3e7f..ca2c916177fee 100644 --- a/applicationset/services/internal/http/client_test.go +++ b/applicationset/services/internal/http/client_test.go @@ -17,13 +17,14 @@ func TestClient(t *testing.T) { w.WriteHeader(http.StatusOK) _, err := w.Write([]byte("Hello, World!")) if err != nil { - assert.NoError(t, fmt.Errorf("Error Write %w", err)) + 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) } @@ -61,7 +62,7 @@ func TestClientDo(t *testing.T) { "key3": 123 }]`)) if err != nil { - assert.NoError(t, fmt.Errorf("Error Write %w", err)) + assert.NoError(t, fmt.Errorf("Error Write %v", err)) } })), clientOptionFns: nil, @@ -104,7 +105,7 @@ func TestClientDo(t *testing.T) { "key3": 123 }]`)) if err != nil { - assert.NoError(t, fmt.Errorf("Error Write %w", err)) + assert.NoError(t, fmt.Errorf("Error Write %v", err)) } })), clientOptionFns: nil, @@ -118,11 +119,13 @@ func TestClientDo(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) } @@ -134,8 +137,8 @@ func TestClientDo(t *testing.T) { if cc.expectedError != nil { assert.EqualError(t, err, cc.expectedError.Error()) } else { - assert.Equal(t, cc.expectedCode, resp.StatusCode) - assert.Equal(t, cc.expected, data) + assert.Equal(t, resp.StatusCode, cc.expectedCode) + assert.Equal(t, data, cc.expected) assert.NoError(t, err) } }) diff --git a/applicationset/services/mocks/Repos.go b/applicationset/services/mocks/Repos.go index 2bc9be358c379..b7620b22f08bb 100644 --- a/applicationset/services/mocks/Repos.go +++ b/applicationset/services/mocks/Repos.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.25.1. DO NOT EDIT. package mocks @@ -13,29 +13,25 @@ type Repos struct { mock.Mock } -// GetDirectories provides a mock function with given fields: ctx, repoURL, revision, noRevisionCache, verifyCommit -func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool, verifyCommit bool) ([]string, error) { - ret := _m.Called(ctx, repoURL, revision, noRevisionCache, verifyCommit) - - if len(ret) == 0 { - panic("no return value specified for GetDirectories") - } +// 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, bool) ([]string, error)); ok { - return rf(ctx, repoURL, revision, noRevisionCache, verifyCommit) + 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, bool) []string); ok { - r0 = rf(ctx, repoURL, revision, noRevisionCache, verifyCommit) + 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, bool) error); ok { - r1 = rf(ctx, repoURL, revision, noRevisionCache, verifyCommit) + 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) } @@ -43,29 +39,25 @@ func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision st return r0, r1 } -// GetFiles provides a mock function with given fields: ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit -func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool, verifyCommit bool) (map[string][]byte, error) { - ret := _m.Called(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit) - - if len(ret) == 0 { - panic("no return value specified for GetFiles") - } +// 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, bool) (map[string][]byte, error)); ok { - return rf(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit) + 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, bool) map[string][]byte); ok { - r0 = rf(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit) + 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, bool) error); ok { - r1 = rf(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit) + 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) } @@ -73,12 +65,13 @@ func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, return r0, r1 } -// 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. -// The first argument is typically a *testing.T value. -func NewRepos(t interface { +type mockConstructorTestingTNewRepos interface { mock.TestingT Cleanup(func()) -}) *Repos { +} + +// 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) 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 index 175683c434d72..95573e0942407 100644 --- a/applicationset/services/plugin/plugin_service.go +++ b/applicationset/services/plugin/plugin_service.go @@ -45,7 +45,7 @@ func NewPluginService(ctx context.Context, appSetName string, baseURL string, to client, err := internalhttp.NewClient(baseURL, clientOptionFns...) if err != nil { - return nil, fmt.Errorf("error creating plugin client: %w", err) + return nil, fmt.Errorf("error creating plugin client: %v", err) } return &Service{ @@ -56,15 +56,17 @@ func NewPluginService(ctx context.Context, appSetName string, baseURL string, to 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: %w", err) + 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': %w", p.appSetName, err) + 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 index 75e7f2c4a095f..6dc81d33df71f 100644 --- a/applicationset/services/plugin/plugin_service_test.go +++ b/applicationset/services/plugin/plugin_service_test.go @@ -23,19 +23,22 @@ func TestPlugin(t *testing.T) { return } _, err := w.Write([]byte(expectedJSON)) + if err != nil { - assert.NoError(t, fmt.Errorf("Error Write %w", err)) + 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) } diff --git a/applicationset/services/pull_request/azure_devops.go b/applicationset/services/pull_request/azure_devops.go index 1d263212cdea1..9090b829ca0c2 100644 --- a/applicationset/services/pull_request/azure_devops.go +++ b/applicationset/services/pull_request/azure_devops.go @@ -36,10 +36,8 @@ type AzureDevOpsService struct { labels []string } -var ( - _ PullRequestService = (*AzureDevOpsService)(nil) - _ AzureDevOpsClientFactory = &devopsFactoryImpl{} -) +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) @@ -82,7 +80,6 @@ func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) { pr.Repository.Name == nil || pr.PullRequestId == nil || pr.SourceRefName == nil || - pr.TargetRefName == nil || pr.LastMergeSourceCommit == nil || pr.LastMergeSourceCommit.CommitId == nil { continue @@ -95,13 +92,10 @@ func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) { if *pr.Repository.Name == a.repo { pullRequests = append(pullRequests, &PullRequest{ - Number: *pr.PullRequestId, - Title: *pr.Title, - Branch: strings.Replace(*pr.SourceRefName, "refs/heads/", "", 1), - TargetBranch: strings.Replace(*pr.TargetRefName, "refs/heads/", "", 1), - HeadSHA: *pr.LastMergeSourceCommit.CommitId, - Labels: azureDevOpsLabels, - Author: strings.Split(*pr.CreatedBy.UniqueName, "@")[0], // Get the part before the @ in the email-address + Number: *pr.PullRequestId, + Branch: strings.Replace(*pr.SourceRefName, "refs/heads/", "", 1), + HeadSHA: *pr.LastMergeSourceCommit.CommitId, + Labels: azureDevOpsLabels, }) } } diff --git a/applicationset/services/pull_request/azure_devops_test.go b/applicationset/services/pull_request/azure_devops_test.go index 24453c93a2195..5ed8f4de78b9d 100644 --- a/applicationset/services/pull_request/azure_devops_test.go +++ b/applicationset/services/pull_request/azure_devops_test.go @@ -4,13 +4,10 @@ import ( "context" "testing" - "github.com/microsoft/azure-devops-go-api/azuredevops/webapi" - "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" - "github.com/stretchr/testify/require" azureMock "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/azure_devops/git/mocks" ) @@ -31,10 +28,6 @@ func createLabelsPtr(x []core.WebApiTagDefinition) *[]core.WebApiTagDefinition { return &x } -func createUniqueNamePtr(x string) *string { - return &x -} - type AzureClientFactoryMock struct { mock *mock.Mock } @@ -62,17 +55,13 @@ func TestListPullRequest(t *testing.T) { teamProject := "myorg_project" repoName := "myorg_project_repo" pr_id := 123 - pr_title := "feat(123)" pr_head_sha := "cd4973d9d14a08ffe6b641a89a68891d6aac8056" ctx := context.Background() - uniqueName := "testName" pullRequestMock := []git.GitPullRequest{ { PullRequestId: createIntPtr(pr_id), - Title: createStringPtr(pr_title), SourceRefName: createStringPtr("refs/heads/feature-branch"), - TargetRefName: createStringPtr("refs/heads/main"), LastMergeSourceCommit: &git.GitCommitRef{ CommitId: createStringPtr(pr_head_sha), }, @@ -80,9 +69,6 @@ func TestListPullRequest(t *testing.T) { Repository: &git.GitRepository{ Name: createStringPtr(repoName), }, - CreatedBy: &webapi.IdentityRef{ - UniqueName: createUniqueNamePtr(uniqueName + "@example.com"), - }, }, } @@ -104,14 +90,11 @@ func TestListPullRequest(t *testing.T) { } list, err := provider.List(ctx) - require.NoError(t, err) - assert.Len(t, list, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(list)) assert.Equal(t, "feature-branch", list[0].Branch) - assert.Equal(t, "main", list[0].TargetBranch) assert.Equal(t, pr_head_sha, list[0].HeadSHA) - assert.Equal(t, "feat(123)", list[0].Title) assert.Equal(t, pr_id, list[0].Number) - assert.Equal(t, uniqueName, list[0].Author) } func TestConvertLabes(t *testing.T) { @@ -232,7 +215,7 @@ func TestBuildURL(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := buildURL(tc.url, tc.organization) - assert.Equal(t, tc.expected, result) + assert.Equal(t, result, tc.expected) }) } } diff --git a/applicationset/services/pull_request/bitbucket_cloud.go b/applicationset/services/pull_request/bitbucket_cloud.go index 48083dcb407e3..5d5f8208f9b06 100644 --- a/applicationset/services/pull_request/bitbucket_cloud.go +++ b/applicationset/services/pull_request/bitbucket_cloud.go @@ -17,9 +17,7 @@ type BitbucketCloudService struct { type BitbucketCloudPullRequest struct { ID int `json:"id"` - Title string `json:"title"` Source BitbucketCloudPullRequestSource `json:"source"` - Author string `json:"author"` } type BitbucketCloudPullRequestSource struct { @@ -62,7 +60,7 @@ func parseUrl(uri string) (*url.URL, error) { 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: %w", baseUrl, owner, repositorySlug, err) + return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %v", baseUrl, owner, repositorySlug, err) } bitbucketClient := bitbucket.NewBasicAuth(username, password) @@ -78,7 +76,7 @@ func NewBitbucketCloudServiceBasicAuth(baseUrl, username, password, owner, repos 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: %w", baseUrl, owner, repositorySlug, err) + return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %v", baseUrl, owner, repositorySlug, err) } bitbucketClient := bitbucket.NewOAuthbearerToken(bearerToken) @@ -104,7 +102,7 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error) response, err := b.client.Repositories.PullRequests.Gets(opts) if err != nil { - return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.owner, b.repositorySlug, err) + return nil, fmt.Errorf("error listing pull requests for %s/%s: %v", b.owner, b.repositorySlug, err) } resp, ok := response.(map[string]interface{}) @@ -119,22 +117,20 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error) jsonStr, err := json.Marshal(repoArray) if err != nil { - return nil, fmt.Errorf("error marshalling response body to json: %w", err) + 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': %w", err) + 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, - Title: pull.Title, Branch: pull.Source.Branch.Name, HeadSHA: pull.Source.Commit.Hash, - Author: pull.Author, }) } diff --git a/applicationset/services/pull_request/bitbucket_cloud_test.go b/applicationset/services/pull_request/bitbucket_cloud_test.go index 8d5f7d80ca144..2f604c1fa9ccf 100644 --- a/applicationset/services/pull_request/bitbucket_cloud_test.go +++ b/applicationset/services/pull_request/bitbucket_cloud_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -27,7 +26,6 @@ func defaultHandlerCloud(t *testing.T) func(http.ResponseWriter, *http.Request) "values": [ { "id": 101, - "title": "feat(foo-bar)", "source": { "branch": { "name": "feature/foo-bar" @@ -36,8 +34,7 @@ func defaultHandlerCloud(t *testing.T) func(http.ResponseWriter, *http.Request) "type": "commit", "hash": "1a8dd249c04a" } - }, - "author": "testName" + } } ] }`) @@ -54,26 +51,26 @@ func TestParseUrlEmptyUrl(t *testing.T) { url, err := parseUrl("") bitbucketUrl, _ := url.Parse("https://api.bitbucket.org/2.0") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, bitbucketUrl, url) } func TestInvalidBaseUrlBasicAuthCloud(t *testing.T) { _, err := NewBitbucketCloudServiceBasicAuth("http:// example.org", "user", "password", "OWNER", "REPO") - require.Error(t, err) + assert.Error(t, err) } func TestInvalidBaseUrlBearerTokenCloud(t *testing.T) { _, err := NewBitbucketCloudServiceBearerToken("http:// example.org", "TOKEN", "OWNER", "REPO") - require.Error(t, err) + assert.Error(t, err) } func TestInvalidBaseUrlNoAuthCloud(t *testing.T) { _, err := NewBitbucketCloudServiceNoAuth("http:// example.org", "OWNER", "REPO") - require.Error(t, err) + assert.Error(t, err) } func TestListPullRequestBearerTokenCloud(t *testing.T) { @@ -83,15 +80,13 @@ func TestListPullRequestBearerTokenCloud(t *testing.T) { })) defer ts.Close() svc, err := NewBitbucketCloudServiceBearerToken(ts.URL, "TOKEN", "OWNER", "REPO") - require.NoError(t, err) + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.NoError(t, err) - assert.Len(t, pullRequests, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(pullRequests)) assert.Equal(t, 101, pullRequests[0].Number) - assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title) assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch) assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA) - assert.Equal(t, "testName", pullRequests[0].Author) } func TestListPullRequestNoAuthCloud(t *testing.T) { @@ -101,15 +96,13 @@ func TestListPullRequestNoAuthCloud(t *testing.T) { })) defer ts.Close() svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") - require.NoError(t, err) + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.NoError(t, err) - assert.Len(t, pullRequests, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(pullRequests)) assert.Equal(t, 101, pullRequests[0].Number) - assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title) assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch) assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA) - assert.Equal(t, "testName", pullRequests[0].Author) } func TestListPullRequestBasicAuthCloud(t *testing.T) { @@ -119,15 +112,13 @@ func TestListPullRequestBasicAuthCloud(t *testing.T) { })) defer ts.Close() svc, err := NewBitbucketCloudServiceBasicAuth(ts.URL, "user", "password", "OWNER", "REPO") - require.NoError(t, err) + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.NoError(t, err) - assert.Len(t, pullRequests, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(pullRequests)) assert.Equal(t, 101, pullRequests[0].Number) - assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title) assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch) assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA) - assert.Equal(t, "testName", pullRequests[0].Author) } func TestListPullRequestPaginationCloud(t *testing.T) { @@ -144,7 +135,6 @@ func TestListPullRequestPaginationCloud(t *testing.T) { "values": [ { "id": 101, - "title": "feat(101)", "source": { "branch": { "name": "feature-101" @@ -153,12 +143,10 @@ func TestListPullRequestPaginationCloud(t *testing.T) { "type": "commit", "hash": "1a8dd249c04a" } - }, - "author": "testName" + } }, { "id": 102, - "title": "feat(102)", "source": { "branch": { "name": "feature-102" @@ -167,8 +155,7 @@ func TestListPullRequestPaginationCloud(t *testing.T) { "type": "commit", "hash": "4cf807e67a6d" } - }, - "author": "testName" + } } ] }`, r.Host)) @@ -181,7 +168,6 @@ func TestListPullRequestPaginationCloud(t *testing.T) { "values": [ { "id": 103, - "title": "feat(103)", "source": { "branch": { "name": "feature-103" @@ -190,8 +176,7 @@ func TestListPullRequestPaginationCloud(t *testing.T) { "type": "commit", "hash": "6344d9623e3b" } - }, - "author": "testName" + } } ] }`, r.Host)) @@ -204,30 +189,24 @@ func TestListPullRequestPaginationCloud(t *testing.T) { })) defer ts.Close() svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") - require.NoError(t, err) + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.NoError(t, err) - assert.Len(t, pullRequests, 3) + assert.NoError(t, err) + assert.Equal(t, 3, len(pullRequests)) assert.Equal(t, PullRequest{ Number: 101, - Title: "feat(101)", Branch: "feature-101", HeadSHA: "1a8dd249c04a", - Author: "testName", }, *pullRequests[0]) assert.Equal(t, PullRequest{ Number: 102, - Title: "feat(102)", Branch: "feature-102", HeadSHA: "4cf807e67a6d", - Author: "testName", }, *pullRequests[1]) assert.Equal(t, PullRequest{ Number: 103, - Title: "feat(103)", Branch: "feature-103", HeadSHA: "6344d9623e3b", - Author: "testName", }, *pullRequests[2]) } @@ -238,7 +217,7 @@ func TestListResponseErrorCloud(t *testing.T) { defer ts.Close() svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") _, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.Error(t, err) + assert.Error(t, err) } func TestListResponseMalformedCloud(t *testing.T) { @@ -262,7 +241,7 @@ func TestListResponseMalformedCloud(t *testing.T) { defer ts.Close() svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") _, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.Error(t, err) + assert.Error(t, err) } func TestListResponseMalformedValuesCloud(t *testing.T) { @@ -286,7 +265,7 @@ func TestListResponseMalformedValuesCloud(t *testing.T) { defer ts.Close() svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") _, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.Error(t, err) + assert.Error(t, err) } func TestListResponseEmptyCloud(t *testing.T) { @@ -309,9 +288,9 @@ func TestListResponseEmptyCloud(t *testing.T) { })) defer ts.Close() svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") - require.NoError(t, err) + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, pullRequests) } @@ -329,7 +308,6 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) { "values": [ { "id": 101, - "title": "feat(101)", "source": { "branch": { "name": "feature-101" @@ -338,12 +316,10 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) { "type": "commit", "hash": "1a8dd249c04a" } - }, - "author": "testName" + } }, { "id": 200, - "title": "feat(200)", "source": { "branch": { "name": "feature-200" @@ -352,8 +328,7 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) { "type": "commit", "hash": "4cf807e67a6d" } - }, - "author": "testName" + } } ] }`, r.Host)) @@ -366,7 +341,6 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) { "values": [ { "id": 102, - "title": "feat(102)", "source": { "branch": { "name": "feature-102" @@ -375,8 +349,7 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) { "type": "commit", "hash": "6344d9623e3b" } - }, - "author": "testName" + } } ] }`, r.Host)) @@ -390,54 +363,48 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) { defer ts.Close() regexp := `feature-1[\d]{2}` svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") - require.NoError(t, err) + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{ { BranchMatch: ®exp, }, }) - require.NoError(t, err) - assert.Len(t, pullRequests, 2) + assert.NoError(t, err) + assert.Equal(t, 2, len(pullRequests)) assert.Equal(t, PullRequest{ Number: 101, - Title: "feat(101)", Branch: "feature-101", HeadSHA: "1a8dd249c04a", - Author: "testName", }, *pullRequests[0]) assert.Equal(t, PullRequest{ Number: 102, - Title: "feat(102)", Branch: "feature-102", HeadSHA: "6344d9623e3b", - Author: "testName", }, *pullRequests[1]) regexp = `.*2$` svc, err = NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") - require.NoError(t, err) + assert.NoError(t, err) pullRequests, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{ { BranchMatch: ®exp, }, }) - require.NoError(t, err) - assert.Len(t, pullRequests, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(pullRequests)) assert.Equal(t, PullRequest{ Number: 102, - Title: "feat(102)", Branch: "feature-102", HeadSHA: "6344d9623e3b", - Author: "testName", }, *pullRequests[0]) regexp = `[\d{2}` svc, err = NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") - require.NoError(t, err) + assert.NoError(t, err) _, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{ { BranchMatch: ®exp, }, }) - require.Error(t, err) + assert.Error(t, err) } diff --git a/applicationset/services/pull_request/bitbucket_server.go b/applicationset/services/pull_request/bitbucket_server.go index 1f2be70edb428..99665d163e1bc 100644 --- a/applicationset/services/pull_request/bitbucket_server.go +++ b/applicationset/services/pull_request/bitbucket_server.go @@ -3,12 +3,10 @@ package pull_request import ( "context" "fmt" - "net/http" + "github.com/argoproj/argo-cd/v2/applicationset/utils" bitbucketv1 "github.com/gfleury/go-bitbucket-v1" log "github.com/sirupsen/logrus" - - "github.com/argoproj/argo-cd/v2/applicationset/utils" ) type BitbucketService struct { @@ -21,7 +19,7 @@ type BitbucketService struct { var _ PullRequestService = (*BitbucketService)(nil) -func NewBitbucketServiceBasicAuth(ctx context.Context, username, password, url, projectKey, repositorySlug string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) { +func NewBitbucketServiceBasicAuth(ctx context.Context, username, password, url, projectKey, repositorySlug string) (PullRequestService, error) { bitbucketConfig := bitbucketv1.NewConfiguration(url) // Avoid the XSRF check bitbucketConfig.AddDefaultHeader("x-atlassian-token", "no-check") @@ -31,29 +29,15 @@ func NewBitbucketServiceBasicAuth(ctx context.Context, username, password, url, UserName: username, Password: password, }) - return newBitbucketService(ctx, bitbucketConfig, projectKey, repositorySlug, scmRootCAPath, insecure, caCerts) -} - -func NewBitbucketServiceBearerToken(ctx context.Context, bearerToken, url, projectKey, repositorySlug string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) { - bitbucketConfig := bitbucketv1.NewConfiguration(url) - // Avoid the XSRF check - bitbucketConfig.AddDefaultHeader("x-atlassian-token", "no-check") - bitbucketConfig.AddDefaultHeader("x-requested-with", "XMLHttpRequest") - - ctx = context.WithValue(ctx, bitbucketv1.ContextAccessToken, bearerToken) - return newBitbucketService(ctx, bitbucketConfig, projectKey, repositorySlug, scmRootCAPath, insecure, caCerts) + return newBitbucketService(ctx, bitbucketConfig, projectKey, repositorySlug) } -func NewBitbucketServiceNoAuth(ctx context.Context, url, projectKey, repositorySlug string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) { - return newBitbucketService(ctx, bitbucketv1.NewConfiguration(url), projectKey, repositorySlug, scmRootCAPath, insecure, caCerts) +func NewBitbucketServiceNoAuth(ctx context.Context, url, projectKey, repositorySlug string) (PullRequestService, error) { + return newBitbucketService(ctx, bitbucketv1.NewConfiguration(url), projectKey, repositorySlug) } -func newBitbucketService(ctx context.Context, bitbucketConfig *bitbucketv1.Configuration, projectKey, repositorySlug string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) { +func newBitbucketService(ctx context.Context, bitbucketConfig *bitbucketv1.Configuration, projectKey, repositorySlug string) (PullRequestService, error) { bitbucketConfig.BasePath = utils.NormalizeBitbucketBasePath(bitbucketConfig.BasePath) - tlsConfig := utils.GetTlsConfig(scmRootCAPath, insecure, caCerts) - bitbucketConfig.HTTPClient = &http.Client{Transport: &http.Transport{ - TLSClientConfig: tlsConfig, - }} bitbucketClient := bitbucketv1.NewAPIClient(ctx, bitbucketConfig) return &BitbucketService{ @@ -72,23 +56,21 @@ func (b *BitbucketService) List(_ context.Context) ([]*PullRequest, error) { for { response, err := b.client.DefaultApi.GetPullRequestsPage(b.projectKey, b.repositorySlug, paged) if err != nil { - return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.projectKey, b.repositorySlug, err) + return nil, fmt.Errorf("error listing pull requests for %s/%s: %v", b.projectKey, b.repositorySlug, err) } pulls, err := bitbucketv1.GetPullRequestsResponse(response) if err != nil { log.Errorf("error parsing pull request response '%v'", response.Values) - return nil, fmt.Errorf("error parsing pull request response for %s/%s: %w", b.projectKey, b.repositorySlug, err) + return nil, fmt.Errorf("error parsing pull request response for %s/%s: %v", b.projectKey, b.repositorySlug, err) } for _, pull := range pulls { pullRequests = append(pullRequests, &PullRequest{ Number: pull.ID, - Title: pull.Title, 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 - Author: pull.Author.User.Name, }) } diff --git a/applicationset/services/pull_request/bitbucket_server_test.go b/applicationset/services/pull_request/bitbucket_server_test.go index 3c9fe1ddd504e..911e3e7e0ccd0 100644 --- a/applicationset/services/pull_request/bitbucket_server_test.go +++ b/applicationset/services/pull_request/bitbucket_server_test.go @@ -2,17 +2,13 @@ package pull_request import ( "context" - "crypto/x509" - "encoding/pem" "io" "net/http" "net/http/httptest" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/stretchr/testify/assert" ) func defaultHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { @@ -28,7 +24,6 @@ func defaultHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { "values": [ { "id": 101, - "title": "feat(ABC) : 123", "toRef": { "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", "displayId": "master", @@ -38,11 +33,6 @@ func defaultHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { "id": "refs/heads/feature-ABC-123", "displayId": "feature-ABC-123", "latestCommit": "cb3cf2e4d1517c83e720d2585b9402dbef71f992" - }, - "author": { - "user": { - "name": "testName" - } } } ], @@ -63,17 +53,15 @@ func TestListPullRequestNoAuth(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil) - require.NoError(t, err) + svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO") + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.NoError(t, err) - assert.Len(t, pullRequests, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(pullRequests)) assert.Equal(t, 101, pullRequests[0].Number) - assert.Equal(t, "feat(ABC) : 123", pullRequests[0].Title) assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch) assert.Equal(t, "master", pullRequests[0].TargetBranch) assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA) - assert.Equal(t, "testName", pullRequests[0].Author) } func TestListPullRequestPagination(t *testing.T) { @@ -89,7 +77,6 @@ func TestListPullRequestPagination(t *testing.T) { "values": [ { "id": 101, - "title": "feat(101)", "toRef": { "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", "displayId": "master", @@ -99,16 +86,10 @@ func TestListPullRequestPagination(t *testing.T) { "id": "refs/heads/feature-101", "displayId": "feature-101", "latestCommit": "ab3cf2e4d1517c83e720d2585b9402dbef71f992" - }, - "author": { - "user": { - "name": "testName" - } } }, { "id": 102, - "title": "feat(102)", "toRef": { "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", "displayId": "branch", @@ -118,11 +99,6 @@ func TestListPullRequestPagination(t *testing.T) { "id": "refs/heads/feature-102", "displayId": "feature-102", "latestCommit": "bb3cf2e4d1517c83e720d2585b9402dbef71f992" - }, - "author": { - "user": { - "name": "testName" - } } } ], @@ -136,7 +112,6 @@ func TestListPullRequestPagination(t *testing.T) { "values": [ { "id": 200, - "title": "feat(200)", "toRef": { "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", "displayId": "master", @@ -146,11 +121,6 @@ func TestListPullRequestPagination(t *testing.T) { "id": "refs/heads/feature-200", "displayId": "feature-200", "latestCommit": "cb3cf2e4d1517c83e720d2585b9402dbef71f992" - }, - "author": { - "user": { - "name": "testName" - } } } ], @@ -164,37 +134,31 @@ func TestListPullRequestPagination(t *testing.T) { } })) defer ts.Close() - svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil) - require.NoError(t, err) + svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO") + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.NoError(t, err) - assert.Len(t, pullRequests, 3) + assert.NoError(t, err) + assert.Equal(t, 3, len(pullRequests)) assert.Equal(t, PullRequest{ Number: 101, - Title: "feat(101)", Branch: "feature-101", TargetBranch: "master", HeadSHA: "ab3cf2e4d1517c83e720d2585b9402dbef71f992", Labels: []string{}, - Author: "testName", }, *pullRequests[0]) assert.Equal(t, PullRequest{ Number: 102, - Title: "feat(102)", Branch: "feature-102", TargetBranch: "branch", HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992", Labels: []string{}, - Author: "testName", }, *pullRequests[1]) assert.Equal(t, PullRequest{ Number: 200, - Title: "feat(200)", Branch: "feature-200", TargetBranch: "master", HeadSHA: "cb3cf2e4d1517c83e720d2585b9402dbef71f992", Labels: []string{}, - Author: "testName", }, *pullRequests[2]) } @@ -206,109 +170,24 @@ func TestListPullRequestBasicAuth(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - svc, err := NewBitbucketServiceBasicAuth(context.Background(), "user", "password", ts.URL, "PROJECT", "REPO", "", false, nil) - require.NoError(t, err) + svc, err := NewBitbucketServiceBasicAuth(context.Background(), "user", "password", ts.URL, "PROJECT", "REPO") + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.NoError(t, err) - assert.Len(t, pullRequests, 1) + assert.NoError(t, err) + 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, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA) } -func TestListPullRequestBearerAuth(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "Bearer tolkien", r.Header.Get("Authorization")) - assert.Equal(t, "no-check", r.Header.Get("X-Atlassian-Token")) - defaultHandler(t)(w, r) - })) - defer ts.Close() - svc, err := NewBitbucketServiceBearerToken(context.Background(), "tolkien", ts.URL, "PROJECT", "REPO", "", false, nil) - require.NoError(t, err) - pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.NoError(t, err) - assert.Len(t, pullRequests, 1) - assert.Equal(t, 101, pullRequests[0].Number) - assert.Equal(t, "feat(ABC) : 123", pullRequests[0].Title) - assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch) - assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA) -} - -func TestListPullRequestTLS(t *testing.T) { - tests := []struct { - name string - tlsInsecure bool - passCerts bool - requireErr bool - }{ - { - name: "TLS Insecure: true, No Certs", - tlsInsecure: true, - passCerts: false, - requireErr: false, - }, - { - name: "TLS Insecure: true, With Certs", - tlsInsecure: true, - passCerts: true, - requireErr: false, - }, - { - name: "TLS Insecure: false, With Certs", - tlsInsecure: false, - passCerts: true, - requireErr: false, - }, - { - name: "TLS Insecure: false, No Certs", - tlsInsecure: false, - passCerts: false, - requireErr: true, - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defaultHandler(t)(w, r) - })) - defer ts.Close() - - var certs []byte - if test.passCerts == true { - for _, cert := range ts.TLS.Certificates { - for _, c := range cert.Certificate { - parsedCert, err := x509.ParseCertificate(c) - require.NoError(t, err, "Failed to parse certificate") - certs = append(certs, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: parsedCert.Raw, - })...) - } - } - } - - svc, err := NewBitbucketServiceBasicAuth(context.Background(), "user", "password", ts.URL, "PROJECT", "REPO", "", test.tlsInsecure, certs) - require.NoError(t, err) - _, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - if test.requireErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} - func TestListResponseError(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) defer ts.Close() - svc, _ := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil) + svc, _ := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO") _, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.Error(t, err) + assert.Error(t, err) } func TestListResponseMalformed(t *testing.T) { @@ -331,9 +210,9 @@ func TestListResponseMalformed(t *testing.T) { } })) defer ts.Close() - svc, _ := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil) + svc, _ := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO") _, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.Error(t, err) + assert.Error(t, err) } func TestListResponseEmpty(t *testing.T) { @@ -356,10 +235,10 @@ func TestListResponseEmpty(t *testing.T) { } })) defer ts.Close() - svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil) - require.NoError(t, err) + svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO") + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, pullRequests) } @@ -376,7 +255,6 @@ func TestListPullRequestBranchMatch(t *testing.T) { "values": [ { "id": 101, - "title": "feat(101)", "toRef": { "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", "displayId": "master", @@ -386,16 +264,10 @@ func TestListPullRequestBranchMatch(t *testing.T) { "id": "refs/heads/feature-101", "displayId": "feature-101", "latestCommit": "ab3cf2e4d1517c83e720d2585b9402dbef71f992" - }, - "author": { - "user": { - "name": "testName" - } } }, { "id": 102, - "title": "feat(102)", "toRef": { "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", "displayId": "branch", @@ -405,11 +277,6 @@ func TestListPullRequestBranchMatch(t *testing.T) { "id": "refs/heads/feature-102", "displayId": "feature-102", "latestCommit": "bb3cf2e4d1517c83e720d2585b9402dbef71f992" - }, - "author": { - "user": { - "name": "testName" - } } } ], @@ -423,7 +290,6 @@ func TestListPullRequestBranchMatch(t *testing.T) { "values": [ { "id": 200, - "title": "feat(200)", "toRef": { "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", "displayId": "master", @@ -433,11 +299,6 @@ func TestListPullRequestBranchMatch(t *testing.T) { "id": "refs/heads/feature-200", "displayId": "feature-200", "latestCommit": "cb3cf2e4d1517c83e720d2585b9402dbef71f992" - }, - "author": { - "user": { - "name": "testName" - } } } ], @@ -452,61 +313,55 @@ func TestListPullRequestBranchMatch(t *testing.T) { })) defer ts.Close() regexp := `feature-1[\d]{2}` - svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil) - require.NoError(t, err) + svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO") + assert.NoError(t, err) pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{ { BranchMatch: ®exp, }, }) - require.NoError(t, err) - assert.Len(t, pullRequests, 2) + assert.NoError(t, err) + assert.Equal(t, 2, len(pullRequests)) assert.Equal(t, PullRequest{ Number: 101, - Title: "feat(101)", Branch: "feature-101", TargetBranch: "master", HeadSHA: "ab3cf2e4d1517c83e720d2585b9402dbef71f992", Labels: []string{}, - Author: "testName", }, *pullRequests[0]) assert.Equal(t, PullRequest{ Number: 102, - Title: "feat(102)", Branch: "feature-102", TargetBranch: "branch", HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992", Labels: []string{}, - Author: "testName", }, *pullRequests[1]) regexp = `.*2$` - svc, err = NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil) - require.NoError(t, err) + svc, err = NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO") + assert.NoError(t, err) pullRequests, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{ { BranchMatch: ®exp, }, }) - require.NoError(t, err) - assert.Len(t, pullRequests, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(pullRequests)) assert.Equal(t, PullRequest{ Number: 102, - Title: "feat(102)", Branch: "feature-102", TargetBranch: "branch", HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992", Labels: []string{}, - Author: "testName", }, *pullRequests[0]) regexp = `[\d{2}` - svc, err = NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil) - require.NoError(t, err) + svc, err = NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO") + assert.NoError(t, err) _, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{ { BranchMatch: ®exp, }, }) - require.Error(t, err) + assert.Error(t, err) } diff --git a/applicationset/services/pull_request/gitea.go b/applicationset/services/pull_request/gitea.go index 5f32e4dc307cf..ff385ff281c6d 100644 --- a/applicationset/services/pull_request/gitea.go +++ b/applicationset/services/pull_request/gitea.go @@ -57,12 +57,10 @@ func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) { for _, pr := range prs { list = append(list, &PullRequest{ Number: int(pr.Index), - Title: pr.Title, Branch: pr.Head.Ref, TargetBranch: pr.Base.Ref, HeadSHA: pr.Head.Sha, Labels: getGiteaPRLabelNames(pr.Labels), - Author: pr.Poster.UserName, }) } return list, nil diff --git a/applicationset/services/pull_request/gitea_test.go b/applicationset/services/pull_request/gitea_test.go index fbb0fb15aa4ce..125c8ee481b3a 100644 --- a/applicationset/services/pull_request/gitea_test.go +++ b/applicationset/services/pull_request/gitea_test.go @@ -10,7 +10,6 @@ import ( "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func giteaMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { @@ -251,16 +250,14 @@ func TestGiteaList(t *testing.T) { giteaMockHandler(t)(w, r) })) host, err := NewGiteaService(context.Background(), "", ts.URL, "test-argocd", "pr-test", false) - require.NoError(t, err) + assert.Nil(t, err) prs, err := host.List(context.Background()) - require.NoError(t, err) - assert.Len(t, prs, 1) - assert.Equal(t, 1, prs[0].Number) - assert.Equal(t, "add an empty file", prs[0].Title) - assert.Equal(t, "test", prs[0].Branch) - assert.Equal(t, "main", prs[0].TargetBranch) - assert.Equal(t, "7bbaf62d92ddfafd9cc8b340c619abaec32bc09f", prs[0].HeadSHA) - assert.Equal(t, "graytshirt", prs[0].Author) + assert.Nil(t, err) + 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") } func TestGetGiteaPRLabelNames(t *testing.T) { diff --git a/applicationset/services/pull_request/github.go b/applicationset/services/pull_request/github.go index b63f2a9de6a8e..7c801e7370f53 100644 --- a/applicationset/services/pull_request/github.go +++ b/applicationset/services/pull_request/github.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v35/github" "golang.org/x/oauth2" ) @@ -35,7 +35,7 @@ func NewGithubService(ctx context.Context, token, url, owner, repo string, label client = github.NewClient(httpClient) } else { var err error - client, err = github.NewClient(httpClient).WithEnterpriseURLs(url, url) + client, err = github.NewEnterpriseClient(url, url, httpClient) if err != nil { return nil, err } @@ -66,12 +66,10 @@ func (g *GithubService) List(ctx context.Context) ([]*PullRequest, error) { } pullRequests = append(pullRequests, &PullRequest{ Number: *pull.Number, - Title: *pull.Title, Branch: *pull.Head.Ref, TargetBranch: *pull.Base.Ref, HeadSHA: *pull.Head.SHA, Labels: getGithubPRLabelNames(pull.Labels), - Author: *pull.User.Login, }) } if resp.NextPage == 0 { diff --git a/applicationset/services/pull_request/github_test.go b/applicationset/services/pull_request/github_test.go index 30b908f9fb1b6..c47031acb7e31 100644 --- a/applicationset/services/pull_request/github_test.go +++ b/applicationset/services/pull_request/github_test.go @@ -3,7 +3,7 @@ package pull_request import ( "testing" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v35/github" "github.com/stretchr/testify/assert" ) diff --git a/applicationset/services/pull_request/gitlab.go b/applicationset/services/pull_request/gitlab.go index c4e49881a4393..04a4f3464f6f0 100644 --- a/applicationset/services/pull_request/gitlab.go +++ b/applicationset/services/pull_request/gitlab.go @@ -6,10 +6,9 @@ import ( "net/http" "os" + "github.com/argoproj/argo-cd/v2/applicationset/utils" "github.com/hashicorp/go-retryablehttp" gitlab "github.com/xanzy/go-gitlab" - - "github.com/argoproj/argo-cd/v2/applicationset/utils" ) type GitLabService struct { @@ -21,7 +20,7 @@ type GitLabService struct { var _ PullRequestService = (*GitLabService)(nil) -func NewGitLabService(ctx context.Context, token, url, project string, labels []string, pullRequestState string, scmRootCAPath string, insecure bool, caCerts []byte) (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 @@ -34,7 +33,7 @@ func NewGitLabService(ctx context.Context, token, url, project string, labels [] } tr := http.DefaultTransport.(*http.Transport).Clone() - tr.TLSClientConfig = utils.GetTlsConfig(scmRootCAPath, insecure, caCerts) + tr.TLSClientConfig = utils.GetTlsConfig(scmRootCAPath, insecure) retryClient := retryablehttp.NewClient() retryClient.HTTPClient.Transport = tr @@ -43,7 +42,7 @@ func NewGitLabService(ctx context.Context, token, url, project string, labels [] client, err := gitlab.NewClient(token, clientOptionFns...) if err != nil { - return nil, fmt.Errorf("error creating Gitlab client: %w", err) + return nil, fmt.Errorf("error creating Gitlab client: %v", err) } return &GitLabService{ @@ -55,12 +54,13 @@ func NewGitLabService(ctx context.Context, token, url, project string, labels [] } func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) { + // Filter the merge requests on labels, if they are specified. - var labels *gitlab.LabelOptions + var labels *gitlab.Labels if len(g.labels) > 0 { - var labelsList gitlab.LabelOptions = g.labels - labels = &labelsList + labels = (*gitlab.Labels)(&g.labels) } + opts := &gitlab.ListProjectMergeRequestsOptions{ ListOptions: gitlab.ListOptions{ PerPage: 100, @@ -76,17 +76,15 @@ func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) { for { mrs, resp, err := g.client.MergeRequests.ListProjectMergeRequests(g.project, opts) if err != nil { - return nil, fmt.Errorf("error listing merge requests for project '%s': %w", g.project, err) + return nil, fmt.Errorf("error listing merge requests for project '%s': %v", g.project, err) } for _, mr := range mrs { pullRequests = append(pullRequests, &PullRequest{ Number: mr.IID, - Title: mr.Title, Branch: mr.SourceBranch, TargetBranch: mr.TargetBranch, HeadSHA: mr.SHA, Labels: mr.Labels, - Author: mr.Author.Username, }) } if resp.NextPage == 0 { diff --git a/applicationset/services/pull_request/gitlab_test.go b/applicationset/services/pull_request/gitlab_test.go index f9e845595e224..59c476fcd713a 100644 --- a/applicationset/services/pull_request/gitlab_test.go +++ b/applicationset/services/pull_request/gitlab_test.go @@ -2,8 +2,6 @@ package pull_request import ( "context" - "crypto/x509" - "encoding/pem" "io" "net/http" "net/http/httptest" @@ -11,7 +9,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func writeMRListResponse(t *testing.T, w io.Writer) { @@ -37,11 +34,11 @@ func TestGitLabServiceCustomBaseURL(t *testing.T) { writeMRListResponse(t, w) }) - svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", nil, "", "", false, nil) - require.NoError(t, err) + svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", nil, "", "", false) + assert.NoError(t, err) _, err = svc.List(context.Background()) - require.NoError(t, err) + assert.NoError(t, err) } func TestGitLabServiceToken(t *testing.T) { @@ -56,11 +53,11 @@ func TestGitLabServiceToken(t *testing.T) { writeMRListResponse(t, w) }) - svc, err := NewGitLabService(context.Background(), "token-123", server.URL, "278964", nil, "", "", false, nil) - require.NoError(t, err) + svc, err := NewGitLabService(context.Background(), "token-123", server.URL, "278964", nil, "", "", false) + assert.NoError(t, err) _, err = svc.List(context.Background()) - require.NoError(t, err) + assert.NoError(t, err) } func TestList(t *testing.T) { @@ -75,18 +72,16 @@ func TestList(t *testing.T) { writeMRListResponse(t, w) }) - svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "", "", false, nil) - require.NoError(t, err) + svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "", "", false) + assert.NoError(t, err) prs, err := svc.List(context.Background()) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, prs, 1) - assert.Equal(t, 15442, prs[0].Number) - assert.Equal(t, "Draft: Use structured logging for DB load balancer", prs[0].Title) - assert.Equal(t, "use-structured-logging-for-db-load-balancer", prs[0].Branch) - assert.Equal(t, "master", prs[0].TargetBranch) - assert.Equal(t, "2fc4e8b972ff3208ec63b6143e34ad67ff343ad7", prs[0].HeadSHA) - assert.Equal(t, "hfyngvason", prs[0].Author) + 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") } func TestListWithLabels(t *testing.T) { @@ -101,11 +96,11 @@ func TestListWithLabels(t *testing.T) { writeMRListResponse(t, w) }) - svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{"feature", "ready"}, "", "", false, nil) - require.NoError(t, err) + svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{"feature", "ready"}, "", "", false) + assert.NoError(t, err) _, err = svc.List(context.Background()) - require.NoError(t, err) + assert.NoError(t, err) } func TestListWithState(t *testing.T) { @@ -120,77 +115,9 @@ func TestListWithState(t *testing.T) { writeMRListResponse(t, w) }) - svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "opened", "", false, nil) - require.NoError(t, err) + svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "opened", "", false) + assert.NoError(t, err) _, err = svc.List(context.Background()) - require.NoError(t, err) -} - -func TestListWithStateTLS(t *testing.T) { - tests := []struct { - name string - tlsInsecure bool - passCerts bool - requireErr bool - }{ - { - name: "TLS Insecure: true, No Certs", - tlsInsecure: true, - passCerts: false, - requireErr: false, - }, - { - name: "TLS Insecure: true, With Certs", - tlsInsecure: true, - passCerts: true, - requireErr: false, - }, - { - name: "TLS Insecure: false, With Certs", - tlsInsecure: false, - passCerts: true, - requireErr: false, - }, - { - name: "TLS Insecure: false, No Certs", - tlsInsecure: false, - passCerts: false, - requireErr: true, - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - writeMRListResponse(t, w) - })) - defer ts.Close() - - var certs []byte - if test.passCerts == true { - for _, cert := range ts.TLS.Certificates { - for _, c := range cert.Certificate { - parsedCert, err := x509.ParseCertificate(c) - require.NoError(t, err, "Failed to parse certificate") - certs = append(certs, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: parsedCert.Raw, - })...) - } - } - } - - svc, err := NewGitLabService(context.Background(), "", ts.URL, "278964", []string{}, "opened", "", test.tlsInsecure, certs) - require.NoError(t, err) - - _, err = svc.List(context.Background()) - if test.requireErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } + assert.NoError(t, err) } diff --git a/applicationset/services/pull_request/interface.go b/applicationset/services/pull_request/interface.go index 07637b3f95973..0015cfe5eafa6 100644 --- a/applicationset/services/pull_request/interface.go +++ b/applicationset/services/pull_request/interface.go @@ -8,8 +8,6 @@ import ( type PullRequest struct { // Number is a number that will be the ID of the pull request. Number int - // Title of the pull request. - Title string // 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. @@ -18,8 +16,6 @@ type PullRequest struct { HeadSHA string // Labels of the pull request. Labels []string - // Author is the author of the pull request. - Author string } type PullRequestService interface { diff --git a/applicationset/services/pull_request/utils.go b/applicationset/services/pull_request/utils.go index 09b5b6ca10eb1..50d4e5a3c0098 100644 --- a/applicationset/services/pull_request/utils.go +++ b/applicationset/services/pull_request/utils.go @@ -16,13 +16,13 @@ func compileFilters(filters []argoprojiov1alpha1.PullRequestGeneratorFilter) ([] if filter.BranchMatch != nil { outFilter.BranchMatch, err = regexp.Compile(*filter.BranchMatch) if err != nil { - return nil, fmt.Errorf("error compiling BranchMatch regexp %q: %w", *filter.BranchMatch, err) + 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: %w", *filter.TargetBranchMatch, err) + return nil, fmt.Errorf("error compiling TargetBranchMatch regexp %q: %v", *filter.TargetBranchMatch, err) } } outFilters = append(outFilters, outFilter) diff --git a/applicationset/services/pull_request/utils_test.go b/applicationset/services/pull_request/utils_test.go index 1c74ae4b66b01..3f813127edab7 100644 --- a/applicationset/services/pull_request/utils_test.go +++ b/applicationset/services/pull_request/utils_test.go @@ -4,27 +4,22 @@ import ( "context" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/stretchr/testify/assert" ) func strp(s string) *string { return &s } - func TestFilterBranchMatchBadRegexp(t *testing.T) { provider, _ := NewFakeService( context.Background(), []*PullRequest{ { Number: 1, - Title: "PR branch1", Branch: "branch1", TargetBranch: "master", HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name1", }, }, nil, @@ -35,7 +30,7 @@ func TestFilterBranchMatchBadRegexp(t *testing.T) { }, } _, err := ListPullRequests(context.Background(), provider, filters) - require.Error(t, err) + assert.Error(t, err) } func TestFilterBranchMatch(t *testing.T) { @@ -44,35 +39,27 @@ func TestFilterBranchMatch(t *testing.T) { []*PullRequest{ { Number: 1, - Title: "PR one", Branch: "one", TargetBranch: "master", HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name1", }, { Number: 2, - Title: "PR two", Branch: "two", TargetBranch: "master", HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name2", }, { Number: 3, - Title: "PR three", Branch: "three", TargetBranch: "master", HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name3", }, { Number: 4, - Title: "PR four", Branch: "four", TargetBranch: "master", HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name4", }, }, nil, @@ -83,7 +70,7 @@ func TestFilterBranchMatch(t *testing.T) { }, } pullRequests, err := ListPullRequests(context.Background(), provider, filters) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, pullRequests, 1) assert.Equal(t, "two", pullRequests[0].Branch) } @@ -94,35 +81,27 @@ func TestFilterTargetBranchMatch(t *testing.T) { []*PullRequest{ { Number: 1, - Title: "PR one", Branch: "one", TargetBranch: "master", HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name1", }, { Number: 2, - Title: "PR two", Branch: "two", TargetBranch: "branch1", HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name2", }, { Number: 3, - Title: "PR three", Branch: "three", TargetBranch: "branch2", HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name3", }, { Number: 4, - Title: "PR four", Branch: "four", TargetBranch: "branch3", HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name4", }, }, nil, @@ -133,7 +112,7 @@ func TestFilterTargetBranchMatch(t *testing.T) { }, } pullRequests, err := ListPullRequests(context.Background(), provider, filters) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, pullRequests, 1) assert.Equal(t, "two", pullRequests[0].Branch) } @@ -144,35 +123,27 @@ func TestMultiFilterOr(t *testing.T) { []*PullRequest{ { Number: 1, - Title: "PR one", Branch: "one", TargetBranch: "master", HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name1", }, { Number: 2, - Title: "PR two", Branch: "two", TargetBranch: "master", HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name2", }, { Number: 3, - Title: "PR three", Branch: "three", TargetBranch: "master", HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name3", }, { Number: 4, - Title: "PR four", Branch: "four", TargetBranch: "master", HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name4", }, }, nil, @@ -186,7 +157,7 @@ func TestMultiFilterOr(t *testing.T) { }, } pullRequests, err := ListPullRequests(context.Background(), provider, filters) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, pullRequests, 3) assert.Equal(t, "two", pullRequests[0].Branch) assert.Equal(t, "three", pullRequests[1].Branch) @@ -199,35 +170,27 @@ func TestMultiFilterOrWithTargetBranchFilter(t *testing.T) { []*PullRequest{ { Number: 1, - Title: "PR one", Branch: "one", TargetBranch: "master", HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name1", }, { Number: 2, - Title: "PR two", Branch: "two", TargetBranch: "branch1", HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name2", }, { Number: 3, - Title: "PR three", Branch: "three", TargetBranch: "branch2", HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name3", }, { Number: 4, - Title: "PR four", Branch: "four", TargetBranch: "branch3", HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name4", }, }, nil, @@ -243,7 +206,7 @@ func TestMultiFilterOrWithTargetBranchFilter(t *testing.T) { }, } pullRequests, err := ListPullRequests(context.Background(), provider, filters) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, pullRequests, 2) assert.Equal(t, "two", pullRequests[0].Branch) assert.Equal(t, "four", pullRequests[1].Branch) @@ -255,26 +218,22 @@ func TestNoFilters(t *testing.T) { []*PullRequest{ { Number: 1, - Title: "PR one", Branch: "one", TargetBranch: "master", HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name1", }, { Number: 2, - Title: "PR two", Branch: "two", TargetBranch: "master", HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", - Author: "name2", }, }, nil, ) filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{} repos, err := ListPullRequests(context.Background(), provider, filters) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, repos, 2) assert.Equal(t, "one", repos[0].Branch) assert.Equal(t, "two", repos[1].Branch) diff --git a/applicationset/services/repo_service.go b/applicationset/services/repo_service.go index f415a9a6d1d7c..64fedc34390b8 100644 --- a/applicationset/services/repo_service.go +++ b/applicationset/services/repo_service.go @@ -6,37 +6,49 @@ import ( "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 easier to test the functionality of the git generator +type RepositoryDB interface { + GetRepository(ctx context.Context, url string) (*v1alpha1.Repository, error) +} + type argoCDService struct { - getRepository func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) + 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, noRevisionCache, verifyCommit bool) (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, noRevisionCache, verifyCommit bool) ([]string, error) + GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) } -func NewArgoCDService(getRepository func(ctx context.Context, url, project string) (*v1alpha1.Repository, error), submoduleEnabled bool, repoClientset apiclient.Clientset, newFileGlobbingEnabled bool) (Repos, error) { +func NewArgoCDService(db db.ArgoDB, submoduleEnabled bool, repoClientset apiclient.Clientset, newFileGlobbingEnabled bool) (Repos, error) { return &argoCDService{ - getRepository: getRepository, + repositoriesDB: db.(RepositoryDB), submoduleEnabled: submoduleEnabled, repoServerClientSet: repoClientset, newFileGlobbingEnabled: newFileGlobbingEnabled, }, nil } -func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache, verifyCommit bool) (map[string][]byte, error) { - repo, err := a.getRepository(ctx, repoURL, "") +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) } @@ -48,7 +60,6 @@ func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision s Path: pattern, NewGitFileGlobbingEnabled: a.newFileGlobbingEnabled, NoRevisionCache: noRevisionCache, - VerifyCommit: verifyCommit, } closer, client, err := a.repoServerClientSet.NewRepoServerClient() if err != nil { @@ -63,8 +74,8 @@ func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision s return fileResponse.GetMap(), nil } -func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache, verifyCommit bool) ([]string, error) { - repo, err := a.getRepository(ctx, repoURL, "") +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) } @@ -74,7 +85,6 @@ func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revi SubmoduleEnabled: a.submoduleEnabled, Revision: revision, NoRevisionCache: noRevisionCache, - VerifyCommit: verifyCommit, } closer, client, err := a.repoServerClientSet.NewRepoServerClient() @@ -88,4 +98,5 @@ func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revi return nil, fmt.Errorf("error retrieving Git Directories: %w", err) } return dirResponse.GetPaths(), nil + } diff --git a/applicationset/services/repo_service_test.go b/applicationset/services/repo_service_test.go index c621c317a9f4f..040fe57f96958 100644 --- a/applicationset/services/repo_service_test.go +++ b/applicationset/services/repo_service_test.go @@ -5,22 +5,23 @@ import ( "fmt" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - + "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" ) func TestGetDirectories(t *testing.T) { + type fields struct { + repositoriesDBFuncs []func(*mocks.RepositoryDB) storecreds git.CredsStore submoduleEnabled bool - getRepository func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) repoServerClientFuncs []func(*repo_mocks.RepoServerServiceClient) } type args struct { @@ -28,7 +29,6 @@ func TestGetDirectories(t *testing.T) { repoURL string revision string noRevisionCache bool - verifyCommit bool } tests := []struct { name string @@ -38,13 +38,17 @@ func TestGetDirectories(t *testing.T) { wantErr assert.ErrorAssertionFunc }{ {name: "ErrorGettingRepos", fields: fields{ - getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) { - return nil, fmt.Errorf("unable to get repos") + repositoriesDBFuncs: []func(*mocks.RepositoryDB){ + func(db *mocks.RepositoryDB) { + db.On("GetRepository", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("unable to get repos")) + }, }, }, args: args{}, want: nil, wantErr: assert.Error}, {name: "ErrorGettingDirs", fields: fields{ - getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) { - return &v1alpha1.Repository{}, nil + 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) { @@ -53,8 +57,10 @@ func TestGetDirectories(t *testing.T) { }, }, args: args{}, want: nil, wantErr: assert.Error}, {name: "HappyCase", fields: fields{ - getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) { - return &v1alpha1.Repository{}, nil + 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) { @@ -64,32 +70,26 @@ func TestGetDirectories(t *testing.T) { }, }, }, args: args{}, want: []string{"foo", "foo/bar", "bar/foo"}, wantErr: assert.NoError}, - {name: "ErrorVerifyingCommit", fields: fields{ - getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) { - return &v1alpha1.Repository{}, nil - }, - repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){ - func(client *repo_mocks.RepoServerServiceClient) { - client.On("GetGitDirectories", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("revision HEAD is not signed")) - }, - }, - }, args: args{}, want: nil, wantErr: assert.Error}, } 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) } a := &argoCDService{ - getRepository: tt.fields.getRepository, + 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, tt.args.verifyCommit) + 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 } @@ -100,10 +100,10 @@ func TestGetDirectories(t *testing.T) { func TestGetFiles(t *testing.T) { type fields struct { + repositoriesDBFuncs []func(*mocks.RepositoryDB) storecreds git.CredsStore submoduleEnabled bool repoServerClientFuncs []func(*repo_mocks.RepoServerServiceClient) - getRepository func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) } type args struct { ctx context.Context @@ -111,7 +111,6 @@ func TestGetFiles(t *testing.T) { revision string pattern string noRevisionCache bool - verifyCommit bool } tests := []struct { name string @@ -121,13 +120,17 @@ func TestGetFiles(t *testing.T) { wantErr assert.ErrorAssertionFunc }{ {name: "ErrorGettingRepos", fields: fields{ - getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) { - return nil, fmt.Errorf("unable to get repos") + repositoriesDBFuncs: []func(*mocks.RepositoryDB){ + func(db *mocks.RepositoryDB) { + db.On("GetRepository", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("unable to get repos")) + }, }, }, args: args{}, want: nil, wantErr: assert.Error}, {name: "ErrorGettingFiles", fields: fields{ - getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) { - return &v1alpha1.Repository{}, nil + 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) { @@ -136,8 +139,10 @@ func TestGetFiles(t *testing.T) { }, }, args: args{}, want: nil, wantErr: assert.Error}, {name: "HappyCase", fields: fields{ - getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) { - return &v1alpha1.Repository{}, nil + 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) { @@ -153,32 +158,26 @@ func TestGetFiles(t *testing.T) { "foo.json": []byte("hello: world!"), "bar.yaml": []byte("yay: appsets"), }, wantErr: assert.NoError}, - {name: "ErrorVerifyingCommit", fields: fields{ - getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) { - return &v1alpha1.Repository{}, nil - }, - repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){ - func(client *repo_mocks.RepoServerServiceClient) { - client.On("GetGitFiles", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("revision HEAD is not signed")) - }, - }, - }, args: args{}, want: nil, wantErr: assert.Error}, } 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) } a := &argoCDService{ - getRepository: tt.fields.getRepository, + 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, tt.args.verifyCommit) + 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 } @@ -188,9 +187,7 @@ func TestGetFiles(t *testing.T) { } func TestNewArgoCDService(t *testing.T) { - service, err := NewArgoCDService(func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) { - return &v1alpha1.Repository{}, nil - }, false, &repo_mocks.Clientset{}, false) - require.NoError(t, err) + 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 index 7732ff5361aa9..280711271cfb0 100644 --- a/applicationset/services/scm_provider/aws_codecommit.go +++ b/applicationset/services/scm_provider/aws_codecommit.go @@ -2,14 +2,13 @@ package scm_provider import ( "context" - "errors" "fmt" + "github.com/aws/aws-sdk-go/aws/request" pathpkg "path" "path/filepath" "strings" - "github.com/aws/aws-sdk-go/aws/request" - + 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" @@ -20,8 +19,6 @@ import ( log "github.com/sirupsen/logrus" "golang.org/x/exp/maps" "k8s.io/utils/strings/slices" - - application "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) const ( @@ -328,8 +325,7 @@ func getCodeCommitFIPSEndpoint(repoUrl string) (string, error) { } func hasAwsError(err error, codes ...string) bool { - var awsErr awserr.Error - if errors.As(err, &awsErr) { + if awsErr, ok := err.(awserr.Error); ok { return slices.Contains(codes, awsErr.Code()) } return false @@ -358,7 +354,7 @@ func createAWSDiscoveryClients(_ context.Context, role string, region string) (* Credentials: assumeRoleCreds, }) if err != nil { - return nil, nil, fmt.Errorf("error creating new AWS discovery session: %w", err) + 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") diff --git a/applicationset/services/scm_provider/aws_codecommit/mocks/AWSCodeCommitClient.go b/applicationset/services/scm_provider/aws_codecommit/mocks/AWSCodeCommitClient.go index 0595bc425a8fc..b9d6f6a5d5956 100644 --- a/applicationset/services/scm_provider/aws_codecommit/mocks/AWSCodeCommitClient.go +++ b/applicationset/services/scm_provider/aws_codecommit/mocks/AWSCodeCommitClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.26.1. DO NOT EDIT. package mocks @@ -17,6 +17,14 @@ 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)) @@ -28,10 +36,6 @@ func (_m *AWSCodeCommitClient) GetFolderWithContext(_a0 context.Context, _a1 *co _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GetFolderWithContext") - } - var r0 *codecommit.GetFolderOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *codecommit.GetFolderInput, ...request.Option) (*codecommit.GetFolderOutput, error)); ok { @@ -54,6 +58,43 @@ func (_m *AWSCodeCommitClient) GetFolderWithContext(_a0 context.Context, _a1 *co 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)) @@ -65,10 +106,6 @@ func (_m *AWSCodeCommitClient) GetRepositoryWithContext(_a0 context.Context, _a1 _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GetRepositoryWithContext") - } - var r0 *codecommit.GetRepositoryOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *codecommit.GetRepositoryInput, ...request.Option) (*codecommit.GetRepositoryOutput, error)); ok { @@ -91,6 +128,43 @@ func (_m *AWSCodeCommitClient) GetRepositoryWithContext(_a0 context.Context, _a1 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)) @@ -102,10 +176,6 @@ func (_m *AWSCodeCommitClient) ListBranchesWithContext(_a0 context.Context, _a1 _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for ListBranchesWithContext") - } - var r0 *codecommit.ListBranchesOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *codecommit.ListBranchesInput, ...request.Option) (*codecommit.ListBranchesOutput, error)); ok { @@ -128,6 +198,43 @@ func (_m *AWSCodeCommitClient) ListBranchesWithContext(_a0 context.Context, _a1 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)) @@ -139,10 +246,6 @@ func (_m *AWSCodeCommitClient) ListRepositoriesWithContext(_a0 context.Context, _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for ListRepositoriesWithContext") - } - var r0 *codecommit.ListRepositoriesOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *codecommit.ListRepositoriesInput, ...request.Option) (*codecommit.ListRepositoriesOutput, error)); ok { @@ -165,12 +268,50 @@ func (_m *AWSCodeCommitClient) ListRepositoriesWithContext(_a0 context.Context, return r0, r1 } -// 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. -// The first argument is typically a *testing.T value. -func NewAWSCodeCommitClient(t interface { +// 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()) -}) *AWSCodeCommitClient { +} + +// 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) diff --git a/applicationset/services/scm_provider/aws_codecommit/mocks/AWSTaggingClient.go b/applicationset/services/scm_provider/aws_codecommit/mocks/AWSTaggingClient.go index a029d785cc2fb..9acd8979b7818 100644 --- a/applicationset/services/scm_provider/aws_codecommit/mocks/AWSTaggingClient.go +++ b/applicationset/services/scm_provider/aws_codecommit/mocks/AWSTaggingClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.26.1. DO NOT EDIT. package mocks @@ -16,6 +16,14 @@ 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)) @@ -27,10 +35,6 @@ func (_m *AWSTaggingClient) GetResourcesWithContext(_a0 context.Context, _a1 *re _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GetResourcesWithContext") - } - var r0 *resourcegroupstaggingapi.GetResourcesOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)); ok { @@ -53,12 +57,50 @@ func (_m *AWSTaggingClient) GetResourcesWithContext(_a0 context.Context, _a1 *re return r0, r1 } -// 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. -// The first argument is typically a *testing.T value. -func NewAWSTaggingClient(t interface { +// 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()) -}) *AWSTaggingClient { +} + +// 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) diff --git a/applicationset/services/scm_provider/aws_codecommit_test.go b/applicationset/services/scm_provider/aws_codecommit_test.go index 00d8240973848..3a4f7c1a9a6a8 100644 --- a/applicationset/services/scm_provider/aws_codecommit_test.go +++ b/applicationset/services/scm_provider/aws_codecommit_test.go @@ -6,15 +6,14 @@ import ( "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" - - "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/aws_codecommit/mocks" - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) type awsCodeCommitTestRepository struct { @@ -178,8 +177,8 @@ func TestAWSCodeCommitListRepos(t *testing.T) { if repo.getRepositoryNilMetadata { repoMetadata = nil } - codeCommitClient. - On("GetRepositoryWithContext", ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(repo.name)}). + 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), @@ -194,14 +193,14 @@ func TestAWSCodeCommitListRepos(t *testing.T) { } if testCase.expectListAtCodeCommit { - codeCommitClient. - On("ListRepositoriesWithContext", ctx, &codecommit.ListRepositoriesInput{}). + codeCommitClient.EXPECT(). + ListRepositoriesWithContext(ctx, &codecommit.ListRepositoriesInput{}). Return(&codecommit.ListRepositoriesOutput{ Repositories: codecommitRepoNameIdPairs, }, testCase.listRepositoryError) } else { - taggingClient. - On("GetResourcesWithContext", ctx, mock.MatchedBy(equalIgnoringTagFilterOrder(&resourcegroupstaggingapi.GetResourcesInput{ + taggingClient.EXPECT(). + GetResourcesWithContext(ctx, mock.MatchedBy(equalIgnoringTagFilterOrder(&resourcegroupstaggingapi.GetResourcesInput{ TagFilters: testCase.expectTagFilters, ResourceTypeFilters: aws.StringSlice([]string{resourceTypeCodeCommitRepository}), }))). @@ -351,8 +350,8 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) { taggingClient := mocks.NewAWSTaggingClient(t) ctx := context.Background() if testCase.expectedGetFolderPath != "" { - codeCommitClient. - On("GetFolderWithContext", ctx, &codecommit.GetFolderInput{ + codeCommitClient.EXPECT(). + GetFolderWithContext(ctx, &codecommit.GetFolderInput{ CommitSpecifier: aws.String(branch), FolderPath: aws.String(testCase.expectedGetFolderPath), RepositoryName: aws.String(repoName), @@ -424,14 +423,14 @@ func TestAWSCodeCommitGetBranches(t *testing.T) { taggingClient := mocks.NewAWSTaggingClient(t) ctx := context.Background() if testCase.allBranches { - codeCommitClient. - On("ListBranchesWithContext", ctx, &codecommit.ListBranchesInput{ + codeCommitClient.EXPECT(). + ListBranchesWithContext(ctx, &codecommit.ListBranchesInput{ RepositoryName: aws.String(name), }). Return(&codecommit.ListBranchesOutput{Branches: aws.StringSlice(testCase.branches)}, testCase.apiError) } else { - codeCommitClient. - On("GetRepositoryWithContext", ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(name)}). + codeCommitClient.EXPECT(). + GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(name)}). Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: &codecommit.RepositoryMetadata{ AccountId: aws.String(organization), DefaultBranch: aws.String(defaultBranch), diff --git a/applicationset/services/scm_provider/azure_devops.go b/applicationset/services/scm_provider/azure_devops.go index a4bb50a0232e3..c71dabd0509f1 100644 --- a/applicationset/services/scm_provider/azure_devops.go +++ b/applicationset/services/scm_provider/azure_devops.go @@ -2,7 +2,6 @@ package scm_provider import ( "context" - "errors" "fmt" netUrl "net/url" "strings" @@ -52,10 +51,8 @@ type AzureDevOpsProvider struct { allBranches bool } -var ( - _ SCMProviderService = &AzureDevOpsProvider{} - _ AzureDevOpsClientFactory = &devopsFactoryImpl{} -) +var _ SCMProviderService = &AzureDevOpsProvider{} +var _ AzureDevOpsClientFactory = &devopsFactoryImpl{} func NewAzureDevOpsProvider(ctx context.Context, accessToken string, org string, url string, project string, allBranches bool) (*AzureDevOpsProvider, error) { if accessToken == "" { @@ -63,6 +60,7 @@ func NewAzureDevOpsProvider(ctx context.Context, accessToken string, org string, } devOpsURL, err := getValidDevOpsURL(url, org) + if err != nil { return nil, err } @@ -79,6 +77,7 @@ func (g *AzureDevOpsProvider) ListRepos(ctx context.Context, cloneProtocol strin } getRepoArgs := azureGit.GetRepositoriesArgs{Project: &g.teamProject} azureRepos, err := gitClient.GetRepositories(ctx, getRepoArgs) + if err != nil { return nil, err } @@ -107,7 +106,7 @@ func (g *AzureDevOpsProvider) RepoHasPath(ctx context.Context, repo *Repository, } var repoId string - if uuid, isUuid := repo.RepositoryId.(uuid.UUID); isUuid { // most likely an UUID, but do type-safe check anyway. Do %v fallback if not expected type. + if uuid, isUuid := repo.RepositoryId.(uuid.UUID); isUuid { //most likely an UUID, but do type-safe check anyway. Do %v fallback if not expected type. repoId = uuid.String() } else { repoId = fmt.Sprintf("%v", repo.RepositoryId) @@ -116,9 +115,9 @@ func (g *AzureDevOpsProvider) RepoHasPath(ctx context.Context, repo *Repository, branchName := repo.Branch getItemArgs := azureGit.GetItemArgs{RepositoryId: &repoId, Project: &g.teamProject, Path: &path, VersionDescriptor: &azureGit.GitVersionDescriptor{Version: &branchName}} _, err = gitClient.GetItem(ctx, getItemArgs) + if err != nil { - var wrappedError azuredevops.WrappedError - if errors.As(err, &wrappedError) && wrappedError.TypeKey != nil { + if wrappedError, isWrappedError := err.(azuredevops.WrappedError); isWrappedError && wrappedError.TypeKey != nil { if *wrappedError.TypeKey == AzureDevOpsErrorsTypeKeyValues.GitItemNotFound { return false, nil } @@ -139,12 +138,11 @@ func (g *AzureDevOpsProvider) GetBranches(ctx context.Context, repo *Repository) repos := []*Repository{} if !g.allBranches { - defaultBranchName := strings.Replace(repo.Branch, "refs/heads/", "", 1) // Azure DevOps returns default branch info like 'refs/heads/main', but does not support branch lookup of this format. + defaultBranchName := strings.Replace(repo.Branch, "refs/heads/", "", 1) //Azure DevOps returns default branch info like 'refs/heads/main', but does not support branch lookup of this format. getBranchArgs := azureGit.GetBranchArgs{RepositoryId: &repo.Repository, Project: &g.teamProject, Name: &defaultBranchName} branchResult, err := gitClient.GetBranch(ctx, getBranchArgs) if err != nil { - var wrappedError azuredevops.WrappedError - if errors.As(err, &wrappedError) && wrappedError.TypeKey != nil { + if wrappedError, isWrappedError := err.(azuredevops.WrappedError); isWrappedError && wrappedError.TypeKey != nil { if *wrappedError.TypeKey == AzureDevOpsErrorsTypeKeyValues.GitRepositoryNotFound { return repos, nil } @@ -172,8 +170,7 @@ func (g *AzureDevOpsProvider) GetBranches(ctx context.Context, repo *Repository) getBranchesRequest := azureGit.GetBranchesArgs{RepositoryId: &repo.Repository, Project: &g.teamProject} branches, err := gitClient.GetBranches(ctx, getBranchesRequest) if err != nil { - var wrappedError azuredevops.WrappedError - if errors.As(err, &wrappedError) && wrappedError.TypeKey != nil { + if wrappedError, isWrappedError := err.(azuredevops.WrappedError); isWrappedError && wrappedError.TypeKey != nil { if *wrappedError.TypeKey == AzureDevOpsErrorsTypeKeyValues.GitRepositoryNotFound { return repos, nil } @@ -212,6 +209,7 @@ func getValidDevOpsURL(url string, org string) (string, error) { devOpsURL := fmt.Sprintf("%s%s%s", url, separator, org) urlCheck, err := netUrl.ParseRequestURI(devOpsURL) + if err != nil { return "", fmt.Errorf("got an invalid URL for the Azure SCM generator: %w", err) } diff --git a/applicationset/services/scm_provider/azure_devops/git/mocks/Client.go b/applicationset/services/scm_provider/azure_devops/git/mocks/Client.go index c3cf024d882fe..7843753c9df5b 100644 --- a/applicationset/services/scm_provider/azure_devops/git/mocks/Client.go +++ b/applicationset/services/scm_provider/azure_devops/git/mocks/Client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.10.4. DO NOT EDIT. package mocks @@ -24,15 +24,7 @@ type Client struct { func (_m *Client) CreateAnnotatedTag(_a0 context.Context, _a1 git.CreateAnnotatedTagArgs) (*git.GitAnnotatedTag, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateAnnotatedTag") - } - var r0 *git.GitAnnotatedTag - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateAnnotatedTagArgs) (*git.GitAnnotatedTag, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateAnnotatedTagArgs) *git.GitAnnotatedTag); ok { r0 = rf(_a0, _a1) } else { @@ -41,6 +33,7 @@ func (_m *Client) CreateAnnotatedTag(_a0 context.Context, _a1 git.CreateAnnotate } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateAnnotatedTagArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -54,15 +47,7 @@ func (_m *Client) CreateAnnotatedTag(_a0 context.Context, _a1 git.CreateAnnotate func (_m *Client) CreateAttachment(_a0 context.Context, _a1 git.CreateAttachmentArgs) (*git.Attachment, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateAttachment") - } - var r0 *git.Attachment - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateAttachmentArgs) (*git.Attachment, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateAttachmentArgs) *git.Attachment); ok { r0 = rf(_a0, _a1) } else { @@ -71,6 +56,7 @@ func (_m *Client) CreateAttachment(_a0 context.Context, _a1 git.CreateAttachment } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateAttachmentArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -84,15 +70,7 @@ func (_m *Client) CreateAttachment(_a0 context.Context, _a1 git.CreateAttachment func (_m *Client) CreateCherryPick(_a0 context.Context, _a1 git.CreateCherryPickArgs) (*git.GitCherryPick, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateCherryPick") - } - var r0 *git.GitCherryPick - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateCherryPickArgs) (*git.GitCherryPick, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateCherryPickArgs) *git.GitCherryPick); ok { r0 = rf(_a0, _a1) } else { @@ -101,6 +79,7 @@ func (_m *Client) CreateCherryPick(_a0 context.Context, _a1 git.CreateCherryPick } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateCherryPickArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -114,15 +93,7 @@ func (_m *Client) CreateCherryPick(_a0 context.Context, _a1 git.CreateCherryPick func (_m *Client) CreateComment(_a0 context.Context, _a1 git.CreateCommentArgs) (*git.Comment, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateComment") - } - var r0 *git.Comment - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateCommentArgs) (*git.Comment, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateCommentArgs) *git.Comment); ok { r0 = rf(_a0, _a1) } else { @@ -131,6 +102,7 @@ func (_m *Client) CreateComment(_a0 context.Context, _a1 git.CreateCommentArgs) } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateCommentArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -144,15 +116,7 @@ func (_m *Client) CreateComment(_a0 context.Context, _a1 git.CreateCommentArgs) func (_m *Client) CreateCommitStatus(_a0 context.Context, _a1 git.CreateCommitStatusArgs) (*git.GitStatus, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateCommitStatus") - } - var r0 *git.GitStatus - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateCommitStatusArgs) (*git.GitStatus, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateCommitStatusArgs) *git.GitStatus); ok { r0 = rf(_a0, _a1) } else { @@ -161,6 +125,7 @@ func (_m *Client) CreateCommitStatus(_a0 context.Context, _a1 git.CreateCommitSt } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateCommitStatusArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -174,15 +139,7 @@ func (_m *Client) CreateCommitStatus(_a0 context.Context, _a1 git.CreateCommitSt func (_m *Client) CreateFavorite(_a0 context.Context, _a1 git.CreateFavoriteArgs) (*git.GitRefFavorite, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateFavorite") - } - var r0 *git.GitRefFavorite - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateFavoriteArgs) (*git.GitRefFavorite, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateFavoriteArgs) *git.GitRefFavorite); ok { r0 = rf(_a0, _a1) } else { @@ -191,6 +148,7 @@ func (_m *Client) CreateFavorite(_a0 context.Context, _a1 git.CreateFavoriteArgs } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateFavoriteArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -204,15 +162,7 @@ func (_m *Client) CreateFavorite(_a0 context.Context, _a1 git.CreateFavoriteArgs func (_m *Client) CreateForkSyncRequest(_a0 context.Context, _a1 git.CreateForkSyncRequestArgs) (*git.GitForkSyncRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateForkSyncRequest") - } - var r0 *git.GitForkSyncRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateForkSyncRequestArgs) (*git.GitForkSyncRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateForkSyncRequestArgs) *git.GitForkSyncRequest); ok { r0 = rf(_a0, _a1) } else { @@ -221,6 +171,7 @@ func (_m *Client) CreateForkSyncRequest(_a0 context.Context, _a1 git.CreateForkS } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateForkSyncRequestArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -234,15 +185,7 @@ func (_m *Client) CreateForkSyncRequest(_a0 context.Context, _a1 git.CreateForkS func (_m *Client) CreateImportRequest(_a0 context.Context, _a1 git.CreateImportRequestArgs) (*git.GitImportRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateImportRequest") - } - var r0 *git.GitImportRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateImportRequestArgs) (*git.GitImportRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateImportRequestArgs) *git.GitImportRequest); ok { r0 = rf(_a0, _a1) } else { @@ -251,6 +194,7 @@ func (_m *Client) CreateImportRequest(_a0 context.Context, _a1 git.CreateImportR } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateImportRequestArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -264,10 +208,6 @@ func (_m *Client) CreateImportRequest(_a0 context.Context, _a1 git.CreateImportR func (_m *Client) CreateLike(_a0 context.Context, _a1 git.CreateLikeArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateLike") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.CreateLikeArgs) error); ok { r0 = rf(_a0, _a1) @@ -282,15 +222,7 @@ func (_m *Client) CreateLike(_a0 context.Context, _a1 git.CreateLikeArgs) error func (_m *Client) CreateMergeRequest(_a0 context.Context, _a1 git.CreateMergeRequestArgs) (*git.GitMerge, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateMergeRequest") - } - var r0 *git.GitMerge - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateMergeRequestArgs) (*git.GitMerge, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateMergeRequestArgs) *git.GitMerge); ok { r0 = rf(_a0, _a1) } else { @@ -299,6 +231,7 @@ func (_m *Client) CreateMergeRequest(_a0 context.Context, _a1 git.CreateMergeReq } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateMergeRequestArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -312,15 +245,7 @@ func (_m *Client) CreateMergeRequest(_a0 context.Context, _a1 git.CreateMergeReq func (_m *Client) CreatePullRequest(_a0 context.Context, _a1 git.CreatePullRequestArgs) (*git.GitPullRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreatePullRequest") - } - var r0 *git.GitPullRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestArgs) (*git.GitPullRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestArgs) *git.GitPullRequest); ok { r0 = rf(_a0, _a1) } else { @@ -329,6 +254,7 @@ func (_m *Client) CreatePullRequest(_a0 context.Context, _a1 git.CreatePullReque } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreatePullRequestArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -342,15 +268,7 @@ func (_m *Client) CreatePullRequest(_a0 context.Context, _a1 git.CreatePullReque func (_m *Client) CreatePullRequestIterationStatus(_a0 context.Context, _a1 git.CreatePullRequestIterationStatusArgs) (*git.GitPullRequestStatus, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreatePullRequestIterationStatus") - } - var r0 *git.GitPullRequestStatus - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestIterationStatusArgs) (*git.GitPullRequestStatus, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestIterationStatusArgs) *git.GitPullRequestStatus); ok { r0 = rf(_a0, _a1) } else { @@ -359,6 +277,7 @@ func (_m *Client) CreatePullRequestIterationStatus(_a0 context.Context, _a1 git. } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreatePullRequestIterationStatusArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -372,15 +291,7 @@ func (_m *Client) CreatePullRequestIterationStatus(_a0 context.Context, _a1 git. func (_m *Client) CreatePullRequestLabel(_a0 context.Context, _a1 git.CreatePullRequestLabelArgs) (*core.WebApiTagDefinition, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreatePullRequestLabel") - } - var r0 *core.WebApiTagDefinition - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestLabelArgs) (*core.WebApiTagDefinition, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestLabelArgs) *core.WebApiTagDefinition); ok { r0 = rf(_a0, _a1) } else { @@ -389,6 +300,7 @@ func (_m *Client) CreatePullRequestLabel(_a0 context.Context, _a1 git.CreatePull } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreatePullRequestLabelArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -402,15 +314,7 @@ func (_m *Client) CreatePullRequestLabel(_a0 context.Context, _a1 git.CreatePull func (_m *Client) CreatePullRequestReviewer(_a0 context.Context, _a1 git.CreatePullRequestReviewerArgs) (*git.IdentityRefWithVote, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreatePullRequestReviewer") - } - var r0 *git.IdentityRefWithVote - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestReviewerArgs) (*git.IdentityRefWithVote, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestReviewerArgs) *git.IdentityRefWithVote); ok { r0 = rf(_a0, _a1) } else { @@ -419,6 +323,7 @@ func (_m *Client) CreatePullRequestReviewer(_a0 context.Context, _a1 git.CreateP } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreatePullRequestReviewerArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -432,15 +337,7 @@ func (_m *Client) CreatePullRequestReviewer(_a0 context.Context, _a1 git.CreateP func (_m *Client) CreatePullRequestReviewers(_a0 context.Context, _a1 git.CreatePullRequestReviewersArgs) (*[]git.IdentityRefWithVote, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreatePullRequestReviewers") - } - var r0 *[]git.IdentityRefWithVote - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestReviewersArgs) (*[]git.IdentityRefWithVote, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestReviewersArgs) *[]git.IdentityRefWithVote); ok { r0 = rf(_a0, _a1) } else { @@ -449,6 +346,7 @@ func (_m *Client) CreatePullRequestReviewers(_a0 context.Context, _a1 git.Create } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreatePullRequestReviewersArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -462,15 +360,7 @@ func (_m *Client) CreatePullRequestReviewers(_a0 context.Context, _a1 git.Create func (_m *Client) CreatePullRequestStatus(_a0 context.Context, _a1 git.CreatePullRequestStatusArgs) (*git.GitPullRequestStatus, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreatePullRequestStatus") - } - var r0 *git.GitPullRequestStatus - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestStatusArgs) (*git.GitPullRequestStatus, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreatePullRequestStatusArgs) *git.GitPullRequestStatus); ok { r0 = rf(_a0, _a1) } else { @@ -479,6 +369,7 @@ func (_m *Client) CreatePullRequestStatus(_a0 context.Context, _a1 git.CreatePul } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreatePullRequestStatusArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -492,15 +383,7 @@ func (_m *Client) CreatePullRequestStatus(_a0 context.Context, _a1 git.CreatePul func (_m *Client) CreatePush(_a0 context.Context, _a1 git.CreatePushArgs) (*git.GitPush, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreatePush") - } - var r0 *git.GitPush - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreatePushArgs) (*git.GitPush, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreatePushArgs) *git.GitPush); ok { r0 = rf(_a0, _a1) } else { @@ -509,6 +392,7 @@ func (_m *Client) CreatePush(_a0 context.Context, _a1 git.CreatePushArgs) (*git. } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreatePushArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -522,15 +406,7 @@ func (_m *Client) CreatePush(_a0 context.Context, _a1 git.CreatePushArgs) (*git. func (_m *Client) CreateRepository(_a0 context.Context, _a1 git.CreateRepositoryArgs) (*git.GitRepository, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateRepository") - } - var r0 *git.GitRepository - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateRepositoryArgs) (*git.GitRepository, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateRepositoryArgs) *git.GitRepository); ok { r0 = rf(_a0, _a1) } else { @@ -539,6 +415,7 @@ func (_m *Client) CreateRepository(_a0 context.Context, _a1 git.CreateRepository } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateRepositoryArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -552,15 +429,7 @@ func (_m *Client) CreateRepository(_a0 context.Context, _a1 git.CreateRepository func (_m *Client) CreateRevert(_a0 context.Context, _a1 git.CreateRevertArgs) (*git.GitRevert, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateRevert") - } - var r0 *git.GitRevert - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateRevertArgs) (*git.GitRevert, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateRevertArgs) *git.GitRevert); ok { r0 = rf(_a0, _a1) } else { @@ -569,6 +438,7 @@ func (_m *Client) CreateRevert(_a0 context.Context, _a1 git.CreateRevertArgs) (* } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateRevertArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -582,15 +452,7 @@ func (_m *Client) CreateRevert(_a0 context.Context, _a1 git.CreateRevertArgs) (* func (_m *Client) CreateThread(_a0 context.Context, _a1 git.CreateThreadArgs) (*git.GitPullRequestCommentThread, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for CreateThread") - } - var r0 *git.GitPullRequestCommentThread - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.CreateThreadArgs) (*git.GitPullRequestCommentThread, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.CreateThreadArgs) *git.GitPullRequestCommentThread); ok { r0 = rf(_a0, _a1) } else { @@ -599,6 +461,7 @@ func (_m *Client) CreateThread(_a0 context.Context, _a1 git.CreateThreadArgs) (* } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.CreateThreadArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -612,10 +475,6 @@ func (_m *Client) CreateThread(_a0 context.Context, _a1 git.CreateThreadArgs) (* func (_m *Client) DeleteAttachment(_a0 context.Context, _a1 git.DeleteAttachmentArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for DeleteAttachment") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.DeleteAttachmentArgs) error); ok { r0 = rf(_a0, _a1) @@ -630,10 +489,6 @@ func (_m *Client) DeleteAttachment(_a0 context.Context, _a1 git.DeleteAttachment func (_m *Client) DeleteComment(_a0 context.Context, _a1 git.DeleteCommentArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for DeleteComment") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.DeleteCommentArgs) error); ok { r0 = rf(_a0, _a1) @@ -648,10 +503,6 @@ func (_m *Client) DeleteComment(_a0 context.Context, _a1 git.DeleteCommentArgs) func (_m *Client) DeleteLike(_a0 context.Context, _a1 git.DeleteLikeArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for DeleteLike") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.DeleteLikeArgs) error); ok { r0 = rf(_a0, _a1) @@ -666,10 +517,6 @@ func (_m *Client) DeleteLike(_a0 context.Context, _a1 git.DeleteLikeArgs) error func (_m *Client) DeletePullRequestIterationStatus(_a0 context.Context, _a1 git.DeletePullRequestIterationStatusArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for DeletePullRequestIterationStatus") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.DeletePullRequestIterationStatusArgs) error); ok { r0 = rf(_a0, _a1) @@ -684,10 +531,6 @@ func (_m *Client) DeletePullRequestIterationStatus(_a0 context.Context, _a1 git. func (_m *Client) DeletePullRequestLabels(_a0 context.Context, _a1 git.DeletePullRequestLabelsArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for DeletePullRequestLabels") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.DeletePullRequestLabelsArgs) error); ok { r0 = rf(_a0, _a1) @@ -702,10 +545,6 @@ func (_m *Client) DeletePullRequestLabels(_a0 context.Context, _a1 git.DeletePul func (_m *Client) DeletePullRequestReviewer(_a0 context.Context, _a1 git.DeletePullRequestReviewerArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for DeletePullRequestReviewer") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.DeletePullRequestReviewerArgs) error); ok { r0 = rf(_a0, _a1) @@ -720,10 +559,6 @@ func (_m *Client) DeletePullRequestReviewer(_a0 context.Context, _a1 git.DeleteP func (_m *Client) DeletePullRequestStatus(_a0 context.Context, _a1 git.DeletePullRequestStatusArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for DeletePullRequestStatus") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.DeletePullRequestStatusArgs) error); ok { r0 = rf(_a0, _a1) @@ -738,10 +573,6 @@ func (_m *Client) DeletePullRequestStatus(_a0 context.Context, _a1 git.DeletePul func (_m *Client) DeleteRefFavorite(_a0 context.Context, _a1 git.DeleteRefFavoriteArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for DeleteRefFavorite") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.DeleteRefFavoriteArgs) error); ok { r0 = rf(_a0, _a1) @@ -756,10 +587,6 @@ func (_m *Client) DeleteRefFavorite(_a0 context.Context, _a1 git.DeleteRefFavori func (_m *Client) DeleteRepository(_a0 context.Context, _a1 git.DeleteRepositoryArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for DeleteRepository") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.DeleteRepositoryArgs) error); ok { r0 = rf(_a0, _a1) @@ -774,10 +601,6 @@ func (_m *Client) DeleteRepository(_a0 context.Context, _a1 git.DeleteRepository func (_m *Client) DeleteRepositoryFromRecycleBin(_a0 context.Context, _a1 git.DeleteRepositoryFromRecycleBinArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for DeleteRepositoryFromRecycleBin") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.DeleteRepositoryFromRecycleBinArgs) error); ok { r0 = rf(_a0, _a1) @@ -792,15 +615,7 @@ func (_m *Client) DeleteRepositoryFromRecycleBin(_a0 context.Context, _a1 git.De func (_m *Client) GetAnnotatedTag(_a0 context.Context, _a1 git.GetAnnotatedTagArgs) (*git.GitAnnotatedTag, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetAnnotatedTag") - } - var r0 *git.GitAnnotatedTag - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetAnnotatedTagArgs) (*git.GitAnnotatedTag, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetAnnotatedTagArgs) *git.GitAnnotatedTag); ok { r0 = rf(_a0, _a1) } else { @@ -809,6 +624,7 @@ func (_m *Client) GetAnnotatedTag(_a0 context.Context, _a1 git.GetAnnotatedTagAr } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetAnnotatedTagArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -822,15 +638,7 @@ func (_m *Client) GetAnnotatedTag(_a0 context.Context, _a1 git.GetAnnotatedTagAr func (_m *Client) GetAttachmentContent(_a0 context.Context, _a1 git.GetAttachmentContentArgs) (io.ReadCloser, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetAttachmentContent") - } - var r0 io.ReadCloser - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetAttachmentContentArgs) (io.ReadCloser, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetAttachmentContentArgs) io.ReadCloser); ok { r0 = rf(_a0, _a1) } else { @@ -839,6 +647,7 @@ func (_m *Client) GetAttachmentContent(_a0 context.Context, _a1 git.GetAttachmen } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetAttachmentContentArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -852,15 +661,7 @@ func (_m *Client) GetAttachmentContent(_a0 context.Context, _a1 git.GetAttachmen func (_m *Client) GetAttachmentZip(_a0 context.Context, _a1 git.GetAttachmentZipArgs) (io.ReadCloser, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetAttachmentZip") - } - var r0 io.ReadCloser - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetAttachmentZipArgs) (io.ReadCloser, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetAttachmentZipArgs) io.ReadCloser); ok { r0 = rf(_a0, _a1) } else { @@ -869,6 +670,7 @@ func (_m *Client) GetAttachmentZip(_a0 context.Context, _a1 git.GetAttachmentZip } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetAttachmentZipArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -882,15 +684,7 @@ func (_m *Client) GetAttachmentZip(_a0 context.Context, _a1 git.GetAttachmentZip func (_m *Client) GetAttachments(_a0 context.Context, _a1 git.GetAttachmentsArgs) (*[]git.Attachment, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetAttachments") - } - var r0 *[]git.Attachment - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetAttachmentsArgs) (*[]git.Attachment, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetAttachmentsArgs) *[]git.Attachment); ok { r0 = rf(_a0, _a1) } else { @@ -899,6 +693,7 @@ func (_m *Client) GetAttachments(_a0 context.Context, _a1 git.GetAttachmentsArgs } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetAttachmentsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -912,15 +707,7 @@ func (_m *Client) GetAttachments(_a0 context.Context, _a1 git.GetAttachmentsArgs func (_m *Client) GetBlob(_a0 context.Context, _a1 git.GetBlobArgs) (*git.GitBlobRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetBlob") - } - var r0 *git.GitBlobRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetBlobArgs) (*git.GitBlobRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetBlobArgs) *git.GitBlobRef); ok { r0 = rf(_a0, _a1) } else { @@ -929,6 +716,7 @@ func (_m *Client) GetBlob(_a0 context.Context, _a1 git.GetBlobArgs) (*git.GitBlo } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetBlobArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -942,15 +730,7 @@ func (_m *Client) GetBlob(_a0 context.Context, _a1 git.GetBlobArgs) (*git.GitBlo func (_m *Client) GetBlobContent(_a0 context.Context, _a1 git.GetBlobContentArgs) (io.ReadCloser, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetBlobContent") - } - var r0 io.ReadCloser - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetBlobContentArgs) (io.ReadCloser, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetBlobContentArgs) io.ReadCloser); ok { r0 = rf(_a0, _a1) } else { @@ -959,6 +739,7 @@ func (_m *Client) GetBlobContent(_a0 context.Context, _a1 git.GetBlobContentArgs } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetBlobContentArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -972,15 +753,7 @@ func (_m *Client) GetBlobContent(_a0 context.Context, _a1 git.GetBlobContentArgs func (_m *Client) GetBlobZip(_a0 context.Context, _a1 git.GetBlobZipArgs) (io.ReadCloser, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetBlobZip") - } - var r0 io.ReadCloser - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetBlobZipArgs) (io.ReadCloser, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetBlobZipArgs) io.ReadCloser); ok { r0 = rf(_a0, _a1) } else { @@ -989,6 +762,7 @@ func (_m *Client) GetBlobZip(_a0 context.Context, _a1 git.GetBlobZipArgs) (io.Re } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetBlobZipArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1002,15 +776,7 @@ func (_m *Client) GetBlobZip(_a0 context.Context, _a1 git.GetBlobZipArgs) (io.Re func (_m *Client) GetBlobsZip(_a0 context.Context, _a1 git.GetBlobsZipArgs) (io.ReadCloser, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetBlobsZip") - } - var r0 io.ReadCloser - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetBlobsZipArgs) (io.ReadCloser, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetBlobsZipArgs) io.ReadCloser); ok { r0 = rf(_a0, _a1) } else { @@ -1019,6 +785,7 @@ func (_m *Client) GetBlobsZip(_a0 context.Context, _a1 git.GetBlobsZipArgs) (io. } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetBlobsZipArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1032,15 +799,7 @@ func (_m *Client) GetBlobsZip(_a0 context.Context, _a1 git.GetBlobsZipArgs) (io. func (_m *Client) GetBranch(_a0 context.Context, _a1 git.GetBranchArgs) (*git.GitBranchStats, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetBranch") - } - var r0 *git.GitBranchStats - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetBranchArgs) (*git.GitBranchStats, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetBranchArgs) *git.GitBranchStats); ok { r0 = rf(_a0, _a1) } else { @@ -1049,6 +808,7 @@ func (_m *Client) GetBranch(_a0 context.Context, _a1 git.GetBranchArgs) (*git.Gi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetBranchArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1062,15 +822,7 @@ func (_m *Client) GetBranch(_a0 context.Context, _a1 git.GetBranchArgs) (*git.Gi func (_m *Client) GetBranches(_a0 context.Context, _a1 git.GetBranchesArgs) (*[]git.GitBranchStats, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetBranches") - } - var r0 *[]git.GitBranchStats - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetBranchesArgs) (*[]git.GitBranchStats, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetBranchesArgs) *[]git.GitBranchStats); ok { r0 = rf(_a0, _a1) } else { @@ -1079,6 +831,7 @@ func (_m *Client) GetBranches(_a0 context.Context, _a1 git.GetBranchesArgs) (*[] } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetBranchesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1092,15 +845,7 @@ func (_m *Client) GetBranches(_a0 context.Context, _a1 git.GetBranchesArgs) (*[] func (_m *Client) GetChanges(_a0 context.Context, _a1 git.GetChangesArgs) (*git.GitCommitChanges, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetChanges") - } - var r0 *git.GitCommitChanges - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetChangesArgs) (*git.GitCommitChanges, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetChangesArgs) *git.GitCommitChanges); ok { r0 = rf(_a0, _a1) } else { @@ -1109,6 +854,7 @@ func (_m *Client) GetChanges(_a0 context.Context, _a1 git.GetChangesArgs) (*git. } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetChangesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1122,15 +868,7 @@ func (_m *Client) GetChanges(_a0 context.Context, _a1 git.GetChangesArgs) (*git. func (_m *Client) GetCherryPick(_a0 context.Context, _a1 git.GetCherryPickArgs) (*git.GitCherryPick, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetCherryPick") - } - var r0 *git.GitCherryPick - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetCherryPickArgs) (*git.GitCherryPick, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetCherryPickArgs) *git.GitCherryPick); ok { r0 = rf(_a0, _a1) } else { @@ -1139,6 +877,7 @@ func (_m *Client) GetCherryPick(_a0 context.Context, _a1 git.GetCherryPickArgs) } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetCherryPickArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1152,15 +891,7 @@ func (_m *Client) GetCherryPick(_a0 context.Context, _a1 git.GetCherryPickArgs) func (_m *Client) GetCherryPickForRefName(_a0 context.Context, _a1 git.GetCherryPickForRefNameArgs) (*git.GitCherryPick, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetCherryPickForRefName") - } - var r0 *git.GitCherryPick - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetCherryPickForRefNameArgs) (*git.GitCherryPick, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetCherryPickForRefNameArgs) *git.GitCherryPick); ok { r0 = rf(_a0, _a1) } else { @@ -1169,6 +900,7 @@ func (_m *Client) GetCherryPickForRefName(_a0 context.Context, _a1 git.GetCherry } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetCherryPickForRefNameArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1182,15 +914,7 @@ func (_m *Client) GetCherryPickForRefName(_a0 context.Context, _a1 git.GetCherry func (_m *Client) GetComment(_a0 context.Context, _a1 git.GetCommentArgs) (*git.Comment, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetComment") - } - var r0 *git.Comment - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetCommentArgs) (*git.Comment, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetCommentArgs) *git.Comment); ok { r0 = rf(_a0, _a1) } else { @@ -1199,6 +923,7 @@ func (_m *Client) GetComment(_a0 context.Context, _a1 git.GetCommentArgs) (*git. } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetCommentArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1212,15 +937,7 @@ func (_m *Client) GetComment(_a0 context.Context, _a1 git.GetCommentArgs) (*git. func (_m *Client) GetComments(_a0 context.Context, _a1 git.GetCommentsArgs) (*[]git.Comment, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetComments") - } - var r0 *[]git.Comment - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetCommentsArgs) (*[]git.Comment, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetCommentsArgs) *[]git.Comment); ok { r0 = rf(_a0, _a1) } else { @@ -1229,6 +946,7 @@ func (_m *Client) GetComments(_a0 context.Context, _a1 git.GetCommentsArgs) (*[] } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetCommentsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1242,15 +960,7 @@ func (_m *Client) GetComments(_a0 context.Context, _a1 git.GetCommentsArgs) (*[] func (_m *Client) GetCommit(_a0 context.Context, _a1 git.GetCommitArgs) (*git.GitCommit, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetCommit") - } - var r0 *git.GitCommit - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetCommitArgs) (*git.GitCommit, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetCommitArgs) *git.GitCommit); ok { r0 = rf(_a0, _a1) } else { @@ -1259,6 +969,7 @@ func (_m *Client) GetCommit(_a0 context.Context, _a1 git.GetCommitArgs) (*git.Gi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetCommitArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1272,15 +983,7 @@ func (_m *Client) GetCommit(_a0 context.Context, _a1 git.GetCommitArgs) (*git.Gi func (_m *Client) GetCommitDiffs(_a0 context.Context, _a1 git.GetCommitDiffsArgs) (*git.GitCommitDiffs, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetCommitDiffs") - } - var r0 *git.GitCommitDiffs - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetCommitDiffsArgs) (*git.GitCommitDiffs, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetCommitDiffsArgs) *git.GitCommitDiffs); ok { r0 = rf(_a0, _a1) } else { @@ -1289,6 +992,7 @@ func (_m *Client) GetCommitDiffs(_a0 context.Context, _a1 git.GetCommitDiffsArgs } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetCommitDiffsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1302,15 +1006,7 @@ func (_m *Client) GetCommitDiffs(_a0 context.Context, _a1 git.GetCommitDiffsArgs func (_m *Client) GetCommits(_a0 context.Context, _a1 git.GetCommitsArgs) (*[]git.GitCommitRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetCommits") - } - var r0 *[]git.GitCommitRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetCommitsArgs) (*[]git.GitCommitRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetCommitsArgs) *[]git.GitCommitRef); ok { r0 = rf(_a0, _a1) } else { @@ -1319,6 +1015,7 @@ func (_m *Client) GetCommits(_a0 context.Context, _a1 git.GetCommitsArgs) (*[]gi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetCommitsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1332,15 +1029,7 @@ func (_m *Client) GetCommits(_a0 context.Context, _a1 git.GetCommitsArgs) (*[]gi func (_m *Client) GetCommitsBatch(_a0 context.Context, _a1 git.GetCommitsBatchArgs) (*[]git.GitCommitRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetCommitsBatch") - } - var r0 *[]git.GitCommitRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetCommitsBatchArgs) (*[]git.GitCommitRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetCommitsBatchArgs) *[]git.GitCommitRef); ok { r0 = rf(_a0, _a1) } else { @@ -1349,6 +1038,7 @@ func (_m *Client) GetCommitsBatch(_a0 context.Context, _a1 git.GetCommitsBatchAr } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetCommitsBatchArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1362,15 +1052,7 @@ func (_m *Client) GetCommitsBatch(_a0 context.Context, _a1 git.GetCommitsBatchAr func (_m *Client) GetDeletedRepositories(_a0 context.Context, _a1 git.GetDeletedRepositoriesArgs) (*[]git.GitDeletedRepository, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetDeletedRepositories") - } - var r0 *[]git.GitDeletedRepository - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetDeletedRepositoriesArgs) (*[]git.GitDeletedRepository, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetDeletedRepositoriesArgs) *[]git.GitDeletedRepository); ok { r0 = rf(_a0, _a1) } else { @@ -1379,6 +1061,7 @@ func (_m *Client) GetDeletedRepositories(_a0 context.Context, _a1 git.GetDeleted } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetDeletedRepositoriesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1392,15 +1075,7 @@ func (_m *Client) GetDeletedRepositories(_a0 context.Context, _a1 git.GetDeleted func (_m *Client) GetForkSyncRequest(_a0 context.Context, _a1 git.GetForkSyncRequestArgs) (*git.GitForkSyncRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetForkSyncRequest") - } - var r0 *git.GitForkSyncRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetForkSyncRequestArgs) (*git.GitForkSyncRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetForkSyncRequestArgs) *git.GitForkSyncRequest); ok { r0 = rf(_a0, _a1) } else { @@ -1409,6 +1084,7 @@ func (_m *Client) GetForkSyncRequest(_a0 context.Context, _a1 git.GetForkSyncReq } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetForkSyncRequestArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1422,15 +1098,7 @@ func (_m *Client) GetForkSyncRequest(_a0 context.Context, _a1 git.GetForkSyncReq func (_m *Client) GetForkSyncRequests(_a0 context.Context, _a1 git.GetForkSyncRequestsArgs) (*[]git.GitForkSyncRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetForkSyncRequests") - } - var r0 *[]git.GitForkSyncRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetForkSyncRequestsArgs) (*[]git.GitForkSyncRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetForkSyncRequestsArgs) *[]git.GitForkSyncRequest); ok { r0 = rf(_a0, _a1) } else { @@ -1439,6 +1107,7 @@ func (_m *Client) GetForkSyncRequests(_a0 context.Context, _a1 git.GetForkSyncRe } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetForkSyncRequestsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1452,15 +1121,7 @@ func (_m *Client) GetForkSyncRequests(_a0 context.Context, _a1 git.GetForkSyncRe func (_m *Client) GetForks(_a0 context.Context, _a1 git.GetForksArgs) (*[]git.GitRepositoryRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetForks") - } - var r0 *[]git.GitRepositoryRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetForksArgs) (*[]git.GitRepositoryRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetForksArgs) *[]git.GitRepositoryRef); ok { r0 = rf(_a0, _a1) } else { @@ -1469,6 +1130,7 @@ func (_m *Client) GetForks(_a0 context.Context, _a1 git.GetForksArgs) (*[]git.Gi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetForksArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1482,15 +1144,7 @@ func (_m *Client) GetForks(_a0 context.Context, _a1 git.GetForksArgs) (*[]git.Gi func (_m *Client) GetImportRequest(_a0 context.Context, _a1 git.GetImportRequestArgs) (*git.GitImportRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetImportRequest") - } - var r0 *git.GitImportRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetImportRequestArgs) (*git.GitImportRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetImportRequestArgs) *git.GitImportRequest); ok { r0 = rf(_a0, _a1) } else { @@ -1499,6 +1153,7 @@ func (_m *Client) GetImportRequest(_a0 context.Context, _a1 git.GetImportRequest } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetImportRequestArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1512,15 +1167,7 @@ func (_m *Client) GetImportRequest(_a0 context.Context, _a1 git.GetImportRequest func (_m *Client) GetItem(_a0 context.Context, _a1 git.GetItemArgs) (*git.GitItem, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetItem") - } - var r0 *git.GitItem - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetItemArgs) (*git.GitItem, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetItemArgs) *git.GitItem); ok { r0 = rf(_a0, _a1) } else { @@ -1529,6 +1176,7 @@ func (_m *Client) GetItem(_a0 context.Context, _a1 git.GetItemArgs) (*git.GitIte } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetItemArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1542,15 +1190,7 @@ func (_m *Client) GetItem(_a0 context.Context, _a1 git.GetItemArgs) (*git.GitIte func (_m *Client) GetItemContent(_a0 context.Context, _a1 git.GetItemContentArgs) (io.ReadCloser, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetItemContent") - } - var r0 io.ReadCloser - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetItemContentArgs) (io.ReadCloser, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetItemContentArgs) io.ReadCloser); ok { r0 = rf(_a0, _a1) } else { @@ -1559,6 +1199,7 @@ func (_m *Client) GetItemContent(_a0 context.Context, _a1 git.GetItemContentArgs } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetItemContentArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1572,15 +1213,7 @@ func (_m *Client) GetItemContent(_a0 context.Context, _a1 git.GetItemContentArgs func (_m *Client) GetItemText(_a0 context.Context, _a1 git.GetItemTextArgs) (io.ReadCloser, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetItemText") - } - var r0 io.ReadCloser - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetItemTextArgs) (io.ReadCloser, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetItemTextArgs) io.ReadCloser); ok { r0 = rf(_a0, _a1) } else { @@ -1589,6 +1222,7 @@ func (_m *Client) GetItemText(_a0 context.Context, _a1 git.GetItemTextArgs) (io. } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetItemTextArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1602,15 +1236,7 @@ func (_m *Client) GetItemText(_a0 context.Context, _a1 git.GetItemTextArgs) (io. func (_m *Client) GetItemZip(_a0 context.Context, _a1 git.GetItemZipArgs) (io.ReadCloser, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetItemZip") - } - var r0 io.ReadCloser - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetItemZipArgs) (io.ReadCloser, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetItemZipArgs) io.ReadCloser); ok { r0 = rf(_a0, _a1) } else { @@ -1619,6 +1245,7 @@ func (_m *Client) GetItemZip(_a0 context.Context, _a1 git.GetItemZipArgs) (io.Re } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetItemZipArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1632,15 +1259,7 @@ func (_m *Client) GetItemZip(_a0 context.Context, _a1 git.GetItemZipArgs) (io.Re func (_m *Client) GetItems(_a0 context.Context, _a1 git.GetItemsArgs) (*[]git.GitItem, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetItems") - } - var r0 *[]git.GitItem - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetItemsArgs) (*[]git.GitItem, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetItemsArgs) *[]git.GitItem); ok { r0 = rf(_a0, _a1) } else { @@ -1649,6 +1268,7 @@ func (_m *Client) GetItems(_a0 context.Context, _a1 git.GetItemsArgs) (*[]git.Gi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetItemsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1662,15 +1282,7 @@ func (_m *Client) GetItems(_a0 context.Context, _a1 git.GetItemsArgs) (*[]git.Gi func (_m *Client) GetItemsBatch(_a0 context.Context, _a1 git.GetItemsBatchArgs) (*[][]git.GitItem, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetItemsBatch") - } - var r0 *[][]git.GitItem - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetItemsBatchArgs) (*[][]git.GitItem, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetItemsBatchArgs) *[][]git.GitItem); ok { r0 = rf(_a0, _a1) } else { @@ -1679,6 +1291,7 @@ func (_m *Client) GetItemsBatch(_a0 context.Context, _a1 git.GetItemsBatchArgs) } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetItemsBatchArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1692,15 +1305,7 @@ func (_m *Client) GetItemsBatch(_a0 context.Context, _a1 git.GetItemsBatchArgs) func (_m *Client) GetLikes(_a0 context.Context, _a1 git.GetLikesArgs) (*[]webapi.IdentityRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetLikes") - } - var r0 *[]webapi.IdentityRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetLikesArgs) (*[]webapi.IdentityRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetLikesArgs) *[]webapi.IdentityRef); ok { r0 = rf(_a0, _a1) } else { @@ -1709,6 +1314,7 @@ func (_m *Client) GetLikes(_a0 context.Context, _a1 git.GetLikesArgs) (*[]webapi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetLikesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1722,15 +1328,7 @@ func (_m *Client) GetLikes(_a0 context.Context, _a1 git.GetLikesArgs) (*[]webapi func (_m *Client) GetMergeBases(_a0 context.Context, _a1 git.GetMergeBasesArgs) (*[]git.GitCommitRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetMergeBases") - } - var r0 *[]git.GitCommitRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetMergeBasesArgs) (*[]git.GitCommitRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetMergeBasesArgs) *[]git.GitCommitRef); ok { r0 = rf(_a0, _a1) } else { @@ -1739,6 +1337,7 @@ func (_m *Client) GetMergeBases(_a0 context.Context, _a1 git.GetMergeBasesArgs) } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetMergeBasesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1752,15 +1351,7 @@ func (_m *Client) GetMergeBases(_a0 context.Context, _a1 git.GetMergeBasesArgs) func (_m *Client) GetMergeRequest(_a0 context.Context, _a1 git.GetMergeRequestArgs) (*git.GitMerge, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetMergeRequest") - } - var r0 *git.GitMerge - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetMergeRequestArgs) (*git.GitMerge, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetMergeRequestArgs) *git.GitMerge); ok { r0 = rf(_a0, _a1) } else { @@ -1769,6 +1360,7 @@ func (_m *Client) GetMergeRequest(_a0 context.Context, _a1 git.GetMergeRequestAr } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetMergeRequestArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1782,15 +1374,7 @@ func (_m *Client) GetMergeRequest(_a0 context.Context, _a1 git.GetMergeRequestAr func (_m *Client) GetPolicyConfigurations(_a0 context.Context, _a1 git.GetPolicyConfigurationsArgs) (*git.GitPolicyConfigurationResponse, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPolicyConfigurations") - } - var r0 *git.GitPolicyConfigurationResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPolicyConfigurationsArgs) (*git.GitPolicyConfigurationResponse, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPolicyConfigurationsArgs) *git.GitPolicyConfigurationResponse); ok { r0 = rf(_a0, _a1) } else { @@ -1799,6 +1383,7 @@ func (_m *Client) GetPolicyConfigurations(_a0 context.Context, _a1 git.GetPolicy } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPolicyConfigurationsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1812,15 +1397,7 @@ func (_m *Client) GetPolicyConfigurations(_a0 context.Context, _a1 git.GetPolicy func (_m *Client) GetPullRequest(_a0 context.Context, _a1 git.GetPullRequestArgs) (*git.GitPullRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequest") - } - var r0 *git.GitPullRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestArgs) (*git.GitPullRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestArgs) *git.GitPullRequest); ok { r0 = rf(_a0, _a1) } else { @@ -1829,6 +1406,7 @@ func (_m *Client) GetPullRequest(_a0 context.Context, _a1 git.GetPullRequestArgs } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1842,15 +1420,7 @@ func (_m *Client) GetPullRequest(_a0 context.Context, _a1 git.GetPullRequestArgs func (_m *Client) GetPullRequestById(_a0 context.Context, _a1 git.GetPullRequestByIdArgs) (*git.GitPullRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestById") - } - var r0 *git.GitPullRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestByIdArgs) (*git.GitPullRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestByIdArgs) *git.GitPullRequest); ok { r0 = rf(_a0, _a1) } else { @@ -1859,6 +1429,7 @@ func (_m *Client) GetPullRequestById(_a0 context.Context, _a1 git.GetPullRequest } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestByIdArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1872,15 +1443,7 @@ func (_m *Client) GetPullRequestById(_a0 context.Context, _a1 git.GetPullRequest func (_m *Client) GetPullRequestCommits(_a0 context.Context, _a1 git.GetPullRequestCommitsArgs) (*git.GetPullRequestCommitsResponseValue, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestCommits") - } - var r0 *git.GetPullRequestCommitsResponseValue - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestCommitsArgs) (*git.GetPullRequestCommitsResponseValue, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestCommitsArgs) *git.GetPullRequestCommitsResponseValue); ok { r0 = rf(_a0, _a1) } else { @@ -1889,6 +1452,7 @@ func (_m *Client) GetPullRequestCommits(_a0 context.Context, _a1 git.GetPullRequ } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestCommitsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1902,15 +1466,7 @@ func (_m *Client) GetPullRequestCommits(_a0 context.Context, _a1 git.GetPullRequ func (_m *Client) GetPullRequestIteration(_a0 context.Context, _a1 git.GetPullRequestIterationArgs) (*git.GitPullRequestIteration, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestIteration") - } - var r0 *git.GitPullRequestIteration - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationArgs) (*git.GitPullRequestIteration, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationArgs) *git.GitPullRequestIteration); ok { r0 = rf(_a0, _a1) } else { @@ -1919,6 +1475,7 @@ func (_m *Client) GetPullRequestIteration(_a0 context.Context, _a1 git.GetPullRe } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestIterationArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1932,15 +1489,7 @@ func (_m *Client) GetPullRequestIteration(_a0 context.Context, _a1 git.GetPullRe func (_m *Client) GetPullRequestIterationChanges(_a0 context.Context, _a1 git.GetPullRequestIterationChangesArgs) (*git.GitPullRequestIterationChanges, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestIterationChanges") - } - var r0 *git.GitPullRequestIterationChanges - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationChangesArgs) (*git.GitPullRequestIterationChanges, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationChangesArgs) *git.GitPullRequestIterationChanges); ok { r0 = rf(_a0, _a1) } else { @@ -1949,6 +1498,7 @@ func (_m *Client) GetPullRequestIterationChanges(_a0 context.Context, _a1 git.Ge } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestIterationChangesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1962,15 +1512,7 @@ func (_m *Client) GetPullRequestIterationChanges(_a0 context.Context, _a1 git.Ge func (_m *Client) GetPullRequestIterationCommits(_a0 context.Context, _a1 git.GetPullRequestIterationCommitsArgs) (*[]git.GitCommitRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestIterationCommits") - } - var r0 *[]git.GitCommitRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationCommitsArgs) (*[]git.GitCommitRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationCommitsArgs) *[]git.GitCommitRef); ok { r0 = rf(_a0, _a1) } else { @@ -1979,6 +1521,7 @@ func (_m *Client) GetPullRequestIterationCommits(_a0 context.Context, _a1 git.Ge } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestIterationCommitsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -1992,15 +1535,7 @@ func (_m *Client) GetPullRequestIterationCommits(_a0 context.Context, _a1 git.Ge func (_m *Client) GetPullRequestIterationStatus(_a0 context.Context, _a1 git.GetPullRequestIterationStatusArgs) (*git.GitPullRequestStatus, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestIterationStatus") - } - var r0 *git.GitPullRequestStatus - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationStatusArgs) (*git.GitPullRequestStatus, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationStatusArgs) *git.GitPullRequestStatus); ok { r0 = rf(_a0, _a1) } else { @@ -2009,6 +1544,7 @@ func (_m *Client) GetPullRequestIterationStatus(_a0 context.Context, _a1 git.Get } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestIterationStatusArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2022,15 +1558,7 @@ func (_m *Client) GetPullRequestIterationStatus(_a0 context.Context, _a1 git.Get func (_m *Client) GetPullRequestIterationStatuses(_a0 context.Context, _a1 git.GetPullRequestIterationStatusesArgs) (*[]git.GitPullRequestStatus, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestIterationStatuses") - } - var r0 *[]git.GitPullRequestStatus - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationStatusesArgs) (*[]git.GitPullRequestStatus, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationStatusesArgs) *[]git.GitPullRequestStatus); ok { r0 = rf(_a0, _a1) } else { @@ -2039,6 +1567,7 @@ func (_m *Client) GetPullRequestIterationStatuses(_a0 context.Context, _a1 git.G } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestIterationStatusesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2052,15 +1581,7 @@ func (_m *Client) GetPullRequestIterationStatuses(_a0 context.Context, _a1 git.G func (_m *Client) GetPullRequestIterations(_a0 context.Context, _a1 git.GetPullRequestIterationsArgs) (*[]git.GitPullRequestIteration, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestIterations") - } - var r0 *[]git.GitPullRequestIteration - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationsArgs) (*[]git.GitPullRequestIteration, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestIterationsArgs) *[]git.GitPullRequestIteration); ok { r0 = rf(_a0, _a1) } else { @@ -2069,6 +1590,7 @@ func (_m *Client) GetPullRequestIterations(_a0 context.Context, _a1 git.GetPullR } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestIterationsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2082,15 +1604,7 @@ func (_m *Client) GetPullRequestIterations(_a0 context.Context, _a1 git.GetPullR func (_m *Client) GetPullRequestLabel(_a0 context.Context, _a1 git.GetPullRequestLabelArgs) (*core.WebApiTagDefinition, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestLabel") - } - var r0 *core.WebApiTagDefinition - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestLabelArgs) (*core.WebApiTagDefinition, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestLabelArgs) *core.WebApiTagDefinition); ok { r0 = rf(_a0, _a1) } else { @@ -2099,6 +1613,7 @@ func (_m *Client) GetPullRequestLabel(_a0 context.Context, _a1 git.GetPullReques } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestLabelArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2112,15 +1627,7 @@ func (_m *Client) GetPullRequestLabel(_a0 context.Context, _a1 git.GetPullReques func (_m *Client) GetPullRequestLabels(_a0 context.Context, _a1 git.GetPullRequestLabelsArgs) (*[]core.WebApiTagDefinition, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestLabels") - } - var r0 *[]core.WebApiTagDefinition - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestLabelsArgs) (*[]core.WebApiTagDefinition, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestLabelsArgs) *[]core.WebApiTagDefinition); ok { r0 = rf(_a0, _a1) } else { @@ -2129,6 +1636,7 @@ func (_m *Client) GetPullRequestLabels(_a0 context.Context, _a1 git.GetPullReque } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestLabelsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2142,15 +1650,7 @@ func (_m *Client) GetPullRequestLabels(_a0 context.Context, _a1 git.GetPullReque func (_m *Client) GetPullRequestProperties(_a0 context.Context, _a1 git.GetPullRequestPropertiesArgs) (interface{}, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestProperties") - } - var r0 interface{} - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestPropertiesArgs) (interface{}, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestPropertiesArgs) interface{}); ok { r0 = rf(_a0, _a1) } else { @@ -2159,6 +1659,7 @@ func (_m *Client) GetPullRequestProperties(_a0 context.Context, _a1 git.GetPullR } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestPropertiesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2172,15 +1673,7 @@ func (_m *Client) GetPullRequestProperties(_a0 context.Context, _a1 git.GetPullR func (_m *Client) GetPullRequestQuery(_a0 context.Context, _a1 git.GetPullRequestQueryArgs) (*git.GitPullRequestQuery, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestQuery") - } - var r0 *git.GitPullRequestQuery - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestQueryArgs) (*git.GitPullRequestQuery, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestQueryArgs) *git.GitPullRequestQuery); ok { r0 = rf(_a0, _a1) } else { @@ -2189,6 +1682,7 @@ func (_m *Client) GetPullRequestQuery(_a0 context.Context, _a1 git.GetPullReques } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestQueryArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2202,15 +1696,7 @@ func (_m *Client) GetPullRequestQuery(_a0 context.Context, _a1 git.GetPullReques func (_m *Client) GetPullRequestReviewer(_a0 context.Context, _a1 git.GetPullRequestReviewerArgs) (*git.IdentityRefWithVote, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestReviewer") - } - var r0 *git.IdentityRefWithVote - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestReviewerArgs) (*git.IdentityRefWithVote, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestReviewerArgs) *git.IdentityRefWithVote); ok { r0 = rf(_a0, _a1) } else { @@ -2219,6 +1705,7 @@ func (_m *Client) GetPullRequestReviewer(_a0 context.Context, _a1 git.GetPullReq } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestReviewerArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2232,15 +1719,7 @@ func (_m *Client) GetPullRequestReviewer(_a0 context.Context, _a1 git.GetPullReq func (_m *Client) GetPullRequestReviewers(_a0 context.Context, _a1 git.GetPullRequestReviewersArgs) (*[]git.IdentityRefWithVote, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestReviewers") - } - var r0 *[]git.IdentityRefWithVote - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestReviewersArgs) (*[]git.IdentityRefWithVote, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestReviewersArgs) *[]git.IdentityRefWithVote); ok { r0 = rf(_a0, _a1) } else { @@ -2249,6 +1728,7 @@ func (_m *Client) GetPullRequestReviewers(_a0 context.Context, _a1 git.GetPullRe } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestReviewersArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2262,15 +1742,7 @@ func (_m *Client) GetPullRequestReviewers(_a0 context.Context, _a1 git.GetPullRe func (_m *Client) GetPullRequestStatus(_a0 context.Context, _a1 git.GetPullRequestStatusArgs) (*git.GitPullRequestStatus, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestStatus") - } - var r0 *git.GitPullRequestStatus - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestStatusArgs) (*git.GitPullRequestStatus, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestStatusArgs) *git.GitPullRequestStatus); ok { r0 = rf(_a0, _a1) } else { @@ -2279,6 +1751,7 @@ func (_m *Client) GetPullRequestStatus(_a0 context.Context, _a1 git.GetPullReque } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestStatusArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2292,15 +1765,7 @@ func (_m *Client) GetPullRequestStatus(_a0 context.Context, _a1 git.GetPullReque func (_m *Client) GetPullRequestStatuses(_a0 context.Context, _a1 git.GetPullRequestStatusesArgs) (*[]git.GitPullRequestStatus, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestStatuses") - } - var r0 *[]git.GitPullRequestStatus - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestStatusesArgs) (*[]git.GitPullRequestStatus, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestStatusesArgs) *[]git.GitPullRequestStatus); ok { r0 = rf(_a0, _a1) } else { @@ -2309,6 +1774,7 @@ func (_m *Client) GetPullRequestStatuses(_a0 context.Context, _a1 git.GetPullReq } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestStatusesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2322,15 +1788,7 @@ func (_m *Client) GetPullRequestStatuses(_a0 context.Context, _a1 git.GetPullReq func (_m *Client) GetPullRequestThread(_a0 context.Context, _a1 git.GetPullRequestThreadArgs) (*git.GitPullRequestCommentThread, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestThread") - } - var r0 *git.GitPullRequestCommentThread - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestThreadArgs) (*git.GitPullRequestCommentThread, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestThreadArgs) *git.GitPullRequestCommentThread); ok { r0 = rf(_a0, _a1) } else { @@ -2339,6 +1797,7 @@ func (_m *Client) GetPullRequestThread(_a0 context.Context, _a1 git.GetPullReque } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestThreadArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2352,15 +1811,7 @@ func (_m *Client) GetPullRequestThread(_a0 context.Context, _a1 git.GetPullReque func (_m *Client) GetPullRequestWorkItemRefs(_a0 context.Context, _a1 git.GetPullRequestWorkItemRefsArgs) (*[]webapi.ResourceRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestWorkItemRefs") - } - var r0 *[]webapi.ResourceRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestWorkItemRefsArgs) (*[]webapi.ResourceRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestWorkItemRefsArgs) *[]webapi.ResourceRef); ok { r0 = rf(_a0, _a1) } else { @@ -2369,6 +1820,7 @@ func (_m *Client) GetPullRequestWorkItemRefs(_a0 context.Context, _a1 git.GetPul } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestWorkItemRefsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2382,15 +1834,7 @@ func (_m *Client) GetPullRequestWorkItemRefs(_a0 context.Context, _a1 git.GetPul func (_m *Client) GetPullRequests(_a0 context.Context, _a1 git.GetPullRequestsArgs) (*[]git.GitPullRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequests") - } - var r0 *[]git.GitPullRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestsArgs) (*[]git.GitPullRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestsArgs) *[]git.GitPullRequest); ok { r0 = rf(_a0, _a1) } else { @@ -2399,6 +1843,7 @@ func (_m *Client) GetPullRequests(_a0 context.Context, _a1 git.GetPullRequestsAr } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2412,15 +1857,7 @@ func (_m *Client) GetPullRequests(_a0 context.Context, _a1 git.GetPullRequestsAr func (_m *Client) GetPullRequestsByProject(_a0 context.Context, _a1 git.GetPullRequestsByProjectArgs) (*[]git.GitPullRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPullRequestsByProject") - } - var r0 *[]git.GitPullRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestsByProjectArgs) (*[]git.GitPullRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPullRequestsByProjectArgs) *[]git.GitPullRequest); ok { r0 = rf(_a0, _a1) } else { @@ -2429,6 +1866,7 @@ func (_m *Client) GetPullRequestsByProject(_a0 context.Context, _a1 git.GetPullR } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPullRequestsByProjectArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2442,15 +1880,7 @@ func (_m *Client) GetPullRequestsByProject(_a0 context.Context, _a1 git.GetPullR func (_m *Client) GetPush(_a0 context.Context, _a1 git.GetPushArgs) (*git.GitPush, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPush") - } - var r0 *git.GitPush - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPushArgs) (*git.GitPush, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPushArgs) *git.GitPush); ok { r0 = rf(_a0, _a1) } else { @@ -2459,6 +1889,7 @@ func (_m *Client) GetPush(_a0 context.Context, _a1 git.GetPushArgs) (*git.GitPus } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPushArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2472,15 +1903,7 @@ func (_m *Client) GetPush(_a0 context.Context, _a1 git.GetPushArgs) (*git.GitPus func (_m *Client) GetPushCommits(_a0 context.Context, _a1 git.GetPushCommitsArgs) (*[]git.GitCommitRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPushCommits") - } - var r0 *[]git.GitCommitRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPushCommitsArgs) (*[]git.GitCommitRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPushCommitsArgs) *[]git.GitCommitRef); ok { r0 = rf(_a0, _a1) } else { @@ -2489,6 +1912,7 @@ func (_m *Client) GetPushCommits(_a0 context.Context, _a1 git.GetPushCommitsArgs } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPushCommitsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2502,15 +1926,7 @@ func (_m *Client) GetPushCommits(_a0 context.Context, _a1 git.GetPushCommitsArgs func (_m *Client) GetPushes(_a0 context.Context, _a1 git.GetPushesArgs) (*[]git.GitPush, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetPushes") - } - var r0 *[]git.GitPush - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetPushesArgs) (*[]git.GitPush, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetPushesArgs) *[]git.GitPush); ok { r0 = rf(_a0, _a1) } else { @@ -2519,6 +1935,7 @@ func (_m *Client) GetPushes(_a0 context.Context, _a1 git.GetPushesArgs) (*[]git. } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetPushesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2532,15 +1949,7 @@ func (_m *Client) GetPushes(_a0 context.Context, _a1 git.GetPushesArgs) (*[]git. func (_m *Client) GetRecycleBinRepositories(_a0 context.Context, _a1 git.GetRecycleBinRepositoriesArgs) (*[]git.GitDeletedRepository, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetRecycleBinRepositories") - } - var r0 *[]git.GitDeletedRepository - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetRecycleBinRepositoriesArgs) (*[]git.GitDeletedRepository, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetRecycleBinRepositoriesArgs) *[]git.GitDeletedRepository); ok { r0 = rf(_a0, _a1) } else { @@ -2549,6 +1958,7 @@ func (_m *Client) GetRecycleBinRepositories(_a0 context.Context, _a1 git.GetRecy } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetRecycleBinRepositoriesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2562,15 +1972,7 @@ func (_m *Client) GetRecycleBinRepositories(_a0 context.Context, _a1 git.GetRecy func (_m *Client) GetRefFavorite(_a0 context.Context, _a1 git.GetRefFavoriteArgs) (*git.GitRefFavorite, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetRefFavorite") - } - var r0 *git.GitRefFavorite - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetRefFavoriteArgs) (*git.GitRefFavorite, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetRefFavoriteArgs) *git.GitRefFavorite); ok { r0 = rf(_a0, _a1) } else { @@ -2579,6 +1981,7 @@ func (_m *Client) GetRefFavorite(_a0 context.Context, _a1 git.GetRefFavoriteArgs } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetRefFavoriteArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2592,15 +1995,7 @@ func (_m *Client) GetRefFavorite(_a0 context.Context, _a1 git.GetRefFavoriteArgs func (_m *Client) GetRefFavorites(_a0 context.Context, _a1 git.GetRefFavoritesArgs) (*[]git.GitRefFavorite, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetRefFavorites") - } - var r0 *[]git.GitRefFavorite - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetRefFavoritesArgs) (*[]git.GitRefFavorite, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetRefFavoritesArgs) *[]git.GitRefFavorite); ok { r0 = rf(_a0, _a1) } else { @@ -2609,6 +2004,7 @@ func (_m *Client) GetRefFavorites(_a0 context.Context, _a1 git.GetRefFavoritesAr } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetRefFavoritesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2622,15 +2018,7 @@ func (_m *Client) GetRefFavorites(_a0 context.Context, _a1 git.GetRefFavoritesAr func (_m *Client) GetRefs(_a0 context.Context, _a1 git.GetRefsArgs) (*git.GetRefsResponseValue, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetRefs") - } - var r0 *git.GetRefsResponseValue - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetRefsArgs) (*git.GetRefsResponseValue, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetRefsArgs) *git.GetRefsResponseValue); ok { r0 = rf(_a0, _a1) } else { @@ -2639,6 +2027,7 @@ func (_m *Client) GetRefs(_a0 context.Context, _a1 git.GetRefsArgs) (*git.GetRef } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetRefsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2652,15 +2041,7 @@ func (_m *Client) GetRefs(_a0 context.Context, _a1 git.GetRefsArgs) (*git.GetRef func (_m *Client) GetRepositories(_a0 context.Context, _a1 git.GetRepositoriesArgs) (*[]git.GitRepository, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetRepositories") - } - var r0 *[]git.GitRepository - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetRepositoriesArgs) (*[]git.GitRepository, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetRepositoriesArgs) *[]git.GitRepository); ok { r0 = rf(_a0, _a1) } else { @@ -2669,6 +2050,7 @@ func (_m *Client) GetRepositories(_a0 context.Context, _a1 git.GetRepositoriesAr } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetRepositoriesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2682,15 +2064,7 @@ func (_m *Client) GetRepositories(_a0 context.Context, _a1 git.GetRepositoriesAr func (_m *Client) GetRepository(_a0 context.Context, _a1 git.GetRepositoryArgs) (*git.GitRepository, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetRepository") - } - var r0 *git.GitRepository - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetRepositoryArgs) (*git.GitRepository, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetRepositoryArgs) *git.GitRepository); ok { r0 = rf(_a0, _a1) } else { @@ -2699,6 +2073,7 @@ func (_m *Client) GetRepository(_a0 context.Context, _a1 git.GetRepositoryArgs) } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetRepositoryArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2712,15 +2087,7 @@ func (_m *Client) GetRepository(_a0 context.Context, _a1 git.GetRepositoryArgs) func (_m *Client) GetRepositoryWithParent(_a0 context.Context, _a1 git.GetRepositoryWithParentArgs) (*git.GitRepository, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetRepositoryWithParent") - } - var r0 *git.GitRepository - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetRepositoryWithParentArgs) (*git.GitRepository, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetRepositoryWithParentArgs) *git.GitRepository); ok { r0 = rf(_a0, _a1) } else { @@ -2729,6 +2096,7 @@ func (_m *Client) GetRepositoryWithParent(_a0 context.Context, _a1 git.GetReposi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetRepositoryWithParentArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2742,15 +2110,7 @@ func (_m *Client) GetRepositoryWithParent(_a0 context.Context, _a1 git.GetReposi func (_m *Client) GetRevert(_a0 context.Context, _a1 git.GetRevertArgs) (*git.GitRevert, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetRevert") - } - var r0 *git.GitRevert - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetRevertArgs) (*git.GitRevert, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetRevertArgs) *git.GitRevert); ok { r0 = rf(_a0, _a1) } else { @@ -2759,6 +2119,7 @@ func (_m *Client) GetRevert(_a0 context.Context, _a1 git.GetRevertArgs) (*git.Gi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetRevertArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2772,15 +2133,7 @@ func (_m *Client) GetRevert(_a0 context.Context, _a1 git.GetRevertArgs) (*git.Gi func (_m *Client) GetRevertForRefName(_a0 context.Context, _a1 git.GetRevertForRefNameArgs) (*git.GitRevert, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetRevertForRefName") - } - var r0 *git.GitRevert - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetRevertForRefNameArgs) (*git.GitRevert, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetRevertForRefNameArgs) *git.GitRevert); ok { r0 = rf(_a0, _a1) } else { @@ -2789,6 +2142,7 @@ func (_m *Client) GetRevertForRefName(_a0 context.Context, _a1 git.GetRevertForR } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetRevertForRefNameArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2802,15 +2156,7 @@ func (_m *Client) GetRevertForRefName(_a0 context.Context, _a1 git.GetRevertForR func (_m *Client) GetStatuses(_a0 context.Context, _a1 git.GetStatusesArgs) (*[]git.GitStatus, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetStatuses") - } - var r0 *[]git.GitStatus - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetStatusesArgs) (*[]git.GitStatus, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetStatusesArgs) *[]git.GitStatus); ok { r0 = rf(_a0, _a1) } else { @@ -2819,6 +2165,7 @@ func (_m *Client) GetStatuses(_a0 context.Context, _a1 git.GetStatusesArgs) (*[] } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetStatusesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2832,15 +2179,7 @@ func (_m *Client) GetStatuses(_a0 context.Context, _a1 git.GetStatusesArgs) (*[] func (_m *Client) GetSuggestions(_a0 context.Context, _a1 git.GetSuggestionsArgs) (*[]git.GitSuggestion, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetSuggestions") - } - var r0 *[]git.GitSuggestion - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetSuggestionsArgs) (*[]git.GitSuggestion, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetSuggestionsArgs) *[]git.GitSuggestion); ok { r0 = rf(_a0, _a1) } else { @@ -2849,6 +2188,7 @@ func (_m *Client) GetSuggestions(_a0 context.Context, _a1 git.GetSuggestionsArgs } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetSuggestionsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2862,15 +2202,7 @@ func (_m *Client) GetSuggestions(_a0 context.Context, _a1 git.GetSuggestionsArgs func (_m *Client) GetThreads(_a0 context.Context, _a1 git.GetThreadsArgs) (*[]git.GitPullRequestCommentThread, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetThreads") - } - var r0 *[]git.GitPullRequestCommentThread - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetThreadsArgs) (*[]git.GitPullRequestCommentThread, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetThreadsArgs) *[]git.GitPullRequestCommentThread); ok { r0 = rf(_a0, _a1) } else { @@ -2879,6 +2211,7 @@ func (_m *Client) GetThreads(_a0 context.Context, _a1 git.GetThreadsArgs) (*[]gi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetThreadsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2892,15 +2225,7 @@ func (_m *Client) GetThreads(_a0 context.Context, _a1 git.GetThreadsArgs) (*[]gi func (_m *Client) GetTree(_a0 context.Context, _a1 git.GetTreeArgs) (*git.GitTreeRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetTree") - } - var r0 *git.GitTreeRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetTreeArgs) (*git.GitTreeRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetTreeArgs) *git.GitTreeRef); ok { r0 = rf(_a0, _a1) } else { @@ -2909,6 +2234,7 @@ func (_m *Client) GetTree(_a0 context.Context, _a1 git.GetTreeArgs) (*git.GitTre } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetTreeArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2922,15 +2248,7 @@ func (_m *Client) GetTree(_a0 context.Context, _a1 git.GetTreeArgs) (*git.GitTre func (_m *Client) GetTreeZip(_a0 context.Context, _a1 git.GetTreeZipArgs) (io.ReadCloser, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for GetTreeZip") - } - var r0 io.ReadCloser - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.GetTreeZipArgs) (io.ReadCloser, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.GetTreeZipArgs) io.ReadCloser); ok { r0 = rf(_a0, _a1) } else { @@ -2939,6 +2257,7 @@ func (_m *Client) GetTreeZip(_a0 context.Context, _a1 git.GetTreeZipArgs) (io.Re } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.GetTreeZipArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2952,15 +2271,7 @@ func (_m *Client) GetTreeZip(_a0 context.Context, _a1 git.GetTreeZipArgs) (io.Re func (_m *Client) QueryImportRequests(_a0 context.Context, _a1 git.QueryImportRequestsArgs) (*[]git.GitImportRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for QueryImportRequests") - } - var r0 *[]git.GitImportRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.QueryImportRequestsArgs) (*[]git.GitImportRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.QueryImportRequestsArgs) *[]git.GitImportRequest); ok { r0 = rf(_a0, _a1) } else { @@ -2969,6 +2280,7 @@ func (_m *Client) QueryImportRequests(_a0 context.Context, _a1 git.QueryImportRe } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.QueryImportRequestsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -2982,15 +2294,7 @@ func (_m *Client) QueryImportRequests(_a0 context.Context, _a1 git.QueryImportRe func (_m *Client) RestoreRepositoryFromRecycleBin(_a0 context.Context, _a1 git.RestoreRepositoryFromRecycleBinArgs) (*git.GitRepository, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for RestoreRepositoryFromRecycleBin") - } - var r0 *git.GitRepository - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.RestoreRepositoryFromRecycleBinArgs) (*git.GitRepository, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.RestoreRepositoryFromRecycleBinArgs) *git.GitRepository); ok { r0 = rf(_a0, _a1) } else { @@ -2999,6 +2303,7 @@ func (_m *Client) RestoreRepositoryFromRecycleBin(_a0 context.Context, _a1 git.R } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.RestoreRepositoryFromRecycleBinArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -3012,10 +2317,6 @@ func (_m *Client) RestoreRepositoryFromRecycleBin(_a0 context.Context, _a1 git.R func (_m *Client) SharePullRequest(_a0 context.Context, _a1 git.SharePullRequestArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for SharePullRequest") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.SharePullRequestArgs) error); ok { r0 = rf(_a0, _a1) @@ -3030,15 +2331,7 @@ func (_m *Client) SharePullRequest(_a0 context.Context, _a1 git.SharePullRequest func (_m *Client) UpdateComment(_a0 context.Context, _a1 git.UpdateCommentArgs) (*git.Comment, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdateComment") - } - var r0 *git.Comment - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.UpdateCommentArgs) (*git.Comment, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.UpdateCommentArgs) *git.Comment); ok { r0 = rf(_a0, _a1) } else { @@ -3047,6 +2340,7 @@ func (_m *Client) UpdateComment(_a0 context.Context, _a1 git.UpdateCommentArgs) } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.UpdateCommentArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -3060,15 +2354,7 @@ func (_m *Client) UpdateComment(_a0 context.Context, _a1 git.UpdateCommentArgs) func (_m *Client) UpdateImportRequest(_a0 context.Context, _a1 git.UpdateImportRequestArgs) (*git.GitImportRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdateImportRequest") - } - var r0 *git.GitImportRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.UpdateImportRequestArgs) (*git.GitImportRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.UpdateImportRequestArgs) *git.GitImportRequest); ok { r0 = rf(_a0, _a1) } else { @@ -3077,6 +2363,7 @@ func (_m *Client) UpdateImportRequest(_a0 context.Context, _a1 git.UpdateImportR } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.UpdateImportRequestArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -3090,15 +2377,7 @@ func (_m *Client) UpdateImportRequest(_a0 context.Context, _a1 git.UpdateImportR func (_m *Client) UpdatePullRequest(_a0 context.Context, _a1 git.UpdatePullRequestArgs) (*git.GitPullRequest, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdatePullRequest") - } - var r0 *git.GitPullRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.UpdatePullRequestArgs) (*git.GitPullRequest, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.UpdatePullRequestArgs) *git.GitPullRequest); ok { r0 = rf(_a0, _a1) } else { @@ -3107,6 +2386,7 @@ func (_m *Client) UpdatePullRequest(_a0 context.Context, _a1 git.UpdatePullReque } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.UpdatePullRequestArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -3120,10 +2400,6 @@ func (_m *Client) UpdatePullRequest(_a0 context.Context, _a1 git.UpdatePullReque func (_m *Client) UpdatePullRequestIterationStatuses(_a0 context.Context, _a1 git.UpdatePullRequestIterationStatusesArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdatePullRequestIterationStatuses") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.UpdatePullRequestIterationStatusesArgs) error); ok { r0 = rf(_a0, _a1) @@ -3138,15 +2414,7 @@ func (_m *Client) UpdatePullRequestIterationStatuses(_a0 context.Context, _a1 gi func (_m *Client) UpdatePullRequestProperties(_a0 context.Context, _a1 git.UpdatePullRequestPropertiesArgs) (interface{}, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdatePullRequestProperties") - } - var r0 interface{} - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.UpdatePullRequestPropertiesArgs) (interface{}, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.UpdatePullRequestPropertiesArgs) interface{}); ok { r0 = rf(_a0, _a1) } else { @@ -3155,6 +2423,7 @@ func (_m *Client) UpdatePullRequestProperties(_a0 context.Context, _a1 git.Updat } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.UpdatePullRequestPropertiesArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -3168,10 +2437,6 @@ func (_m *Client) UpdatePullRequestProperties(_a0 context.Context, _a1 git.Updat func (_m *Client) UpdatePullRequestReviewers(_a0 context.Context, _a1 git.UpdatePullRequestReviewersArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdatePullRequestReviewers") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.UpdatePullRequestReviewersArgs) error); ok { r0 = rf(_a0, _a1) @@ -3186,10 +2451,6 @@ func (_m *Client) UpdatePullRequestReviewers(_a0 context.Context, _a1 git.Update func (_m *Client) UpdatePullRequestStatuses(_a0 context.Context, _a1 git.UpdatePullRequestStatusesArgs) error { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdatePullRequestStatuses") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context, git.UpdatePullRequestStatusesArgs) error); ok { r0 = rf(_a0, _a1) @@ -3204,15 +2465,7 @@ func (_m *Client) UpdatePullRequestStatuses(_a0 context.Context, _a1 git.UpdateP func (_m *Client) UpdateRef(_a0 context.Context, _a1 git.UpdateRefArgs) (*git.GitRef, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdateRef") - } - var r0 *git.GitRef - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.UpdateRefArgs) (*git.GitRef, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.UpdateRefArgs) *git.GitRef); ok { r0 = rf(_a0, _a1) } else { @@ -3221,6 +2474,7 @@ func (_m *Client) UpdateRef(_a0 context.Context, _a1 git.UpdateRefArgs) (*git.Gi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.UpdateRefArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -3234,15 +2488,7 @@ func (_m *Client) UpdateRef(_a0 context.Context, _a1 git.UpdateRefArgs) (*git.Gi func (_m *Client) UpdateRefs(_a0 context.Context, _a1 git.UpdateRefsArgs) (*[]git.GitRefUpdateResult, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdateRefs") - } - var r0 *[]git.GitRefUpdateResult - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.UpdateRefsArgs) (*[]git.GitRefUpdateResult, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.UpdateRefsArgs) *[]git.GitRefUpdateResult); ok { r0 = rf(_a0, _a1) } else { @@ -3251,6 +2497,7 @@ func (_m *Client) UpdateRefs(_a0 context.Context, _a1 git.UpdateRefsArgs) (*[]gi } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.UpdateRefsArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -3264,15 +2511,7 @@ func (_m *Client) UpdateRefs(_a0 context.Context, _a1 git.UpdateRefsArgs) (*[]gi func (_m *Client) UpdateRepository(_a0 context.Context, _a1 git.UpdateRepositoryArgs) (*git.GitRepository, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdateRepository") - } - var r0 *git.GitRepository - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.UpdateRepositoryArgs) (*git.GitRepository, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.UpdateRepositoryArgs) *git.GitRepository); ok { r0 = rf(_a0, _a1) } else { @@ -3281,6 +2520,7 @@ func (_m *Client) UpdateRepository(_a0 context.Context, _a1 git.UpdateRepository } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.UpdateRepositoryArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -3294,15 +2534,7 @@ func (_m *Client) UpdateRepository(_a0 context.Context, _a1 git.UpdateRepository func (_m *Client) UpdateThread(_a0 context.Context, _a1 git.UpdateThreadArgs) (*git.GitPullRequestCommentThread, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for UpdateThread") - } - var r0 *git.GitPullRequestCommentThread - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, git.UpdateThreadArgs) (*git.GitPullRequestCommentThread, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, git.UpdateThreadArgs) *git.GitPullRequestCommentThread); ok { r0 = rf(_a0, _a1) } else { @@ -3311,6 +2543,7 @@ func (_m *Client) UpdateThread(_a0 context.Context, _a1 git.UpdateThreadArgs) (* } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, git.UpdateThreadArgs) error); ok { r1 = rf(_a0, _a1) } else { @@ -3319,17 +2552,3 @@ func (_m *Client) UpdateThread(_a0 context.Context, _a1 git.UpdateThreadArgs) (* return r0, r1 } - -// NewClient creates a new instance of Client. 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 NewClient(t interface { - mock.TestingT - Cleanup(func()) -}) *Client { - mock := &Client{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/applicationset/services/scm_provider/azure_devops_test.go b/applicationset/services/scm_provider/azure_devops_test.go index d718802ad3295..219e770d71250 100644 --- a/applicationset/services/scm_provider/azure_devops_test.go +++ b/applicationset/services/scm_provider/azure_devops_test.go @@ -8,17 +8,15 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" + azureMock "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/azure_devops/git/mocks" "github.com/microsoft/azure-devops-go-api/azuredevops" azureGit "github.com/microsoft/azure-devops-go-api/azuredevops/git" - - azureMock "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/azure_devops/git/mocks" ) func s(input string) *string { - return ptr.To(input) + return pointer.String(input) } func TestAzureDevopsRepoHasPath(t *testing.T) { @@ -91,24 +89,26 @@ func TestAzureDevopsRepoHasPath(t *testing.T) { hasPath, err := provider.RepoHasPath(ctx, repo, path) if testCase.clientError != nil { - require.ErrorContains(t, err, testCase.clientError.Error()) + assert.ErrorContains(t, err, testCase.clientError.Error()) gitClientMock.AssertNotCalled(t, "GetItem", ctx, azureGit.GetItemArgs{Project: &teamProject, Path: &path, VersionDescriptor: &azureGit.GitVersionDescriptor{Version: &branchName}, RepositoryId: repoId}) return } if testCase.returnError { - require.ErrorContains(t, err, testCase.errorMessage) + assert.ErrorContains(t, err, testCase.errorMessage) } assert.Equal(t, testCase.pathFound, hasPath) gitClientMock.AssertCalled(t, "GetItem", ctx, azureGit.GetItemArgs{Project: &teamProject, Path: &path, VersionDescriptor: &azureGit.GitVersionDescriptor{Version: &branchName}, RepositoryId: repoId}) + }) } } func TestGetDefaultBranchOnDisabledRepo(t *testing.T) { + organization := "myorg" teamProject := "myorg_project" repoName := "myorg_project_repo" @@ -155,9 +155,9 @@ func TestGetDefaultBranchOnDisabledRepo(t *testing.T) { branches, err := provider.GetBranches(ctx, repo) if testCase.shouldReturnError { - require.Error(t, err) + assert.Error(t, err) } else { - require.NoError(t, err) + assert.NoError(t, err) } assert.Empty(t, branches) @@ -168,6 +168,7 @@ func TestGetDefaultBranchOnDisabledRepo(t *testing.T) { } func TestGetAllBranchesOnDisabledRepo(t *testing.T) { + organization := "myorg" teamProject := "myorg_project" repoName := "myorg_project_repo" @@ -214,9 +215,9 @@ func TestGetAllBranchesOnDisabledRepo(t *testing.T) { branches, err := provider.GetBranches(ctx, repo) if testCase.shouldReturnError { - require.Error(t, err) + assert.Error(t, err) } else { - require.NoError(t, err) + assert.NoError(t, err) } assert.Empty(t, branches) @@ -227,7 +228,9 @@ func TestGetAllBranchesOnDisabledRepo(t *testing.T) { } func TestAzureDevOpsGetDefaultBranchStripsRefsName(t *testing.T) { + t.Run("Get branches only default branch removes characters before querying azure devops", func(t *testing.T) { + organization := "myorg" teamProject := "myorg_project" repoName := "myorg_project_repo" @@ -250,7 +253,7 @@ func TestAzureDevOpsGetDefaultBranchStripsRefsName(t *testing.T) { provider := AzureDevOpsProvider{organization: organization, teamProject: teamProject, clientFactory: clientFactoryMock, allBranches: false} branches, err := provider.GetBranches(ctx, repo) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, branches, 1) assert.Equal(t, strippedBranchName, branches[0].Branch) @@ -307,7 +310,7 @@ func TestAzureDevOpsGetBranchesDefultBranchOnly(t *testing.T) { branches, err := provider.GetBranches(ctx, repo) if testCase.clientError != nil { - require.ErrorContains(t, err, testCase.clientError.Error()) + assert.ErrorContains(t, err, testCase.clientError.Error()) gitClientMock.AssertNotCalled(t, "GetBranch", ctx, azureGit.GetBranchArgs{RepositoryId: &repoName, Project: &teamProject, Name: &defaultBranch}) return @@ -315,7 +318,7 @@ func TestAzureDevOpsGetBranchesDefultBranchOnly(t *testing.T) { if testCase.getBranchesApiError != nil { assert.Empty(t, branches) - require.ErrorContains(t, err, testCase.getBranchesApiError.Error()) + assert.ErrorContains(t, err, testCase.getBranchesApiError.Error()) } else { if testCase.expectedBranch != nil { assert.NotEmpty(t, branches) @@ -391,20 +394,21 @@ func TestAzureDevopsGetBranches(t *testing.T) { branches, err := provider.GetBranches(ctx, repo) if testCase.expectedProcessingErrorMsg != "" { - require.ErrorContains(t, err, testCase.expectedProcessingErrorMsg) + assert.ErrorContains(t, err, testCase.expectedProcessingErrorMsg) assert.Nil(t, branches) return } if testCase.clientError != nil { - require.ErrorContains(t, err, testCase.clientError.Error()) + assert.ErrorContains(t, err, testCase.clientError.Error()) gitClientMock.AssertNotCalled(t, "GetBranches", ctx, azureGit.GetBranchesArgs{RepositoryId: &repoName, Project: &teamProject}) return + } if testCase.getBranchesApiError != nil { assert.Empty(t, branches) - require.ErrorContains(t, err, testCase.getBranchesApiError.Error()) + assert.ErrorContains(t, err, testCase.getBranchesApiError.Error()) } else { if len(*testCase.expectedBranches) > 0 { assert.NotEmpty(t, branches) @@ -468,14 +472,14 @@ func TestGetAzureDevopsRepositories(t *testing.T) { {Name: s("missing_default_branch"), RemoteUrl: s("https://remoteurl.u"), Id: repoId}, {DefaultBranch: s("missing_name"), RemoteUrl: s("https://remoteurl.u"), Id: repoId}, {Name: s("missing_remote_url"), DefaultBranch: s("main"), Id: repoId}, - {Name: s("missing_id"), DefaultBranch: s("main"), RemoteUrl: s("https://remoteurl.u")}, - }, + {Name: s("missing_id"), DefaultBranch: s("main"), RemoteUrl: s("https://remoteurl.u")}}, expectedNumberOfRepos: 1, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { + gitClientMock := azureMock.Client{} gitClientMock.On("GetRepositories", ctx, azureGit.GetRepositoriesArgs{Project: s(teamProject)}).Return(&testCase.repositories, testCase.getRepositoriesError) @@ -487,7 +491,7 @@ func TestGetAzureDevopsRepositories(t *testing.T) { repositories, err := provider.ListRepos(ctx, "https") if testCase.getRepositoriesError != nil { - require.Error(t, err, "Expected an error from test case %v", testCase.name) + assert.Error(t, err, "Expected an error from test case %v", testCase.name) } if testCase.expectedNumberOfRepos == 0 { diff --git a/applicationset/services/scm_provider/bitbucket_cloud.go b/applicationset/services/scm_provider/bitbucket_cloud.go index 4468aee687f7d..3c453f6b9c17d 100644 --- a/applicationset/services/scm_provider/bitbucket_cloud.go +++ b/applicationset/services/scm_provider/bitbucket_cloud.go @@ -46,12 +46,13 @@ func (c *ExtendedClient) GetContents(repo *Repository, path string) (bool, error return true, nil } - return false, fmt.Errorf("%s", resp.Status) + return false, fmt.Errorf(resp.Status) } var _ SCMProviderService = &BitBucketCloudProvider{} func NewBitBucketCloudProvider(ctx context.Context, owner string, user string, password string, allBranches bool) (*BitBucketCloudProvider, error) { + client := &ExtendedClient{ bitbucket.NewBasicAuth(user, password), user, @@ -65,13 +66,13 @@ func (g *BitBucketCloudProvider) GetBranches(ctx context.Context, repo *Reposito repos := []*Repository{} branches, err := g.listBranches(repo) if err != nil { - return nil, fmt.Errorf("error listing branches for %s/%s: %w", repo.Organization, repo.Repository, err) + return nil, fmt.Errorf("error listing branches for %s/%s: %v", repo.Organization, repo.Repository, err) } for _, branch := range branches { hash, ok := branch.Target["hash"].(string) if !ok { - return nil, fmt.Errorf("error getting SHA for branch for %s/%s/%s: %w", g.owner, repo.Repository, branch.Name, err) + return nil, fmt.Errorf("error getting SHA for branch for %s/%s/%s: %v", g.owner, repo.Repository, branch.Name, err) } repos = append(repos, &Repository{ Organization: repo.Organization, @@ -97,12 +98,12 @@ func (g *BitBucketCloudProvider) ListRepos(ctx context.Context, cloneProtocol st repos := []*Repository{} accountReposResp, err := g.client.Repositories.ListForAccount(opt) if err != nil { - return nil, fmt.Errorf("error listing repositories for %s: %w", g.owner, err) + return nil, fmt.Errorf("error listing repositories for %s: %v", g.owner, err) } for _, bitBucketRepo := range accountReposResp.Items { cloneUrl, err := findCloneURL(cloneProtocol, &bitBucketRepo) if err != nil { - return nil, fmt.Errorf("error fetching clone url for repo %s: %w", bitBucketRepo.Slug, err) + return nil, fmt.Errorf("error fetching clone url for repo %s: %v", bitBucketRepo.Slug, err) } repos = append(repos, &Repository{ Organization: g.owner, @@ -150,9 +151,11 @@ func (g *BitBucketCloudProvider) listBranches(repo *Repository) ([]bitbucket.Rep return nil, err } return branches.Branches, nil + } func findCloneURL(cloneProtocol string, repo *bitbucket.Repository) (*string, error) { + cloneLinks, ok := repo.Links["clone"].([]interface{}) if !ok { return nil, fmt.Errorf("unknown type returned from repo links") diff --git a/applicationset/services/scm_provider/bitbucket_cloud_test.go b/applicationset/services/scm_provider/bitbucket_cloud_test.go index d4127dbbf4002..fca03e1693ade 100644 --- a/applicationset/services/scm_provider/bitbucket_cloud_test.go +++ b/applicationset/services/scm_provider/bitbucket_cloud_test.go @@ -8,7 +8,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -19,7 +18,7 @@ func TestBitbucketHasRepo(t *testing.T) { res.WriteHeader(http.StatusNotFound) _, err := res.Write([]byte("")) if err != nil { - require.NoError(t, fmt.Errorf("Error in mock response %w", err)) + assert.NoError(t, fmt.Errorf("Error in mock response %v", err)) } } if req.URL.Path == "/repositories/test-owner/testmike/src/dc1edb6c7d650d8ba67719ddf7b662ad8f8fb798/.gitignore" { @@ -56,7 +55,7 @@ func TestBitbucketHasRepo(t *testing.T) { "size": 624 }`)) if err != nil { - require.NoError(t, fmt.Errorf("Error in mock response %w", err)) + assert.NoError(t, fmt.Errorf("Error in mock response %v", err)) } } })) @@ -96,7 +95,7 @@ func TestBitbucketHasRepo(t *testing.T) { } hasPath, err := provider.RepoHasPath(context.Background(), repo, c.path) if err != nil { - require.Error(t, fmt.Errorf("Error in test %w", err)) + assert.Error(t, fmt.Errorf("Error in test %v", err)) } if c.status != http.StatusOK { assert.False(t, hasPath) @@ -209,7 +208,7 @@ func TestBitbucketListRepos(t *testing.T) { "size": 1 }`)) if err != nil { - require.NoError(t, fmt.Errorf("Error in mock response %w", err)) + assert.NoError(t, fmt.Errorf("Error in mock response %v", err)) } } if req.URL.Path == "/repositories/test-owner/testmike/refs/branches/main" { @@ -304,7 +303,7 @@ func TestBitbucketListRepos(t *testing.T) { } }`)) if err != nil { - require.NoError(t, fmt.Errorf("Error in mock response %w", err)) + assert.NoError(t, fmt.Errorf("Error in mock response %v", err)) } } if req.URL.Path == "/repositories/test-owner" { @@ -443,7 +442,7 @@ func TestBitbucketListRepos(t *testing.T) { "size": 1 }`)) if err != nil { - require.NoError(t, fmt.Errorf("Error in mock response %w", err)) + assert.NoError(t, fmt.Errorf("Error in mock response %v", err)) } } })) @@ -490,9 +489,9 @@ func TestBitbucketListRepos(t *testing.T) { provider, _ := NewBitBucketCloudProvider(context.Background(), c.owner, "user", "password", c.allBranches) rawRepos, err := ListRepos(context.Background(), provider, c.filters, c.proto) if c.hasError { - require.Error(t, err) + assert.Error(t, err) } else { - require.NoError(t, err) + assert.NoError(t, err) repos := []*Repository{} branches := []string{} for _, r := range rawRepos { diff --git a/applicationset/services/scm_provider/bitbucket_server.go b/applicationset/services/scm_provider/bitbucket_server.go index 4f723a547059f..9e46569512156 100644 --- a/applicationset/services/scm_provider/bitbucket_server.go +++ b/applicationset/services/scm_provider/bitbucket_server.go @@ -2,15 +2,12 @@ package scm_provider import ( "context" - "errors" "fmt" "io" - "net/http" + "github.com/argoproj/argo-cd/v2/applicationset/utils" bitbucketv1 "github.com/gfleury/go-bitbucket-v1" log "github.com/sirupsen/logrus" - - "github.com/argoproj/argo-cd/v2/applicationset/utils" ) type BitbucketServerProvider struct { @@ -21,7 +18,7 @@ type BitbucketServerProvider struct { var _ SCMProviderService = &BitbucketServerProvider{} -func NewBitbucketServerProviderBasicAuth(ctx context.Context, username, password, url, projectKey string, allBranches bool, scmRootCAPath string, insecure bool, caCerts []byte) (*BitbucketServerProvider, error) { +func NewBitbucketServerProviderBasicAuth(ctx context.Context, username, password, url, projectKey string, allBranches bool) (*BitbucketServerProvider, error) { bitbucketConfig := bitbucketv1.NewConfiguration(url) // Avoid the XSRF check bitbucketConfig.AddDefaultHeader("x-atlassian-token", "no-check") @@ -31,29 +28,15 @@ func NewBitbucketServerProviderBasicAuth(ctx context.Context, username, password UserName: username, Password: password, }) - return newBitbucketServerProvider(ctx, bitbucketConfig, projectKey, allBranches, scmRootCAPath, insecure, caCerts) -} - -func NewBitbucketServerProviderBearerToken(ctx context.Context, bearerToken, url, projectKey string, allBranches bool, scmRootCAPath string, insecure bool, caCerts []byte) (*BitbucketServerProvider, error) { - bitbucketConfig := bitbucketv1.NewConfiguration(url) - // Avoid the XSRF check - bitbucketConfig.AddDefaultHeader("x-atlassian-token", "no-check") - bitbucketConfig.AddDefaultHeader("x-requested-with", "XMLHttpRequest") - - ctx = context.WithValue(ctx, bitbucketv1.ContextAccessToken, bearerToken) - return newBitbucketServerProvider(ctx, bitbucketConfig, projectKey, allBranches, scmRootCAPath, insecure, caCerts) + return newBitbucketServerProvider(ctx, bitbucketConfig, projectKey, allBranches) } -func NewBitbucketServerProviderNoAuth(ctx context.Context, url, projectKey string, allBranches bool, scmRootCAPath string, insecure bool, caCerts []byte) (*BitbucketServerProvider, error) { - return newBitbucketServerProvider(ctx, bitbucketv1.NewConfiguration(url), projectKey, allBranches, scmRootCAPath, insecure, caCerts) +func NewBitbucketServerProviderNoAuth(ctx context.Context, url, projectKey string, allBranches bool) (*BitbucketServerProvider, error) { + return newBitbucketServerProvider(ctx, bitbucketv1.NewConfiguration(url), projectKey, allBranches) } -func newBitbucketServerProvider(ctx context.Context, bitbucketConfig *bitbucketv1.Configuration, projectKey string, allBranches bool, scmRootCAPath string, insecure bool, caCerts []byte) (*BitbucketServerProvider, error) { +func newBitbucketServerProvider(ctx context.Context, bitbucketConfig *bitbucketv1.Configuration, projectKey string, allBranches bool) (*BitbucketServerProvider, error) { bitbucketConfig.BasePath = utils.NormalizeBitbucketBasePath(bitbucketConfig.BasePath) - tlsConfig := utils.GetTlsConfig(scmRootCAPath, insecure, caCerts) - bitbucketConfig.HTTPClient = &http.Client{Transport: &http.Transport{ - TLSClientConfig: tlsConfig, - }} bitbucketClient := bitbucketv1.NewAPIClient(ctx, bitbucketConfig) return &BitbucketServerProvider{ @@ -71,12 +54,12 @@ func (b *BitbucketServerProvider) ListRepos(_ context.Context, cloneProtocol str for { response, err := b.client.DefaultApi.GetRepositoriesWithOptions(b.projectKey, paged) if err != nil { - return nil, fmt.Errorf("error listing repositories for %s: %w", b.projectKey, err) + return nil, fmt.Errorf("error listing repositories for %s: %v", b.projectKey, err) } repositories, err := bitbucketv1.GetRepositoriesResponse(response) if err != nil { log.Errorf("error parsing repositories response '%v'", response.Values) - return nil, fmt.Errorf("error parsing repositories response %s: %w", b.projectKey, err) + return nil, fmt.Errorf("error parsing repositories response %s: %v", b.projectKey, err) } for _, bitbucketRepo := range repositories { var url string @@ -143,7 +126,7 @@ func (b *BitbucketServerProvider) GetBranches(_ context.Context, repo *Repositor repos := []*Repository{} branches, err := b.listBranches(repo) if err != nil { - return nil, fmt.Errorf("error listing branches for %s/%s: %w", repo.Organization, repo.Repository, err) + return nil, fmt.Errorf("error listing branches for %s/%s: %v", repo.Organization, repo.Repository, err) } for _, branch := range branches { @@ -180,12 +163,12 @@ func (b *BitbucketServerProvider) listBranches(repo *Repository) ([]bitbucketv1. for { response, err := b.client.DefaultApi.GetBranches(repo.Organization, repo.Repository, paged) if err != nil { - return nil, fmt.Errorf("error listing branches for %s/%s: %w", repo.Organization, repo.Repository, err) + return nil, fmt.Errorf("error listing branches for %s/%s: %v", repo.Organization, repo.Repository, err) } bitbucketBranches, err := bitbucketv1.GetBranchesResponse(response) if err != nil { log.Errorf("error parsing branches response '%v'", response.Values) - return nil, fmt.Errorf("error parsing branches response for %s/%s: %w", repo.Organization, repo.Repository, err) + return nil, fmt.Errorf("error parsing branches response for %s/%s: %v", repo.Organization, repo.Repository, err) } branches = append(branches, bitbucketBranches...) @@ -203,7 +186,7 @@ func (b *BitbucketServerProvider) getDefaultBranch(org string, repo string) (*bi response, err := b.client.DefaultApi.GetDefaultBranch(org, repo) // The API will return 404 if a default branch is set but doesn't exist. In case the repo is empty and default branch is unset, // we will get an EOF and a nil response. - if (response != nil && response.StatusCode == 404) || (response == nil && err != nil && errors.Is(err, io.EOF)) { + 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 1d399f8751cbc..d403bd72caaac 100644 --- a/applicationset/services/scm_provider/bitbucket_server_test.go +++ b/applicationset/services/scm_provider/bitbucket_server_test.go @@ -2,15 +2,12 @@ package scm_provider import ( "context" - "crypto/x509" - "encoding/pem" "io" "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func defaultHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { @@ -82,8 +79,8 @@ func defaultHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { } func verifyDefaultRepo(t *testing.T, err error, repos []*Repository) { - require.NoError(t, err) - assert.Len(t, repos, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(repos)) assert.Equal(t, Repository{ Organization: "PROJECT", Repository: "REPO", @@ -101,8 +98,8 @@ func TestListReposNoAuth(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) + assert.NoError(t, err) repos, err := provider.ListRepos(context.Background(), "ssh") verifyDefaultRepo(t, err, repos) } @@ -193,11 +190,11 @@ func TestListReposPagination(t *testing.T) { } })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) + assert.NoError(t, err) repos, err := provider.ListRepos(context.Background(), "ssh") - require.NoError(t, err) - assert.Len(t, repos, 2) + assert.NoError(t, err) + assert.Equal(t, 2, len(repos)) assert.Equal(t, Repository{ Organization: "PROJECT", Repository: "REPO", @@ -270,8 +267,8 @@ func TestGetBranchesBranchPagination(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) + assert.NoError(t, err) repos, err := provider.GetBranches(context.Background(), &Repository{ Organization: "PROJECT", Repository: "REPO", @@ -279,8 +276,8 @@ func TestGetBranchesBranchPagination(t *testing.T) { Labels: []string{}, RepositoryId: 1, }) - require.NoError(t, err) - assert.Len(t, repos, 2) + assert.NoError(t, err) + assert.Equal(t, 2, len(repos)) assert.Equal(t, Repository{ Organization: "PROJECT", Repository: "REPO", @@ -323,8 +320,8 @@ func TestGetBranchesDefaultOnly(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) + assert.NoError(t, err) repos, err := provider.GetBranches(context.Background(), &Repository{ Organization: "PROJECT", Repository: "REPO", @@ -332,8 +329,8 @@ func TestGetBranchesDefaultOnly(t *testing.T) { Labels: []string{}, RepositoryId: 1, }) - require.NoError(t, err) - assert.Len(t, repos, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(repos)) assert.Equal(t, Repository{ Organization: "PROJECT", Repository: "REPO", @@ -355,8 +352,8 @@ func TestGetBranchesMissingDefault(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) + assert.NoError(t, err) repos, err := provider.GetBranches(context.Background(), &Repository{ Organization: "PROJECT", Repository: "REPO", @@ -364,7 +361,7 @@ func TestGetBranchesMissingDefault(t *testing.T) { Labels: []string{}, RepositoryId: 1, }) - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, repos) } @@ -377,8 +374,8 @@ func TestGetBranchesEmptyRepo(t *testing.T) { } })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) + assert.NoError(t, err) repos, err := provider.GetBranches(context.Background(), &Repository{ Organization: "PROJECT", Repository: "REPO", @@ -387,7 +384,7 @@ func TestGetBranchesEmptyRepo(t *testing.T) { RepositoryId: 1, }) assert.Empty(t, repos) - require.NoError(t, err) + assert.NoError(t, err) } func TestGetBranchesErrorDefaultBranch(t *testing.T) { @@ -400,8 +397,8 @@ func TestGetBranchesErrorDefaultBranch(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) + assert.NoError(t, err) _, err = provider.GetBranches(context.Background(), &Repository{ Organization: "PROJECT", Repository: "REPO", @@ -409,74 +406,7 @@ func TestGetBranchesErrorDefaultBranch(t *testing.T) { Labels: []string{}, RepositoryId: 1, }) - require.Error(t, err) -} - -func TestListReposTLS(t *testing.T) { - tests := []struct { - name string - tlsInsecure bool - passCerts bool - requireErr bool - }{ - { - name: "TLS Insecure: true, No Certs", - tlsInsecure: true, - passCerts: false, - requireErr: false, - }, - { - name: "TLS Insecure: true, With Certs", - tlsInsecure: true, - passCerts: true, - requireErr: false, - }, - { - name: "TLS Insecure: false, With Certs", - tlsInsecure: false, - passCerts: true, - requireErr: false, - }, - { - name: "TLS Insecure: false, No Certs", - tlsInsecure: false, - passCerts: false, - requireErr: true, - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defaultHandler(t)(w, r) - })) - defer ts.Close() - - var certs []byte - if test.passCerts == true { - for _, cert := range ts.TLS.Certificates { - for _, c := range cert.Certificate { - parsedCert, err := x509.ParseCertificate(c) - require.NoError(t, err, "Failed to parse certificate") - certs = append(certs, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: parsedCert.Raw, - })...) - } - } - } - - provider, err := NewBitbucketServerProviderBasicAuth(context.Background(), "user", "password", ts.URL, "PROJECT", true, "", test.tlsInsecure, certs) - require.NoError(t, err) - _, err = provider.ListRepos(context.Background(), "ssh") - if test.requireErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } + assert.Error(t, err) } func TestListReposBasicAuth(t *testing.T) { @@ -486,21 +416,8 @@ func TestListReposBasicAuth(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderBasicAuth(context.Background(), "user", "password", ts.URL, "PROJECT", true, "", false, nil) - require.NoError(t, err) - repos, err := provider.ListRepos(context.Background(), "ssh") - verifyDefaultRepo(t, err, repos) -} - -func TestListReposBearerAuth(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "Bearer tolkien", r.Header.Get("Authorization")) - assert.Equal(t, "no-check", r.Header.Get("X-Atlassian-Token")) - defaultHandler(t)(w, r) - })) - defer ts.Close() - provider, err := NewBitbucketServerProviderBearerToken(context.Background(), "tolkien", ts.URL, "PROJECT", true, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderBasicAuth(context.Background(), "user", "password", ts.URL, "PROJECT", true) + assert.NoError(t, err) repos, err := provider.ListRepos(context.Background(), "ssh") verifyDefaultRepo(t, err, repos) } @@ -526,11 +443,11 @@ func TestListReposDefaultBranch(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) + assert.NoError(t, err) repos, err := provider.ListRepos(context.Background(), "ssh") - require.NoError(t, err) - assert.Len(t, repos, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(repos)) assert.Equal(t, Repository{ Organization: "PROJECT", Repository: "REPO", @@ -552,10 +469,10 @@ func TestListReposMissingDefaultBranch(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) + assert.NoError(t, err) repos, err := provider.ListRepos(context.Background(), "ssh") - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, repos) } @@ -569,10 +486,10 @@ func TestListReposErrorDefaultBranch(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) + assert.NoError(t, err) _, err = provider.ListRepos(context.Background(), "ssh") - require.Error(t, err) + assert.Error(t, err) } func TestListReposCloneProtocol(t *testing.T) { @@ -581,11 +498,11 @@ func TestListReposCloneProtocol(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) + assert.NoError(t, err) repos, err := provider.ListRepos(context.Background(), "https") - require.NoError(t, err) - assert.Len(t, repos, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(repos)) assert.Equal(t, Repository{ Organization: "PROJECT", Repository: "REPO", @@ -603,10 +520,10 @@ func TestListReposUnknownProtocol(t *testing.T) { defaultHandler(t)(w, r) })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) + assert.NoError(t, err) _, errProtocol := provider.ListRepos(context.Background(), "http") - require.Error(t, errProtocol) + assert.NotNil(t, errProtocol) } func TestBitbucketServerHasPath(t *testing.T) { @@ -641,37 +558,37 @@ func TestBitbucketServerHasPath(t *testing.T) { } })) defer ts.Close() - provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true, "", false, nil) - require.NoError(t, err) + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) + assert.NoError(t, err) repo := &Repository{ Organization: "PROJECT", Repository: "REPO", Branch: "main", } ok, err := provider.RepoHasPath(context.Background(), repo, "pkg") - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, ok) ok, err = provider.RepoHasPath(context.Background(), repo, "pkg/") - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, ok) ok, err = provider.RepoHasPath(context.Background(), repo, "anotherpkg/file.txt") - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, ok) ok, err = provider.RepoHasPath(context.Background(), repo, "anotherpkg/missing.txt") - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, ok) ok, err = provider.RepoHasPath(context.Background(), repo, "notathing") - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, ok) ok, err = provider.RepoHasPath(context.Background(), repo, "return-redirect") - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, ok) _, err = provider.RepoHasPath(context.Background(), repo, "unauthorized-response") - require.Error(t, err) + assert.Error(t, err) } diff --git a/applicationset/services/scm_provider/gitea_test.go b/applicationset/services/scm_provider/gitea_test.go index 231913761014b..3d17e3175c4f8 100644 --- a/applicationset/services/scm_provider/gitea_test.go +++ b/applicationset/services/scm_provider/gitea_test.go @@ -258,7 +258,6 @@ func giteaMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { } } } - func TestGiteaListRepos(t *testing.T) { cases := []struct { name, proto, url string @@ -306,9 +305,9 @@ func TestGiteaListRepos(t *testing.T) { provider, _ := NewGiteaProvider(context.Background(), "test-argocd", "", ts.URL, c.allBranches, false) rawRepos, err := ListRepos(context.Background(), provider, c.filters, c.proto) if c.hasError { - require.Error(t, err) + assert.NotNil(t, err) } else { - require.NoError(t, err) + assert.Nil(t, err) // Just check that this one project shows up. Not a great test but better thing nothing? repos := []*Repository{} branches := []string{} @@ -342,19 +341,19 @@ func TestGiteaHasPath(t *testing.T) { t.Run("file exists", func(t *testing.T) { ok, err := host.RepoHasPath(context.Background(), repo, "README.md") - require.NoError(t, err) + assert.Nil(t, err) assert.True(t, ok) }) t.Run("directory exists", func(t *testing.T) { ok, err := host.RepoHasPath(context.Background(), repo, "gitea") - require.NoError(t, err) + assert.Nil(t, err) assert.True(t, ok) }) t.Run("does not exists", func(t *testing.T) { ok, err := host.RepoHasPath(context.Background(), repo, "notathing") - require.NoError(t, err) + assert.Nil(t, err) assert.False(t, ok) }) } diff --git a/applicationset/services/scm_provider/github.go b/applicationset/services/scm_provider/github.go index 9d7457c47b990..1a6edae5837e9 100644 --- a/applicationset/services/scm_provider/github.go +++ b/applicationset/services/scm_provider/github.go @@ -2,11 +2,12 @@ package scm_provider import ( "context" + "errors" "fmt" "net/http" "os" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v35/github" "golang.org/x/oauth2" ) @@ -35,7 +36,7 @@ func NewGithubProvider(ctx context.Context, organization string, token string, u client = github.NewClient(httpClient) } else { var err error - client, err = github.NewClient(httpClient).WithEnterpriseURLs(url, url) + client, err = github.NewEnterpriseClient(url, url, httpClient) if err != nil { return nil, err } @@ -119,11 +120,14 @@ func (g *GithubProvider) RepoHasPath(ctx context.Context, repo *Repository, path func (g *GithubProvider) listBranches(ctx context.Context, repo *Repository) ([]github.Branch, error) { // If we don't specifically want to query for all branches, just use the default branch and call it a day. if !g.allBranches { - defaultBranch, resp, err := g.client.Repositories.GetBranch(ctx, repo.Organization, repo.Repository, repo.Branch, 0) + defaultBranch, _, err := g.client.Repositories.GetBranch(ctx, repo.Organization, repo.Repository, repo.Branch) if err != nil { - if resp.StatusCode == http.StatusNotFound { - // Default branch doesn't exist, so the repo is empty. - return []github.Branch{}, nil + var githubErrorResponse *github.ErrorResponse + if errors.As(err, &githubErrorResponse) { + if githubErrorResponse.Response.StatusCode == http.StatusNotFound { + // Default branch doesn't exist, so the repo is empty. + return []github.Branch{}, nil + } } return nil, err } diff --git a/applicationset/services/scm_provider/github_test.go b/applicationset/services/scm_provider/github_test.go index 03b59c801721a..d413250f03126 100644 --- a/applicationset/services/scm_provider/github_test.go +++ b/applicationset/services/scm_provider/github_test.go @@ -8,7 +8,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -245,9 +244,9 @@ func TestGithubListRepos(t *testing.T) { provider, _ := NewGithubProvider(context.Background(), "argoproj", "", ts.URL, c.allBranches) rawRepos, err := ListRepos(context.Background(), provider, c.filters, c.proto) if c.hasError { - require.Error(t, err) + assert.Error(t, err) } else { - require.NoError(t, err) + assert.NoError(t, err) // Just check that this one project shows up. Not a great test but better thing nothing? repos := []*Repository{} branches := []string{} @@ -279,11 +278,11 @@ func TestGithubHasPath(t *testing.T) { Branch: "master", } ok, err := host.RepoHasPath(context.Background(), repo, "pkg/") - require.NoError(t, err) + assert.Nil(t, err) assert.True(t, ok) ok, err = host.RepoHasPath(context.Background(), repo, "notathing/") - require.NoError(t, err) + assert.Nil(t, err) assert.False(t, ok) } @@ -300,26 +299,26 @@ func TestGithubGetBranches(t *testing.T) { } repos, err := host.GetBranches(context.Background(), repo) if err != nil { - require.NoError(t, err) + assert.NoError(t, err) } else { - assert.Equal(t, "master", repos[0].Branch) + assert.Equal(t, repos[0].Branch, "master") } - // Branch Doesn't exists instead of error will return no error + //Branch Doesn't exists instead of error will return no error repo2 := &Repository{ Organization: "argoproj", Repository: "applicationset", Branch: "main", } _, err = host.GetBranches(context.Background(), repo2) - require.NoError(t, err) + assert.NoError(t, err) // Get all branches host.allBranches = true repos, err = host.GetBranches(context.Background(), repo) if err != nil { - require.NoError(t, err) + assert.NoError(t, err) } else { // considering master branch to exist. - assert.Len(t, repos, 1) + assert.Equal(t, len(repos), 1) } } diff --git a/applicationset/services/scm_provider/gitlab.go b/applicationset/services/scm_provider/gitlab.go index 0acc1898bf382..ca174de540887 100644 --- a/applicationset/services/scm_provider/gitlab.go +++ b/applicationset/services/scm_provider/gitlab.go @@ -7,10 +7,9 @@ import ( "os" pathpkg "path" + "github.com/argoproj/argo-cd/v2/applicationset/utils" "github.com/hashicorp/go-retryablehttp" "github.com/xanzy/go-gitlab" - - "github.com/argoproj/argo-cd/v2/applicationset/utils" ) type GitlabProvider struct { @@ -24,7 +23,7 @@ type GitlabProvider struct { var _ SCMProviderService = &GitlabProvider{} -func NewGitlabProvider(ctx context.Context, organization string, token string, url string, allBranches, includeSubgroups, includeSharedProjects, insecure bool, scmRootCAPath, topic string, caCerts []byte) (*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") @@ -32,7 +31,7 @@ func NewGitlabProvider(ctx context.Context, organization string, token string, u var client *gitlab.Client tr := http.DefaultTransport.(*http.Transport).Clone() - tr.TLSClientConfig = utils.GetTlsConfig(scmRootCAPath, insecure, caCerts) + tr.TLSClientConfig = utils.GetTlsConfig(scmRootCAPath, insecure) retryClient := retryablehttp.NewClient() retryClient.HTTPClient.Transport = tr @@ -58,7 +57,7 @@ func (g *GitlabProvider) GetBranches(ctx context.Context, repo *Repository) ([]* repos := []*Repository{} branches, err := g.listBranches(ctx, repo) if err != nil { - return nil, fmt.Errorf("error listing branches for %s/%s: %w", repo.Organization, repo.Repository, err) + return nil, fmt.Errorf("error listing branches for %s/%s: %v", repo.Organization, repo.Repository, err) } for _, branch := range branches { @@ -87,7 +86,7 @@ func (g *GitlabProvider) ListRepos(ctx context.Context, cloneProtocol string) ([ for { gitlabRepos, resp, err := g.client.Groups.ListGroupProjects(g.organization, opt) if err != nil { - return nil, fmt.Errorf("error listing projects for %s: %w", g.organization, err) + return nil, fmt.Errorf("error listing projects for %s: %v", g.organization, err) } for _, gitlabRepo := range gitlabRepos { var url string diff --git a/applicationset/services/scm_provider/gitlab_test.go b/applicationset/services/scm_provider/gitlab_test.go index 12f2b8b377a2a..b93616fa8367f 100644 --- a/applicationset/services/scm_provider/gitlab_test.go +++ b/applicationset/services/scm_provider/gitlab_test.go @@ -2,8 +2,6 @@ package scm_provider import ( "context" - "crypto/x509" - "encoding/pem" "fmt" "io" "net/http" @@ -11,7 +9,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -1049,7 +1046,6 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { } } } - func TestGitlabListRepos(t *testing.T) { cases := []struct { name, proto, url, topic string @@ -1123,12 +1119,12 @@ func TestGitlabListRepos(t *testing.T) { })) 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, c.includeSharedProjects, c.insecure, "", c.topic, nil) + 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 { - require.Error(t, err) + assert.NotNil(t, err) } else { - require.NoError(t, err) + assert.Nil(t, err) // Just check that this one project shows up. Not a great test but better than nothing? repos := []*Repository{} uniqueRepos := map[string]int{} @@ -1147,11 +1143,11 @@ func TestGitlabListRepos(t *testing.T) { } // In case of listing subgroups, validate the number of returned projects if c.includeSubgroups || c.includeSharedProjects { - assert.Len(t, uniqueRepos, 2) + assert.Equal(t, 2, len(uniqueRepos)) } // In case we filter on the topic, ensure we got only one repo returned if c.topic != "" { - assert.Len(t, uniqueRepos, 1) + assert.Equal(t, 1, len(uniqueRepos)) } } }) @@ -1162,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, true, false, "", "", nil) + host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, true, false, "", "") repo := &Repository{ Organization: "test-argocd-proton", Repository: "argocd", @@ -1198,7 +1194,7 @@ func TestGitlabHasPath(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { ok, err := host.RepoHasPath(context.Background(), repo, c.path) - require.NoError(t, err) + assert.Nil(t, err) assert.Equal(t, c.exists, ok) }) } @@ -1208,7 +1204,7 @@ 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, "", "", nil) + host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, true, false, "", "") repo := &Repository{ RepositoryId: 27084533, @@ -1216,8 +1212,8 @@ func TestGitlabGetBranches(t *testing.T) { } t.Run("branch exists", func(t *testing.T) { repos, err := host.GetBranches(context.Background(), repo) - require.NoError(t, err) - assert.Equal(t, "master", repos[0].Branch) + assert.Nil(t, err) + assert.Equal(t, repos[0].Branch, "master") }) repo2 := &Repository{ @@ -1226,77 +1222,6 @@ func TestGitlabGetBranches(t *testing.T) { } t.Run("unknown branch", func(t *testing.T) { _, err := host.GetBranches(context.Background(), repo2) - require.NoError(t, err) + assert.NoError(t, err) }) } - -func TestGetBranchesTLS(t *testing.T) { - tests := []struct { - name string - tlsInsecure bool - passCerts bool - requireErr bool - }{ - { - name: "TLS Insecure: true, No Certs", - tlsInsecure: true, - passCerts: false, - requireErr: false, - }, - { - name: "TLS Insecure: true, With Certs", - tlsInsecure: true, - passCerts: true, - requireErr: false, - }, - { - name: "TLS Insecure: false, With Certs", - tlsInsecure: false, - passCerts: true, - requireErr: false, - }, - { - name: "TLS Insecure: false, No Certs", - tlsInsecure: false, - passCerts: false, - requireErr: true, - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - gitlabMockHandler(t)(w, r) - })) - defer ts.Close() - - var certs []byte - if test.passCerts == true { - for _, cert := range ts.TLS.Certificates { - for _, c := range cert.Certificate { - parsedCert, err := x509.ParseCertificate(c) - require.NoError(t, err, "Failed to parse certificate") - certs = append(certs, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: parsedCert.Raw, - })...) - } - } - } - - host, err := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, true, test.tlsInsecure, "", "", certs) - require.NoError(t, err) - repo := &Repository{ - RepositoryId: 27084533, - Branch: "master", - } - _, err = host.GetBranches(context.Background(), repo) - if test.requireErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} diff --git a/applicationset/services/scm_provider/mock.go b/applicationset/services/scm_provider/mock.go index 15d8548e26b48..bf7e452c81c3a 100644 --- a/applicationset/services/scm_provider/mock.go +++ b/applicationset/services/scm_provider/mock.go @@ -44,6 +44,7 @@ func (m *MockProvider) GetBranches(_ context.Context, repo *Repository) ([]*Repo branchRepos = append(branchRepos, candidateRepo) } } + } return branchRepos, nil } diff --git a/applicationset/services/scm_provider/utils.go b/applicationset/services/scm_provider/utils.go index b7cdbf460605b..e92923f52707b 100644 --- a/applicationset/services/scm_provider/utils.go +++ b/applicationset/services/scm_provider/utils.go @@ -17,14 +17,14 @@ func compileFilters(filters []argoprojiov1alpha1.SCMProviderGeneratorFilter) ([] if filter.RepositoryMatch != nil { outFilter.RepositoryMatch, err = regexp.Compile(*filter.RepositoryMatch) if err != nil { - return nil, fmt.Errorf("error compiling RepositoryMatch regexp %q: %w", *filter.RepositoryMatch, err) + return nil, fmt.Errorf("error compiling RepositoryMatch regexp %q: %v", *filter.RepositoryMatch, err) } outFilter.FilterType = FilterTypeRepo } if filter.LabelMatch != nil { outFilter.LabelMatch, err = regexp.Compile(*filter.LabelMatch) if err != nil { - return nil, fmt.Errorf("error compiling LabelMatch regexp %q: %w", *filter.LabelMatch, err) + return nil, fmt.Errorf("error compiling LabelMatch regexp %q: %v", *filter.LabelMatch, err) } outFilter.FilterType = FilterTypeRepo } @@ -39,7 +39,7 @@ func compileFilters(filters []argoprojiov1alpha1.SCMProviderGeneratorFilter) ([] if filter.BranchMatch != nil { outFilter.BranchMatch, err = regexp.Compile(*filter.BranchMatch) if err != nil { - return nil, fmt.Errorf("error compiling BranchMatch regexp %q: %w", *filter.BranchMatch, err) + return nil, fmt.Errorf("error compiling BranchMatch regexp %q: %v", *filter.BranchMatch, err) } outFilter.FilterType = FilterTypeBranch } diff --git a/applicationset/services/scm_provider/utils_test.go b/applicationset/services/scm_provider/utils_test.go index 83c6c4fc23d9e..5ef6d582f8d34 100644 --- a/applicationset/services/scm_provider/utils_test.go +++ b/applicationset/services/scm_provider/utils_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -38,7 +37,7 @@ func TestFilterRepoMatch(t *testing.T) { }, } repos, err := ListRepos(context.Background(), provider, filters, "") - require.NoError(t, err) + assert.Nil(t, err) assert.Len(t, repos, 2) assert.Equal(t, "one", repos[0].Repository) assert.Equal(t, "three", repos[1].Repository) @@ -67,7 +66,7 @@ func TestFilterLabelMatch(t *testing.T) { }, } repos, err := ListRepos(context.Background(), provider, filters, "") - require.NoError(t, err) + assert.Nil(t, err) assert.Len(t, repos, 2) assert.Equal(t, "one", repos[0].Repository) assert.Equal(t, "two", repos[1].Repository) @@ -93,7 +92,7 @@ func TestFilterPathExists(t *testing.T) { }, } repos, err := ListRepos(context.Background(), provider, filters, "") - require.NoError(t, err) + assert.Nil(t, err) assert.Len(t, repos, 1) assert.Equal(t, "two", repos[0].Repository) } @@ -118,10 +117,9 @@ func TestFilterPathDoesntExists(t *testing.T) { }, } repos, err := ListRepos(context.Background(), provider, filters, "") - require.NoError(t, err) + assert.Nil(t, err) assert.Len(t, repos, 2) } - func TestFilterRepoMatchBadRegexp(t *testing.T) { provider := &MockProvider{ Repos: []*Repository{ @@ -136,7 +134,7 @@ func TestFilterRepoMatchBadRegexp(t *testing.T) { }, } _, err := ListRepos(context.Background(), provider, filters, "") - require.Error(t, err) + assert.NotNil(t, err) } func TestFilterLabelMatchBadRegexp(t *testing.T) { @@ -153,7 +151,7 @@ func TestFilterLabelMatchBadRegexp(t *testing.T) { }, } _, err := ListRepos(context.Background(), provider, filters, "") - require.Error(t, err) + assert.NotNil(t, err) } func TestFilterBranchMatch(t *testing.T) { @@ -187,7 +185,7 @@ func TestFilterBranchMatch(t *testing.T) { }, } repos, err := ListRepos(context.Background(), provider, filters, "") - require.NoError(t, err) + assert.Nil(t, err) assert.Len(t, repos, 2) assert.Equal(t, "one", repos[0].Repository) assert.Equal(t, "two", repos[0].Branch) @@ -219,7 +217,7 @@ func TestMultiFilterAnd(t *testing.T) { }, } repos, err := ListRepos(context.Background(), provider, filters, "") - require.NoError(t, err) + assert.Nil(t, err) assert.Len(t, repos, 1) assert.Equal(t, "two", repos[0].Repository) } @@ -250,7 +248,7 @@ func TestMultiFilterOr(t *testing.T) { }, } repos, err := ListRepos(context.Background(), provider, filters, "") - require.NoError(t, err) + assert.Nil(t, err) assert.Len(t, repos, 3) assert.Equal(t, "one", repos[0].Repository) assert.Equal(t, "two", repos[1].Repository) @@ -276,7 +274,7 @@ func TestNoFilters(t *testing.T) { } filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{} repos, err := ListRepos(context.Background(), provider, filters, "") - require.NoError(t, err) + assert.Nil(t, err) assert.Len(t, repos, 3) assert.Equal(t, "one", repos[0].Repository) assert.Equal(t, "two", repos[1].Repository) @@ -313,10 +311,8 @@ func TestApplicableFilterMap(t *testing.T) { BranchMatch: ®exp.Regexp{}, FilterType: FilterTypeBranch, } - filterMap := getApplicableFilters([]*Filter{ - &branchFilter, &repoFilter, - &pathExistsFilter, &labelMatchFilter, &unsetFilter, &additionalBranchFilter, &pathDoesntExistsFilter, - }) + filterMap := getApplicableFilters([]*Filter{&branchFilter, &repoFilter, + &pathExistsFilter, &labelMatchFilter, &unsetFilter, &additionalBranchFilter, &pathDoesntExistsFilter}) assert.Len(t, filterMap[FilterTypeRepo], 2) assert.Len(t, filterMap[FilterTypeBranch], 4) diff --git a/applicationset/status/resource_status.go b/applicationset/status/resource_status.go deleted file mode 100644 index 4e9db5ff560e9..0000000000000 --- a/applicationset/status/resource_status.go +++ /dev/null @@ -1,57 +0,0 @@ -package status - -import ( - argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" -) - -func BuildResourceStatus(statusMap map[string]argov1alpha1.ResourceStatus, apps []argov1alpha1.Application) map[string]argov1alpha1.ResourceStatus { - appMap := map[string]argov1alpha1.Application{} - for _, app := range apps { - appCopy := app - appMap[app.Name] = app - - gvk := app.GroupVersionKind() - // Create status if it does not exist - status, ok := statusMap[app.Name] - if !ok { - status = argov1alpha1.ResourceStatus{ - Group: gvk.Group, - Version: gvk.Version, - Kind: gvk.Kind, - Name: app.Name, - Namespace: app.Namespace, - Status: app.Status.Sync.Status, - Health: &appCopy.Status.Health, - } - } - - status.Group = gvk.Group - status.Version = gvk.Version - status.Kind = gvk.Kind - status.Name = app.Name - status.Namespace = app.Namespace - status.Status = app.Status.Sync.Status - status.Health = &appCopy.Status.Health - - statusMap[app.Name] = status - } - cleanupDeletedApplicationStatuses(statusMap, appMap) - - return statusMap -} - -func GetResourceStatusMap(appset *argov1alpha1.ApplicationSet) map[string]argov1alpha1.ResourceStatus { - statusMap := map[string]argov1alpha1.ResourceStatus{} - for _, status := range appset.Status.Resources { - statusMap[status.Name] = status - } - return statusMap -} - -func cleanupDeletedApplicationStatuses(statusMap map[string]argov1alpha1.ResourceStatus, apps map[string]argov1alpha1.Application) { - for name := range statusMap { - if _, ok := apps[name]; !ok { - delete(statusMap, name) - } - } -} diff --git a/applicationset/utils/applicationset_lister.go b/applicationset/utils/applicationset_lister.go deleted file mode 100644 index 5e9d65936333a..0000000000000 --- a/applicationset/utils/applicationset_lister.go +++ /dev/null @@ -1,63 +0,0 @@ -package utils - -import ( - "context" - - "k8s.io/apimachinery/pkg/labels" - ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" - - . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - . "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" -) - -// Implements AppsetLister interface with controller-runtime client -type AppsetLister struct { - Client ctrlclient.Client -} - -func NewAppsetLister(client ctrlclient.Client) ApplicationSetLister { - return &AppsetLister{Client: client} -} - -func (l *AppsetLister) List(selector labels.Selector) (ret []*ApplicationSet, err error) { - return clientListAppsets(l.Client, ctrlclient.ListOptions{}) -} - -// ApplicationSets returns an object that can list and get ApplicationSets. -func (l *AppsetLister) ApplicationSets(namespace string) ApplicationSetNamespaceLister { - return &appsetNamespaceLister{ - Client: l.Client, - Namespace: namespace, - } -} - -// Implements ApplicationSetNamespaceLister -type appsetNamespaceLister struct { - Client ctrlclient.Client - Namespace string -} - -func (n *appsetNamespaceLister) List(selector labels.Selector) (ret []*ApplicationSet, err error) { - return clientListAppsets(n.Client, ctrlclient.ListOptions{Namespace: n.Namespace}) -} - -func (n *appsetNamespaceLister) Get(name string) (*ApplicationSet, error) { - appset := ApplicationSet{} - err := n.Client.Get(context.TODO(), ctrlclient.ObjectKeyFromObject(&appset), &appset) - return &appset, err -} - -func clientListAppsets(client ctrlclient.Client, listOptions ctrlclient.ListOptions) (ret []*ApplicationSet, err error) { - var appsetlist ApplicationSetList - var results []*ApplicationSet - - err = client.List(context.TODO(), &appsetlist, &listOptions) - - if err == nil { - for _, appset := range appsetlist.Items { - results = append(results, appset.DeepCopy()) - } - } - - return results, err -} diff --git a/applicationset/utils/clusterUtils.go b/applicationset/utils/clusterUtils.go index 8c44dc1246be5..3b34a5a863dbd 100644 --- a/applicationset/utils/clusterUtils.go +++ b/applicationset/utils/clusterUtils.go @@ -17,7 +17,7 @@ import ( appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "k8s.io/client-go/kubernetes" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" ) // The contents of this file are from @@ -55,14 +55,16 @@ func ValidateDestination(ctx context.Context, dest *appv1.ApplicationDestination if dest.Server == "" { server, err := getDestinationServer(ctx, dest.Name, clientset, argoCDNamespace) if err != nil { - return fmt.Errorf("unable to find destination server: %w", err) + return fmt.Errorf("unable to find destination server: %v", err) } if server == "" { return fmt.Errorf("application references destination cluster %s which does not exist", dest.Name) } dest.SetInferredServer(server) - } else if !dest.IsServerInferred() { - return fmt.Errorf("application destination can't have both name and server defined: %s %s", dest.Name, dest.Server) + } else { + if !dest.IsServerInferred() { + return fmt.Errorf("application destination can't have both name and server defined: %s %s", dest.Name, dest.Server) + } } } return nil @@ -91,6 +93,7 @@ func getDestinationServer(ctx context.Context, clusterName string, clientset kub } func ListClusters(ctx context.Context, clientset kubernetes.Interface, namespace string) (*appv1.ClusterList, error) { + clusterSecretsList, err := clientset.CoreV1().Secrets(namespace).List(ctx, metav1.ListOptions{LabelSelector: common.LabelKeySecretType + "=" + common.LabelValueSecretTypeCluster}) if err != nil { @@ -111,7 +114,7 @@ func ListClusters(ctx context.Context, clientset kubernetes.Interface, namespace // This line has changed from the original Argo CD code: now receives an error, and handles it cluster, err := secretToCluster(&clusterSecret) if err != nil || cluster == nil { - return nil, fmt.Errorf("unable to convert cluster secret to cluster object '%s': %w", clusterSecret.Name, err) + return nil, fmt.Errorf("unable to convert cluster secret to cluster object '%s': %v", clusterSecret.Name, err) } clusterList.Items[i] = *cluster @@ -132,12 +135,9 @@ func getLocalCluster(clientset kubernetes.Interface) *appv1.Cluster { initLocalCluster.Do(func() { info, err := clientset.Discovery().ServerVersion() if err == nil { - // nolint:staticcheck localCluster.ServerVersion = fmt.Sprintf("%s.%s", info.Major, info.Minor) - // nolint:staticcheck localCluster.ConnectionState = appv1.ConnectionState{Status: appv1.ConnectionStatusSuccessful} } else { - // nolint:staticcheck localCluster.ConnectionState = appv1.ConnectionState{ Status: appv1.ConnectionStatusFailed, Message: err.Error(), @@ -146,7 +146,6 @@ func getLocalCluster(clientset kubernetes.Interface) *appv1.Cluster { }) cluster := localCluster.DeepCopy() now := metav1.Now() - // nolint:staticcheck cluster.ConnectionState.ModifiedAt = &now return cluster } @@ -181,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 = ptr.To(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 9e8694359b6bd..70332afdd80fb 100644 --- a/applicationset/utils/clusterUtils_test.go +++ b/applicationset/utils/clusterUtils_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -34,14 +33,14 @@ func Test_secretToCluster(t *testing.T) { }, } cluster, err := secretToCluster(secret) - require.NoError(t, err) - assert.Equal(t, argoappv1.Cluster{ + assert.Nil(t, err) + assert.Equal(t, *cluster, argoappv1.Cluster{ Name: "test", Server: "http://mycluster", Config: argoappv1.ClusterConfig{ Username: "foo", }, - }, *cluster) + }) } // From Argo CD util/db/cluster_test.go @@ -57,14 +56,15 @@ func Test_secretToCluster_NoConfig(t *testing.T) { }, } cluster, err := secretToCluster(secret) - require.NoError(t, err) - assert.Equal(t, argoappv1.Cluster{ + assert.Nil(t, err) + assert.Equal(t, *cluster, argoappv1.Cluster{ Name: "test", Server: "http://mycluster", - }, *cluster) + }) } func createClusterSecret(secretName string, clusterName string, clusterServer string) *corev1.Secret { + secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, @@ -81,19 +81,22 @@ func createClusterSecret(secretName string, clusterName string, clusterServer st } return secret + } // From util/argo/argo_test.go // (ported to use kubeclientset) func TestValidateDestination(t *testing.T) { + t.Run("Validate destination with server url", func(t *testing.T) { + dest := argoappv1.ApplicationDestination{ Server: "https://127.0.0.1:6443", Namespace: "default", } appCond := ValidateDestination(context.Background(), &dest, nil, fakeNamespace) - require.NoError(t, appCond) + assert.Nil(t, appCond) assert.False(t, dest.IsServerInferred()) }) @@ -108,7 +111,7 @@ func TestValidateDestination(t *testing.T) { kubeclientset := fake.NewSimpleClientset(objects...) appCond := ValidateDestination(context.Background(), &dest, kubeclientset, fakeNamespace) - require.NoError(t, appCond) + assert.Nil(t, appCond) assert.Equal(t, "https://127.0.0.1:6443", dest.Server) assert.True(t, dest.IsServerInferred()) }) @@ -171,4 +174,5 @@ func TestValidateDestination(t *testing.T) { 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 c602f002b0d47..301d477bab2db 100644 --- a/applicationset/utils/createOrUpdate.go +++ b/applicationset/utils/createOrUpdate.go @@ -37,6 +37,7 @@ import ( // // It returns the executed operation and an error. func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ignoreAppDifferences argov1alpha1.ApplicationSetIgnoreDifferences, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts, obj *argov1alpha1.Application, f controllerutil.MutateFn) (controllerutil.OperationResult, error) { + key := client.ObjectKeyFromObject(obj) if err := c.Get(ctx, key, obj); err != nil { if !errors.IsNotFound(err) { diff --git a/applicationset/utils/kubernetes.go b/applicationset/utils/kubernetes.go deleted file mode 100644 index f9e90bf1d9f81..0000000000000 --- a/applicationset/utils/kubernetes.go +++ /dev/null @@ -1,54 +0,0 @@ -package utils - -import ( - "context" - "fmt" - - corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - - argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" -) - -// getSecretRef gets the value of the key for the specified Secret resource. -func GetSecretRef(ctx context.Context, k8sClient client.Client, ref *argoprojiov1alpha1.SecretRef, namespace string) (string, error) { - if ref == nil { - return "", nil - } - - secret := &corev1.Secret{} - err := k8sClient.Get( - ctx, - client.ObjectKey{ - Name: ref.SecretName, - Namespace: namespace, - }, - secret) - if err != nil { - return "", fmt.Errorf("error fetching secret %s/%s: %w", namespace, ref.SecretName, err) - } - tokenBytes, ok := secret.Data[ref.Key] - if !ok { - return "", fmt.Errorf("key %q in secret %s/%s not found", ref.Key, namespace, ref.SecretName) - } - return string(tokenBytes), nil -} - -func GetConfigMapData(ctx context.Context, k8sClient client.Client, ref *argoprojiov1alpha1.ConfigMapKeyRef, namespace string) ([]byte, error) { - if ref == nil { - return nil, nil - } - - configMap := &corev1.ConfigMap{} - err := k8sClient.Get(ctx, client.ObjectKey{Name: ref.ConfigMapName, Namespace: namespace}, configMap) - if err != nil { - return nil, err - } - - data, ok := configMap.Data[ref.Key] - if !ok { - return nil, fmt.Errorf("key %s not found in ConfigMap %s", ref.Key, configMap.Name) - } - - return []byte(data), nil -} diff --git a/applicationset/utils/kubernetes_test.go b/applicationset/utils/kubernetes_test.go deleted file mode 100644 index bddda0c473073..0000000000000 --- a/applicationset/utils/kubernetes_test.go +++ /dev/null @@ -1,146 +0,0 @@ -package utils - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - - argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" -) - -func TestGetSecretRef(t *testing.T) { - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "test-secret", Namespace: "test"}, - Data: map[string][]byte{ - "my-token": []byte("secret"), - }, - } - client := fake.NewClientBuilder().WithObjects(secret).Build() - ctx := context.Background() - - cases := []struct { - name, namespace, token string - ref *argoprojiov1alpha1.SecretRef - hasError bool - }{ - { - name: "valid ref", - ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "my-token"}, - namespace: "test", - token: "secret", - hasError: false, - }, - { - name: "nil ref", - ref: nil, - namespace: "test", - token: "", - hasError: false, - }, - { - name: "wrong name", - ref: &argoprojiov1alpha1.SecretRef{SecretName: "other", Key: "my-token"}, - namespace: "test", - token: "", - hasError: true, - }, - { - name: "wrong key", - ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "other-token"}, - namespace: "test", - token: "", - hasError: true, - }, - { - name: "wrong namespace", - ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "my-token"}, - namespace: "other", - token: "", - hasError: true, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - token, err := GetSecretRef(ctx, client, c.ref, c.namespace) - if c.hasError { - require.Error(t, err) - } else { - require.NoError(t, err) - } - assert.Equal(t, c.token, token) - }) - } -} - -func TestGetConfigMapData(t *testing.T) { - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "test-configmap", Namespace: "test"}, - Data: map[string]string{ - "my-data": "configmap-data", - }, - } - client := fake.NewClientBuilder().WithObjects(configMap).Build() - ctx := context.Background() - - cases := []struct { - name, namespace, data string - ref *argoprojiov1alpha1.ConfigMapKeyRef - hasError bool - }{ - { - name: "valid ref", - ref: &argoprojiov1alpha1.ConfigMapKeyRef{ConfigMapName: "test-configmap", Key: "my-data"}, - namespace: "test", - data: "configmap-data", - hasError: false, - }, - { - name: "nil ref", - ref: nil, - namespace: "test", - data: "", - hasError: false, - }, - { - name: "wrong name", - ref: &argoprojiov1alpha1.ConfigMapKeyRef{ConfigMapName: "other", Key: "my-data"}, - namespace: "test", - data: "", - hasError: true, - }, - { - name: "wrong key", - ref: &argoprojiov1alpha1.ConfigMapKeyRef{ConfigMapName: "test-configmap", Key: "other-data"}, - namespace: "test", - data: "", - hasError: true, - }, - { - name: "wrong namespace", - ref: &argoprojiov1alpha1.ConfigMapKeyRef{ConfigMapName: "test-configmap", Key: "my-data"}, - namespace: "other", - data: "", - hasError: true, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - data, err := GetConfigMapData(ctx, client, c.ref, c.namespace) - if c.hasError { - require.Error(t, err) - } else { - require.NoError(t, err) - } - if !c.hasError { - assert.Equal(t, c.data, string(data)) - } - }) - } -} diff --git a/applicationset/utils/map.go b/applicationset/utils/map.go index e15baec29bd9c..4e45e1c3fe2d2 100644 --- a/applicationset/utils/map.go +++ b/applicationset/utils/map.go @@ -26,6 +26,7 @@ func ConvertToMapStringInterface(mapStringString map[string]string) map[string]i } func CombineStringMaps(aSI map[string]interface{}, bSI map[string]interface{}) (map[string]string, error) { + a := ConvertToMapStringString(aSI) b := ConvertToMapStringString(bSI) @@ -48,6 +49,7 @@ func CombineStringMaps(aSI map[string]interface{}, bSI map[string]interface{}) ( // CombineStringMapsAllowDuplicates merges two maps. Where there are duplicates, take the latter map's value. func CombineStringMapsAllowDuplicates(aSI map[string]interface{}, bSI map[string]interface{}) (map[string]string, error) { + a := ConvertToMapStringString(aSI) b := ConvertToMapStringString(bSI) diff --git a/applicationset/utils/map_test.go b/applicationset/utils/map_test.go index c12216e0e1ac6..860bb046cc253 100644 --- a/applicationset/utils/map_test.go +++ b/applicationset/utils/map_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestCombineStringMaps(t *testing.T) { @@ -50,9 +49,10 @@ func TestCombineStringMaps(t *testing.T) { if testCaseCopy.expectedErr != nil { assert.EqualError(t, err, testCaseCopy.expectedErr.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCaseCopy.expected, got) } + }) } } diff --git a/applicationset/utils/mocks/Renderer.go b/applicationset/utils/mocks/Renderer.go deleted file mode 100644 index 3b108f74e7864..0000000000000 --- a/applicationset/utils/mocks/Renderer.go +++ /dev/null @@ -1,86 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" - - v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" -) - -// Renderer is an autogenerated mock type for the Renderer type -type Renderer struct { - mock.Mock -} - -// RenderTemplateParams provides a mock function with given fields: tmpl, syncPolicy, params, useGoTemplate, goTemplateOptions -func (_m *Renderer) RenderTemplateParams(tmpl *v1alpha1.Application, syncPolicy *v1alpha1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (*v1alpha1.Application, error) { - ret := _m.Called(tmpl, syncPolicy, params, useGoTemplate, goTemplateOptions) - - if len(ret) == 0 { - panic("no return value specified for RenderTemplateParams") - } - - var r0 *v1alpha1.Application - var r1 error - if rf, ok := ret.Get(0).(func(*v1alpha1.Application, *v1alpha1.ApplicationSetSyncPolicy, map[string]interface{}, bool, []string) (*v1alpha1.Application, error)); ok { - return rf(tmpl, syncPolicy, params, useGoTemplate, goTemplateOptions) - } - if rf, ok := ret.Get(0).(func(*v1alpha1.Application, *v1alpha1.ApplicationSetSyncPolicy, map[string]interface{}, bool, []string) *v1alpha1.Application); ok { - r0 = rf(tmpl, syncPolicy, params, useGoTemplate, goTemplateOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.Application) - } - } - - if rf, ok := ret.Get(1).(func(*v1alpha1.Application, *v1alpha1.ApplicationSetSyncPolicy, map[string]interface{}, bool, []string) error); ok { - r1 = rf(tmpl, syncPolicy, params, useGoTemplate, goTemplateOptions) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Replace provides a mock function with given fields: tmpl, replaceMap, useGoTemplate, goTemplateOptions -func (_m *Renderer) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) { - ret := _m.Called(tmpl, replaceMap, useGoTemplate, goTemplateOptions) - - if len(ret) == 0 { - panic("no return value specified for Replace") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(string, map[string]interface{}, bool, []string) (string, error)); ok { - return rf(tmpl, replaceMap, useGoTemplate, goTemplateOptions) - } - if rf, ok := ret.Get(0).(func(string, map[string]interface{}, bool, []string) string); ok { - r0 = rf(tmpl, replaceMap, useGoTemplate, goTemplateOptions) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(string, map[string]interface{}, bool, []string) error); ok { - r1 = rf(tmpl, replaceMap, useGoTemplate, goTemplateOptions) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewRenderer creates a new instance of Renderer. 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 NewRenderer(t interface { - mock.TestingT - Cleanup(func()) -}) *Renderer { - mock := &Renderer{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/applicationset/utils/selector.go b/applicationset/utils/selector.go index 6012fdb46f61c..53db73a5b3a48 100644 --- a/applicationset/utils/selector.go +++ b/applicationset/utils/selector.go @@ -2,16 +2,15 @@ package utils import ( "fmt" - "sort" - "strconv" - "strings" - 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 ( diff --git a/applicationset/utils/utils.go b/applicationset/utils/utils.go index 4122dee28a657..2d128eb81a16c 100644 --- a/applicationset/utils/utils.go +++ b/applicationset/utils/utils.go @@ -23,7 +23,6 @@ import ( log "github.com/sirupsen/logrus" argoappsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - "github.com/argoproj/argo-cd/v2/util/glob" ) var sprigFuncMap = sprig.GenericFuncMap() // a singleton for better performance @@ -45,10 +44,7 @@ type Renderer interface { Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) } -type Render struct{} - -func IsNamespaceAllowed(namespaces []string, namespace string) bool { - return glob.MatchStringInList(namespaces, namespace, glob.REGEXP) +type Render struct { } func copyValueIntoUnexported(destination, value reflect.Value) { @@ -58,7 +54,7 @@ func copyValueIntoUnexported(destination, value reflect.Value) { } func copyUnexported(copy, original reflect.Value) { - unexported := reflect.NewAt(original.Type(), unsafe.Pointer(original.UnsafeAddr())).Elem() + var unexported = reflect.NewAt(original.Type(), unsafe.Pointer(original.UnsafeAddr())).Elem() copyValueIntoUnexported(copy, unexported) } @@ -131,7 +127,7 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri // If it is a struct we translate each field case reflect.Struct: for i := 0; i < original.NumField(); i += 1 { - currentType := fmt.Sprintf("%s.%s", original.Type().Field(i).Name, original.Type().PkgPath()) + var currentType = fmt.Sprintf("%s.%s", original.Type().Field(i).Name, original.Type().PkgPath()) // specific case time if currentType == "time.Time" { copy.Field(i).Set(original.Field(i)) @@ -273,8 +269,9 @@ func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy * // b) there IS a syncPolicy, but preserveResourcesOnDeletion is set to false // See TestRenderTemplateParamsFinalizers in util_test.go for test-based definition of behaviour if (syncPolicy == nil || !syncPolicy.PreserveResourcesOnDeletion) && - len(replacedTmpl.ObjectMeta.Finalizers) == 0 { - replacedTmpl.ObjectMeta.Finalizers = []string{"resources-finalizer.argocd.argoproj.io"} + ((*replacedTmpl).ObjectMeta.Finalizers == nil || len((*replacedTmpl).ObjectMeta.Finalizers) == 0) { + + (*replacedTmpl).ObjectMeta.Finalizers = []string{"resources-finalizer.argocd.argoproj.io"} } return replacedTmpl, nil @@ -488,7 +485,8 @@ func SlugifyName(args ...interface{}) string { return urlSlug } -func getTlsConfigWithCACert(scmRootCAPath string, caCerts []byte) *tls.Config { +func getTlsConfigWithCACert(scmRootCAPath string) *tls.Config { + tlsConfig := &tls.Config{} if scmRootCAPath != "" { @@ -502,12 +500,8 @@ func getTlsConfigWithCACert(scmRootCAPath string, caCerts []byte) *tls.Config { log.Errorf("error reading certificate from file '%s', proceeding without custom rootCA : %s", scmRootCAPath, err) return tlsConfig } - caCerts = append(caCerts, rootCA...) - } - - if len(caCerts) > 0 { certPool := x509.NewCertPool() - ok := certPool.AppendCertsFromPEM(caCerts) + ok := certPool.AppendCertsFromPEM([]byte(rootCA)) if !ok { log.Errorf("failed to append certificates from PEM: proceeding without custom rootCA") } else { @@ -517,8 +511,8 @@ func getTlsConfigWithCACert(scmRootCAPath string, caCerts []byte) *tls.Config { return tlsConfig } -func GetTlsConfig(scmRootCAPath string, insecure bool, caCerts []byte) *tls.Config { - tlsConfig := getTlsConfigWithCACert(scmRootCAPath, caCerts) +func GetTlsConfig(scmRootCAPath string, insecure bool) *tls.Config { + tlsConfig := getTlsConfigWithCACert(scmRootCAPath) if insecure { tlsConfig.InsecureSkipVerify = true diff --git a/applicationset/utils/utils_test.go b/applicationset/utils/utils_test.go index 8d19a2cffa260..3b4702bc35c3f 100644 --- a/applicationset/utils/utils_test.go +++ b/applicationset/utils/utils_test.go @@ -21,6 +21,7 @@ import ( ) func TestRenderTemplateParams(t *testing.T) { + // Believe it or not, this is actually less complex than the equivalent solution using reflection fieldMap := map[string]func(app *argoappsv1.Application) *string{} fieldMap["Path"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.Path } @@ -164,8 +165,11 @@ func TestRenderTemplateParams(t *testing.T) { } for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + for fieldName, getPtrFunc := range fieldMap { + // Clone the template application application := emptyApplication.DeepCopy() @@ -180,21 +184,23 @@ func TestRenderTemplateParams(t *testing.T) { // the target field has been templated into the expected value actualValue := *getPtrFunc(newApplication) assert.Equal(t, test.expectedVal, actualValue, "Field '%s' had an unexpected value. expected: '%s' value: '%s'", fieldName, test.expectedVal, actualValue) - assert.Equal(t, "annotation-value", newApplication.ObjectMeta.Annotations["annotation-key"]) - assert.Equal(t, "annotation-value2", newApplication.ObjectMeta.Annotations["annotation-key2"]) - assert.Equal(t, "label-value", newApplication.ObjectMeta.Labels["label-key"]) - assert.Equal(t, "label-value2", newApplication.ObjectMeta.Labels["label-key2"]) - assert.Equal(t, "application-one", newApplication.ObjectMeta.Name) - assert.Equal(t, "default", newApplication.ObjectMeta.Namespace) + assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-key"], "annotation-value") + assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-key2"], "annotation-value2") + assert.Equal(t, newApplication.ObjectMeta.Labels["label-key"], "label-value") + assert.Equal(t, newApplication.ObjectMeta.Labels["label-key2"], "label-value2") + assert.Equal(t, newApplication.ObjectMeta.Name, "application-one") + assert.Equal(t, newApplication.ObjectMeta.Namespace, "default") assert.Equal(t, newApplication.ObjectMeta.UID, types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b")) assert.Equal(t, newApplication.ObjectMeta.CreationTimestamp, application.ObjectMeta.CreationTimestamp) - require.NoError(t, err) + assert.NoError(t, err) } }) } + } func TestRenderHelmValuesObjectJson(t *testing.T) { + params := map[string]interface{}{ "test": "Hello world", } @@ -237,17 +243,19 @@ func TestRenderHelmValuesObjectJson(t *testing.T) { render := Render{} newApplication, err := render.RenderTemplateParams(application, nil, params, true, []string{}) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, newApplication) var unmarshaled interface{} err = json.Unmarshal(newApplication.Spec.Source.Helm.ValuesObject.Raw, &unmarshaled) - require.NoError(t, err) - assert.Equal(t, "Hello world", unmarshaled.(map[string]interface{})["some"].(map[string]interface{})["string"]) + 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", } @@ -287,17 +295,19 @@ func TestRenderHelmValuesObjectYaml(t *testing.T) { render := Render{} newApplication, err := render.RenderTemplateParams(application, nil, params, true, []string{}) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, newApplication) var unmarshaled interface{} err = json.Unmarshal(newApplication.Spec.Source.Helm.ValuesObject.Raw, &unmarshaled) - require.NoError(t, err) - assert.Equal(t, "Hello world", unmarshaled.(map[string]interface{})["some"].(map[string]interface{})["string"]) + 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 fieldMap := map[string]func(app *argoappsv1.Application) *string{} fieldMap["Path"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.Path } @@ -606,8 +616,11 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) { } for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + for fieldName, getPtrFunc := range fieldMap { + // Clone the template application application := emptyApplication.DeepCopy() @@ -621,18 +634,18 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) { // Retrieve the value of the target field from the newApplication, then verify that // the target field has been templated into the expected value if test.errorMessage != "" { - require.Error(t, err) + assert.Error(t, err) assert.Equal(t, test.errorMessage, err.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) actualValue := *getPtrFunc(newApplication) assert.Equal(t, test.expectedVal, actualValue, "Field '%s' had an unexpected value. expected: '%s' value: '%s'", fieldName, test.expectedVal, actualValue) - assert.Equal(t, "annotation-value", newApplication.ObjectMeta.Annotations["annotation-key"]) - assert.Equal(t, "annotation-value2", newApplication.ObjectMeta.Annotations["annotation-key2"]) - assert.Equal(t, "label-value", newApplication.ObjectMeta.Labels["label-key"]) - assert.Equal(t, "label-value2", newApplication.ObjectMeta.Labels["label-key2"]) - assert.Equal(t, "application-one", newApplication.ObjectMeta.Name) - assert.Equal(t, "default", newApplication.ObjectMeta.Namespace) + assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-key"], "annotation-value") + assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-key2"], "annotation-value2") + assert.Equal(t, newApplication.ObjectMeta.Labels["label-key"], "label-value") + assert.Equal(t, newApplication.ObjectMeta.Labels["label-key2"], "label-value2") + assert.Equal(t, newApplication.ObjectMeta.Name, "application-one") + assert.Equal(t, newApplication.ObjectMeta.Namespace, "default") assert.Equal(t, newApplication.ObjectMeta.UID, types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b")) assert.Equal(t, newApplication.ObjectMeta.CreationTimestamp, application.ObjectMeta.CreationTimestamp) } @@ -666,7 +679,7 @@ func TestRenderGeneratorParams_does_not_panic(t *testing.T) { }, } _, err := render.RenderGeneratorParams(generator, params, true, []string{}) - require.NoError(t, err) + assert.NoError(t, err) } func TestRenderTemplateKeys(t *testing.T) { @@ -688,7 +701,7 @@ func TestRenderTemplateKeys(t *testing.T) { 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, "annotation-some-value", newApplication.ObjectMeta.Annotations["annotation-some-key"]) + assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-some-key"], "annotation-some-value") }) t.Run("gotemplate", func(t *testing.T) { application := &argoappsv1.Application{ @@ -708,7 +721,7 @@ func TestRenderTemplateKeys(t *testing.T) { 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, "annotation-some-value", newApplication.ObjectMeta.Annotations["annotation-some-key"]) + assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-some-key"], "annotation-some-value") }) } @@ -716,11 +729,12 @@ 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{}) - require.Error(t, err) + assert.Error(t, err) }) } func TestRenderTemplateParamsFinalizers(t *testing.T) { + emptyApplication := &argoappsv1.Application{ Spec: argoappsv1.ApplicationSpec{ Source: &argoappsv1.ApplicationSource{ @@ -799,7 +813,9 @@ func TestRenderTemplateParamsFinalizers(t *testing.T) { expectedFinalizers: []string{"resources-finalizer.argocd.argoproj.io/background"}, }, } { + t.Run(c.testName, func(t *testing.T) { + // Clone the template application application := emptyApplication.DeepCopy() application.Finalizers = c.existingFinalizers @@ -812,19 +828,23 @@ func TestRenderTemplateParamsFinalizers(t *testing.T) { render := Render{} res, err := render.RenderTemplateParams(application, c.syncPolicy, params, true, nil) - require.NoError(t, err) + assert.Nil(t, err) assert.ElementsMatch(t, res.Finalizers, c.expectedFinalizers) + }) + } + } func TestCheckInvalidGenerators(t *testing.T) { + scheme := runtime.NewScheme() err := argoappsv1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = argoappsv1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) for _, c := range []struct { testName string @@ -912,7 +932,7 @@ func TestCheckInvalidGenerators(t *testing.T) { hook := logtest.NewGlobal() _ = CheckInvalidGenerators(&c.appSet) - assert.GreaterOrEqual(t, len(hook.Entries), 1, c.testName) + assert.True(t, len(hook.Entries) >= 1, c.testName) assert.NotNil(t, hook.LastEntry(), c.testName) if hook.LastEntry() != nil { assert.Equal(t, logrus.WarnLevel, hook.LastEntry().Level, c.testName) @@ -923,11 +943,12 @@ func TestCheckInvalidGenerators(t *testing.T) { } func TestInvalidGenerators(t *testing.T) { + scheme := runtime.NewScheme() err := argoappsv1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = argoappsv1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) for _, c := range []struct { testName string @@ -1260,8 +1281,11 @@ func TestSlugify(t *testing.T) { } func TestGetTLSConfig(t *testing.T) { + // certParsed, err := tls.X509KeyPair(test.Cert, test.PrivateKey) + // require.NoError(t, err) + temppath := t.TempDir() - certFromFile := ` + cert := ` -----BEGIN CERTIFICATE----- MIIFvTCCA6WgAwIBAgIUGrTmW3qc39zqnE08e3qNDhUkeWswDQYJKoZIhvcNAQEL BQAwbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv @@ -1295,96 +1319,50 @@ NoB2rjufaB0GQi1azdboMvdGSOxhSCAR8otWT5yDrywCqVnEvjw0oxKmuRduNe2/ r2AaraPFgrprnxUibP4L7jxdr+iiw5bWN9/B81PodrS7n5TNtnfnpZD6X6rThqOP xO7Tr5lAo74vNUkF2EHNaI28/RGnJPm2TIxZqy4rNH6L -----END CERTIFICATE----- -` - - certFromCM := ` ------BEGIN CERTIFICATE----- -MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS -MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw -MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r -bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U -aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P -YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk -POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu -h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE -AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv -bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI -5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv -cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2 -+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B -grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK -5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/ -WkBKOclmOV2xlTVuPw== ------END CERTIFICATE----- ` rootCAPath := path.Join(temppath, "foo.example.com") - err := os.WriteFile(rootCAPath, []byte(certFromFile), 0o666) + 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 - caCerts []byte validateCertInTlsConfig bool }{ { name: "Insecure mode configured, SCM Root CA Path not set", scmRootCAPath: "", insecure: true, - caCerts: nil, validateCertInTlsConfig: false, }, { name: "SCM Root CA Path set, Insecure mode set to false", scmRootCAPath: rootCAPath, insecure: false, - caCerts: nil, validateCertInTlsConfig: true, }, { name: "SCM Root CA Path set, Insecure mode set to true", scmRootCAPath: rootCAPath, insecure: true, - caCerts: nil, - validateCertInTlsConfig: true, - }, - { - name: "Cert passed, Insecure mode set to false", - scmRootCAPath: "", - insecure: false, - caCerts: []byte(certFromCM), - validateCertInTlsConfig: true, - }, - { - name: "SCM Root CA Path set, cert passed, Insecure mode set to false", - scmRootCAPath: rootCAPath, - insecure: false, - caCerts: []byte(certFromCM), validateCertInTlsConfig: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - certPool := x509.NewCertPool() - tlsConfig := GetTlsConfig(testCase.scmRootCAPath, testCase.insecure, testCase.caCerts) + tlsConfig := GetTlsConfig(testCase.scmRootCAPath, testCase.insecure) assert.Equal(t, testCase.insecure, tlsConfig.InsecureSkipVerify) - if testCase.caCerts != nil { - ok := certPool.AppendCertsFromPEM([]byte(certFromCM)) - assert.True(t, ok) - } - if testCase.scmRootCAPath != "" { - ok := certPool.AppendCertsFromPEM([]byte(certFromFile)) - assert.True(t, ok) - } - assert.NotNil(t, tlsConfig) if testCase.validateCertInTlsConfig { + assert.NotNil(t, tlsConfig) assert.True(t, tlsConfig.RootCAs.Equal(certPool)) } }) diff --git a/applicationset/webhook/testdata/github-commit-event-feature-branch.json b/applicationset/webhook/testdata/github-commit-event-feature-branch.json deleted file mode 100644 index 2cbe577da34de..0000000000000 --- a/applicationset/webhook/testdata/github-commit-event-feature-branch.json +++ /dev/null @@ -1,186 +0,0 @@ -{ - "ref": "refs/heads/env/dev", - "before": "d5c1ffa8e294bc18c639bfb4e0df499251034414", - "after": "63738bb582c8b540af7bcfc18f87c575c3ed66e0", - "created": false, - "deleted": false, - "forced": true, - "base_ref": null, - "compare": "https://github.com/org/repo/compare/d5c1ffa8e294...63738bb582c8", - "commits": [ - { - "id": "63738bb582c8b540af7bcfc18f87c575c3ed66e0", - "tree_id": "64897da445207e409ad05af93b1f349ad0a4ee19", - "distinct": true, - "message": "Add staging-argocd-demo environment", - "timestamp": "2018-05-04T15:40:02-07:00", - "url": "https://github.com/org/repo/commit/63738bb582c8b540af7bcfc18f87c575c3ed66e0", - "author": { - "name": "Jesse Suen", - "email": "Jesse_Suen@example.com", - "username": "org" - }, - "committer": { - "name": "Jesse Suen", - "email": "Jesse_Suen@example.com", - "username": "org" - }, - "added": [ - "ksapps/test-app/environments/staging-argocd-demo/main.jsonnet", - "ksapps/test-app/environments/staging-argocd-demo/params.libsonnet" - ], - "removed": [ - - ], - "modified": [ - "ksapps/test-app/app.yaml" - ] - } - ], - "head_commit": { - "id": "63738bb582c8b540af7bcfc18f87c575c3ed66e0", - "tree_id": "64897da445207e409ad05af93b1f349ad0a4ee19", - "distinct": true, - "message": "Add staging-argocd-demo environment", - "timestamp": "2018-05-04T15:40:02-07:00", - "url": "https://github.com/org/repo/commit/63738bb582c8b540af7bcfc18f87c575c3ed66e0", - "author": { - "name": "Jesse Suen", - "email": "Jesse_Suen@example.com", - "username": "org" - }, - "committer": { - "name": "Jesse Suen", - "email": "Jesse_Suen@example.com", - "username": "org" - }, - "added": [ - "ksapps/test-app/environments/staging-argocd-demo/main.jsonnet", - "ksapps/test-app/environments/staging-argocd-demo/params.libsonnet" - ], - "removed": [ - - ], - "modified": [ - "ksapps/test-app/app.yaml" - ] - }, - "repository": { - "id": 123060978, - "name": "repo", - "full_name": "org/repo", - "owner": { - "name": "org", - "email": "org@users.noreply.github.com", - "login": "org", - "id": 12677113, - "avatar_url": "https://avatars0.githubusercontent.com/u/12677113?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/org", - "html_url": "https://github.com/org", - "followers_url": "https://api.github.com/users/org/followers", - "following_url": "https://api.github.com/users/org/following{/other_user}", - "gists_url": "https://api.github.com/users/org/gists{/gist_id}", - "starred_url": "https://api.github.com/users/org/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/org/subscriptions", - "organizations_url": "https://api.github.com/users/org/orgs", - "repos_url": "https://api.github.com/users/org/repos", - "events_url": "https://api.github.com/users/org/events{/privacy}", - "received_events_url": "https://api.github.com/users/org/received_events", - "type": "User", - "site_admin": false - }, - "private": false, - "html_url": "https://github.com/org/repo", - "description": "Test Repository", - "fork": false, - "url": "https://github.com/org/repo", - "forks_url": "https://api.github.com/repos/org/repo/forks", - "keys_url": "https://api.github.com/repos/org/repo/keys{/key_id}", - "collaborators_url": "https://api.github.com/repos/org/repo/collaborators{/collaborator}", - "teams_url": "https://api.github.com/repos/org/repo/teams", - "hooks_url": "https://api.github.com/repos/org/repo/hooks", - "issue_events_url": "https://api.github.com/repos/org/repo/issues/events{/number}", - "events_url": "https://api.github.com/repos/org/repo/events", - "assignees_url": "https://api.github.com/repos/org/repo/assignees{/user}", - "branches_url": "https://api.github.com/repos/org/repo/branches{/branch}", - "tags_url": "https://api.github.com/repos/org/repo/tags", - "blobs_url": "https://api.github.com/repos/org/repo/git/blobs{/sha}", - "git_tags_url": "https://api.github.com/repos/org/repo/git/tags{/sha}", - "git_refs_url": "https://api.github.com/repos/org/repo/git/refs{/sha}", - "trees_url": "https://api.github.com/repos/org/repo/git/trees{/sha}", - "statuses_url": "https://api.github.com/repos/org/repo/statuses/{sha}", - "languages_url": "https://api.github.com/repos/org/repo/languages", - "stargazers_url": "https://api.github.com/repos/org/repo/stargazers", - "contributors_url": "https://api.github.com/repos/org/repo/contributors", - "subscribers_url": "https://api.github.com/repos/org/repo/subscribers", - "subscription_url": "https://api.github.com/repos/org/repo/subscription", - "commits_url": "https://api.github.com/repos/org/repo/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/org/repo/git/commits{/sha}", - "comments_url": "https://api.github.com/repos/org/repo/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/org/repo/issues/comments{/number}", - "contents_url": "https://api.github.com/repos/org/repo/contents/{+path}", - "compare_url": "https://api.github.com/repos/org/repo/compare/{base}...{head}", - "merges_url": "https://api.github.com/repos/org/repo/merges", - "archive_url": "https://api.github.com/repos/org/repo/{archive_format}{/ref}", - "downloads_url": "https://api.github.com/repos/org/repo/downloads", - "issues_url": "https://api.github.com/repos/org/repo/issues{/number}", - "pulls_url": "https://api.github.com/repos/org/repo/pulls{/number}", - "milestones_url": "https://api.github.com/repos/org/repo/milestones{/number}", - "notifications_url": "https://api.github.com/repos/org/repo/notifications{?since,all,participating}", - "labels_url": "https://api.github.com/repos/org/repo/labels{/name}", - "releases_url": "https://api.github.com/repos/org/repo/releases{/id}", - "deployments_url": "https://api.github.com/repos/org/repo/deployments", - "created_at": 1519698615, - "updated_at": "2018-05-04T22:37:55Z", - "pushed_at": 1525473610, - "git_url": "git://github.com/org/repo.git", - "ssh_url": "git@github.com:org/repo.git", - "clone_url": "https://github.com/org/repo.git", - "svn_url": "https://github.com/org/repo", - "homepage": null, - "size": 538, - "stargazers_count": 0, - "watchers_count": 0, - "language": null, - "has_issues": true, - "has_projects": true, - "has_downloads": true, - "has_wiki": true, - "has_pages": false, - "forks_count": 1, - "mirror_url": null, - "archived": false, - "open_issues_count": 0, - "license": null, - "forks": 1, - "open_issues": 0, - "watchers": 0, - "default_branch": "master", - "stargazers": 0, - "master_branch": "master" - }, - "pusher": { - "name": "org", - "email": "org@users.noreply.github.com" - }, - "sender": { - "login": "org", - "id": 12677113, - "avatar_url": "https://avatars0.githubusercontent.com/u/12677113?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/org", - "html_url": "https://github.com/org", - "followers_url": "https://api.github.com/users/org/followers", - "following_url": "https://api.github.com/users/org/following{/other_user}", - "gists_url": "https://api.github.com/users/org/gists{/gist_id}", - "starred_url": "https://api.github.com/users/org/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/org/subscriptions", - "organizations_url": "https://api.github.com/users/org/orgs", - "repos_url": "https://api.github.com/users/org/repos", - "events_url": "https://api.github.com/users/org/events{/privacy}", - "received_events_url": "https://api.github.com/users/org/received_events", - "type": "User", - "site_admin": false - } - } \ No newline at end of file diff --git a/applicationset/webhook/webhook.go b/applicationset/webhook/webhook.go index e1c5d63cdb440..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" @@ -9,7 +10,6 @@ import ( "regexp" "strconv" "strings" - "sync" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/util/retry" @@ -19,7 +19,6 @@ import ( "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argosettings "github.com/argoproj/argo-cd/v2/util/settings" - "github.com/argoproj/argo-cd/v2/util/webhook" "github.com/go-playground/webhooks/v6/azuredevops" "github.com/go-playground/webhooks/v6/github" @@ -27,17 +26,18 @@ import ( log "github.com/sirupsen/logrus" ) -const payloadQueueSize = 50000 +var ( + errBasicAuthVerificationFailed = errors.New("basic auth verification failed") +) type WebhookHandler struct { - sync.WaitGroup // for testing - namespace string - github *github.Webhook - gitlab *gitlab.Webhook - azuredevops *azuredevops.Webhook - client client.Client - generators map[string]generators.Generator - queue chan interface{} + 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 { @@ -68,54 +68,43 @@ type prGeneratorGitlabInfo struct { APIHostname string } -func NewWebhookHandler(namespace string, webhookParallelism int, argocdSettingsMgr *argosettings.SettingsManager, client client.Client, generators map[string]generators.Generator) (*WebhookHandler, error) { +func NewWebhookHandler(namespace string, argocdSettingsMgr *argosettings.SettingsManager, client client.Client, generators map[string]generators.Generator) (*WebhookHandler, error) { // register the webhook secrets stored under "argocd-secret" for verifying incoming payloads argocdSettings, err := argocdSettingsMgr.GetSettings() if err != nil { - return nil, fmt.Errorf("Failed to get argocd settings: %w", err) + return nil, fmt.Errorf("Failed to get argocd settings: %v", err) } githubHandler, err := github.New(github.Options.Secret(argocdSettings.WebhookGitHubSecret)) if err != nil { - return nil, fmt.Errorf("Unable to init GitHub webhook: %w", err) + return nil, fmt.Errorf("Unable to init GitHub webhook: %v", err) } gitlabHandler, err := gitlab.New(gitlab.Options.Secret(argocdSettings.WebhookGitLabSecret)) if err != nil { - return nil, fmt.Errorf("Unable to init GitLab webhook: %w", err) + return nil, fmt.Errorf("Unable to init GitLab webhook: %v", err) } - azuredevopsHandler, err := azuredevops.New(azuredevops.Options.BasicAuth(argocdSettings.WebhookAzureDevOpsUsername, argocdSettings.WebhookAzureDevOpsPassword)) + azuredevopsHandler, err := azuredevops.New() if err != nil { - return nil, fmt.Errorf("Unable to init Azure DevOps webhook: %w", err) - } - - webhookHandler := &WebhookHandler{ - namespace: namespace, - github: githubHandler, - gitlab: gitlabHandler, - azuredevops: azuredevopsHandler, - client: client, - generators: generators, - queue: make(chan interface{}, payloadQueueSize), + return nil, fmt.Errorf("Unable to init Azure DevOps webhook: %v", err) } - - webhookHandler.startWorkerPool(webhookParallelism) - - return webhookHandler, nil -} - -func (h *WebhookHandler) startWorkerPool(webhookParallelism int) { - for i := 0; i < webhookParallelism; i++ { - h.Add(1) - go func() { - defer h.Done() - for { - payload, ok := <-h.queue - if !ok { - return - } - h.HandleEvent(payload) + 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, + azuredevops: azuredevopsHandler, + azuredevopsAuthHandler: azuredevopsAuthHandler, + client: client, + generators: generators, + }, nil } func (h *WebhookHandler) HandleEvent(payload interface{}) { @@ -166,7 +155,13 @@ func (h *WebhookHandler) Handler(w http.ResponseWriter, r *http.Request) { 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") != "": - payload, err = h.azuredevops.Parse(r, azuredevops.GitPushEventType, azuredevops.GitPullRequestCreatedEventType, azuredevops.GitPullRequestUpdatedEventType, azuredevops.GitPullRequestMergedEventType) + 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) @@ -183,12 +178,12 @@ func (h *WebhookHandler) Handler(w http.ResponseWriter, r *http.Request) { return } - select { - case h.queue <- payload: - default: - log.Info("Queue is full, discarding webhook payload") - http.Error(w, "Queue is full, discarding webhook payload", http.StatusServiceUnavailable) - } + h.HandleEvent(payload) +} + +func parseRevision(ref string) string { + refParts := strings.SplitN(ref, "/", 3) + return refParts[len(refParts)-1] } func getGitGeneratorInfo(payload interface{}) *gitGeneratorInfo { @@ -200,16 +195,16 @@ func getGitGeneratorInfo(payload interface{}) *gitGeneratorInfo { switch payload := payload.(type) { case github.PushPayload: webURL = payload.Repository.HTMLURL - revision = webhook.ParseRevision(payload.Ref) + revision = parseRevision(payload.Ref) touchedHead = payload.Repository.DefaultBranch == revision case gitlab.PushEventPayload: webURL = payload.Project.WebURL - revision = webhook.ParseRevision(payload.Ref) + 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 = webhook.ParseRevision(payload.Resource.RefUpdates[0].Name) + 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: @@ -369,12 +364,12 @@ func shouldRefreshPluginGenerator(gen *v1alpha1.PluginGenerator) bool { } func genRevisionHasChanged(gen *v1alpha1.GitGenerator, revision string, touchedHead bool) bool { - targetRev := webhook.ParseRevision(gen.Revision) + targetRev := parseRevision(gen.Revision) if targetRev == "HEAD" || targetRev == "" { // revision is head return touchedHead } - return targetRev == revision || gen.Revision == revision + return targetRev == revision } func gitGeneratorUsesURL(gen *v1alpha1.GitGenerator, webURL string, repoRegexp *regexp.Regexp) bool { @@ -519,7 +514,7 @@ func (h *WebhookHandler) shouldRefreshMatrixGenerator(gen *v1alpha1.MatrixGenera relGenerators := generators.GetRelevantGenerators(requestedGenerator0, h.generators) params := []map[string]interface{}{} for _, g := range relGenerators { - p, err := g.GenerateParams(requestedGenerator0, appSet, h.client) + p, err := g.GenerateParams(requestedGenerator0, appSet) if err != nil { log.Error(err) return false diff --git a/applicationset/webhook/webhook_test.go b/applicationset/webhook/webhook_test.go index 046bbf35f09ab..d22b1a07ca6f2 100644 --- a/applicationset/webhook/webhook_test.go +++ b/applicationset/webhook/webhook_test.go @@ -14,12 +14,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" corev1 "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" "sigs.k8s.io/controller-runtime/pkg/client/fake" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -39,7 +37,7 @@ func (g *generatorMock) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGene return &v1alpha1.ApplicationSetTemplate{} } -func (g *generatorMock) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, _ *v1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) { +func (g *generatorMock) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, _ *v1alpha1.ApplicationSet) ([]map[string]interface{}, error) { return []map[string]interface{}{}, nil } @@ -67,15 +65,6 @@ func TestWebhookHandler(t *testing.T) { expectedStatusCode: http.StatusOK, expectedRefresh: true, }, - { - desc: "WebHook from a GitHub repository via Commit shorthand", - headerKey: "X-GitHub-Event", - headerValue: "push", - payloadFile: "github-commit-event-feature-branch.json", - effectedAppSets: []string{"github-shorthand", "matrix-pull-request-github-plugin", "plugin"}, - expectedStatusCode: http.StatusOK, - expectedRefresh: true, - }, { desc: "WebHook from a GitHub repository via Commit to branch", headerKey: "X-GitHub-Event", @@ -187,13 +176,12 @@ func TestWebhookHandler(t *testing.T) { } namespace := "test" - webhookParallelism := 10 fakeClient := newFakeClient(namespace) scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err) + assert.Nil(t, err) for _, test := range tt { t.Run(test.desc, func(t *testing.T) { @@ -201,7 +189,6 @@ func TestWebhookHandler(t *testing.T) { fakeAppWithGitGenerator("git-github", namespace, "https://github.com/org/repo"), fakeAppWithGitGenerator("git-gitlab", namespace, "https://gitlab/group/name"), fakeAppWithGitGenerator("git-azure-devops", namespace, "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git"), - fakeAppWithGitGeneratorWithRevision("github-shorthand", namespace, "https://github.com/org/repo", "env/dev"), fakeAppWithGithubPullRequestGenerator("pull-request-github", namespace, "CodErTOcat", "Hello-World"), fakeAppWithGitlabPullRequestGenerator("pull-request-gitlab", namespace, "100500"), fakeAppWithAzureDevOpsPullRequestGenerator("pull-request-azure-devops", namespace, "DefaultCollection", "Fabrikam"), @@ -217,24 +204,22 @@ func TestWebhookHandler(t *testing.T) { fakeAppWithMergeAndNestedGitGenerator("merge-nested-git-github", namespace, "https://github.com/org/repo"), ).Build() set := argosettings.NewSettingsManager(context.TODO(), fakeClient, namespace) - h, err := NewWebhookHandler(namespace, webhookParallelism, set, fc, mockGenerators()) - require.NoError(t, err) + h, err := NewWebhookHandler(namespace, set, fc, mockGenerators()) + assert.Nil(t, err) req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil) req.Header.Set(test.headerKey, test.headerValue) eventJSON, err := os.ReadFile(filepath.Join("testdata", test.payloadFile)) - require.NoError(t, err) + assert.NoError(t, err) req.Body = io.NopCloser(bytes.NewReader(eventJSON)) w := httptest.NewRecorder() h.Handler(w, req) - close(h.queue) - h.Wait() - assert.Equal(t, test.expectedStatusCode, w.Code) + assert.Equal(t, w.Code, test.expectedStatusCode) list := &v1alpha1.ApplicationSetList{} err = fc.List(context.TODO(), list) - require.NoError(t, err) + assert.Nil(t, err) effectedAppSetsAsExpected := make(map[string]bool) for _, appSetName := range test.effectedAppSets { effectedAppSetsAsExpected[appSetName] = false @@ -312,62 +297,14 @@ func mockGenerators() map[string]generators.Generator { } func TestGenRevisionHasChanged(t *testing.T) { - type args struct { - gen *v1alpha1.GitGenerator - revision string - touchedHead bool - } - tests := []struct { - name string - args args - want bool - }{ - {name: "touchedHead", args: args{ - gen: &v1alpha1.GitGenerator{}, - revision: "main", - touchedHead: true, - }, want: true}, - {name: "didntTouchHead", args: args{ - gen: &v1alpha1.GitGenerator{}, - revision: "main", - touchedHead: false, - }, want: false}, - {name: "foundEqualShort", args: args{ - gen: &v1alpha1.GitGenerator{Revision: "dev"}, - revision: "dev", - touchedHead: true, - }, want: true}, - {name: "foundEqualLongGen", args: args{ - gen: &v1alpha1.GitGenerator{Revision: "refs/heads/dev"}, - revision: "dev", - touchedHead: true, - }, want: true}, - {name: "foundNotEqualLongGen", args: args{ - gen: &v1alpha1.GitGenerator{Revision: "refs/heads/dev"}, - revision: "main", - touchedHead: true, - }, want: false}, - {name: "foundNotEqualShort", args: args{ - gen: &v1alpha1.GitGenerator{Revision: "dev"}, - revision: "main", - touchedHead: false, - }, want: false}, - {name: "foundEqualTag", args: args{ - gen: &v1alpha1.GitGenerator{Revision: "v3.14.1"}, - revision: "v3.14.1", - touchedHead: false, - }, want: true}, - {name: "foundEqualTagLongGen", args: args{ - gen: &v1alpha1.GitGenerator{Revision: "refs/tags/v3.14.1"}, - revision: "v3.14.1", - touchedHead: false, - }, want: true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equalf(t, tt.want, genRevisionHasChanged(tt.args.gen, tt.args.revision, tt.args.touchedHead), "genRevisionHasChanged(%v, %v, %v)", tt.args.gen, tt.args.revision, tt.args.touchedHead) - }) - } + assert.True(t, genRevisionHasChanged(&v1alpha1.GitGenerator{}, "master", true)) + assert.False(t, genRevisionHasChanged(&v1alpha1.GitGenerator{}, "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(&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 { @@ -389,12 +326,6 @@ func fakeAppWithGitGenerator(name, namespace, repo string) *v1alpha1.Application } } -func fakeAppWithGitGeneratorWithRevision(name, namespace, repo, revision string) *v1alpha1.ApplicationSet { - appSet := fakeAppWithGitGenerator(name, namespace, repo) - appSet.Spec.Generators[0].Git.Revision = revision - return appSet -} - func fakeAppWithGitlabPullRequestGenerator(name, namespace, projectId string) *v1alpha1.ApplicationSet { return &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -775,7 +706,7 @@ func fakeAppWithMatrixAndPullRequestGeneratorWithPluginGenerator(name, namespace func newFakeClient(ns string) *kubefake.Clientset { s := runtime.NewScheme() s.AddKnownTypes(v1alpha1.SchemeGroupVersion, &v1alpha1.ApplicationSet{}) - return kubefake.NewClientset(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "argocd-cm", Namespace: ns, Labels: map[string]string{ + return kubefake.NewSimpleClientset(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "argocd-cm", Namespace: ns, Labels: map[string]string{ "app.kubernetes.io/part-of": "argocd", }}}, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ diff --git a/assets/badge.svg b/assets/badge.svg index f1dab6b6cb711..cc216ccdd1508 100644 --- a/assets/badge.svg +++ b/assets/badge.svg @@ -5,7 +5,6 @@ - @@ -15,7 +14,6 @@ - diff --git a/assets/builtin-policy.csv b/assets/builtin-policy.csv index 81c8ca5092cb4..9413b53d1cba5 100644 --- a/assets/builtin-policy.csv +++ b/assets/builtin-policy.csv @@ -1,10 +1,10 @@ # Built-in policy which defines two roles: role:readonly and role:admin, # and additionally assigns the admin user to the role:admin role. # There are two policy formats: -# 1. Applications, applicationsets, logs, and exec (which belong to a project): -# p, , , , /, +# 1. Applications, logs, and exec (which belong to a project): +# p, , , , / # 2. All other resources: -# p, , , , , +# p, , , , p, role:readonly, applications, get, */*, allow p, role:readonly, certificates, get, *, allow diff --git a/assets/swagger.json b/assets/swagger.json index 1e8a981c51c66..95be211916f9f 100644 --- a/assets/swagger.json +++ b/assets/swagger.json @@ -384,7 +384,7 @@ "parameters": [ { "type": "string", - "description": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names\n+optional", + "description": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#names\n+optional", "name": "application.metadata.name", "in": "path", "required": true @@ -975,25 +975,6 @@ "type": "string", "name": "project", "in": "query" - }, - { - "type": "array", - "items": { - "type": "string", - "format": "int64" - }, - "collectionFormat": "multi", - "name": "sourcePositions", - "in": "query" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi", - "name": "revisions", - "in": "query" } ], "responses": { @@ -1633,20 +1614,6 @@ "type": "string", "name": "project", "in": "query" - }, - { - "type": "integer", - "format": "int32", - "description": "source index (for multi source apps).", - "name": "sourceIndex", - "in": "query" - }, - { - "type": "integer", - "format": "int32", - "description": "versionId from historical data (for multi source apps).", - "name": "versionId", - "in": "query" } ], "responses": { @@ -1697,20 +1664,6 @@ "type": "string", "name": "project", "in": "query" - }, - { - "type": "integer", - "format": "int32", - "description": "source index (for multi source apps).", - "name": "sourceIndex", - "in": "query" - }, - { - "type": "integer", - "format": "int32", - "description": "versionId from historical data (for multi source apps).", - "name": "versionId", - "in": "query" } ], "responses": { @@ -1967,11 +1920,6 @@ "type": "boolean", "name": "upsert", "in": "query" - }, - { - "type": "boolean", - "name": "dryRun", - "in": "query" } ], "responses": { @@ -2063,43 +2011,6 @@ } } }, - "/api/v1/applicationsets/{name}/resource-tree": { - "get": { - "tags": [ - "ApplicationSetService" - ], - "summary": "ResourceTree returns resource tree", - "operationId": "ApplicationSetService_ResourceTree", - "parameters": [ - { - "type": "string", - "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": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v1alpha1ApplicationSetTree" - } - }, - "default": { - "description": "An unexpected error response.", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - } - } - }, "/api/v1/certificates": { "get": { "tags": [ @@ -3001,7 +2912,7 @@ "parameters": [ { "type": "string", - "description": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names\n+optional", + "description": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#names\n+optional", "name": "project.metadata.name", "in": "path", "required": true @@ -3204,7 +3115,7 @@ "parameters": [ { "type": "string", - "description": "URL is the URL to which these credentials match", + "description": "URL is the URL that this credentials matches to", "name": "creds.url", "in": "path", "required": true @@ -3284,12 +3195,6 @@ "description": "Whether to force a cache refresh on repo's connection state.", "name": "forceRefresh", "in": "query" - }, - { - "type": "string", - "description": "App project for query.", - "name": "appProject", - "in": "query" } ], "responses": { @@ -3412,12 +3317,6 @@ "description": "Whether to force a cache refresh on repo's connection state.", "name": "forceRefresh", "in": "query" - }, - { - "type": "string", - "description": "App project for query.", - "name": "appProject", - "in": "query" } ], "responses": { @@ -3454,12 +3353,6 @@ "description": "Whether to force a cache refresh on repo's connection state.", "name": "forceRefresh", "in": "query" - }, - { - "type": "string", - "description": "App project for query.", - "name": "appProject", - "in": "query" } ], "responses": { @@ -3544,12 +3437,6 @@ "description": "Whether to force a cache refresh on repo's connection state.", "name": "forceRefresh", "in": "query" - }, - { - "type": "string", - "description": "App project for query.", - "name": "appProject", - "in": "query" } ], "responses": { @@ -3587,12 +3474,6 @@ "description": "Whether to force a cache refresh on repo's connection state.", "name": "forceRefresh", "in": "query" - }, - { - "type": "string", - "description": "App project for query.", - "name": "appProject", - "in": "query" } ], "responses": { @@ -4338,19 +4219,6 @@ "revision": { "type": "string" }, - "revisions": { - "type": "array", - "items": { - "type": "string" - } - }, - "sourcePositions": { - "type": "array", - "items": { - "type": "string", - "format": "int64" - } - }, "strategy": { "$ref": "#/definitions/v1alpha1SyncStrategy" }, @@ -4489,27 +4357,6 @@ } } }, - "applicationsetApplicationSetGenerateRequest": { - "type": "object", - "title": "ApplicationSetGetQuery is a query for applicationset resources", - "properties": { - "applicationSet": { - "$ref": "#/definitions/v1alpha1ApplicationSet" - } - } - }, - "applicationsetApplicationSetGenerateResponse": { - "type": "object", - "title": "ApplicationSetGenerateResponse is a response for applicationset generate request", - "properties": { - "applications": { - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha1Application" - } - } - } - }, "applicationsetApplicationSetResponse": { "type": "object", "properties": { @@ -4535,43 +4382,6 @@ } } }, - "applicationv1alpha1ResourceStatus": { - "type": "object", - "title": "ResourceStatus holds the current sync and health status of a resource\nTODO: describe members of this type", - "properties": { - "group": { - "type": "string" - }, - "health": { - "$ref": "#/definitions/v1alpha1HealthStatus" - }, - "hook": { - "type": "boolean" - }, - "kind": { - "type": "string" - }, - "name": { - "type": "string" - }, - "namespace": { - "type": "string" - }, - "requiresPruning": { - "type": "boolean" - }, - "status": { - "type": "string" - }, - "syncWave": { - "type": "integer", - "format": "int64" - }, - "version": { - "type": "string" - } - } - }, "clusterClusterID": { "type": "object", "title": "ClusterID holds a cluster server URL or cluster name", @@ -4716,9 +4526,6 @@ "help": { "$ref": "#/definitions/clusterHelp" }, - "impersonationEnabled": { - "type": "boolean" - }, "kustomizeOptions": { "$ref": "#/definitions/v1alpha1KustomizeOptions" }, @@ -5110,13 +4917,6 @@ "repositoryManifestResponse": { "type": "object", "properties": { - "commands": { - "type": "array", - "title": "Commands is the list of commands used to hydrate the manifests", - "items": { - "type": "string" - } - }, "manifests": { "type": "array", "items": { @@ -5231,16 +5031,6 @@ }, "source": { "$ref": "#/definitions/v1alpha1ApplicationSource" - }, - "sourceIndex": { - "type": "integer", - "format": "int32", - "title": "source index (for multi source apps)" - }, - "versionId": { - "type": "integer", - "format": "int32", - "title": "versionId from historical data (for multi source apps)" } } }, @@ -5525,7 +5315,7 @@ "properties": { "matchExpressions": { "type": "array", - "title": "matchExpressions is a list of label selector requirements. The requirements are ANDed.\n+optional\n+listType=atomic", + "title": "matchExpressions is a list of label selector requirements. The requirements are ANDed.\n+optional", "items": { "$ref": "#/definitions/v1LabelSelectorRequirement" } @@ -5544,8 +5334,8 @@ "type": "object", "properties": { "key": { - "description": "key is the label key that the selector applies to.", - "type": "string" + "type": "string", + "title": "key is the label key that the selector applies to.\n+patchMergeKey=key\n+patchStrategy=merge" }, "operator": { "description": "operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist.", @@ -5553,7 +5343,7 @@ }, "values": { "type": "array", - "title": "values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch.\n+optional\n+listType=atomic", + "title": "values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch.\n+optional", "items": { "type": "string" } @@ -5595,10 +5385,6 @@ "type": "string", "title": "IP is set for load-balancer ingress points that are IP based\n(typically GCE or OpenStack load-balancers)\n+optional" }, - "ipMode": { - "type": "string", - "title": "IPMode specifies how the load-balancer IP behaves, and may only be specified when the ip field is specified.\nSetting this to \"VIP\" indicates that traffic is delivered to the node with\nthe destination set to the load-balancer's IP and port.\nSetting this to \"Proxy\" indicates that traffic is delivered to the node or pod with\nthe destination set to the node's IP and node port or the pod's IP and port.\nService implementations may use this information to adjust traffic routing.\n+optional" - }, "ports": { "type": "array", "title": "Ports is a list of records of service ports\nIf used, every port defined in the service should have an entry in it\n+listType=atomic\n+optional", @@ -5677,7 +5463,7 @@ "type": "string" }, "kubeProxyVersion": { - "description": "Deprecated: KubeProxy Version reported by the node.", + "description": "KubeProxy Version reported by the node.", "type": "string" }, "kubeletVersion": { @@ -5708,7 +5494,7 @@ "properties": { "annotations": { "type": "object", - "title": "Annotations is an unstructured key value map stored with a resource that may be\nset by external tools to store and retrieve arbitrary metadata. They are not\nqueryable and should be preserved when modifying objects.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations\n+optional", + "title": "Annotations is an unstructured key value map stored with a resource that may be\nset by external tools to store and retrieve arbitrary metadata. They are not\nqueryable and should be preserved when modifying objects.\nMore info: http://kubernetes.io/docs/user-guide/annotations\n+optional", "additionalProperties": { "type": "string" } @@ -5726,7 +5512,7 @@ }, "finalizers": { "type": "array", - "title": "Must be empty before the object is deleted from the registry. Each entry\nis an identifier for the responsible component that will remove the entry\nfrom the list. If the deletionTimestamp of the object is non-nil, entries\nin this list can only be removed.\nFinalizers may be processed and removed in any order. Order is NOT enforced\nbecause it introduces significant risk of stuck finalizers.\nfinalizers is a shared field, any actor with permission can reorder it.\nIf the finalizer list is processed in order, then this can lead to a situation\nin which the component responsible for the first finalizer in the list is\nwaiting for a signal (field value, external system, or other) produced by a\ncomponent responsible for a finalizer later in the list, resulting in a deadlock.\nWithout enforced ordering finalizers are free to order amongst themselves and\nare not vulnerable to ordering changes in the list.\n+optional\n+patchStrategy=merge\n+listType=set", + "title": "Must be empty before the object is deleted from the registry. Each entry\nis an identifier for the responsible component that will remove the entry\nfrom the list. If the deletionTimestamp of the object is non-nil, entries\nin this list can only be removed.\nFinalizers may be processed and removed in any order. Order is NOT enforced\nbecause it introduces significant risk of stuck finalizers.\nfinalizers is a shared field, any actor with permission can reorder it.\nIf the finalizer list is processed in order, then this can lead to a situation\nin which the component responsible for the first finalizer in the list is\nwaiting for a signal (field value, external system, or other) produced by a\ncomponent responsible for a finalizer later in the list, resulting in a deadlock.\nWithout enforced ordering finalizers are free to order amongst themselves and\nare not vulnerable to ordering changes in the list.\n+optional\n+patchStrategy=merge", "items": { "type": "string" } @@ -5742,13 +5528,13 @@ }, "labels": { "type": "object", - "title": "Map of string keys and values that can be used to organize and categorize\n(scope and select) objects. May match selectors of replication controllers\nand services.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels\n+optional", + "title": "Map of string keys and values that can be used to organize and categorize\n(scope and select) objects. May match selectors of replication controllers\nand services.\nMore info: http://kubernetes.io/docs/user-guide/labels\n+optional", "additionalProperties": { "type": "string" } }, "managedFields": { - "description": "ManagedFields maps workflow-id and version to the set of fields\nthat are managed by that workflow. This is mostly for internal\nhousekeeping, and users typically shouldn't need to set or\nunderstand this field. A workflow can be the user's name, a\ncontroller's name, or the name of a specific apply path like\n\"ci-cd\". The set of fields is always in the version that the\nworkflow used when modifying the object.\n\n+optional\n+listType=atomic", + "description": "ManagedFields maps workflow-id and version to the set of fields\nthat are managed by that workflow. This is mostly for internal\nhousekeeping, and users typically shouldn't need to set or\nunderstand this field. A workflow can be the user's name, a\ncontroller's name, or the name of a specific apply path like\n\"ci-cd\". The set of fields is always in the version that the\nworkflow used when modifying the object.\n\n+optional", "type": "array", "items": { "$ref": "#/definitions/v1ManagedFieldsEntry" @@ -5756,15 +5542,15 @@ }, "name": { "type": "string", - "title": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names\n+optional" + "title": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#names\n+optional" }, "namespace": { - "description": "Namespace defines the space within which each name must be unique. An empty namespace is\nequivalent to the \"default\" namespace, but \"default\" is the canonical representation.\nNot all objects are required to be scoped to a namespace - the value of this field for\nthose objects will be empty.\n\nMust be a DNS_LABEL.\nCannot be updated.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces\n+optional", + "description": "Namespace defines the space within which each name must be unique. An empty namespace is\nequivalent to the \"default\" namespace, but \"default\" is the canonical representation.\nNot all objects are required to be scoped to a namespace - the value of this field for\nthose objects will be empty.\n\nMust be a DNS_LABEL.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/namespaces\n+optional", "type": "string" }, "ownerReferences": { "type": "array", - "title": "List of objects depended by this object. If ALL objects in the list have\nbeen deleted, this object will be garbage collected. If this object is managed by a controller,\nthen an entry in this list will point to this controller, with the controller field set to true.\nThere cannot be more than one managing controller.\n+optional\n+patchMergeKey=uid\n+patchStrategy=merge\n+listType=map\n+listMapKey=uid", + "title": "List of objects depended by this object. If ALL objects in the list have\nbeen deleted, this object will be garbage collected. If this object is managed by a controller,\nthen an entry in this list will point to this controller, with the controller field set to true.\nThere cannot be more than one managing controller.\n+optional\n+patchMergeKey=uid\n+patchStrategy=merge", "items": { "$ref": "#/definitions/v1OwnerReference" } @@ -5778,7 +5564,7 @@ "title": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.\n+optional" }, "uid": { - "description": "UID is the unique in time and space value for this object. It is typically generated by\nthe server on successful creation of a resource and is not allowed to change on PUT\noperations.\n\nPopulated by the system.\nRead-only.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids\n+optional", + "description": "UID is the unique in time and space value for this object. It is typically generated by\nthe server on successful creation of a resource and is not allowed to change on PUT\noperations.\n\nPopulated by the system.\nRead-only.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#uids\n+optional", "type": "string" } } @@ -5839,11 +5625,11 @@ }, "name": { "type": "string", - "title": "Name of the referent.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names" + "title": "Name of the referent.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#names" }, "uid": { "type": "string", - "title": "UID of the referent.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids" + "title": "UID of the referent.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#uids" } } }, @@ -5878,10 +5664,6 @@ "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" @@ -5940,13 +5722,6 @@ "type": "string", "title": "Description contains optional project description" }, - "destinationServiceAccounts": { - "description": "DestinationServiceAccounts holds information about the service accounts to be impersonated for the application sync operation for each destination.", - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha1ApplicationDestinationServiceAccount" - } - }, "destinations": { "type": "array", "title": "Destinations contains list of destinations available for deployment", @@ -6027,7 +5802,7 @@ }, "v1alpha1Application": { "type": "object", - "title": "Application is a definition of Application resource.\n+genclient\n+genclient:noStatus\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+kubebuilder:resource:path=applications,shortName=app;apps\n+kubebuilder:printcolumn:name=\"Sync Status\",type=string,JSONPath=`.status.sync.status`\n+kubebuilder:printcolumn:name=\"Health Status\",type=string,JSONPath=`.status.health.status`\n+kubebuilder:printcolumn:name=\"Revision\",type=string,JSONPath=`.status.sync.revision`,priority=10\n+kubebuilder:printcolumn:name=\"Project\",type=string,JSONPath=`.spec.project`,priority=10", + "title": "Application is a definition of Application resource.\n+genclient\n+genclient:noStatus\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+kubebuilder:resource:path=applications,shortName=app;apps\n+kubebuilder:printcolumn:name=\"Sync Status\",type=string,JSONPath=`.status.sync.status`\n+kubebuilder:printcolumn:name=\"Health Status\",type=string,JSONPath=`.status.health.status`\n+kubebuilder:printcolumn:name=\"Revision\",type=string,JSONPath=`.status.sync.revision`,priority=10", "properties": { "metadata": { "$ref": "#/definitions/v1ObjectMeta" @@ -6078,24 +5853,6 @@ } } }, - "v1alpha1ApplicationDestinationServiceAccount": { - "description": "ApplicationDestinationServiceAccount holds information about the service account to be impersonated for the application sync operation.", - "type": "object", - "properties": { - "defaultServiceAccount": { - "type": "string", - "title": "ServiceAccountName to be used for impersonation during the sync operation" - }, - "namespace": { - "description": "Namespace specifies the target namespace for the application's resources.", - "type": "string" - }, - "server": { - "description": "Server specifies the URL of the target cluster's Kubernetes control plane API.", - "type": "string" - } - } - }, "v1alpha1ApplicationList": { "type": "object", "title": "ApplicationList is list of Application resources\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object", @@ -6182,19 +5939,12 @@ "step": { "type": "string", "title": "Step tracks which step this Application should be updated in" - }, - "targetrevisions": { - "description": "TargetRevision tracks the desired revisions the Application should be synced to.", - "type": "array", - "items": { - "type": "string" - } } } }, "v1alpha1ApplicationSetCondition": { "type": "object", - "title": "ApplicationSetCondition contains details about an applicationset condition, which is usually an error or warning", + "title": "ApplicationSetCondition contains details about an applicationset condition, which is usally an error or warning", "properties": { "lastTransitionTime": { "$ref": "#/definitions/v1Time" @@ -6415,13 +6165,6 @@ "items": { "$ref": "#/definitions/v1alpha1ApplicationSetCondition" } - }, - "resources": { - "description": "Resources is a list of Applications resources managed by this application set.", - "type": "array", - "items": { - "$ref": "#/definitions/applicationv1alpha1ResourceStatus" - } } } }, @@ -6493,19 +6236,6 @@ } } }, - "v1alpha1ApplicationSetTree": { - "type": "object", - "title": "ApplicationSetTree holds nodes which belongs to the application\nUsed to build a tree of an ApplicationSet and its children", - "properties": { - "nodes": { - "type": "array", - "title": "Nodes contains list of nodes which are directly managed by the applicationset", - "items": { - "$ref": "#/definitions/v1alpha1ResourceNode" - } - } - } - }, "v1alpha1ApplicationSource": { "type": "object", "title": "ApplicationSource contains all required information about the source of an application", @@ -6569,13 +6299,6 @@ "type": "object", "title": "ApplicationSourceHelm holds helm specific options", "properties": { - "apiVersions": { - "description": "APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default,\nArgo CD uses the API versions of the target cluster. The format is [group/]version/kind.", - "type": "array", - "items": { - "type": "string" - } - }, "fileParameters": { "type": "array", "title": "FileParameters are file parameters to the helm template", @@ -6587,14 +6310,6 @@ "type": "boolean", "title": "IgnoreMissingValueFiles prevents helm template from failing when valueFiles do not exist locally by not appending them to helm template --values" }, - "kubeVersion": { - "description": "KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD\nuses the Kubernetes version of the target cluster.", - "type": "string" - }, - "namespace": { - "description": "Namespace is an optional namespace to template with. If left empty, defaults to the app's destination namespace.", - "type": "string" - }, "parameters": { "type": "array", "title": "Parameters is a list of Helm parameters which are passed to the helm template command upon manifest generation", @@ -6665,13 +6380,6 @@ "type": "object", "title": "ApplicationSourceKustomize holds options specific to an Application source specific to Kustomize", "properties": { - "apiVersions": { - "description": "APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default,\nArgo CD uses the API versions of the target cluster. The format is [group/]version/kind.", - "type": "array", - "items": { - "type": "string" - } - }, "commonAnnotations": { "type": "object", "title": "CommonAnnotations is a list of additional annotations to add to rendered manifests", @@ -6712,14 +6420,6 @@ "type": "string" } }, - "kubeVersion": { - "description": "KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD\nuses the Kubernetes version of the target cluster.", - "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" @@ -6887,7 +6587,7 @@ "type": "array", "title": "Resources is a list of Kubernetes resources managed by this application", "items": { - "$ref": "#/definitions/applicationv1alpha1ResourceStatus" + "$ref": "#/definitions/v1alpha1ResourceStatus" } }, "sourceType": { @@ -6953,11 +6653,6 @@ "items": { "$ref": "#/definitions/v1alpha1ResourceNode" } - }, - "shardsCount": { - "type": "integer", - "format": "int64", - "title": "ShardsCount contains total number of shards the application tree is split into" } } }, @@ -7005,15 +6700,6 @@ } } }, - "v1alpha1BearerTokenBitbucket": { - "description": "BearerTokenBitbucket defines the Bearer token for BitBucket AppToken auth.", - "type": "object", - "properties": { - "tokenRef": { - "$ref": "#/definitions/v1alpha1SecretRef" - } - } - }, "v1alpha1BearerTokenBitbucketCloud": { "description": "BearerTokenBitbucketCloud defines the Bearer token for BitBucket AppToken auth.", "type": "object", @@ -7098,7 +6784,7 @@ }, "serverVersion": { "type": "string", - "title": "Deprecated: use Info.ServerVersion field instead.\nThe server version" + "title": "DEPRECATED: use Info.ServerVersion field instead.\nThe server version" }, "shard": { "description": "Shard contains optional shard number. Calculated on the fly by the application controller if not specified.", @@ -7276,18 +6962,6 @@ } } }, - "v1alpha1ConfigMapKeyRef": { - "description": "Utility struct for a reference to a configmap key.", - "type": "object", - "properties": { - "configMapName": { - "type": "string" - }, - "key": { - "type": "string" - } - } - }, "v1alpha1ConnectionState": { "type": "object", "title": "ConnectionState contains information about remote resource connection state, currently used for clusters and repositories", @@ -8112,16 +7786,6 @@ "basicAuth": { "$ref": "#/definitions/v1alpha1BasicAuthBitbucketServer" }, - "bearerToken": { - "$ref": "#/definitions/v1alpha1BearerTokenBitbucket" - }, - "caRef": { - "$ref": "#/definitions/v1alpha1ConfigMapKeyRef" - }, - "insecure": { - "type": "boolean", - "title": "Allow self-signed TLS / Certificates; default: false" - }, "project": { "description": "Project to scan. Required.", "type": "string" @@ -8152,9 +7816,6 @@ "description": "The GitLab API URL to talk to. If blank, uses https://gitlab.com/.", "type": "string" }, - "caRef": { - "$ref": "#/definitions/v1alpha1ConfigMapKeyRef" - }, "insecure": { "type": "boolean", "title": "Skips validating the SCM provider's TLS certificate - useful for self-signed certificates.; default: false" @@ -8270,10 +7931,6 @@ "type": "string", "title": "GithubAppPrivateKey specifies the private key PEM data for authentication via GitHub app" }, - "noProxy": { - "type": "string", - "title": "NoProxy specifies a list of targets where the proxy isn't used, applies only in cases where the proxy is applied" - }, "password": { "type": "string", "title": "Password for authenticating at the repo server" @@ -8300,7 +7957,7 @@ }, "url": { "type": "string", - "title": "URL is the URL to which these credentials match" + "title": "URL is the URL that this credentials matches to" }, "username": { "type": "string", @@ -8380,17 +8037,13 @@ "type": "string", "title": "Name specifies a name to be used for this repo. Only used with Helm repos" }, - "noProxy": { - "type": "string", - "title": "NoProxy specifies a list of targets where the proxy isn't used, applies only in cases where the proxy is applied" - }, "password": { "type": "string", "title": "Password contains the password or PAT used for authenticating at the remote repository" }, "project": { "type": "string", - "title": "Reference between project and repository that allows it to be automatically added as an item inside SourceRepos project entity" + "title": "Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity" }, "proxy": { "type": "string", @@ -8781,6 +8434,43 @@ } } }, + "v1alpha1ResourceStatus": { + "type": "object", + "title": "ResourceStatus holds the current sync and health status of a resource\nTODO: describe members of this type", + "properties": { + "group": { + "type": "string" + }, + "health": { + "$ref": "#/definitions/v1alpha1HealthStatus" + }, + "hook": { + "type": "boolean" + }, + "kind": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "requiresPruning": { + "type": "boolean" + }, + "status": { + "type": "string" + }, + "syncWave": { + "type": "integer", + "format": "int64" + }, + "version": { + "type": "string" + } + } + }, "v1alpha1RetryStrategy": { "type": "object", "title": "RetryStrategy contains information about the strategy to apply when a sync failed", @@ -8810,9 +8500,6 @@ "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" @@ -9003,16 +8690,6 @@ "basicAuth": { "$ref": "#/definitions/v1alpha1BasicAuthBitbucketServer" }, - "bearerToken": { - "$ref": "#/definitions/v1alpha1BearerTokenBitbucket" - }, - "caRef": { - "$ref": "#/definitions/v1alpha1ConfigMapKeyRef" - }, - "insecure": { - "type": "boolean", - "title": "Allow self-signed TLS / Certificates; default: false" - }, "project": { "description": "Project to scan. Required.", "type": "string" @@ -9113,9 +8790,6 @@ "description": "The Gitlab API URL to talk to.", "type": "string" }, - "caRef": { - "$ref": "#/definitions/v1alpha1ConfigMapKeyRef" - }, "group": { "description": "Gitlab group to scan. Required. You can use either the project id (recommended) or the full namespaced path.", "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 95dc64466ccef..0d8aa6856ed4c 100644 --- a/cmd/argocd-application-controller/commands/argocd_application_controller.go +++ b/cmd/argocd-application-controller/commands/argocd_application_controller.go @@ -4,9 +4,6 @@ import ( "context" "fmt" "math" - "os" - "os/signal" - "syscall" "time" "github.com/argoproj/pkg/stats" @@ -24,7 +21,6 @@ import ( 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" - "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/argo/normalizers" cacheutil "github.com/argoproj/argo-cd/v2/util/cache" appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" @@ -63,7 +59,6 @@ func NewCommand() *cobra.Command { metricsPort int metricsCacheExpiration time.Duration metricsAplicationLabels []string - metricsAplicationConditions []string kubectlParallelismLimit int64 cacheSource func() (*appstatecache.Cache, error) redisClient *redis.Client @@ -79,11 +74,8 @@ func NewCommand() *cobra.Command { enableDynamicClusterDistribution bool serverSideDiff bool ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts - - // argocd k8s event logging flag - enableK8sEvent []string ) - command := cobra.Command{ + var command = cobra.Command{ Use: cliName, Short: "Run ArgoCD Application Controller", Long: "ArgoCD application controller is a Kubernetes controller that continuously monitors running applications and compares the current, live state against the desired target state (as specified in the repo). This command runs Application Controller in the foreground. It can be configured by following options.", @@ -172,7 +164,6 @@ func NewCommand() *cobra.Command { metricsPort, metricsCacheExpiration, metricsAplicationLabels, - metricsAplicationConditions, kubectlParallelismLimit, persistResourceHealth, clusterSharding, @@ -181,7 +172,6 @@ func NewCommand() *cobra.Command { serverSideDiff, enableDynamicClusterDistribution, ignoreNormalizerOpts, - enableK8sEvent, ) errors.CheckError(err) cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer()) @@ -198,22 +188,10 @@ func NewCommand() *cobra.Command { defer closeTracer() } - // Graceful shutdown code - sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) - go func() { - s := <-sigCh - log.Printf("got signal %v, attempting graceful shutdown", s) - cancel() - }() - go appController.Run(ctx, statusProcessors, operationProcessors) - <-ctx.Done() - - log.Println("clean shutdown") - - return nil + // Wait forever + select {} }, } @@ -236,14 +214,13 @@ func NewCommand() *cobra.Command { 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().StringSliceVar(&metricsAplicationConditions, "metrics-application-conditions", []string{}, "List of Application conditions that will be added to the argocd_application_conditions 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") - command.Flags().StringVar(&shardingAlgorithm, "sharding-method", env.StringFromEnv(common.EnvControllerShardingAlgorithm, common.DefaultShardingAlgorithm), "Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin, consistent-hashing] ") + 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") @@ -256,13 +233,8 @@ func NewCommand() *cobra.Command { 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\")") command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout-seconds", env.ParseDurationFromEnv("ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT", 0*time.Second, 0, math.MaxInt64), "Set ignore normalizer JQ execution timeout") - // argocd k8s event logging flag - command.Flags().StringSliceVar(&enableK8sEvent, "enable-k8s-event", env.StringsFromEnv("ARGOCD_ENABLE_K8S_EVENT", argo.DefaultEnableEventList(), ","), "Enable ArgoCD to use k8s event. For disabling all events, set the value as `none`. (e.g --enable-k8s-event=none), For enabling specific events, set the value as `event reason`. (e.g --enable-k8s-event=StatusRefreshed,ResourceCreated)") - - cacheSource = appstatecache.AddCacheFlagsToCmd(&command, cacheutil.Options{ - OnClientCreated: func(client *redis.Client) { - redisClient = client - }, + cacheSource = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) { + redisClient = client }) return &command } diff --git a/cmd/argocd-applicationset-controller/commands/applicationset_controller.go b/cmd/argocd-applicationset-controller/commands/applicationset_controller.go index d2f4ce36d98cf..9adbc3e64a685 100644 --- a/cmd/argocd-applicationset-controller/commands/applicationset_controller.go +++ b/cmd/argocd-applicationset-controller/commands/applicationset_controller.go @@ -12,7 +12,6 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" - logutils "github.com/argoproj/argo-cd/v2/util/log" "github.com/argoproj/argo-cd/v2/util/tls" "github.com/argoproj/argo-cd/v2/applicationset/controllers" @@ -31,11 +30,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "k8s.io/client-go/tools/clientcmd" - ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache" - ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" - metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - appsetmetrics "github.com/argoproj/argo-cd/v2/applicationset/metrics" "github.com/argoproj/argo-cd/v2/applicationset/services" appv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" @@ -70,14 +65,12 @@ func NewCommand() *cobra.Command { allowedScmProviders []string globalPreservedAnnotations []string globalPreservedLabels []string - metricsAplicationsetLabels []string enableScmProviders bool - webhookParallelism int ) scheme := runtime.NewScheme() _ = clientgoscheme.AddToScheme(scheme) _ = appv1alpha1.AddToScheme(scheme) - command := cobra.Command{ + var command = cobra.Command{ Use: "controller", Short: "Starts Argo CD ApplicationSet controller", RunE: func(c *cobra.Command, args []string) error { @@ -98,8 +91,6 @@ func NewCommand() *cobra.Command { cli.SetLogFormat(cmdutil.LogFormat) cli.SetLogLevel(cmdutil.LogLevel) - ctrl.SetLogger(logutils.NewLogrusLogger(logutils.NewWithCurrentConfig())) - restConfig, err := clientConfig.ClientConfig() errors.CheckError(err) @@ -111,7 +102,7 @@ func NewCommand() *cobra.Command { os.Exit(1) } - // By default, watch all namespaces + // By default watch all namespace var watchedNamespace string = "" // If the applicationset-namespaces contains only one namespace it corresponds to the current namespace @@ -122,36 +113,17 @@ func NewCommand() *cobra.Command { os.Exit(1) } - var cacheOpt ctrlcache.Options - - if watchedNamespace != "" { - cacheOpt = ctrlcache.Options{ - DefaultNamespaces: map[string]ctrlcache.Config{ - watchedNamespace: {}, - }, - } - } - - cfg := ctrl.GetConfigOrDie() - err = appv1alpha1.SetK8SConfigDefaults(cfg) - if err != nil { - log.Error(err, "Unable to apply K8s REST config defaults") - os.Exit(1) - } - - mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme, - Metrics: metricsserver.Options{ - BindAddress: metricsAddr, - }, - Cache: cacheOpt, + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: metricsAddr, + Namespace: watchedNamespace, HealthProbeBindAddress: probeBindAddr, + Port: 9443, LeaderElection: enableLeaderElection, LeaderElectionID: "58ac56fa.applicationsets.argoproj.io", - Client: ctrlclient.Options{ - DryRun: &dryRun, - }, + DryRunClient: dryRun, }) + if err != nil { log.Error(err, "unable to start manager") os.Exit(1) @@ -165,11 +137,13 @@ func NewCommand() *cobra.Command { appSetConfig := appclientset.NewForConfigOrDie(mgr.GetConfig()) argoCDDB := db.NewDB(namespace, argoSettingsMgr, k8sClient) - scmConfig := generators.NewSCMConfig(scmRootCAPath, allowedScmProviders, enableScmProviders, github_app.NewAuthCredentials(argoCDDB.(db.RepoCredsDB))) + scmAuth := generators.SCMAuthProviders{ + GitHubApps: github_app.NewAuthCredentials(argoCDDB.(db.RepoCredsDB)), + } tlsConfig := apiclient.TLSConfiguration{ DisableTLS: repoServerPlaintext, - StrictValidation: repoServerStrictTLS, + StrictValidation: repoServerPlaintext, } if !repoServerPlaintext && repoServerStrictTLS { @@ -182,13 +156,45 @@ func NewCommand() *cobra.Command { } repoClientset := apiclient.NewRepoServerClientset(argocdRepoServer, repoServerTimeoutSeconds, tlsConfig) - argoCDService, err := services.NewArgoCDService(argoCDDB.GetRepository, gitSubmoduleEnabled, repoClientset, enableNewGitFileGlobbing) + argoCDService, err := services.NewArgoCDService(argoCDDB, gitSubmoduleEnabled, repoClientset, enableNewGitFileGlobbing) errors.CheckError(err) - topLevelGenerators := generators.GetGenerators(ctx, mgr.GetClient(), k8sClient, namespace, argoCDService, dynamicClient, scmConfig) + terminalGenerators := map[string]generators.Generator{ + "List": generators.NewListGenerator(), + "Clusters": generators.NewClusterGenerator(mgr.GetClient(), ctx, k8sClient, namespace), + "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, scmRootCAPath, allowedScmProviders, enableScmProviders), + "Plugin": generators.NewPluginGenerator(mgr.GetClient(), ctx, k8sClient, namespace), + } + + nestedGenerators := map[string]generators.Generator{ + "List": terminalGenerators["List"], + "Clusters": terminalGenerators["Clusters"], + "Git": terminalGenerators["Git"], + "SCMProvider": terminalGenerators["SCMProvider"], + "ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"], + "PullRequest": terminalGenerators["PullRequest"], + "Plugin": terminalGenerators["Plugin"], + "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"], + "Plugin": terminalGenerators["Plugin"], + "Matrix": generators.NewMatrixGenerator(nestedGenerators), + "Merge": generators.NewMergeGenerator(nestedGenerators), + } // start a webhook server that listens to incoming webhook payloads - webhookHandler, err := webhook.NewWebhookHandler(namespace, webhookParallelism, argoSettingsMgr, mgr.GetClient(), topLevelGenerators) + webhookHandler, err := webhook.NewWebhookHandler(namespace, argoSettingsMgr, mgr.GetClient(), topLevelGenerators) if err != nil { log.Error(err, "failed to create webhook handler") } @@ -196,13 +202,6 @@ func NewCommand() *cobra.Command { startWebhookServer(webhookHandler, webhookAddr) } - metrics := appsetmetrics.NewApplicationsetMetrics( - utils.NewAppsetLister(mgr.GetClient()), - metricsAplicationsetLabels, - func(appset *appv1alpha1.ApplicationSet) bool { - return utils.IsNamespaceAllowed(applicationSetNamespaces, appset.Namespace) - }) - if err = (&controllers.ApplicationSetReconciler{ Generators: topLevelGenerators, Client: mgr.GetClient(), @@ -220,7 +219,7 @@ func NewCommand() *cobra.Command { SCMRootCAPath: scmRootCAPath, GlobalPreservedAnnotations: globalPreservedAnnotations, GlobalPreservedLabels: globalPreservedLabels, - Metrics: &metrics, + Cache: mgr.GetCache(), }).SetupWithManager(mgr, enableProgressiveSyncs, maxConcurrentReconciliations); err != nil { log.Error(err, "unable to create controller", "controller", "ApplicationSet") os.Exit(1) @@ -244,7 +243,7 @@ func NewCommand() *cobra.Command { "Enabling this will ensure there is only one active controller manager.") 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", ""), "Modify how application is synced between the generator and the cluster. Default is '' (empty), which means AppSets default to 'sync', but they may override that default. Setting an explicit value prevents AppSet-level overrides, unless --allow-policy-override is enabled. Explicit options are: 'sync' (create & update & delete), '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") @@ -261,8 +260,6 @@ func NewCommand() *cobra.Command { 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") - command.Flags().IntVar(&webhookParallelism, "webhook-parallelism-limit", env.ParseNumFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_WEBHOOK_PARALLELISM_LIMIT", 50, 1, 1000), "Number of webhook requests processed concurrently") - command.Flags().StringSliceVar(&metricsAplicationsetLabels, "metrics-applicationset-labels", []string{}, "List of Application labels that will be added to the argocd_applicationset_labels metric") return &command } @@ -270,7 +267,7 @@ func startWebhookServer(webhookHandler *webhook.WebhookHandler, webhookAddr stri mux := http.NewServeMux() mux.HandleFunc("/api/webhook", webhookHandler.Handler) go func() { - log.Infof("Starting webhook server %s", webhookAddr) + log.Info("Starting webhook server") err := http.ListenAndServe(webhookAddr, mux) if err != nil { log.Error(err, "failed to start webhook server") diff --git a/cmd/argocd-cmp-server/commands/argocd_cmp_server.go b/cmd/argocd-cmp-server/commands/argocd_cmp_server.go index 197f52e01ade7..526a199cb5490 100644 --- a/cmd/argocd-cmp-server/commands/argocd_cmp_server.go +++ b/cmd/argocd-cmp-server/commands/argocd_cmp_server.go @@ -30,7 +30,7 @@ func NewCommand() *cobra.Command { otlpHeaders map[string]string otlpAttrs []string ) - command := cobra.Command{ + 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. The following configuration options are available:", @@ -81,8 +81,8 @@ func NewCommand() *cobra.Command { }, } - command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_CMP_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json") - command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_CMP_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: trace|debug|info|warn|error") + command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json") + 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") diff --git a/cmd/argocd-dex/commands/argocd_dex.go b/cmd/argocd-dex/commands/argocd_dex.go index 43efbbb050dd5..2b070ec895e41 100644 --- a/cmd/argocd-dex/commands/argocd_dex.go +++ b/cmd/argocd-dex/commands/argocd_dex.go @@ -28,7 +28,7 @@ const ( ) func NewCommand() *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: cliName, Short: "argocd-dex tools used by Argo CD", Long: "argocd-dex has internal utility tools used by Argo CD", @@ -48,7 +48,7 @@ func NewRunDexCommand() *cobra.Command { clientConfig clientcmd.ClientConfig disableTLS bool ) - command := cobra.Command{ + var command = cobra.Command{ Use: "rundex", Short: "Runs dex generating a config using settings from the Argo CD configmap and secret", RunE: func(c *cobra.Command, args []string) error { @@ -79,11 +79,11 @@ func NewRunDexCommand() *cobra.Command { log.Fatalf("could not create TLS config: %v", err) } certPem, keyPem := tls.EncodeX509KeyPair(config.Certificates[0]) - err = os.WriteFile("/tmp/tls.crt", certPem, 0o600) + err = os.WriteFile("/tmp/tls.crt", certPem, 0600) if err != nil { log.Fatalf("could not write TLS certificate: %v", err) } - err = os.WriteFile("/tmp/tls.key", keyPem, 0o600) + err = os.WriteFile("/tmp/tls.key", keyPem, 0600) if err != nil { log.Fatalf("could not write TLS key: %v", err) } @@ -102,7 +102,7 @@ func NewRunDexCommand() *cobra.Command { if len(dexCfgBytes) == 0 { log.Infof("dex is not configured") } else { - err = os.WriteFile("/tmp/dex.yaml", dexCfgBytes, 0o644) + err = os.WriteFile("/tmp/dex.yaml", dexCfgBytes, 0644) errors.CheckError(err) log.Debug(redactor(string(dexCfgBytes))) cmd = exec.Command("dex", "serve", "/tmp/dex.yaml") @@ -136,8 +136,8 @@ func NewRunDexCommand() *cobra.Command { } clientConfig = cli.AddKubectlFlagsToCmd(&command) - command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json") - command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error") + command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json") + command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error") command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_DEX_SERVER_DISABLE_TLS", false), "Disable TLS on the HTTP endpoint") return &command } @@ -148,7 +148,7 @@ func NewGenDexConfigCommand() *cobra.Command { out string disableTLS bool ) - command := cobra.Command{ + var command = cobra.Command{ Use: "gendexcfg", Short: "Generates a dex config from Argo CD settings", RunE: func(c *cobra.Command, args []string) error { @@ -196,7 +196,7 @@ func NewGenDexConfigCommand() *cobra.Command { errors.CheckError(err) fmt.Print(string(maskedDexCfgBytes)) } else { - err = os.WriteFile(out, dexCfgBytes, 0o644) + err = os.WriteFile(out, dexCfgBytes, 0644) errors.CheckError(err) } return nil @@ -204,8 +204,8 @@ func NewGenDexConfigCommand() *cobra.Command { } clientConfig = cli.AddKubectlFlagsToCmd(&command) - command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json") - command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error") + command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json") + command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error") command.Flags().StringVarP(&out, "out", "o", "", "Output to the specified file instead of stdout") command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_DEX_SERVER_DISABLE_TLS", false), "Disable TLS on the HTTP endpoint") return &command diff --git a/cmd/argocd-git-ask-pass/commands/argocd_git_ask_pass.go b/cmd/argocd-git-ask-pass/commands/argocd_git_ask_pass.go index 0b9d05787a6e1..8f457527b78b2 100644 --- a/cmd/argocd-git-ask-pass/commands/argocd_git_ask_pass.go +++ b/cmd/argocd-git-ask-pass/commands/argocd_git_ask_pass.go @@ -5,6 +5,8 @@ import ( "os" "strings" + "github.com/argoproj/argo-cd/v2/util/git" + "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -21,7 +23,7 @@ const ( ) func NewCommand() *cobra.Command { - command := cobra.Command{ + var command = cobra.Command{ Use: cliName, Short: "Argo CD git credential helper", DisableAutoGenTag: true, @@ -31,9 +33,9 @@ func NewCommand() *cobra.Command { if len(os.Args) != 2 { errors.CheckError(fmt.Errorf("expected 1 argument, got %d", len(os.Args)-1)) } - nonce := os.Getenv(askpass.ASKPASS_NONCE_ENV) + nonce := os.Getenv(git.ASKPASS_NONCE_ENV) if nonce == "" { - errors.CheckError(fmt.Errorf("%s is not set", askpass.ASKPASS_NONCE_ENV)) + errors.CheckError(fmt.Errorf("%s is not set", git.ASKPASS_NONCE_ENV)) } conn, err := grpc_util.BlockingDial(ctx, "unix", askpass.SocketPath, nil, grpc.WithTransportCredentials(insecure.NewCredentials())) errors.CheckError(err) diff --git a/cmd/argocd-k8s-auth/commands/argocd_k8s_auth.go b/cmd/argocd-k8s-auth/commands/argocd_k8s_auth.go index 4049d5dc6d491..ce0f3ee3a2f49 100644 --- a/cmd/argocd-k8s-auth/commands/argocd_k8s_auth.go +++ b/cmd/argocd-k8s-auth/commands/argocd_k8s_auth.go @@ -9,7 +9,7 @@ const ( ) func NewCommand() *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: cliName, Short: "argocd-k8s-auth a set of commands to generate k8s auth token", DisableAutoGenTag: true, diff --git a/cmd/argocd-k8s-auth/commands/aws.go b/cmd/argocd-k8s-auth/commands/aws.go index 1794a5bf57b39..79a118d2653a3 100644 --- a/cmd/argocd-k8s-auth/commands/aws.go +++ b/cmd/argocd-k8s-auth/commands/aws.go @@ -37,14 +37,13 @@ func newAWSCommand() *cobra.Command { var ( clusterName string roleARN string - profile string ) - command := &cobra.Command{ + 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, profile, getSignedRequest) + presignedURLString, err := getSignedRequestWithRetry(ctx, time.Minute, 5*time.Second, clusterName, roleARN, 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 @@ -54,34 +53,31 @@ 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, profile string) (string, error) +type getSignedRequestFunc func(clusterName, roleARN string) (string, error) -func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Duration, clusterName, roleARN string, profile string, fn getSignedRequestFunc) (string, error) { +func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Duration, clusterName, roleARN string, fn getSignedRequestFunc) (string, error) { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() for { - signed, err := fn(clusterName, roleARN, profile) + signed, err := fn(clusterName, roleARN) if err == nil { return signed, nil } select { case <-ctx.Done(): - return "", fmt.Errorf("timeout while trying to get signed aws request: last error: %w", err) + return "", fmt.Errorf("timeout while trying to get signed aws request: last error: %s", err) case <-time.After(interval): } } } -func getSignedRequest(clusterName, roleARN string, profile string) (string, error) { - sess, err := session.NewSessionWithOptions(session.Options{ - Profile: profile, - }) +func getSignedRequest(clusterName, roleARN string) (string, error) { + sess, err := session.NewSession() if err != nil { - return "", fmt.Errorf("error creating new AWS session: %w", err) + return "", fmt.Errorf("error creating new AWS session: %s", err) } stsAPI := sts.New(sess) if roleARN != "" { @@ -92,7 +88,7 @@ func getSignedRequest(clusterName, roleARN string, profile string) (string, erro request.HTTPRequest.Header.Add(clusterIDHeader, clusterName) signed, err := request.Presign(requestPresignParam) if err != nil { - return "", fmt.Errorf("error presigning AWS request: %w", err) + return "", fmt.Errorf("error presigning AWS request: %s", err) } return signed, nil } diff --git a/cmd/argocd-k8s-auth/commands/aws_test.go b/cmd/argocd-k8s-auth/commands/aws_test.go index 7e31e50efbba8..c22449eba42be 100644 --- a/cmd/argocd-k8s-auth/commands/aws_test.go +++ b/cmd/argocd-k8s-auth/commands/aws_test.go @@ -7,7 +7,6 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestGetSignedRequestWithRetry(t *testing.T) { @@ -23,10 +22,10 @@ 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 - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "token", signed) }) t.Run("will return signed request on third attempt", func(t *testing.T) { @@ -42,10 +41,10 @@ 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 - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "token", signed) }) t.Run("will return error on timeout", func(t *testing.T) { @@ -58,10 +57,10 @@ 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 - require.Error(t, err) + assert.Error(t, err) assert.Equal(t, "", signed) }) } @@ -71,7 +70,7 @@ type signedRequestMock struct { returnFunc func(m *signedRequestMock) (string, error) } -func (m *signedRequestMock) getSignedRequestMock(clusterName, roleARN string, profile string) (string, error) { +func (m *signedRequestMock) getSignedRequestMock(clusterName, roleARN 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 index f4c3b9d3c96b9..bc45bbacef48b 100644 --- a/cmd/argocd-k8s-auth/commands/azure.go +++ b/cmd/argocd-k8s-auth/commands/azure.go @@ -20,10 +20,10 @@ const ( func newAzureCommand() *cobra.Command { o := token.NewOptions() - // we'll use default of WorkloadIdentityLogin for the login flow + //we'll use default of WorkloadIdentityLogin for the login flow o.LoginMethod = token.WorkloadIdentityLogin o.ServerID = DEFAULT_AAD_SERVER_APPLICATION_ID - command := &cobra.Command{ + var command = &cobra.Command{ Use: "azure", Run: func(c *cobra.Command, args []string) { o.UpdateFromEnv() diff --git a/cmd/argocd-k8s-auth/commands/gcp.go b/cmd/argocd-k8s-auth/commands/gcp.go index 388d274072488..65d9c9ffe3325 100644 --- a/cmd/argocd-k8s-auth/commands/gcp.go +++ b/cmd/argocd-k8s-auth/commands/gcp.go @@ -10,19 +10,20 @@ import ( "github.com/argoproj/argo-cd/v2/util/errors" ) -// defaultGCPScopes: -// - cloud-platform is the base scope to authenticate to GCP. -// - userinfo.email is used to authenticate to GKE APIs with gserviceaccount -// email instead of numeric uniqueID. -// -// https://github.com/kubernetes/client-go/blob/be758edd136e61a1bffadf1c0235fceb8aee8e9e/plugin/pkg/client/auth/gcp/gcp.go#L59 -var defaultGCPScopes = []string{ - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/userinfo.email", -} +var ( + // defaultGCPScopes: + // - cloud-platform is the base scope to authenticate to GCP. + // - userinfo.email is used to authenticate to GKE APIs with gserviceaccount + // email instead of numeric uniqueID. + // https://github.com/kubernetes/client-go/blob/be758edd136e61a1bffadf1c0235fceb8aee8e9e/plugin/pkg/client/auth/gcp/gcp.go#L59 + defaultGCPScopes = []string{ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/userinfo.email", + } +) func newGCPCommand() *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "gcp", Run: func(c *cobra.Command, args []string) { ctx := c.Context() diff --git a/cmd/argocd-notification/commands/controller.go b/cmd/argocd-notification/commands/controller.go index 7245a0b75a667..cb30fd5277d4b 100644 --- a/cmd/argocd-notification/commands/controller.go +++ b/cmd/argocd-notification/commands/controller.go @@ -1,14 +1,10 @@ package commands import ( - "context" "fmt" "net/http" "os" - "os/signal" "strings" - "sync" - "syscall" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" @@ -62,12 +58,11 @@ func NewCommand() *cobra.Command { applicationNamespaces []string selfServiceNotificationEnabled bool ) - command := cobra.Command{ + var command = cobra.Command{ Use: "controller", Short: "Starts Argo CD Notifications controller", RunE: func(c *cobra.Command, args []string) error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := c.Context() vers := common.GetVersion() namespace, _, err := clientConfig.Namespace() @@ -151,17 +146,6 @@ func NewCommand() *cobra.Command { return fmt.Errorf("failed to initialize controller: %w", err) } - sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - defer wg.Done() - s := <-sigCh - log.Printf("got signal %v, attempting graceful shutdown", s) - cancel() - }() - go ctrl.Run(ctx, processorsCount) <-ctx.Done() return nil @@ -175,7 +159,7 @@ func NewCommand() *cobra.Command { 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", common.DefaultRepoServerAddr, "Argo CD repo server address") - command.Flags().BoolVar(&argocdRepoServerPlaintext, "argocd-repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_NOTIFICATION_CONTROLLER_REPO_SERVER_PLAINTEXT", false), "Use a plaintext client (non-TLS) to connect to repository server") + 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") diff --git a/cmd/argocd-repo-server/commands/argocd_repo_server.go b/cmd/argocd-repo-server/commands/argocd_repo_server.go index f8bb868f0bd0f..85e0d45862926 100644 --- a/cmd/argocd-repo-server/commands/argocd_repo_server.go +++ b/cmd/argocd-repo-server/commands/argocd_repo_server.go @@ -5,10 +5,6 @@ import ( "math" "net" "net/http" - "os" - "os/signal" - "sync" - "syscall" "time" "github.com/argoproj/pkg/stats" @@ -74,10 +70,8 @@ func NewCommand() *cobra.Command { helmManifestMaxExtractedSize string helmRegistryMaxIndexSize string disableManifestMaxExtractedSize bool - includeHiddenDirectories bool - cmpUseManifestGeneratePaths bool ) - command := cobra.Command{ + var command = cobra.Command{ Use: cliName, Short: "Run ArgoCD Repository Server", Long: "ArgoCD Repository Server is an internal service which maintains a local cache of the Git repository holding the application manifests, and is responsible for generating and returning the Kubernetes manifests. This command runs Repository Server in the foreground. It can be configured by following options.", @@ -120,7 +114,7 @@ func NewCommand() *cobra.Command { helmRegistryMaxIndexSizeQuantity, err := resource.ParseQuantity(helmRegistryMaxIndexSize) errors.CheckError(err) - askPassServer := askpass.NewServer(askpass.SocketPath) + askPassServer := askpass.NewServer() metricsServer := metrics.NewMetricsServer() cacheutil.CollectMetrics(redisClient, metricsServer) server, err := reposerver.NewServer(metricsServer, cache, tlsConfigCustomizer, repository.RepoServerInitConstants{ @@ -136,8 +130,6 @@ func NewCommand() *cobra.Command { StreamedManifestMaxTarSize: streamedManifestMaxTarSizeQuantity.ToDec().Value(), HelmManifestMaxExtractedSize: helmManifestMaxExtractedSizeQuantity.ToDec().Value(), HelmRegistryMaxIndexSize: helmRegistryMaxIndexSizeQuantity.ToDec().Value(), - IncludeHiddenDirectories: includeHiddenDirectories, - CMPUseManifestGeneratePaths: cmpUseManifestGeneratePaths, }, askPassServer) errors.CheckError(err) @@ -179,7 +171,7 @@ func NewCommand() *cobra.Command { }) http.Handle("/metrics", metricsServer.GetHandler()) go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf("%s:%d", metricsHost, metricsPort), nil)) }() - go func() { errors.CheckError(askPassServer.Run()) }() + go func() { errors.CheckError(askPassServer.Run(askpass.SocketPath)) }() if gpg.IsGPGEnabled() { log.Infof("Initializing GnuPG keyring at %s", common.GetGnuPGHomePath()) @@ -198,27 +190,8 @@ func NewCommand() *cobra.Command { stats.RegisterStackDumper() stats.StartStatsTicker(10 * time.Minute) stats.RegisterHeapDumper("memprofile") - - // Graceful shutdown code adapted from https://gist.github.com/embano1/e0bf49d24f1cdd07cffad93097c04f0a - sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - s := <-sigCh - log.Printf("got signal %v, attempting graceful shutdown", s) - grpc.GracefulStop() - wg.Done() - }() - - log.Println("starting grpc server") err = grpc.Serve(listener) - if err != nil { - log.Fatalf("could not serve: %v", err) - } - wg.Wait() - log.Println("clean shutdown") - + errors.CheckError(err) return nil }, } @@ -242,13 +215,9 @@ func NewCommand() *cobra.Command { 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") - command.Flags().BoolVar(&includeHiddenDirectories, "include-hidden-directories", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_INCLUDE_HIDDEN_DIRECTORIES", false), "Include hidden directories from Git") - command.Flags().BoolVar(&cmpUseManifestGeneratePaths, "plugin-use-manifest-generate-paths", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_PLUGIN_USE_MANIFEST_GENERATE_PATHS", false), "Pass the resources described in argocd.argoproj.io/manifest-generate-paths value to the cmpserver to generate the application manifests.") tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command) - cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, cacheutil.Options{ - OnClientCreated: func(client *redis.Client) { - redisClient = client - }, + cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, 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 d1e9cf05f98d5..eb5898bb05313 100644 --- a/cmd/argocd-server/commands/argocd_server.go +++ b/cmd/argocd-server/commands/argocd_server.go @@ -7,28 +7,20 @@ import ( "strings" "time" - "github.com/redis/go-redis/v9" - "k8s.io/apimachinery/pkg/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "github.com/argoproj/pkg/stats" + "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" - "sigs.k8s.io/controller-runtime/pkg/client" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" "github.com/argoproj/argo-cd/v2/common" "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" - "github.com/argoproj/argo-cd/v2/util/argo" - 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" @@ -47,7 +39,6 @@ const ( var ( failureRetryCount = env.ParseNumFromEnv(failureRetryCountEnv, 0, 0, 10) failureRetryPeriodMilliSeconds = env.ParseNumFromEnv(failureRetryPeriodMilliSecondsEnv, 100, 0, 1000) - gitSubmoduleEnabled = env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true) ) // NewCommand returns a new instance of an argocd command @@ -75,7 +66,6 @@ func NewCommand() *cobra.Command { enableGZip bool tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error) cacheSrc func() (*servercache.Cache, error) - repoServerCacheSrc func() (*reposervercache.Cache, error) frameOptions string contentSecurityPolicy string repoServerPlaintext bool @@ -85,18 +75,8 @@ func NewCommand() *cobra.Command { staticAssetsDir string applicationNamespaces []string enableProxyExtension bool - webhookParallelism int - - // ApplicationSet - enableNewGitFileGlobbing bool - scmRootCAPath string - allowedScmProviders []string - enableScmProviders bool - - // argocd k8s event logging flag - enableK8sEvent []string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: cliName, Short: "Run the ArgoCD API server", Long: "The API server is a gRPC/REST server which exposes the API consumed by the Web UI, CLI, and CI/CD systems. This command runs API server in the foreground. It can be configured by following options.", @@ -127,8 +107,6 @@ func NewCommand() *cobra.Command { errors.CheckError(err) cache, err := cacheSrc() errors.CheckError(err) - repoServerCache, err := repoServerCacheSrc() - errors.CheckError(err) kubeclientset := kubernetes.NewForConfigOrDie(config) @@ -146,16 +124,6 @@ func NewCommand() *cobra.Command { StrictValidation: repoServerStrictTLS, } - dynamicClient := dynamic.NewForConfigOrDie(config) - - scheme := runtime.NewScheme() - _ = clientgoscheme.AddToScheme(scheme) - _ = v1alpha1.AddToScheme(scheme) - - controllerClient, err := client.New(config, client.Options{Scheme: scheme}) - errors.CheckError(err) - controllerClient = client.NewDryRunClient(controllerClient) - // Load CA information to use for validating connections to the // repository server, if strict TLS validation was requested. if !repoServerPlaintext && repoServerStrictTLS { @@ -205,49 +173,36 @@ func NewCommand() *cobra.Command { } argoCDOpts := server.ArgoCDServerOpts{ - Insecure: insecure, - ListenPort: listenPort, - ListenHost: listenHost, - MetricsPort: metricsPort, - MetricsHost: metricsHost, - Namespace: namespace, - BaseHRef: baseHRef, - RootPath: rootPath, - DynamicClientset: dynamicClient, - KubeControllerClientset: controllerClient, - KubeClientset: kubeclientset, - AppClientset: appClientSet, - RepoClientset: repoclientset, - DexServerAddr: dexServerAddress, - DexTLSConfig: dexTlsConfig, - DisableAuth: disableAuth, - ContentTypes: contentTypesList, - EnableGZip: enableGZip, - TLSConfigCustomizer: tlsConfigCustomizer, - Cache: cache, - RepoServerCache: repoServerCache, - XFrameOptions: frameOptions, - ContentSecurityPolicy: contentSecurityPolicy, - RedisClient: redisClient, - StaticAssetsDir: staticAssetsDir, - ApplicationNamespaces: applicationNamespaces, - EnableProxyExtension: enableProxyExtension, - WebhookParallelism: webhookParallelism, - EnableK8sEvent: enableK8sEvent, - } - - appsetOpts := server.ApplicationSetOpts{ - GitSubmoduleEnabled: gitSubmoduleEnabled, - EnableNewGitFileGlobbing: enableNewGitFileGlobbing, - ScmRootCAPath: scmRootCAPath, - AllowedScmProviders: allowedScmProviders, - EnableScmProviders: enableScmProviders, + Insecure: insecure, + ListenPort: listenPort, + ListenHost: listenHost, + MetricsPort: metricsPort, + MetricsHost: metricsHost, + Namespace: namespace, + BaseHRef: baseHRef, + RootPath: rootPath, + KubeClientset: kubeclientset, + AppClientset: appClientSet, + RepoClientset: repoclientset, + DexServerAddr: dexServerAddress, + DexTLSConfig: dexTlsConfig, + DisableAuth: disableAuth, + ContentTypes: contentTypesList, + EnableGZip: enableGZip, + TLSConfigCustomizer: tlsConfigCustomizer, + Cache: cache, + XFrameOptions: frameOptions, + ContentSecurityPolicy: contentSecurityPolicy, + RedisClient: redisClient, + StaticAssetsDir: staticAssetsDir, + ApplicationNamespaces: applicationNamespaces, + EnableProxyExtension: enableProxyExtension, } stats.RegisterStackDumper() stats.StartStatsTicker(10 * time.Minute) stats.RegisterHeapDumper("memprofile") - argocd := server.NewServer(ctx, argoCDOpts, appsetOpts) + argocd := server.NewServer(ctx, argoCDOpts) argocd.Init(ctx) lns, err := argocd.Listen() errors.CheckError(err) @@ -270,7 +225,7 @@ 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 `), @@ -307,21 +262,9 @@ func NewCommand() *cobra.Command { command.Flags().BoolVar(&dexServerStrictTLS, "dex-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_SERVER_DEX_SERVER_STRICT_TLS", false), "Perform strict validation of TLS certificates when connecting to dex server") 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") - command.Flags().IntVar(&webhookParallelism, "webhook-parallelism-limit", env.ParseNumFromEnv("ARGOCD_SERVER_WEBHOOK_PARALLELISM_LIMIT", 50, 1, 1000), "Number of webhook requests processed concurrently") - command.Flags().StringSliceVar(&enableK8sEvent, "enable-k8s-event", env.StringsFromEnv("ARGOCD_ENABLE_K8S_EVENT", argo.DefaultEnableEventList(), ","), "Enable ArgoCD to use k8s event. For disabling all events, set the value as `none`. (e.g --enable-k8s-event=none), For enabling specific events, set the value as `event reason`. (e.g --enable-k8s-event=StatusRefreshed,ResourceCreated)") - - // Flags related to the applicationSet component. - command.Flags().StringVar(&scmRootCAPath, "appset-scm-root-ca-path", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH", ""), "Provide Root CA Path for self-signed TLS Certificates") - command.Flags().BoolVar(&enableScmProviders, "appset-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().StringSliceVar(&allowedScmProviders, "appset-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(&enableNewGitFileGlobbing, "appset-enable-new-git-file-globbing", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING", false), "Enable new globbing in Git files generator.") - tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command) - cacheSrc = servercache.AddCacheFlagsToCmd(command, cacheutil.Options{ - OnClientCreated: func(client *redis.Client) { - redisClient = client - }, + cacheSrc = servercache.AddCacheFlagsToCmd(command, 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 0466cb142a0e1..5472859551f75 100644 --- a/cmd/argocd/commands/account.go +++ b/cmd/argocd/commands/account.go @@ -30,7 +30,7 @@ import ( ) func NewAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "account", Short: "Manage account settings", Example: templates.Examples(` @@ -68,7 +68,7 @@ func NewAccountUpdatePasswordCommand(clientOpts *argocdclient.ClientOptions) *co currentPassword string newPassword string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "update-password", Short: "Update an account's password", Long: ` @@ -151,8 +151,10 @@ has appropriate RBAC permissions to change other accounts. } func NewAccountGetUserInfoCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ + var ( + output string + ) + var command = &cobra.Command{ Use: "get-user-info", Short: "Get user info", Example: templates.Examples(` @@ -256,7 +258,9 @@ func printAccountsTable(items []*accountpkg.Account) { } func NewAccountListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string + var ( + output string + ) cmd := &cobra.Command{ Use: "list", Short: "List accounts", @@ -408,7 +412,9 @@ argocd account generate-token --account `, } func NewAccountDeleteTokenCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var account string + var ( + account string + ) cmd := &cobra.Command{ Use: "delete-token", Short: "Deletes account token", diff --git a/cmd/argocd/commands/admin/admin.go b/cmd/argocd/commands/admin/admin.go index 6c120bd425cdb..73f93deb898c9 100644 --- a/cmd/argocd/commands/admin/admin.go +++ b/cmd/argocd/commands/admin/admin.go @@ -1,13 +1,10 @@ package admin import ( - "context" "reflect" - "strings" "github.com/spf13/cobra" apiv1 "k8s.io/api/core/v1" - v1 "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" @@ -40,18 +37,95 @@ var ( // NewAdminCommand returns a new instance of an argocd command func NewAdminCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - pathOpts := clientcmd.NewDefaultPathOptions() + var ( + pathOpts = clientcmd.NewDefaultPathOptions() + ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "admin", Short: "Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access", DisableAutoGenTag: true, Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) }, - Example: `# Access the Argo CD web UI + 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 `, @@ -86,12 +160,11 @@ func newArgoCDClientsets(config *rest.Config, namespace string) *argoCDClientset dynamicIf, err := dynamic.NewForConfig(config) errors.CheckError(err) return &argoCDClientsets{ - configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace), - secrets: dynamicIf.Resource(secretResource).Namespace(namespace), - // To support applications and applicationsets in any namespace we will watch all namespaces and filter them afterwards - applications: dynamicIf.Resource(applicationsResource), + configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace), + secrets: dynamicIf.Resource(secretResource).Namespace(namespace), + applications: dynamicIf.Resource(applicationsResource).Namespace(namespace), projects: dynamicIf.Resource(appprojectsResource).Namespace(namespace), - applicationSets: dynamicIf.Resource(appplicationSetResource), + applicationSets: dynamicIf.Resource(appplicationSetResource).Namespace(namespace), } } @@ -185,16 +258,13 @@ func isArgoCDConfigMap(name string) bool { return true } return false + } // specsEqual returns if the spec, data, labels, annotations, and finalizers of the two // supplied objects are equal, indicating that no update is necessary during importing func specsEqual(left, right unstructured.Unstructured) bool { - leftAnnotation := left.GetAnnotations() - rightAnnotation := right.GetAnnotations() - delete(leftAnnotation, apiv1.LastAppliedConfigAnnotation) - delete(rightAnnotation, apiv1.LastAppliedConfigAnnotation) - if !reflect.DeepEqual(leftAnnotation, rightAnnotation) { + if !reflect.DeepEqual(left.GetAnnotations(), right.GetAnnotations()) { return false } if !reflect.DeepEqual(left.GetLabels(), right.GetLabels()) { @@ -227,51 +297,34 @@ func specsEqual(left, right unstructured.Unstructured) bool { return false } -type argocdAdditonalNamespaces struct { - applicationNamespaces []string - applicationsetNamespaces []string -} - -const ( - applicationsetNamespacesCmdParamsKey = "applicationsetcontroller.namespaces" - applicationNamespacesCmdParamsKey = "application.namespaces" -) - -// Get additional namespaces from argocd-cmd-params -func getAdditionalNamespaces(ctx context.Context, argocdClientsets *argoCDClientsets) *argocdAdditonalNamespaces { - applicationNamespaces := make([]string, 0) - applicationsetNamespaces := make([]string, 0) - - un, err := argocdClientsets.configMaps.Get(ctx, common.ArgoCDCmdParamsConfigMapName, v1.GetOptions{}) - errors.CheckError(err) - var cm apiv1.ConfigMap - err = runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &cm) - errors.CheckError(err) - - namespacesListFromString := func(namespaces string) []string { - listOfNamespaces := []string{} - - ss := strings.Split(namespaces, ",") - - for _, namespace := range ss { - if namespace != "" { - listOfNamespaces = append(listOfNamespaces, strings.TrimSpace(namespace)) +func iterateStringFields(obj interface{}, callback func(name string, val string) string) { + if mapField, ok := obj.(map[string]interface{}); ok { + for field, val := range mapField { + if strVal, ok := val.(string); ok { + mapField[field] = callback(field, strVal) + } else { + iterateStringFields(val, callback) } } - - return listOfNamespaces - } - - if strNamespaces, ok := cm.Data[applicationNamespacesCmdParamsKey]; ok { - applicationNamespaces = namespacesListFromString(strNamespaces) - } - - if strNamespaces, ok := cm.Data[applicationsetNamespacesCmdParamsKey]; ok { - applicationsetNamespaces = namespacesListFromString(strNamespaces) + } else if arrayField, ok := obj.([]interface{}); ok { + for i := range arrayField { + iterateStringFields(arrayField[i], callback) + } } +} - return &argocdAdditonalNamespaces{ - applicationNamespaces: applicationNamespaces, - applicationsetNamespaces: applicationsetNamespaces, - } +func redactor(dirtyString string) string { + config := make(map[string]interface{}) + err := yaml.Unmarshal([]byte(dirtyString), &config) + errors.CheckError(err) + iterateStringFields(config, func(name string, val string) string { + if name == "clientSecret" || name == "secret" || name == "bindPW" { + return "********" + } else { + return val + } + }) + data, err := yaml.Marshal(config) + errors.CheckError(err) + return string(data) } diff --git a/cmd/argocd/commands/admin/admin_test.go b/cmd/argocd/commands/admin/admin_test.go deleted file mode 100644 index 85f59b5dee699..0000000000000 --- a/cmd/argocd/commands/admin/admin_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package admin - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "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" -) - -func TestGetAdditionalNamespaces(t *testing.T) { - createArgoCDCmdCMWithKeys := func(data map[string]interface{}) *unstructured.Unstructured { - return &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{ - "name": "argocd-cmd-params-cm", - "namespace": "argocd", - }, - "data": data, - }, - } - } - - testCases := []struct { - CmdParamsKeys map[string]interface{} - expected argocdAdditonalNamespaces - description string - }{ - { - description: "empty configmap should return no additional namespaces", - CmdParamsKeys: map[string]interface{}{}, - expected: argocdAdditonalNamespaces{applicationNamespaces: []string{}, applicationsetNamespaces: []string{}}, - }, - { - description: "empty strings in respective keys in cm shoud return empty namespace list", - CmdParamsKeys: map[string]interface{}{applicationsetNamespacesCmdParamsKey: "", applicationNamespacesCmdParamsKey: ""}, - expected: argocdAdditonalNamespaces{applicationNamespaces: []string{}, applicationsetNamespaces: []string{}}, - }, - { - description: "when only one of the keys in the cm is set only correct respective list of namespaces should be returned", - CmdParamsKeys: map[string]interface{}{applicationNamespacesCmdParamsKey: "foo, bar*"}, - expected: argocdAdditonalNamespaces{applicationsetNamespaces: []string{}, applicationNamespaces: []string{"foo", "bar*"}}, - }, - { - description: "when only one of the keys in the cm is set only correct respective list of namespaces should be returned", - CmdParamsKeys: map[string]interface{}{applicationsetNamespacesCmdParamsKey: "foo, bar*"}, - expected: argocdAdditonalNamespaces{applicationNamespaces: []string{}, applicationsetNamespaces: []string{"foo", "bar*"}}, - }, - { - description: "whitespaces are removed for both multiple and single namespace", - CmdParamsKeys: map[string]interface{}{applicationNamespacesCmdParamsKey: " bar ", applicationsetNamespacesCmdParamsKey: " foo , bar* "}, - expected: argocdAdditonalNamespaces{applicationNamespaces: []string{"bar"}, applicationsetNamespaces: []string{"foo", "bar*"}}, - }, - } - - for _, c := range testCases { - fakeDynClient := dynfake.NewSimpleDynamicClient(runtime.NewScheme(), createArgoCDCmdCMWithKeys(c.CmdParamsKeys)) - - argoCDClientsets := &argoCDClientsets{ - configMaps: fakeDynClient.Resource(configMapResource).Namespace("argocd"), - applications: fakeDynClient.Resource(schema.GroupVersionResource{}), - applicationSets: fakeDynClient.Resource(schema.GroupVersionResource{}), - secrets: fakeDynClient.Resource(schema.GroupVersionResource{}), - projects: fakeDynClient.Resource(schema.GroupVersionResource{}), - } - - result := getAdditionalNamespaces(context.TODO(), argoCDClientsets) - assert.Equal(t, c.expected, *result) - } -} diff --git a/cmd/argocd/commands/admin/app.go b/cmd/argocd/commands/admin/app.go index e8869493d05fc..889124071dc9b 100644 --- a/cmd/argocd/commands/admin/app.go +++ b/cmd/argocd/commands/admin/app.go @@ -24,7 +24,6 @@ import ( "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" @@ -44,7 +43,7 @@ import ( ) func NewAppCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "app", Short: "Manage applications configuration", Example: ` @@ -78,9 +77,8 @@ func NewGenAppSpecCommand() *cobra.Command { outputFormat string annotations []string inline bool - setFinalizer bool ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "generate-spec APPNAME", Short: "Generate declarative config for an application", Example: ` @@ -113,9 +111,7 @@ func NewGenAppSpecCommand() *cobra.Command { c.HelpFunc()(c, args) os.Exit(1) } - if setFinalizer { - app.Finalizers = append(app.Finalizers, "resources-finalizer.argocd.argoproj.io") - } + out, closer, err := getOutWriter(inline, fileURL) errors.CheckError(err) defer io.Close(closer) @@ -129,7 +125,6 @@ func NewGenAppSpecCommand() *cobra.Command { command.Flags().StringArrayVarP(&annotations, "annotations", "", []string{}, "Set metadata annotations (e.g. example=value)") command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml") command.Flags().BoolVarP(&inline, "inline", "i", false, "If set then generated resource is written back to the file specified in --file flag") - command.Flags().BoolVar(&setFinalizer, "set-finalizer", false, "Sets deletion finalizer on the application, application resources will be cascaded on deletion") // Only complete files with appropriate extension. err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"}) @@ -163,7 +158,7 @@ func printLine(format string, a ...interface{}) { } func NewDiffReconcileResults() *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "diff-reconcile-results PATH1 PATH2", Short: "Compare results of two reconciliations and print diff.", Run: func(c *cobra.Command, args []string) { @@ -253,7 +248,7 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "get-reconcile-results PATH", Short: "Reconcile all applications and stores reconciliation summary in the specified file.", Run: func(c *cobra.Command, args []string) { @@ -276,26 +271,18 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *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{} - 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 + repoServerPodLabelSelector := common.LabelKeyAppName + "=" + clientOpts.RepoServerName repoServerPort, err := kubeutil.PortForward(8081, namespace, &overrides, repoServerPodLabelSelector) errors.CheckError(err) repoServerAddress = fmt.Sprintf("localhost:%d", repoServerPort) } repoServerClient := reposerverclient.NewRepoServerClientset(repoServerAddress, 60, reposerverclient.TLSConfiguration{DisableTLS: false, StrictValidation: false}) + + appClientset := appclientset.NewForConfigOrDie(cfg) + kubeClientset := kubernetes.NewForConfigOrDie(cfg) result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache, serverSideDiff, ignoreNormalizerOpts) errors.CheckError(err) } else { @@ -332,7 +319,7 @@ func saveToFile(err error, outputFormat string, result reconcileResults, outputP return fmt.Errorf("format %s is not supported", outputFormat) } - return os.WriteFile(outputPath, data, 0o644) + return os.WriteFile(outputPath, data, 0644) } func getReconcileResults(ctx context.Context, appClientset appclientset.Interface, namespace string, selector string) ([]appReconcileResult, error) { @@ -387,7 +374,8 @@ func reconcileApplications( return true }, func(r *http.Request) error { return nil - }, []string{}, []string{}) + }, []string{}) + if err != nil { return nil, err } @@ -437,7 +425,7 @@ func reconcileApplications( sources = append(sources, app.Spec.GetSource()) revisions = append(revisions, app.Spec.GetSource().TargetRevision) - res, err := appStateManager.CompareAppState(&app, proj, revisions, sources, false, false, nil, false, false) + res, err := appStateManager.CompareAppState(&app, proj, revisions, sources, false, false, nil, false) if err != nil { return nil, err } @@ -452,5 +440,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) {}, &sharding.ClusterSharding{}, argo.NewResourceTracking()) + return cache.NewLiveStateCache(argoDB, appInformer, settingsMgr, kubeutil.NewKubectl(), server, func(managedByApp map[string]bool, ref apiv1.ObjectReference) {}, nil, argo.NewResourceTracking()) } diff --git a/cmd/argocd/commands/admin/app_test.go b/cmd/argocd/commands/admin/app_test.go index cadce3e857009..c7bc515094439 100644 --- a/cmd/argocd/commands/admin/app_test.go +++ b/cmd/argocd/commands/admin/app_test.go @@ -9,7 +9,6 @@ import ( "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -44,7 +43,9 @@ func TestGetReconcileResults(t *testing.T) { }) result, err := getReconcileResults(ctx, appClientset, "default", "") - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } expectedResults := []appReconcileResult{{ Name: "test", @@ -117,15 +118,17 @@ func TestGetReconcileResults_Refresh(t *testing.T) { normalizers.IgnoreNormalizerOpts{}, ) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } - assert.Equal(t, health.HealthStatusMissing, result[0].Health.Status) - assert.Equal(t, v1alpha1.SyncStatusCodeOutOfSync, result[0].Sync.Status) + assert.Equal(t, result[0].Health.Status, health.HealthStatusMissing) + assert.Equal(t, result[0].Sync.Status, v1alpha1.SyncStatusCodeOutOfSync) } func TestDiffReconcileResults_NoDifferences(t *testing.T) { logs, err := captureStdout(func() { - require.NoError(t, diffReconcileResults( + assert.NoError(t, diffReconcileResults( reconcileResults{Applications: []appReconcileResult{{ Name: "app1", Sync: &v1alpha1.SyncStatus{Status: v1alpha1.SyncStatusCodeOutOfSync}, @@ -136,13 +139,13 @@ func TestDiffReconcileResults_NoDifferences(t *testing.T) { }}}, )) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "app1\n", logs) } func TestDiffReconcileResults_DifferentApps(t *testing.T) { logs, err := captureStdout(func() { - require.NoError(t, diffReconcileResults( + assert.NoError(t, diffReconcileResults( reconcileResults{Applications: []appReconcileResult{{ Name: "app1", Sync: &v1alpha1.SyncStatus{Status: v1alpha1.SyncStatusCodeOutOfSync}, @@ -159,7 +162,7 @@ func TestDiffReconcileResults_DifferentApps(t *testing.T) { }}}, )) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, `app1 app2 1,9d0 diff --git a/cmd/argocd/commands/admin/backup.go b/cmd/argocd/commands/admin/backup.go index 918bbc234b9a2..49e0615c64ba4 100644 --- a/cmd/argocd/commands/admin/backup.go +++ b/cmd/argocd/commands/admin/backup.go @@ -20,18 +20,15 @@ import ( "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" - secutil "github.com/argoproj/argo-cd/v2/util/security" ) // NewExportCommand defines a new command for exporting Kubernetes and Argo CD resources. func NewExportCommand() *cobra.Command { var ( - clientConfig clientcmd.ClientConfig - out string - applicationNamespaces []string - applicationsetNamespaces []string + clientConfig clientcmd.ClientConfig + out string ) - command := cobra.Command{ + var command = cobra.Command{ Use: "export", Short: "Export all Argo CD data to stdout (default) or a file", Run: func(c *cobra.Command, args []string) { @@ -61,47 +58,34 @@ func NewExportCommand() *cobra.Command { acdClients := newArgoCDClientsets(config, namespace) acdConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDConfigMapName, v1.GetOptions{}) errors.CheckError(err) - export(writer, *acdConfigMap, namespace) + export(writer, *acdConfigMap) acdRBACConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDRBACConfigMapName, v1.GetOptions{}) errors.CheckError(err) - export(writer, *acdRBACConfigMap, namespace) + export(writer, *acdRBACConfigMap) acdKnownHostsConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDKnownHostsConfigMapName, v1.GetOptions{}) errors.CheckError(err) - export(writer, *acdKnownHostsConfigMap, namespace) + export(writer, *acdKnownHostsConfigMap) acdTLSCertsConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDTLSCertsConfigMapName, v1.GetOptions{}) errors.CheckError(err) - export(writer, *acdTLSCertsConfigMap, namespace) + export(writer, *acdTLSCertsConfigMap) referencedSecrets := getReferencedSecrets(*acdConfigMap) secrets, err := acdClients.secrets.List(ctx, v1.ListOptions{}) errors.CheckError(err) for _, secret := range secrets.Items { if isArgoCDSecret(referencedSecrets, secret) { - export(writer, secret, namespace) + export(writer, secret) } } projects, err := acdClients.projects.List(ctx, v1.ListOptions{}) errors.CheckError(err) for _, proj := range projects.Items { - export(writer, proj, namespace) + export(writer, proj) } - - additionalNamespaces := getAdditionalNamespaces(ctx, acdClients) - - if len(applicationNamespaces) == 0 { - applicationNamespaces = additionalNamespaces.applicationNamespaces - } - if len(applicationsetNamespaces) == 0 { - applicationsetNamespaces = additionalNamespaces.applicationsetNamespaces - } - applications, err := acdClients.applications.List(ctx, v1.ListOptions{}) errors.CheckError(err) for _, app := range applications.Items { - // Export application only if it is in one of the enabled namespaces - if secutil.IsNamespaceEnabled(app.GetNamespace(), namespace, applicationNamespaces) { - export(writer, app, namespace) - } + export(writer, app) } applicationSets, err := acdClients.applicationSets.List(ctx, v1.ListOptions{}) if err != nil && !apierr.IsNotFound(err) { @@ -113,9 +97,7 @@ func NewExportCommand() *cobra.Command { } if applicationSets != nil { for _, appSet := range applicationSets.Items { - if secutil.IsNamespaceEnabled(appSet.GetNamespace(), namespace, applicationsetNamespaces) { - export(writer, appSet, namespace) - } + export(writer, appSet) } } }, @@ -123,24 +105,20 @@ func NewExportCommand() *cobra.Command { clientConfig = cli.AddKubectlFlagsToCmd(&command) command.Flags().StringVarP(&out, "out", "o", "-", "Output to the specified file instead of stdout") - command.Flags().StringSliceVarP(&applicationNamespaces, "application-namespaces", "", []string{}, fmt.Sprintf("Comma separated list of namespace globs to export applications from. If not provided value from '%s' in %s will be used,if it's not defined only applications from Argo CD namespace will be exported", applicationNamespacesCmdParamsKey, common.ArgoCDCmdParamsConfigMapName)) - command.Flags().StringSliceVarP(&applicationsetNamespaces, "applicationset-namespaces", "", []string{}, fmt.Sprintf("Comma separated list of namespace globs to export applicationsets from. If not provided value from '%s' in %s will be used,if it's not defined only applicationsets from Argo CD namespace will be exported", applicationsetNamespacesCmdParamsKey, common.ArgoCDCmdParamsConfigMapName)) + return &command } // NewImportCommand defines a new command for exporting Kubernetes and Argo CD resources. func NewImportCommand() *cobra.Command { var ( - clientConfig clientcmd.ClientConfig - prune bool - dryRun bool - verbose bool - stopOperation bool - ignoreTracking bool - applicationNamespaces []string - applicationsetNamespaces []string + clientConfig clientcmd.ClientConfig + prune bool + dryRun bool + verbose bool + stopOperation bool ) - command := cobra.Command{ + var command = cobra.Command{ Use: "import SOURCE", Short: "Import Argo CD data from stdin (specify `-') or a file", Run: func(c *cobra.Command, args []string) { @@ -157,8 +135,6 @@ func NewImportCommand() *cobra.Command { namespace, _, err := clientConfig.Namespace() errors.CheckError(err) acdClients := newArgoCDClientsets(config, namespace) - client, err := dynamic.NewForConfig(config) - errors.CheckError(err) var input []byte if in := args[0]; in == "-" { @@ -172,15 +148,6 @@ func NewImportCommand() *cobra.Command { dryRunMsg = " (dry run)" } - additionalNamespaces := getAdditionalNamespaces(ctx, acdClients) - - if len(applicationNamespaces) == 0 { - applicationNamespaces = additionalNamespaces.applicationNamespaces - } - if len(applicationsetNamespaces) == 0 { - applicationsetNamespaces = additionalNamespaces.applicationsetNamespaces - } - // pruneObjects tracks live objects and it's current resource version. any remaining // items in this map indicates the resource should be pruned since it no longer appears // in the backup @@ -192,7 +159,7 @@ func NewImportCommand() *cobra.Command { var referencedSecrets map[string]bool for _, cm := range configMaps.Items { if isArgoCDConfigMap(cm.GetName()) { - pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName(), Namespace: cm.GetNamespace()}] = cm + pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName()}] = cm } if cm.GetName() == common.ArgoCDConfigMapName { referencedSecrets = getReferencedSecrets(cm) @@ -203,20 +170,18 @@ func NewImportCommand() *cobra.Command { errors.CheckError(err) for _, secret := range secrets.Items { if isArgoCDSecret(referencedSecrets, secret) { - pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName(), Namespace: secret.GetNamespace()}] = secret + pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName()}] = secret } } applications, err := acdClients.applications.List(ctx, v1.ListOptions{}) errors.CheckError(err) for _, app := range applications.Items { - if secutil.IsNamespaceEnabled(app.GetNamespace(), namespace, applicationNamespaces) { - pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationKind, Name: app.GetName(), Namespace: app.GetNamespace()}] = 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: application.Group, Kind: application.AppProjectKind, Name: proj.GetName(), Namespace: proj.GetNamespace()}] = 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) { @@ -226,9 +191,7 @@ func NewImportCommand() *cobra.Command { } if applicationSets != nil { for _, appSet := range applicationSets.Items { - if secutil.IsNamespaceEnabled(appSet.GetNamespace(), namespace, applicationsetNamespaces) { - pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationSetKind, Name: appSet.GetName(), Namespace: appSet.GetNamespace()}] = appSet - } + pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationSetKind, Name: appSet.GetName()}] = appSet } } @@ -237,41 +200,22 @@ func NewImportCommand() *cobra.Command { errors.CheckError(err) for _, bakObj := range backupObjects { gvk := bakObj.GroupVersionKind() - // For objects without namespace, assume they belong in ArgoCD namespace - if bakObj.GetNamespace() == "" { - bakObj.SetNamespace(namespace) - } - key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName(), Namespace: bakObj.GetNamespace()} + key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName()} liveObj, exists := pruneObjects[key] delete(pruneObjects, key) var dynClient dynamic.ResourceInterface switch bakObj.GetKind() { case "Secret": - dynClient = client.Resource(secretResource).Namespace(bakObj.GetNamespace()) + dynClient = acdClients.secrets case "ConfigMap": - dynClient = client.Resource(configMapResource).Namespace(bakObj.GetNamespace()) + dynClient = acdClients.configMaps case application.AppProjectKind: - dynClient = client.Resource(appprojectsResource).Namespace(bakObj.GetNamespace()) + dynClient = acdClients.projects case application.ApplicationKind: - dynClient = client.Resource(applicationsResource).Namespace(bakObj.GetNamespace()) - // If application is not in one of the allowed namespaces do not import it - if !secutil.IsNamespaceEnabled(bakObj.GetNamespace(), namespace, applicationNamespaces) { - continue - } + dynClient = acdClients.applications case application.ApplicationSetKind: - dynClient = client.Resource(appplicationSetResource).Namespace(bakObj.GetNamespace()) - // If applicationset is not in one of the allowed namespaces do not import it - if !secutil.IsNamespaceEnabled(bakObj.GetNamespace(), namespace, applicationsetNamespaces) { - continue - } - } - - // If there is a live object, remove the tracking annotations/label that might conflict - // when argo is managed with an application. - if ignoreTracking && exists { - updateTracking(bakObj, &liveObj) + dynClient = acdClients.applicationSets } - if !exists { isForbidden := false if !dryRun { @@ -284,8 +228,9 @@ func NewImportCommand() *cobra.Command { } } if !isForbidden { - fmt.Printf("%s/%s %s in namespace %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), bakObj.GetNamespace(), dryRunMsg) + fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg) } + } else if specsEqual(*bakObj, liveObj) && checkAppHasNoNeedToStopOperation(liveObj, stopOperation) { if verbose { fmt.Printf("%s/%s %s unchanged%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg) @@ -303,7 +248,7 @@ func NewImportCommand() *cobra.Command { } } if !isForbidden { - fmt.Printf("%s/%s %s in namespace %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), bakObj.GetNamespace(), dryRunMsg) + fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg) } } } @@ -314,11 +259,11 @@ func NewImportCommand() *cobra.Command { var dynClient dynamic.ResourceInterface switch key.Kind { case "Secret": - dynClient = client.Resource(secretResource).Namespace(liveObj.GetNamespace()) + dynClient = acdClients.secrets case application.AppProjectKind: - dynClient = client.Resource(appprojectsResource).Namespace(liveObj.GetNamespace()) + dynClient = acdClients.projects case application.ApplicationKind: - dynClient = client.Resource(applicationsResource).Namespace(liveObj.GetNamespace()) + dynClient = acdClients.applications if !dryRun { if finalizers := liveObj.GetFinalizers(); len(finalizers) > 0 { newLive := liveObj.DeepCopy() @@ -330,7 +275,7 @@ func NewImportCommand() *cobra.Command { } } case application.ApplicationSetKind: - dynClient = client.Resource(appplicationSetResource).Namespace(liveObj.GetNamespace()) + dynClient = acdClients.applicationSets default: log.Fatalf("Unexpected kind '%s' in prune list", key.Kind) } @@ -357,11 +302,8 @@ func NewImportCommand() *cobra.Command { clientConfig = cli.AddKubectlFlagsToCmd(&command) command.Flags().BoolVar(&dryRun, "dry-run", false, "Print what will be performed") command.Flags().BoolVar(&prune, "prune", false, "Prune secrets, applications and projects which do not appear in the backup") - command.Flags().BoolVar(&ignoreTracking, "ignore-tracking", false, "Do not update the tracking annotation if the resource is already tracked") command.Flags().BoolVar(&verbose, "verbose", false, "Verbose output (versus only changed output)") command.Flags().BoolVar(&stopOperation, "stop-operation", false, "Stop any existing operations") - command.Flags().StringSliceVarP(&applicationNamespaces, "application-namespaces", "", []string{}, fmt.Sprintf("Comma separated list of namespace globs to which import of applications is allowed. If not provided value from '%s' in %s will be used,if it's not defined only applications without an explicit namespace will be imported to the Argo CD namespace", applicationNamespacesCmdParamsKey, common.ArgoCDCmdParamsConfigMapName)) - command.Flags().StringSliceVarP(&applicationsetNamespaces, "applicationset-namespaces", "", []string{}, fmt.Sprintf("Comma separated list of namespace globs which import of applicationsets is allowed. If not provided value from '%s' in %s will be used,if it's not defined only applicationsets without an explicit namespace will be imported to the Argo CD namespace", applicationsetNamespacesCmdParamsKey, common.ArgoCDCmdParamsConfigMapName)) return &command } @@ -379,14 +321,13 @@ func checkAppHasNoNeedToStopOperation(liveObj unstructured.Unstructured, stopOpe } // export writes the unstructured object and removes extraneous cruft from output before writing -func export(w io.Writer, un unstructured.Unstructured, argocdNamespace string) { +func export(w io.Writer, un unstructured.Unstructured) { name := un.GetName() finalizers := un.GetFinalizers() apiVersion := un.GetAPIVersion() kind := un.GetKind() labels := un.GetLabels() annotations := un.GetAnnotations() - namespace := un.GetNamespace() unstructured.RemoveNestedField(un.Object, "metadata") un.SetName(name) un.SetFinalizers(finalizers) @@ -394,9 +335,6 @@ func export(w io.Writer, un unstructured.Unstructured, argocdNamespace string) { un.SetKind(kind) un.SetLabels(labels) un.SetAnnotations(annotations) - if namespace != argocdNamespace { - un.SetNamespace(namespace) - } data, err := yaml.Marshal(un.Object) errors.CheckError(err) _, err = w.Write(data) @@ -431,32 +369,3 @@ func updateLive(bak, live *unstructured.Unstructured, stopOperation bool) *unstr } return newLive } - -// updateTracking will update the tracking label and annotation in the bak resources to the -// value of the live resource. -func updateTracking(bak, live *unstructured.Unstructured) { - // update the common annotation - bakAnnotations := bak.GetAnnotations() - liveAnnotations := live.GetAnnotations() - if liveAnnotations != nil && bakAnnotations != nil { - if v, ok := liveAnnotations[common.AnnotationKeyAppInstance]; ok { - if _, ok := bakAnnotations[common.AnnotationKeyAppInstance]; ok { - bakAnnotations[common.AnnotationKeyAppInstance] = v - bak.SetAnnotations(bakAnnotations) - } - } - } - - // update the common label - // A custom label can be set, but it is impossible to know which instance is managing the application - bakLabels := bak.GetLabels() - liveLabels := live.GetLabels() - if liveLabels != nil && bakLabels != nil { - if v, ok := liveLabels[common.LabelKeyAppInstance]; ok { - if _, ok := bakLabels[common.LabelKeyAppInstance]; ok { - bakLabels[common.LabelKeyAppInstance] = v - bak.SetLabels(bakLabels) - } - } - } -} diff --git a/cmd/argocd/commands/admin/backup_test.go b/cmd/argocd/commands/admin/backup_test.go deleted file mode 100644 index b4fd07ad04c1a..0000000000000 --- a/cmd/argocd/commands/admin/backup_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package admin - -import ( - "testing" - - "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - - "github.com/argoproj/argo-cd/v2/common" -) - -func newBackupObject(trackingValue string, trackingLabel bool, trackingAnnotation bool) *unstructured.Unstructured { - cm := v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-configmap", - Namespace: "namespace", - }, - Data: map[string]string{ - "foo": "bar", - }, - } - if trackingLabel { - cm.SetLabels(map[string]string{ - common.LabelKeyAppInstance: trackingValue, - }) - } - if trackingAnnotation { - cm.SetAnnotations(map[string]string{ - common.AnnotationKeyAppInstance: trackingValue, - }) - } - return kube.MustToUnstructured(&cm) -} - -func Test_updateTracking(t *testing.T) { - type args struct { - bak *unstructured.Unstructured - live *unstructured.Unstructured - } - tests := []struct { - name string - args args - expected *unstructured.Unstructured - }{ - { - name: "update annotation when present in live", - args: args{ - bak: newBackupObject("bak", false, true), - live: newBackupObject("live", false, true), - }, - expected: newBackupObject("live", false, true), - }, - { - name: "update default label when present in live", - args: args{ - bak: newBackupObject("bak", true, true), - live: newBackupObject("live", true, true), - }, - expected: newBackupObject("live", true, true), - }, - { - name: "do not update if live object does not have tracking", - args: args{ - bak: newBackupObject("bak", true, true), - live: newBackupObject("live", false, false), - }, - expected: newBackupObject("bak", true, true), - }, - { - name: "do not update if bak object does not have tracking", - args: args{ - bak: newBackupObject("bak", false, false), - live: newBackupObject("live", true, true), - }, - expected: newBackupObject("bak", false, false), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - updateTracking(tt.args.bak, tt.args.live) - assert.Equal(t, tt.expected, tt.args.bak) - }) - } -} diff --git a/cmd/argocd/commands/admin/cluster.go b/cmd/argocd/commands/admin/cluster.go index 48ee0254fd1b7..fbad907615cf5 100644 --- a/cmd/argocd/commands/admin/cluster.go +++ b/cmd/argocd/commands/admin/cluster.go @@ -19,13 +19,14 @@ import ( "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - "k8s.io/utils/ptr" + "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" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" "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/pkg/client/clientset/versioned" "github.com/argoproj/argo-cd/v2/util/argo" cacheutil "github.com/argoproj/argo-cd/v2/util/cache" @@ -41,7 +42,7 @@ import ( ) func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientcmd.PathOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "cluster", Short: "Manage clusters configuration", Example: ` @@ -71,7 +72,7 @@ argocd admin cluster namespaces my-cluster `, } type ClusterWithInfo struct { - v1alpha1.Cluster + argoappv1.Cluster // Shard holds controller shard number that handles the cluster Shard int // Namespaces holds list of namespaces managed by Argo CD in the cluster @@ -86,12 +87,8 @@ 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) + clusterShardingCache.Init(clustersList) clusterShards := clusterShardingCache.GetDistribution() var cache *appstatecache.Cache @@ -106,9 +103,14 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie } redisOptions := &redis.Options{Addr: fmt.Sprintf("localhost:%d", port)} - if err = common.SetOptionalRedisPasswordFromKubeConfig(ctx, kubeClient, namespace, redisOptions); err != nil { - log.Warnf("Failed to fetch & set redis password for namespace %s: %v", namespace, err) + + secret, err := kubeClient.CoreV1().Secrets(namespace).Get(context.Background(), defaulRedisInitialPasswordSecretName, v1.GetOptions{}) + if err == nil { + if _, ok := secret.Data[defaultResisInitialPasswordKey]; ok { + redisOptions.Password = string(secret.Data[defaultResisInitialPasswordKey]) + } } + client := redis.NewClient(redisOptions) compressionType, err := cacheutil.CompressionTypeFromString(redisCompressionStr) if err != nil { @@ -122,6 +124,10 @@ 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) @@ -134,6 +140,12 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie batchSize := 10 batchesCount := int(math.Ceil(float64(len(clusters)) / float64(batchSize))) + clusterSharding := &sharding.ClusterSharding{ + Shard: shard, + Replicas: replicas, + Shards: make(map[string]int), + Clusters: make(map[string]*v1alpha1.Cluster), + } for batchNum := 0; batchNum < batchesCount; batchNum++ { batchStart := batchSize * batchNum batchEnd := batchSize * (batchNum + 1) @@ -145,8 +157,10 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie clusterShard := 0 cluster := batch[i] if replicas > 0 { - clusterShard = clusterShards[cluster.Server] - cluster.Shard = ptr.To(int64(clusterShard)) + distributionFunction := sharding.GetDistributionFunction(clusterSharding.GetClusterAccessor(), common.DefaultShardingAlgorithm, replicas) + distributionFunction(&cluster) + 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 { @@ -173,8 +187,7 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie 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: appControllerPodLabelSelector, - }) + LabelSelector: appControllerPodLabelSelector}) if err != nil { return 0, err } @@ -191,7 +204,7 @@ func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm portForwardRedis bool redisCompressionStr string ) - command := cobra.Command{ + var command = cobra.Command{ Use: "shards", 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) { @@ -225,7 +238,7 @@ func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm 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, consistent-hashing] ") + 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) @@ -317,8 +330,10 @@ func runClusterNamespacesCommand(ctx context.Context, clientConfig clientcmd.Cli } func NewClusterNamespacesCommand() *cobra.Command { - var clientConfig clientcmd.ClientConfig - command := cobra.Command{ + var ( + clientConfig clientcmd.ClientConfig + ) + var command = cobra.Command{ Use: "namespaces", Short: "Print information namespaces which Argo CD manages in each cluster.", Run: func(cmd *cobra.Command, args []string) { @@ -361,7 +376,7 @@ func NewClusterEnableNamespacedMode() *cobra.Command { clusterResources bool namespacesCount int ) - command := cobra.Command{ + var command = cobra.Command{ Use: "enable-namespaced-mode PATTERN", Short: "Enable namespaced mode for clusters which name matches to the specified pattern.", Run: func(cmd *cobra.Command, args []string) { @@ -396,6 +411,7 @@ func NewClusterEnableNamespacedMode() *cobra.Command { } else { fmt.Println("done (dry run)") } + } return nil })) @@ -414,7 +430,7 @@ func NewClusterDisableNamespacedMode() *cobra.Command { clientConfig clientcmd.ClientConfig dryRun bool ) - command := cobra.Command{ + var command = cobra.Command{ Use: "disable-namespaced-mode PATTERN", Short: "Disable namespaced mode for clusters which name matches to the specified pattern.", Run: func(cmd *cobra.Command, args []string) { @@ -454,6 +470,7 @@ func NewClusterDisableNamespacedMode() *cobra.Command { } else { fmt.Println("done (dry run)") } + } return nil })) @@ -474,7 +491,7 @@ func NewClusterStatsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma portForwardRedis bool redisCompressionStr string ) - command := cobra.Command{ + var command = cobra.Command{ Use: "stats", Short: "Prints information cluster statistics and inferred shard number", Example: ` @@ -516,7 +533,7 @@ argocd admin cluster stats target-cluster`, 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, consistent-hashing] ") + 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) @@ -530,8 +547,10 @@ argocd admin cluster stats target-cluster`, // NewClusterConfig returns a new instance of `argocd admin kubeconfig` command func NewClusterConfig() *cobra.Command { - var clientConfig clientcmd.ClientConfig - command := &cobra.Command{ + var ( + clientConfig clientcmd.ClientConfig + ) + var command = &cobra.Command{ Use: "kubeconfig CLUSTER_URL OUTPUT_PATH", Short: "Generates kubeconfig for the specified cluster", DisableAutoGenTag: true, @@ -582,7 +601,7 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command labels []string annotations []string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "generate-spec CONTEXT", Short: "Generate declarative config for a cluster", Run: func(c *cobra.Command, args []string) { @@ -617,16 +636,15 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command errors.CheckError(err) kubeClientset := fake.NewSimpleClientset() - var awsAuthConf *v1alpha1.AWSAuthConfig - var execProviderConf *v1alpha1.ExecProviderConfig + var awsAuthConf *argoappv1.AWSAuthConfig + var execProviderConf *argoappv1.ExecProviderConfig if clusterOpts.AwsClusterName != "" { - awsAuthConf = &v1alpha1.AWSAuthConfig{ + awsAuthConf = &argoappv1.AWSAuthConfig{ ClusterName: clusterOpts.AwsClusterName, RoleARN: clusterOpts.AwsRoleArn, - Profile: clusterOpts.AwsProfile, } } else if clusterOpts.ExecProviderCommand != "" { - execProviderConf = &v1alpha1.ExecProviderConfig{ + execProviderConf = &argoappv1.ExecProviderConfig{ Command: clusterOpts.ExecProviderCommand, Args: clusterOpts.ExecProviderArgs, Env: clusterOpts.ExecProviderEnv, @@ -650,7 +668,7 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, bearerToken, awsAuthConf, execProviderConf, labelsMap, annotationsMap) if clusterOpts.InClusterEndpoint() { - clst.Server = v1alpha1.KubernetesInternalAPIServerAddr + clst.Server = argoappv1.KubernetesInternalAPIServerAddr } if clusterOpts.ClusterEndpoint == string(cmdutil.KubePublicEndpoint) { // Ignore `kube-public` cluster endpoints, since this command is intended to run without invoking any network connections. diff --git a/cmd/argocd/commands/admin/dashboard.go b/cmd/argocd/commands/admin/dashboard.go index 41abce130e833..21b621d264022 100644 --- a/cmd/argocd/commands/admin/dashboard.go +++ b/cmd/argocd/commands/admin/dashboard.go @@ -3,11 +3,10 @@ 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/util/cli" - "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" diff --git a/cmd/argocd/commands/admin/generatespec_utils_test.go b/cmd/argocd/commands/admin/generatespec_utils_test.go index 9c5735df5838d..ea71b1ffa76ae 100644 --- a/cmd/argocd/commands/admin/generatespec_utils_test.go +++ b/cmd/argocd/commands/admin/generatespec_utils_test.go @@ -34,7 +34,7 @@ func TestGetOutWriter_InlineOn(t *testing.T) { assert.Equal(t, tmpFile, out.(*os.File).Name()) _, err = os.Stat(fmt.Sprintf("%s.back", tmpFile)) - require.NoError(t, err, "Back file must be created") + assert.NoError(t, err, "Back file must be created") } func TestPrintResources_Secret_YAML(t *testing.T) { @@ -43,7 +43,7 @@ func TestPrintResources_Secret_YAML(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "my-secret"}, Data: map[string][]byte{"my-secret-key": []byte("my-secret-data")}, }) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, `apiVersion: v1 kind: Secret diff --git a/cmd/argocd/commands/admin/initial_password.go b/cmd/argocd/commands/admin/initial_password.go index bcd699d3d0dc3..a130ee875ae60 100644 --- a/cmd/argocd/commands/admin/initial_password.go +++ b/cmd/argocd/commands/admin/initial_password.go @@ -17,11 +17,14 @@ const initialPasswordSecretName = "argocd-initial-admin-secret" // NewInitialPasswordCommand defines a new command to retrieve Argo CD initial password. func NewInitialPasswordCommand() *cobra.Command { - var clientConfig clientcmd.ClientConfig - command := cobra.Command{ + var ( + clientConfig clientcmd.ClientConfig + ) + var command = cobra.Command{ Use: "initial-password", Short: "Prints initial password to log in to Argo CD for the first time", Run: func(c *cobra.Command, args []string) { + config, err := clientConfig.ClientConfig() errors.CheckError(err) namespace, _, err := clientConfig.Namespace() diff --git a/cmd/argocd/commands/admin/notifications.go b/cmd/argocd/commands/admin/notifications.go index 32ae589270938..3cbac0a53b5c2 100644 --- a/cmd/argocd/commands/admin/notifications.go +++ b/cmd/argocd/commands/admin/notifications.go @@ -15,13 +15,14 @@ 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" - - "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) -var applications = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationPlural} +var ( + applications = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationPlural} +) func NewNotificationsCommand() *cobra.Command { var ( @@ -35,8 +36,7 @@ func NewNotificationsCommand() *cobra.Command { "notifications", "argocd admin notifications", applications, - settings.GetFactorySettingsForCLI(&argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false), - 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) diff --git a/cmd/argocd/commands/admin/project.go b/cmd/argocd/commands/admin/project.go index 9ba14ab80d961..8d4d5615bc826 100644 --- a/cmd/argocd/commands/admin/project.go +++ b/cmd/argocd/commands/admin/project.go @@ -23,7 +23,7 @@ import ( ) func NewProjectsCommand() *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "proj", Short: "Manage projects configuration", Run: func(c *cobra.Command, args []string) { @@ -45,7 +45,7 @@ func NewGenProjectSpecCommand() *cobra.Command { outputFormat string inline bool ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "generate-spec PROJECT", Short: "Generate declarative config for a project", Example: templates.Examples(` @@ -151,7 +151,7 @@ func NewUpdatePolicyRuleCommand() *cobra.Command { permission string dryRun bool ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "update-role-policy PROJECT_GLOB MODIFICATION ACTION", Short: "Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions.", Example: ` # Add policy that allows executing any action (action/*) to roles which name matches to *deployer* in all projects diff --git a/cmd/argocd/commands/admin/project_allowlist.go b/cmd/argocd/commands/admin/project_allowlist.go index 9f436f2f3a81c..460ea21d93329 100644 --- a/cmd/argocd/commands/admin/project_allowlist.go +++ b/cmd/argocd/commands/admin/project_allowlist.go @@ -38,7 +38,7 @@ func NewProjectAllowListGenCommand() *cobra.Command { clientConfig clientcmd.ClientConfig out string ) - command := &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 @@ -88,15 +88,15 @@ argocd admin proj generate-allow-list /path/to/clusterrole.yaml my-project`, func getResourceList(clientConfig clientcmd.ClientConfig) ([]*metav1.APIResourceList, error) { config, err := clientConfig.ClientConfig() if err != nil { - return nil, fmt.Errorf("error while creating client config: %w", err) + return nil, fmt.Errorf("error while creating client config: %s", err) } disco, err := discovery.NewDiscoveryClientForConfig(config) if err != nil { - return nil, fmt.Errorf("error while creating discovery client: %w", err) + return nil, fmt.Errorf("error while creating discovery client: %s", err) } serverResources, err := disco.ServerPreferredResources() if err != nil { - return nil, fmt.Errorf("error while getting server resources: %w", err) + return nil, fmt.Errorf("error while getting server resources: %s", err) } return serverResources, nil } @@ -104,23 +104,23 @@ func getResourceList(clientConfig clientcmd.ClientConfig) ([]*metav1.APIResource func generateProjectAllowList(serverResources []*metav1.APIResourceList, clusterRoleFileName string, projName string) (*v1alpha1.AppProject, error) { yamlBytes, err := os.ReadFile(clusterRoleFileName) if err != nil { - return nil, fmt.Errorf("error reading cluster role file: %w", err) + return nil, fmt.Errorf("error reading cluster role file: %s", err) } var obj unstructured.Unstructured err = yaml.Unmarshal(yamlBytes, &obj) if err != nil { - return nil, fmt.Errorf("error unmarshalling cluster role file yaml: %w", err) + return nil, fmt.Errorf("error unmarshalling cluster role file yaml: %s", err) } clusterRole := &rbacv1.ClusterRole{} err = scheme.Scheme.Convert(&obj, clusterRole, nil) if err != nil { - return nil, fmt.Errorf("error converting cluster role yaml into ClusterRole struct: %w", err) + return nil, fmt.Errorf("error converting cluster role yaml into ClusterRole struct: %s", err) } resourceList := make([]metav1.GroupKind, 0) for _, rule := range clusterRole.Rules { - if len(rule.APIGroups) == 0 { + if len(rule.APIGroups) <= 0 { continue } diff --git a/cmd/argocd/commands/admin/project_allowlist_test.go b/cmd/argocd/commands/admin/project_allowlist_test.go index 7c22fd1c0ee75..c4634fb9310c1 100644 --- a/cmd/argocd/commands/admin/project_allowlist_test.go +++ b/cmd/argocd/commands/admin/project_allowlist_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -16,6 +15,6 @@ func TestProjectAllowListGen(t *testing.T) { resourceList := []*metav1.APIResourceList{{APIResources: []metav1.APIResource{res}}} globalProj, err := generateProjectAllowList(resourceList, "testdata/test_clusterrole.yaml", "testproj") - require.NoError(t, err) - assert.NotEmpty(t, globalProj.Spec.NamespaceResourceWhitelist) + assert.NoError(t, err) + assert.True(t, len(globalProj.Spec.NamespaceResourceWhitelist) > 0) } diff --git a/cmd/argocd/commands/admin/project_test.go b/cmd/argocd/commands/admin/project_test.go index 341cd48f5c92a..93d8626ce1b25 100644 --- a/cmd/argocd/commands/admin/project_test.go +++ b/cmd/argocd/commands/admin/project_test.go @@ -5,7 +5,6 @@ import ( "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/pkg/apis/application/v1alpha1" @@ -35,17 +34,17 @@ func TestUpdateProjects_FindMatchingProject(t *testing.T) { clientset := fake.NewSimpleClientset(newProj("foo", "test"), newProj("bar", "test")) modification, err := getModification("set", "*", "*", "allow") - require.NoError(t, err) + assert.NoError(t, err) err = updateProjects(ctx, clientset.ArgoprojV1alpha1().AppProjects(namespace), "ba*", "*", "set", modification, false) - require.NoError(t, err) + assert.NoError(t, err) fooProj, err := clientset.ArgoprojV1alpha1().AppProjects(namespace).Get(ctx, "foo", v1.GetOptions{}) - require.NoError(t, err) - assert.Empty(t, fooProj.Spec.Roles[0].Policies) + assert.NoError(t, err) + assert.Len(t, fooProj.Spec.Roles[0].Policies, 0) barProj, err := clientset.ArgoprojV1alpha1().AppProjects(namespace).Get(ctx, "bar", v1.GetOptions{}) - require.NoError(t, err) - assert.EqualValues(t, []string{"p, proj:bar:test, *, set, bar/*, allow"}, barProj.Spec.Roles[0].Policies) + assert.NoError(t, err) + assert.EqualValues(t, barProj.Spec.Roles[0].Policies, []string{"p, proj:bar:test, *, set, bar/*, allow"}) } func TestUpdateProjects_FindMatchingRole(t *testing.T) { @@ -54,26 +53,26 @@ func TestUpdateProjects_FindMatchingRole(t *testing.T) { clientset := fake.NewSimpleClientset(newProj("proj", "foo", "bar")) modification, err := getModification("set", "*", "*", "allow") - require.NoError(t, err) + assert.NoError(t, err) err = updateProjects(ctx, clientset.ArgoprojV1alpha1().AppProjects(namespace), "*", "fo*", "set", modification, false) - require.NoError(t, err) + assert.NoError(t, err) proj, err := clientset.ArgoprojV1alpha1().AppProjects(namespace).Get(ctx, "proj", v1.GetOptions{}) - require.NoError(t, err) - assert.EqualValues(t, []string{"p, proj:proj:foo, *, set, proj/*, allow"}, proj.Spec.Roles[0].Policies) - assert.Empty(t, proj.Spec.Roles[1].Policies) + assert.NoError(t, err) + assert.EqualValues(t, proj.Spec.Roles[0].Policies, []string{"p, proj:proj:foo, *, set, proj/*, allow"}) + assert.Len(t, proj.Spec.Roles[1].Policies, 0) } func TestGetModification_SetPolicy(t *testing.T) { modification, err := getModification("set", "*", "*", "allow") - require.NoError(t, err) + assert.NoError(t, err) policy := modification("proj", "myaction") assert.Equal(t, "*, myaction, proj/*, allow", policy) } func TestGetModification_RemovePolicy(t *testing.T) { modification, err := getModification("remove", "*", "*", "allow") - require.NoError(t, err) + assert.NoError(t, err) policy := modification("proj", "myaction") assert.Equal(t, "", policy) } diff --git a/cmd/argocd/commands/admin/redis_initial_password.go b/cmd/argocd/commands/admin/redis_initial_password.go index 3f89b54010659..8fa1e70ad890e 100644 --- a/cmd/argocd/commands/admin/redis_initial_password.go +++ b/cmd/argocd/commands/admin/redis_initial_password.go @@ -6,20 +6,23 @@ import ( "fmt" "math/big" - "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/util/cli" apierr "k8s.io/apimachinery/pkg/api/errors" + + "github.com/argoproj/argo-cd/v2/util/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" - "github.com/argoproj/argo-cd/v2/common" - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - "github.com/argoproj/argo-cd/v2/util/cli" - "github.com/argoproj/argo-cd/v2/util/errors" + "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" ) +const defaulRedisInitialPasswordSecretName = "argocd-redis" +const defaultResisInitialPasswordKey = "auth" + func generateRandomPassword() (string, error) { const initialPasswordLength = 16 const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" @@ -37,16 +40,18 @@ func generateRandomPassword() (string, error) { // NewRedisInitialPasswordCommand defines a new command to ensure Argo CD Redis password secret exists. func NewRedisInitialPasswordCommand() *cobra.Command { - var clientConfig clientcmd.ClientConfig - command := cobra.Command{ + var ( + clientConfig clientcmd.ClientConfig + ) + var command = cobra.Command{ Use: "redis-initial-password", Short: "Ensure the Redis password exists, creating a new one if necessary.", Run: func(c *cobra.Command, args []string) { namespace, _, err := clientConfig.Namespace() errors.CheckError(err) - redisInitialPasswordSecretName := common.DefaultRedisInitialPasswordSecretName - redisInitialPasswordKey := common.DefaultRedisInitialPasswordKey + redisInitialPasswordSecretName := defaulRedisInitialPasswordSecretName + redisInitialPasswordKey := defaultResisInitialPasswordKey fmt.Printf("Checking for initial Redis password in secret %s/%s at key %s. \n", namespace, redisInitialPasswordSecretName, redisInitialPasswordKey) config, err := clientConfig.ClientConfig() diff --git a/cmd/argocd/commands/admin/repo.go b/cmd/argocd/commands/admin/repo.go index ea55b341b486c..208a6ef8550f8 100644 --- a/cmd/argocd/commands/admin/repo.go +++ b/cmd/argocd/commands/admin/repo.go @@ -25,7 +25,7 @@ const ( ) func NewRepoCommand() *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "repo", Short: "Manage repositories configuration", Run: func(c *cobra.Command, args []string) { @@ -44,7 +44,7 @@ func NewGenRepoSpecCommand() *cobra.Command { ) // For better readability and easier formatting - repoAddExamples := ` + var repoAddExamples = ` # Add a Git repository via SSH using a private key for authentication, ignoring the server's host key: argocd admin repo generate-spec git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa @@ -67,7 +67,7 @@ func NewGenRepoSpecCommand() *cobra.Command { argocd admin repo generate-spec helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test ` - command := &cobra.Command{ + var command = &cobra.Command{ Use: "generate-spec REPOURL", Short: "Generate declarative config for a repo", Example: repoAddExamples, @@ -157,7 +157,7 @@ func NewGenRepoSpecCommand() *cobra.Command { _, err := argoDB.CreateRepository(ctx, &repoOpts.Repo) errors.CheckError(err) - secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(ctx, db.RepoURLToSecretName(repoSecretPrefix, repoOpts.Repo.Repo, repoOpts.Repo.Project), v1.GetOptions{}) + secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(ctx, db.RepoURLToSecretName(repoSecretPrefix, repoOpts.Repo.Repo), v1.GetOptions{}) errors.CheckError(err) errors.CheckError(PrintResources(outputFormat, os.Stdout, secret)) diff --git a/cmd/argocd/commands/admin/secrets_redactor_test.go b/cmd/argocd/commands/admin/secrets_redactor_test.go new file mode 100644 index 0000000000000..cb1b3e78dbfea --- /dev/null +++ b/cmd/argocd/commands/admin/secrets_redactor_test.go @@ -0,0 +1,94 @@ +package admin + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var textToRedact = ` +connectors: +- config: + clientID: aabbccddeeff00112233 + clientSecret: | + theSecret + orgs: + - name: your-github-org + redirectURI: https://argocd.example.com/api/dex/callback + id: github + name: GitHub + type: github +- config: + bindDN: uid=serviceaccount,cn=users,dc=example,dc=com + bindPW: theSecret + host: ldap.example.com:636 + id: ldap + name: LDAP + type: ldap +grpc: + addr: 0.0.0.0:5557 +telemetry: + http: 0.0.0.0:5558 +issuer: https://argocd.example.com/api/dex +oauth2: + skipApprovalScreen: true +staticClients: +- id: argo-cd + name: Argo CD + redirectURIs: + - https://argocd.example.com/auth/callback + secret: Dis9M-GA11oTwZVQQWdDklPQw-sWXZkWJFyyEhMs +- id: argo-cd-cli + name: Argo CD CLI + public: true + redirectURIs: + - http://localhost +storage: + type: memory +web: + http: 0.0.0.0:5556` + +var expectedRedaction = `connectors: +- config: + clientID: aabbccddeeff00112233 + clientSecret: '********' + orgs: + - name: your-github-org + redirectURI: https://argocd.example.com/api/dex/callback + id: github + name: GitHub + type: github +- config: + bindDN: uid=serviceaccount,cn=users,dc=example,dc=com + bindPW: '********' + host: ldap.example.com:636 + id: ldap + name: LDAP + type: ldap +grpc: + addr: 0.0.0.0:5557 +issuer: https://argocd.example.com/api/dex +oauth2: + skipApprovalScreen: true +staticClients: +- id: argo-cd + name: Argo CD + redirectURIs: + - https://argocd.example.com/auth/callback + secret: '********' +- id: argo-cd-cli + name: Argo CD CLI + public: true + redirectURIs: + - http://localhost +storage: + type: memory +telemetry: + http: 0.0.0.0:5558 +web: + http: 0.0.0.0:5556 +` + +func TestSecretsRedactor(t *testing.T) { + assert.Equal(t, expectedRedaction, redactor(textToRedact)) +} diff --git a/cmd/argocd/commands/admin/settings.go b/cmd/argocd/commands/admin/settings.go index 7cd49b0c94031..d739031a7944a 100644 --- a/cmd/argocd/commands/admin/settings.go +++ b/cmd/argocd/commands/admin/settings.go @@ -146,9 +146,11 @@ func (opts *settingsOpts) getK8sClient() (*kubernetes.Clientset, string, error) } func NewSettingsCommand() *cobra.Command { - var opts settingsOpts + var ( + opts settingsOpts + ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "settings", Short: "Provides set of commands for settings validation and troubleshooting", Run: func(c *cobra.Command, args []string) { @@ -159,7 +161,7 @@ func NewSettingsCommand() *cobra.Command { command.AddCommand(NewValidateSettingsCommand(&opts)) command.AddCommand(NewResourceOverridesCommand(&opts)) - command.AddCommand(NewRBACCommand(&opts)) + command.AddCommand(NewRBACCommand()) opts.clientConfig = cli.AddKubectlFlagsToCmd(command) command.PersistentFlags().StringVar(&opts.argocdCMPath, "argocd-cm-path", "", "Path to local argocd-cm.yaml file") @@ -200,12 +202,12 @@ var validatorsByGroup = map[string]settingValidator{ ssoProvider := "" if general.DexConfig != "" { if _, err := settings.UnmarshalDexConfig(general.DexConfig); err != nil { - return "", fmt.Errorf("invalid dex.config: %w", err) + return "", fmt.Errorf("invalid dex.config: %v", err) } ssoProvider = "Dex" } else if general.OIDCConfigRAW != "" { if err := settings.ValidateOIDCConfig(general.OIDCConfigRAW); err != nil { - return "", fmt.Errorf("invalid oidc.config: %w", err) + return "", fmt.Errorf("invalid oidc.config: %v", err) } ssoProvider = "OIDC" } @@ -216,6 +218,7 @@ var validatorsByGroup = map[string]settingValidator{ summary = summary + " ('url' field is missing)" } } else if ssoProvider != "" && general.URL != "" { + } else { summary = "SSO is not configured" } @@ -274,7 +277,9 @@ var validatorsByGroup = map[string]settingValidator{ } func NewValidateSettingsCommand(cmdCtx commandContext) *cobra.Command { - var groups []string + var ( + groups []string + ) var allGroups []string for k := range validatorsByGroup { @@ -284,7 +289,7 @@ func NewValidateSettingsCommand(cmdCtx commandContext) *cobra.Command { return allGroups[i] < allGroups[j] }) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "validate", Short: "Validate settings", Long: "Validates settings specified in 'argocd-cm' ConfigMap and 'argocd-secret' Secret", @@ -336,7 +341,7 @@ argocd admin settings validate --group accounts --group plugins --load-cluster-s } func NewResourceOverridesCommand(cmdCtx commandContext) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "resource-overrides", Short: "Troubleshoot resource overrides", Run: func(c *cobra.Command, args []string) { @@ -398,7 +403,7 @@ func executeIgnoreResourceUpdatesOverrideCommand(ctx context.Context, cmdCtx com } func NewResourceIgnoreDifferencesCommand(cmdCtx commandContext) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "ignore-differences RESOURCE_YAML_PATH", Short: "Renders fields excluded from diffing", Long: "Renders ignored fields using the 'ignoreDifferences' setting specified in the 'resource.customizations' field of 'argocd-cm' ConfigMap", @@ -448,8 +453,10 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo } func NewResourceIgnoreResourceUpdatesCommand(cmdCtx commandContext) *cobra.Command { - var ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts - command := &cobra.Command{ + var ( + ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts + ) + 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", @@ -496,7 +503,7 @@ argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml - } func NewResourceHealthCommand(cmdCtx commandContext) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "health RESOURCE_YAML_PATH", Short: "Assess resource health", Long: "Assess resource health using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap", @@ -529,7 +536,7 @@ argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path . } func NewResourceActionListCommand(cmdCtx commandContext) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "list-actions RESOURCE_YAML_PATH", Short: "List available resource actions", Long: "List actions available for given resource action using the lua scripts configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields", @@ -573,7 +580,7 @@ argocd admin settings resource-overrides action list /tmp/deploy.yaml --argocd-c } func NewResourceActionRunCommand(cmdCtx commandContext) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "run-action RESOURCE_YAML_PATH ACTION", Aliases: []string{"action"}, Short: "Executes resource action", @@ -622,6 +629,7 @@ argocd admin settings resource-overrides action run /tmp/deploy.yaml restart --a fmt.Println(bytes.NewBuffer(yamlBytes).String()) } } + }) }, } diff --git a/cmd/argocd/commands/admin/settings_rbac.go b/cmd/argocd/commands/admin/settings_rbac.go index dc8faf657b520..1c09fa0d1cfe7 100644 --- a/cmd/argocd/commands/admin/settings_rbac.go +++ b/cmd/argocd/commands/admin/settings_rbac.go @@ -18,18 +18,11 @@ import ( "github.com/argoproj/argo-cd/v2/server/rbacpolicy" "github.com/argoproj/argo-cd/v2/util/assets" "github.com/argoproj/argo-cd/v2/util/cli" - "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/rbac" ) -type actionTraitMap map[string]rbacTrait - -type rbacTrait struct { - allowPath bool -} - // Provide a mapping of short-hand resource names to their RBAC counterparts -var resourceMap = map[string]string{ +var resourceMap map[string]string = map[string]string{ "account": rbacpolicy.ResourceAccounts, "app": rbacpolicy.ResourceApplications, "apps": rbacpolicy.ResourceApplications, @@ -39,7 +32,6 @@ var resourceMap = map[string]string{ "certs": rbacpolicy.ResourceCertificates, "certificate": rbacpolicy.ResourceCertificates, "cluster": rbacpolicy.ResourceClusters, - "extension": rbacpolicy.ResourceExtensions, "gpgkey": rbacpolicy.ResourceGPGKeys, "key": rbacpolicy.ResourceGPGKeys, "log": rbacpolicy.ResourceLogs, @@ -53,87 +45,47 @@ var resourceMap = map[string]string{ "repository": rbacpolicy.ResourceRepositories, } -var projectScoped = map[string]bool{ +// List of allowed RBAC resources +var validRBACResources map[string]bool = map[string]bool{ + 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.ResourceClusters: true, + rbacpolicy.ResourceProjects: true, rbacpolicy.ResourceRepositories: true, } -// List of allowed RBAC resources -var validRBACResourcesActions = map[string]actionTraitMap{ - rbacpolicy.ResourceAccounts: accountsActions, - rbacpolicy.ResourceApplications: applicationsActions, - rbacpolicy.ResourceApplicationSets: defaultCRUDActions, - rbacpolicy.ResourceCertificates: defaultCRDActions, - rbacpolicy.ResourceClusters: defaultCRUDActions, - rbacpolicy.ResourceExtensions: extensionActions, - rbacpolicy.ResourceGPGKeys: defaultCRDActions, - rbacpolicy.ResourceLogs: logsActions, - rbacpolicy.ResourceExec: execActions, - rbacpolicy.ResourceProjects: defaultCRUDActions, - rbacpolicy.ResourceRepositories: defaultCRUDActions, -} - // List of allowed RBAC actions -var defaultCRUDActions = actionTraitMap{ - rbacpolicy.ActionCreate: rbacTrait{}, - rbacpolicy.ActionGet: rbacTrait{}, - rbacpolicy.ActionUpdate: rbacTrait{}, - rbacpolicy.ActionDelete: rbacTrait{}, -} - -var defaultCRDActions = actionTraitMap{ - rbacpolicy.ActionCreate: rbacTrait{}, - rbacpolicy.ActionGet: rbacTrait{}, - rbacpolicy.ActionDelete: rbacTrait{}, -} - -var applicationsActions = actionTraitMap{ - rbacpolicy.ActionCreate: rbacTrait{}, - rbacpolicy.ActionGet: rbacTrait{}, - rbacpolicy.ActionUpdate: rbacTrait{allowPath: true}, - rbacpolicy.ActionDelete: rbacTrait{allowPath: true}, - rbacpolicy.ActionAction: rbacTrait{allowPath: true}, - rbacpolicy.ActionOverride: rbacTrait{}, - rbacpolicy.ActionSync: rbacTrait{}, -} - -var accountsActions = actionTraitMap{ - rbacpolicy.ActionCreate: rbacTrait{}, - rbacpolicy.ActionUpdate: rbacTrait{}, -} - -var execActions = actionTraitMap{ - rbacpolicy.ActionCreate: rbacTrait{}, -} - -var logsActions = actionTraitMap{ - rbacpolicy.ActionGet: rbacTrait{}, -} - -var extensionActions = actionTraitMap{ - rbacpolicy.ActionInvoke: rbacTrait{}, +var validRBACActions map[string]bool = map[string]bool{ + rbacpolicy.ActionAction: true, + rbacpolicy.ActionCreate: true, + rbacpolicy.ActionDelete: true, + rbacpolicy.ActionGet: true, + rbacpolicy.ActionOverride: true, + rbacpolicy.ActionSync: true, + rbacpolicy.ActionUpdate: true, } // NewRBACCommand is the command for 'rbac' -func NewRBACCommand(cmdCtx commandContext) *cobra.Command { - command := &cobra.Command{ +func NewRBACCommand() *cobra.Command { + var command = &cobra.Command{ Use: "rbac", Short: "Validate and test RBAC configuration", Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) }, } - command.AddCommand(NewRBACCanCommand(cmdCtx)) + command.AddCommand(NewRBACCanCommand()) command.AddCommand(NewRBACValidateCommand()) return command } -// NewRBACCanCommand is the command for 'rbac can' -func NewRBACCanCommand(cmdCtx commandContext) *cobra.Command { +// NewRBACCanRoleCommand is the command for 'rbac can-role' +func NewRBACCanCommand() *cobra.Command { var ( policyFile string defaultRole string @@ -146,7 +98,7 @@ func NewRBACCanCommand(cmdCtx commandContext) *cobra.Command { subResource string clientConfig clientcmd.ClientConfig ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "can ROLE/SUBJECT ACTION RESOURCE [SUB-RESOURCE]", Short: "Check RBAC permissions for a role or subject", Long: ` @@ -185,6 +137,11 @@ argocd admin settings rbac can someuser create application 'default/app' --defau subResource = args[3] } + userPolicy := "" + builtinPolicy := "" + + var newDefaultRole string + namespace, nsOverride, err := clientConfig.Namespace() if err != nil { log.Fatalf("could not create k8s client: %v", err) @@ -208,7 +165,6 @@ argocd admin settings rbac can someuser create application 'default/app' --defau userPolicy, newDefaultRole, matchMode := getPolicy(ctx, policyFile, realClientset, namespace) // Use built-in policy as augmentation if requested - builtinPolicy := "" if useBuiltin { builtinPolicy = assets.BuiltinPolicyCSV } @@ -219,30 +175,7 @@ argocd admin settings rbac can someuser create application 'default/app' --defau defaultRole = newDefaultRole } - // Logs RBAC will be enforced only if an internal var serverRBACLogEnforceEnable - // (representing server.rbac.log.enforce.enable env var in argocd-cm) - // is defined and has a "true" value - // Otherwise, no RBAC enforcement for logs will take place (meaning, 'can' request on a logs resource will result in "yes", - // even if there is no explicit RBAC allow, or if there is an explicit RBAC deny) - var isLogRbacEnforced func() bool - if nsOverride && policyFile == "" { - if resolveRBACResourceName(resource) == rbacpolicy.ResourceLogs { - isLogRbacEnforced = func() bool { - if opts, ok := cmdCtx.(*settingsOpts); ok { - opts.loadClusterSettings = true - opts.clientConfig = clientConfig - settingsMgr, err := opts.createSettingsManager(ctx) - errors.CheckError(err) - logEnforceEnable, err := settingsMgr.GetServerRBACLogEnforceEnable() - errors.CheckError(err) - return logEnforceEnable - } - return false - } - } - } - res := checkPolicy(subject, action, resource, subResource, builtinPolicy, userPolicy, defaultRole, matchMode, strict, isLogRbacEnforced) - + res := checkPolicy(subject, action, resource, subResource, builtinPolicy, userPolicy, defaultRole, matchMode, strict) if res { if !quiet { fmt.Println("Yes") @@ -273,7 +206,7 @@ func NewRBACValidateCommand() *cobra.Command { clientConfig clientcmd.ClientConfig ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "validate [--policy-file POLICYFILE] [--namespace NAMESPACE]", Short: "Validate RBAC policy", Long: ` @@ -288,8 +221,8 @@ argocd admin settings rbac validate --policy-file policy.csv # 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. +# 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. @@ -388,16 +321,20 @@ func getPolicyFromFile(policyFile string) (string, string, string, error) { // Retrieve policy information from a ConfigMap func getPolicyFromConfigMap(cm *corev1.ConfigMap) (string, string, string) { var ( + userPolicy string defaultRole string ok bool ) - + userPolicy, ok = cm.Data[rbac.ConfigMapPolicyCSVKey] + if !ok { + userPolicy = "" + } defaultRole, ok = cm.Data[rbac.ConfigMapPolicyDefaultKey] if !ok { defaultRole = "" } - return rbac.PolicyCSV(cm.Data), defaultRole, cm.Data[rbac.ConfigMapMatchModeKey] + return userPolicy, defaultRole, cm.Data[rbac.ConfigMapMatchModeKey] } // getPolicyConfigMap fetches the RBAC config map from K8s cluster @@ -411,7 +348,7 @@ func getPolicyConfigMap(ctx context.Context, client kubernetes.Interface, namesp // checkPolicy checks whether given subject is allowed to execute specified // action against specified resource -func checkPolicy(subject, action, resource, subResource, builtinPolicy, userPolicy, defaultRole, matchMode string, strict bool, isLogRbacEnforced func() bool) bool { +func checkPolicy(subject, action, resource, subResource, builtinPolicy, userPolicy, defaultRole, matchMode string, strict bool) bool { enf := rbac.NewEnforcer(nil, "argocd", "argocd-rbac-cm", nil) enf.SetDefaultRole(defaultRole) enf.SetMatchMode(matchMode) @@ -439,25 +376,23 @@ func checkPolicy(subject, action, resource, subResource, builtinPolicy, userPoli // If in strict mode, validate that given RBAC resource and action are // actually valid tokens. if strict { - if err := validateRBACResourceAction(realResource, action); err != nil { - log.Fatalf("error in RBAC request: %v", err) - return false + if !isValidRBACResource(realResource) { + log.Fatalf("error in RBAC request: '%s' is not a valid resource name", realResource) + } + if !isValidRBACAction(action) { + log.Fatalf("error in RBAC request: '%s' is not a valid action name", action) } } - // Some project scoped resources have a special notation - for simplicity's sake, + // Application resources have a special notation - for simplicity's sake, // if user gives no sub-resource (or specifies simple '*'), we construct // the required notation by setting subresource to '*/*'. - if projectScoped[realResource] { + if realResource == rbacpolicy.ResourceApplications { if subResource == "*" || subResource == "" { subResource = "*/*" } } - if realResource == rbacpolicy.ResourceLogs { - if isLogRbacEnforced != nil && !isLogRbacEnforced() { - return true - } - } + return enf.Enforce(subject, realResource, action, subResource) } @@ -471,18 +406,17 @@ func resolveRBACResourceName(name string) string { } } -// validateRBACResourceAction checks whether a given resource is a valid RBAC resource. -// If it is, it validates that the action is a valid RBAC action for this resource. -func validateRBACResourceAction(resource, action string) error { - validActions, ok := validRBACResourcesActions[resource] - if !ok { - return fmt.Errorf("'%s' is not a valid resource name", resource) +// 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 +} - realAction, _, hasPath := strings.Cut(action, "/") - actionTrait, ok := validActions[realAction] - if !ok || hasPath && !actionTrait.allowPath { - return fmt.Errorf("'%s' is not a valid action for %s", action, resource) - } - return nil +// isValidRBACResource checks whether a given resource is a valid RBAC resource +func isValidRBACResource(resource string) bool { + _, ok := validRBACResources[resource] + return ok } diff --git a/cmd/argocd/commands/admin/settings_rbac_test.go b/cmd/argocd/commands/admin/settings_rbac_test.go index 9fe9ab6953a68..79835ffd0c14d 100644 --- a/cmd/argocd/commands/admin/settings_rbac_test.go +++ b/cmd/argocd/commands/admin/settings_rbac_test.go @@ -5,6 +5,7 @@ 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" @@ -13,9 +14,6 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - - "github.com/argoproj/argo-cd/v2/server/rbacpolicy" - "github.com/argoproj/argo-cd/v2/util/assets" ) type FakeClientConfig struct { @@ -43,75 +41,35 @@ func (f *FakeClientConfig) ConfigAccess() clientcmd.ConfigAccess { return nil } -func Test_validateRBACResourceAction(t *testing.T) { - type args struct { - resource string - action string - } - tests := []struct { - name string - args args - valid bool - }{ - { - name: "Test valid resource and action", - args: args{ - resource: rbacpolicy.ResourceApplications, - action: rbacpolicy.ActionCreate, - }, - valid: true, - }, - { - name: "Test invalid resource", - args: args{ - resource: "invalid", - }, - valid: false, - }, - { - name: "Test invalid action", - args: args{ - resource: rbacpolicy.ResourceApplications, - action: "invalid", - }, - valid: false, - }, - { - name: "Test invalid action for resource", - args: args{ - resource: rbacpolicy.ResourceLogs, - action: rbacpolicy.ActionCreate, - }, - valid: false, - }, - { - name: "Test valid action with path", - args: args{ - resource: rbacpolicy.ResourceApplications, - action: rbacpolicy.ActionAction + "/apps/Deployment/restart", - }, - valid: true, - }, - { - name: "Test invalid action with path", - args: args{ - resource: rbacpolicy.ResourceApplications, - action: rbacpolicy.ActionGet + "/apps/Deployment/restart", - }, - valid: false, - }, +func Test_isValidRBACAction(t *testing.T) { + for k := range validRBACActions { + t.Run(k, func(t *testing.T) { + ok := isValidRBACAction(k) + assert.True(t, ok) + }) } + t.Run("invalid", func(t *testing.T) { + ok := isValidRBACAction("invalid") + assert.False(t, ok) + }) +} + +func Test_isValidRBACAction_ActionAction(t *testing.T) { + ok := isValidRBACAction("action/apps/Deployment/restart") + assert.True(t, ok) +} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := validateRBACResourceAction(tt.args.resource, tt.args.action) - if tt.valid { - assert.NoError(t, result) - } else { - assert.Error(t, result) - } +func Test_isValidRBACResource(t *testing.T) { + for k := range validRBACResources { + t.Run(k, func(t *testing.T) { + ok := isValidRBACResource(k) + assert.True(t, ok) }) } + t.Run("invalid", func(t *testing.T) { + ok := isValidRBACResource("invalid") + assert.False(t, ok) + }) } func Test_PolicyFromCSV(t *testing.T) { @@ -130,16 +88,6 @@ func Test_PolicyFromYAML(t *testing.T) { require.NotEmpty(t, uPol) require.Equal(t, "role:unknown", dRole) require.Empty(t, matchMode) - require.True(t, checkPolicy("my-org:team-qa", "update", "project", "foo", - "", uPol, dRole, matchMode, true, nil)) -} - -func trueLogRbacEnforce() bool { - return true -} - -func falseLogRbacEnforce() bool { - return false } func Test_PolicyFromK8s(t *testing.T) { @@ -163,105 +111,43 @@ func Test_PolicyFromK8s(t *testing.T) { require.Equal(t, "", matchMode) t.Run("get applications", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "applications", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil) + ok := checkPolicy("role:user", "get", "applications", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) require.True(t, ok) }) t.Run("get clusters", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "clusters", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil) + ok := checkPolicy("role:user", "get", "clusters", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) require.True(t, ok) }) t.Run("get certificates", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil) + ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) require.False(t, ok) }) t.Run("get certificates by default role", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "glob", true, nil) + ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "glob", true) require.True(t, ok) }) t.Run("get certificates by default role without builtin policy", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "certificates", "*", "", uPol, "role:readonly", "glob", true, nil) + ok := checkPolicy("role:user", "get", "certificates", "*", "", uPol, "role:readonly", "glob", true) require.False(t, ok) }) t.Run("use regex match mode instead of glob", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "certificates", ".*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "regex", true, nil) - require.False(t, ok) - }) - t.Run("get logs", func(t *testing.T) { - ok := checkPolicy("role:test", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil) - require.True(t, ok) - }) - // no function is provided to check if logs rbac is enforced or not, so the policy permissions are queried to determine if no-such-user can get logs - t.Run("no-such-user get logs", func(t *testing.T) { - ok := checkPolicy("no-such-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil) - require.False(t, ok) - }) - // logs rbac policy is enforced, and no-such-user is not granted logs permission in user policy, so the result should be false (cannot get logs) - t.Run("no-such-user get logs rbac enforced", func(t *testing.T) { - ok := checkPolicy("no-such-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, trueLogRbacEnforce) - require.False(t, ok) - }) - // no-such-user is not granted logs permission in user policy, but logs rbac policy is not enforced, so logs permission is open to all - t.Run("no-such-user get logs rbac not enforced", func(t *testing.T) { - ok := checkPolicy("no-such-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, falseLogRbacEnforce) - require.True(t, ok) - }) - // no function is provided to check if logs rbac is enforced or not, so the policy permissions are queried to determine if log-deny-user can get logs - t.Run("log-deny-user get logs", func(t *testing.T) { - ok := checkPolicy("log-deny-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil) - require.False(t, ok) - }) - // logs rbac policy is enforced, and log-deny-user is denied logs permission in user policy, so the result should be false (cannot get logs) - t.Run("log-deny-user get logs rbac enforced", func(t *testing.T) { - ok := checkPolicy("log-deny-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, trueLogRbacEnforce) + ok := checkPolicy("role:user", "get", "certificates", ".*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "regex", true) require.False(t, ok) }) - // log-deny-user is denied logs permission in user policy, but logs rbac policy is not enforced, so logs permission is open to all - t.Run("log-deny-user get logs rbac not enforced", func(t *testing.T) { - ok := checkPolicy("log-deny-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, falseLogRbacEnforce) - require.True(t, ok) - }) - // no function is provided to check if logs rbac is enforced or not, so the policy permissions are queried to determine if log-allow-user can get logs - t.Run("log-allow-user get logs", func(t *testing.T) { - ok := checkPolicy("log-allow-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil) - require.True(t, ok) - }) - // logs rbac policy is enforced, and log-allow-user is granted logs permission in user policy, so the result should be true (can get logs) - t.Run("log-allow-user get logs rbac enforced", func(t *testing.T) { - ok := checkPolicy("log-allow-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, trueLogRbacEnforce) - require.True(t, ok) - }) - // log-allow-user is granted logs permission in user policy, and logs rbac policy is not enforced, so logs permission is open to all - t.Run("log-allow-user get logs rbac not enforced", func(t *testing.T) { - ok := checkPolicy("log-allow-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, falseLogRbacEnforce) - require.True(t, ok) - }) - t.Run("get logs", func(t *testing.T) { - ok := checkPolicy("role:test", "get", "logs", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil) - require.True(t, ok) - }) t.Run("get logs", func(t *testing.T) { - ok := checkPolicy("role:test", "get", "logs", "", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil) + 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, nil) + 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, nil) - require.True(t, ok) - }) - // trueLogRbacEnforce or falseLogRbacEnforce should not affect non-logs resources - t.Run("create applicationsets with trueLogRbacEnforce", func(t *testing.T) { - ok := checkPolicy("role:user", "create", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, trueLogRbacEnforce) - require.True(t, ok) - }) - t.Run("create applicationsets with falseLogRbacEnforce", func(t *testing.T) { - ok := checkPolicy("role:user", "create", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, trueLogRbacEnforce) + 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, nil) + ok := checkPolicy("role:user", "delete", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) require.True(t, ok) }) } @@ -301,49 +187,49 @@ p, role:readonly, certificates, get, .*, allow p, role:, certificates, get, .*, allow` t.Run("get applications", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "applications", ".*/.*", builtInPolicy, uPol, dRole, "regex", true, nil) + ok := checkPolicy("role:user", "get", "applications", ".*/.*", builtInPolicy, uPol, dRole, "regex", true) require.True(t, ok) }) t.Run("get clusters", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "clusters", ".*", builtInPolicy, uPol, dRole, "regex", true, nil) + ok := checkPolicy("role:user", "get", "clusters", ".*", builtInPolicy, uPol, dRole, "regex", true) require.True(t, ok) }) t.Run("get certificates", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, dRole, "regex", true, nil) + ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, dRole, "regex", true) require.False(t, ok) }) t.Run("get certificates by default role", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, "role:readonly", "regex", true, nil) + ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, "role:readonly", "regex", true) require.True(t, ok) }) t.Run("get certificates by default role without builtin policy", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "certificates", ".*", "", uPol, "role:readonly", "regex", true, nil) + ok := checkPolicy("role:user", "get", "certificates", ".*", "", uPol, "role:readonly", "regex", true) require.False(t, ok) }) t.Run("use glob match mode instead of regex", func(t *testing.T) { - ok := checkPolicy("role:user", "get", "certificates", ".+", builtInPolicy, uPol, dRole, "glob", true, nil) + 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, nil) + 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, nil) + 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, nil) + 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, nil) + ok := checkPolicy("role:user", "delete", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true) require.True(t, ok) }) } func TestNewRBACCanCommand(t *testing.T) { - command := NewRBACCanCommand(&settingsOpts{}) + command := NewRBACCanCommand() require.NotNil(t, command) assert.Equal(t, "can", command.Name()) diff --git a/cmd/argocd/commands/admin/settings_test.go b/cmd/argocd/commands/admin/settings_test.go index 4db8ae7ba5eeb..ff817017f4be5 100644 --- a/cmd/argocd/commands/admin/settings_test.go +++ b/cmd/argocd/commands/admin/settings_test.go @@ -13,7 +13,6 @@ import ( "github.com/argoproj/argo-cd/v2/util/settings" "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" @@ -36,6 +35,7 @@ func captureStdout(callback func()) (string, error) { utils.Close(w) data, err := io.ReadAll(r) + if err != nil { return "", err } @@ -97,16 +97,22 @@ metadata: name: argocd-cm data: url: https://myargocd.com`) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } defer utils.Close(closer) opts := settingsOpts{argocdCMPath: f} settingsManager, err := opts.createSettingsManager(ctx) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } argoCDSettings, err := settingsManager.GetSettings() - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } assert.Equal(t, "https://myargocd.com", argoCDSettings.URL) } @@ -197,11 +203,12 @@ admissionregistration.k8s.io/MutatingWebhookConfiguration: } summary, err := validator(newSettingsManager(tc.data)) if tc.containsSummary != "" { - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, summary, tc.containsSummary) } else if tc.containsError != "" { - require.Error(t, err) - assert.Contains(t, err.Error(), tc.containsError) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), tc.containsError) + } } }) } @@ -266,10 +273,10 @@ func TestValidateSettingsCommand_NoErrors(t *testing.T) { cmd := NewValidateSettingsCommand(newCmdContext(map[string]string{})) out, err := captureStdout(func() { err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) }) - require.NoError(t, err) + assert.NoError(t, err) for k := range validatorsByGroup { assert.Contains(t, out, fmt.Sprintf("✅ %s", k)) } @@ -277,7 +284,9 @@ func TestValidateSettingsCommand_NoErrors(t *testing.T) { func TestResourceOverrideIgnoreDifferences(t *testing.T) { f, closer, err := tempFile(testDeploymentYAML) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } defer utils.Close(closer) t.Run("NoOverridesConfigured", func(t *testing.T) { @@ -285,9 +294,9 @@ func TestResourceOverrideIgnoreDifferences(t *testing.T) { out, err := captureStdout(func() { cmd.SetArgs([]string{"ignore-differences", f}) err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Ignore differences are not configured for 'apps/Deployment'\n") }) @@ -296,33 +305,33 @@ func TestResourceOverrideIgnoreDifferences(t *testing.T) { "resource.customizations": `apps/Deployment: ignoreDifferences: | jsonPointers: - - /spec`, - })) + - /spec`})) out, err := captureStdout(func() { cmd.SetArgs([]string{"ignore-differences", f}) err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "< spec:") }) } func TestResourceOverrideHealth(t *testing.T) { f, closer, err := tempFile(testCustomResourceYAML) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } defer utils.Close(closer) t.Run("NoHealthAssessment", func(t *testing.T) { cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ - "resource.customizations": `example.com/ExampleResource: {}`, - })) + "resource.customizations": `example.com/ExampleResource: {}`})) out, err := captureStdout(func() { cmd.SetArgs([]string{"health", f}) err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Health script is not configured for 'example.com/ExampleResource'\n") }) @@ -331,14 +340,13 @@ func TestResourceOverrideHealth(t *testing.T) { "resource.customizations": `example.com/ExampleResource: health.lua: | return { status = "Progressing" } -`, - })) +`})) out, err := captureStdout(func() { cmd.SetArgs([]string{"health", f}) err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Progressing") }) @@ -347,37 +355,39 @@ func TestResourceOverrideHealth(t *testing.T) { "resource.customizations": `example.com/*: health.lua: | return { status = "Progressing" } -`, - })) +`})) out, err := captureStdout(func() { cmd.SetArgs([]string{"health", f}) err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Progressing") }) } func TestResourceOverrideAction(t *testing.T) { f, closer, err := tempFile(testDeploymentYAML) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } defer utils.Close(closer) cronJobFile, closer, err := tempFile(testCronJobYAML) - require.NoError(t, err) + 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: {}`, - })) + "resource.customizations": `apps/Deployment: {}`})) out, err := captureStdout(func() { cmd.SetArgs([]string{"run-action", f, "test"}) err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Actions are not configured") }) @@ -395,22 +405,21 @@ func TestResourceOverrideAction(t *testing.T) { action.lua: | obj.metadata.labels["test"] = 'updated' return obj -`, - })) +`})) out, err := captureStdout(func() { cmd.SetArgs([]string{"run-action", f, "test"}) err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "test: updated") out, err = captureStdout(func() { cmd.SetArgs([]string{"list-actions", f}) err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, `NAME DISABLED restart false resume false @@ -440,24 +449,23 @@ resume false result = {} result[1] = impactedResource1 return result -`, - })) +`})) out, err := captureStdout(func() { cmd.SetArgs([]string{"run-action", cronJobFile, "test"}) err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) }) - require.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() - require.NoError(t, err) + assert.NoError(t, err) }) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "NAME") assert.Contains(t, out, "DISABLED") assert.Contains(t, out, "create-a-job") 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 ed98df6c94879..bf947fb8b7110 100644 --- a/cmd/argocd/commands/admin/testdata/rbac/argocd-rbac-cm.yaml +++ b/cmd/argocd/commands/admin/testdata/rbac/argocd-rbac-cm.yaml @@ -12,10 +12,6 @@ data: p, role:user, applicationsets, delete, */*, allow p, role:user, logs, get, */*, allow g, test, role:user - policy.overlay.csv: | - p, role:tester, applications, *, */*, allow - p, role:tester, projects, *, *, allow - g, my-org:team-qa, role:tester policy.default: role:unknown kind: ConfigMap metadata: diff --git a/cmd/argocd/commands/admin/testdata/rbac/policy.csv b/cmd/argocd/commands/admin/testdata/rbac/policy.csv index a9f830ae92d8c..b18d0904f5f60 100644 --- a/cmd/argocd/commands/admin/testdata/rbac/policy.csv +++ b/cmd/argocd/commands/admin/testdata/rbac/policy.csv @@ -10,6 +10,4 @@ p, role:user, applicationsets, delete, */*, allow p, role:test, certificates, get, *, allow p, role:test, logs, get, */*, allow p, role:test, exec, create, */*, allow -p, log-allow-user, logs, get, */*, allow -p, log-deny-user, logs, get, */*, deny g, test, role:user diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index 500c0ada88260..2e28ce0d3dac0 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -3,7 +3,6 @@ package commands import ( "context" "encoding/json" - std_errors "errors" "fmt" "io" "os" @@ -29,8 +28,7 @@ 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/ptr" + "k8s.io/utils/pointer" "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" @@ -52,7 +50,6 @@ import ( "github.com/argoproj/argo-cd/v2/util/git" "github.com/argoproj/argo-cd/v2/util/grpc" argoio "github.com/argoproj/argo-cd/v2/util/io" - logutils "github.com/argoproj/argo-cd/v2/util/log" "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" @@ -60,7 +57,7 @@ import ( // NewApplicationCommand returns a new instance of an `argocd app` command func NewApplicationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "app", Short: "Manage applications", Example: ` # List all the applications. @@ -96,8 +93,6 @@ 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 } @@ -107,7 +102,6 @@ type watchOpts struct { operation bool suspended bool degraded bool - delete bool } // NewApplicationCreateCommand returns a new instance of an `argocd app create` command @@ -122,7 +116,7 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra. setFinalizer bool appNamespace string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "create APPNAME", Short: "Create an application", Example: ` # Create a directory app @@ -140,15 +134,13 @@ 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) @@ -270,6 +262,7 @@ func hasAppChanged(appReq, appRes *argoappv1.Application, upsert bool) bool { } 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{}) @@ -294,7 +287,7 @@ func parentChildDetails(appIf application.ApplicationServiceClient, ctx context. return mapUidToNode, mapParentToChild, parentNode } -func printHeader(acdClient argocdclient.Client, app *argoappv1.Application, ctx context.Context, windows *argoappv1.SyncWindows, showOperation bool, showParams bool, sourcePosition int) { +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) @@ -310,22 +303,20 @@ func printHeader(acdClient argocdclient.Client, app *argoappv1.Application, ctx printOperationResult(app.Status.OperationState) } if showParams { - printParams(app, sourcePosition) + printParams(app) } } // NewApplicationGetCommand returns a new instance of an `argocd app get` command func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - refresh bool - hardRefresh bool - output string - showParams bool - showOperation bool - appNamespace string - sourcePosition int + refresh bool + hardRefresh bool + output string + showParams bool + showOperation bool ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "get APPNAME", Short: "Get application details", Example: templates.Examples(` @@ -344,9 +335,6 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com # Show application parameters and overrides argocd app get my-app --show-params - # Show application parameters and overrides for a source at position 1 under spec.sources of app my-app - argocd app get my-app --show-params --source-position 1 - # Refresh application data when retrieving argocd app get my-app --refresh @@ -370,23 +358,15 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + appName, appNs := argo.ParseFromQualifiedName(args[0], "") app, err := appIf.Get(ctx, &application.ApplicationQuery{ Name: &appName, Refresh: getRefreshType(refresh, hardRefresh), AppNamespace: &appNs, }) - errors.CheckError(err) - if app.Spec.HasMultipleSources() { - if sourcePosition <= 0 { - errors.CheckError(fmt.Errorf("Source position should be specified and must be greater than 0 for applications with multiple sources")) - } - if len(app.Spec.GetSources()) < sourcePosition { - errors.CheckError(fmt.Errorf("Source position should be less than the number of sources in the application")) - } - } + errors.CheckError(err) pConn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie() defer argoio.Close(pConn) @@ -400,7 +380,7 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com err := PrintResource(app, output) errors.CheckError(err) case "wide", "": - printHeader(acdClient, app, ctx, windows, showOperation, showParams, sourcePosition) + printHeader(acdClient, app, ctx, windows, showOperation, showParams) if len(app.Status.Resources) > 0 { fmt.Println() w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) @@ -408,14 +388,14 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com _ = w.Flush() } case "tree": - printHeader(acdClient, app, ctx, windows, showOperation, showParams, sourcePosition) + printHeader(acdClient, app, ctx, windows, showOperation, showParams) mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs) if len(mapUidToNode) > 0 { fmt.Println() printTreeView(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) } case "tree=detailed": - printHeader(acdClient, app, ctx, windows, showOperation, showParams, sourcePosition) + printHeader(acdClient, app, ctx, windows, showOperation, showParams) mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs) if len(mapUidToNode) > 0 { fmt.Println() @@ -431,8 +411,6 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com 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") - command.Flags().IntVar(&sourcePosition, "source-position", -1, "Position of the source from the list of sources of the app. Counting starts at 1.") return command } @@ -451,7 +429,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co container string previous bool ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "logs APPNAME", Short: "Get logs of application pods", Example: templates.Examples(` @@ -510,16 +488,16 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co stream, err := appIf.PodLogs(ctx, &application.ApplicationPodLogsQuery{ Name: &appName, Group: &group, - Namespace: ptr.To(namespace), + Namespace: pointer.String(namespace), Kind: &kind, ResourceName: &resourceName, - Follow: ptr.To(follow), - TailLines: ptr.To(tail), - SinceSeconds: ptr.To(sinceSeconds), + Follow: pointer.Bool(follow), + TailLines: pointer.Int64(tail), + SinceSeconds: pointer.Int64(sinceSeconds), UntilTime: &untilTime, Filter: &filter, - Container: ptr.To(container), - Previous: ptr.To(previous), + Container: pointer.String(container), + Previous: pointer.Bool(previous), AppNamespace: &appNs, }) if err != nil { @@ -527,10 +505,10 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } for { msg, err := stream.Recv() + if err == io.EOF { + return + } if err != nil { - if std_errors.Is(err, io.EOF) { - return - } st, ok := status.FromError(err) if !ok { log.Fatalf("stream read failed: %v", err) @@ -568,19 +546,16 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } 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) - if !app.Spec.HasMultipleSources() { - fmt.Println("Source:") - } else { - fmt.Println("Sources:") - } - for _, source := range app.Spec.GetSources() { - printAppSourceDetails(&source) - } + fmt.Printf(printOpFmtStr, "Repo:", source.RepoURL) + fmt.Printf(printOpFmtStr, "Target:", source.TargetRevision) + fmt.Printf(printOpFmtStr, "Path:", source.Path) + printAppSourceDetails(&source) var wds []string var status string var allow, deny, inactiveAllows bool @@ -628,7 +603,7 @@ func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *ar syncPolicy += " (Prune)" } } else { - syncPolicy = "Manual" + syncPolicy = "" } fmt.Printf(printOpFmtStr, "Sync Policy:", syncPolicy) syncStatusStr := string(app.Status.Sync.Status) @@ -650,19 +625,11 @@ 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.IsRef() { - 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) } } @@ -714,22 +681,9 @@ func truncateString(str string, num int) string { } // printParams prints parameters and overrides -func printParams(app *argoappv1.Application, sourcePosition int) { - var source *argoappv1.ApplicationSource - - if app.Spec.HasMultipleSources() { - // Get the source by the sourcePosition whose params you'd like to print - source = app.Spec.GetSourcePtrByPosition(sourcePosition) - if source == nil { - source = &argoappv1.ApplicationSource{} - } - } else { - src := app.Spec.GetSource() - source = &src - } - - if source.Helm != nil { - printHelmParams(source.Helm) +func printParams(app *argoappv1.Application) { + if app.Spec.GetSource().Helm != nil { + printHelmParams(app.Spec.GetSource().Helm) } } @@ -758,11 +712,9 @@ 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 - appNamespace string - sourcePosition int + appOpts cmdutil.AppOptions ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "set APPNAME", Short: "Set application parameters", Example: templates.Examples(` @@ -772,8 +724,11 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com # 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 for a source at position 1 under spec.sources of app my-app. source-position starts at 1. - argocd app set my-app --source-position 1 --repo https://github.com/argoproj/argocd-example-apps.git + # 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 application parameters and specify the namespace argocd app set my-app --parameter key1=value1 --parameter key2=value2 --namespace my-namespace @@ -786,30 +741,21 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + appName, appNs := argo.ParseFromQualifiedName(args[0], "") argocdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := argocdClient.NewApplicationClientOrDie() defer argoio.Close(conn) app, err := appIf.Get(ctx, &application.ApplicationQuery{Name: &appName, AppNamespace: &appNs}) errors.CheckError(err) - if app.Spec.HasMultipleSources() { - if sourcePosition <= 0 { - errors.CheckError(fmt.Errorf("Source position should be specified and must be greater than 0 for applications with multiple sources")) - } - if len(app.Spec.GetSources()) < sourcePosition { - errors.CheckError(fmt.Errorf("Source position should be less than the number of sources in the application")) - } - } - - visited := cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts, sourcePosition) + visited := cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts) if visited == 0 { log.Error("Please set at least one option to update") c.HelpFunc()(c, args) os.Exit(1) } - setParameterOverrides(app, appOpts.Parameters, sourcePosition) + setParameterOverrides(app, appOpts.Parameters) _, err = appIf.UpdateSpec(ctx, &application.ApplicationUpdateSpecRequest{ Name: &app.Name, Spec: &app.Spec, @@ -820,8 +766,6 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com }, } cmdutil.AddAppFlags(command, &appOpts) - command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Set application parameters in namespace") - command.Flags().IntVar(&sourcePosition, "source-position", -1, "Position of the source from the list of sources of the app. Counting starts at 1.") return command } @@ -839,7 +783,6 @@ type unsetOpts struct { ignoreMissingValueFiles bool pluginEnvs []string passCredentials bool - ref bool } // IsZero returns true when the Application options for kustomize are considered empty @@ -855,22 +798,17 @@ func (o *unsetOpts) KustomizeIsZero() bool { // NewApplicationUnsetCommand returns a new instance of an `argocd app unset` command func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var sourcePosition int appOpts := cmdutil.AppOptions{} opts := unsetOpts{} - var appNamespace string - command := &cobra.Command{ + 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 suffix + # Unset kustomize override prefix argocd app unset my-app --namesuffix - # Unset kustomize override suffix for source at position 1 under spec.sources of app my-app. source-position starts at 1. - argocd app unset my-app --source-position 1 --namesuffix - # Unset parameter override argocd app unset my-app -p COMPONENT=PARAM`, @@ -881,25 +819,14 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C c.HelpFunc()(c, args) os.Exit(1) } - - appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + appName, appNs := argo.ParseFromQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) app, err := appIf.Get(ctx, &application.ApplicationQuery{Name: &appName, AppNamespace: &appNs}) errors.CheckError(err) - if app.Spec.HasMultipleSources() { - if sourcePosition <= 0 { - errors.CheckError(fmt.Errorf("Source position should be specified and must be greater than 0 for applications with multiple sources")) - } - if len(app.Spec.GetSources()) < sourcePosition { - errors.CheckError(fmt.Errorf("Source position should be less than the number of sources in the application")) - } - } - - source := app.Spec.GetSourcePtrByPosition(sourcePosition) - - updated, nothingToUnset := unset(source, opts) + source := app.Spec.GetSource() + updated, nothingToUnset := unset(&source, opts) if nothingToUnset { c.HelpFunc()(c, args) os.Exit(1) @@ -908,7 +835,7 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C return } - cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts, sourcePosition) + cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts) _, err = appIf.UpdateSpec(ctx, &application.ApplicationUpdateSpecRequest{ Name: &app.Name, Spec: &app.Spec, @@ -918,7 +845,6 @@ 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") @@ -931,22 +857,13 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C 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(&sourcePosition, "source-position", -1, "Position of the source from the list of sources of the app. Counting starts at 1.") return command } func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, nothingToUnset bool) { - needToUnsetRef := false - if opts.ref && source.IsRef() { - source.Ref = "" - updated = true - needToUnsetRef = true - } - if source.Kustomize != nil { if opts.KustomizeIsZero() { - return updated, !needToUnsetRef + return false, true } if opts.namePrefix && source.Kustomize.NamePrefix != "" { @@ -996,7 +913,7 @@ func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, n } if source.Helm != nil { if len(opts.parameters) == 0 && len(opts.valuesFiles) == 0 && !opts.valuesLiteral && !opts.ignoreMissingValueFiles && !opts.passCredentials { - return updated, !needToUnsetRef + return false, true } for _, paramStr := range opts.parameters { helmParams := source.Helm.Parameters @@ -1033,10 +950,9 @@ func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, n updated = true } } - if source.Plugin != nil { if len(opts.pluginEnvs) == 0 { - return false, !needToUnsetRef + return false, true } for _, env := range opts.pluginEnvs { err := source.Plugin.RemoveEnvEntry(env) @@ -1062,8 +978,7 @@ func targetObjects(resources []*argoappv1.ResourceDiff) ([]*unstructured.Unstruc } 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 { + 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 { @@ -1076,22 +991,20 @@ func getLocalObjects(ctx context.Context, app *argoappv1.Application, proj *argo } func getLocalObjectsString(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions, - trackingMethod string, -) []string { + 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, - TrackingMethod: trackingMethod, - ProjectName: proj.Name, - ProjectSourceRepos: proj.Spec.SourceRepos, - AnnotationManifestGeneratePaths: app.GetAnnotation(argoappv1.AnnotationKeyManifestGeneratePaths), + 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) @@ -1145,16 +1058,13 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co localRepoRoot string serverSideGenerate bool localIncludes []string - appNamespace string - revisions []string - sourcePositions []int64 ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts ) shortDesc := "Perform a diff against the target and live state." - command := &cobra.Command{ + 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\nKubernetes Secrets are ignored from this diff.", + 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", Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -1162,15 +1072,10 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co c.HelpFunc()(c, args) os.Exit(2) } - - if len(revisions) != len(sourcePositions) { - errors.CheckError(fmt.Errorf("While using revisions and source-positions, length of values for both flags should be same.")) - } - clientset := headless.NewClientOrDie(clientOpts, c) conn, appIf := clientset.NewApplicationClientOrDie() defer argoio.Close(conn) - appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + appName, appNs := argo.ParseFromQualifiedName(args[0], "") app, err := appIf.Get(ctx, &application.ApplicationQuery{ Name: &appName, Refresh: getRefreshType(refresh, hardRefresh), @@ -1185,27 +1090,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co argoSettings, err := settingsIf.Get(ctx, &settings.SettingsQuery{}) errors.CheckError(err) diffOption := &DifferenceOption{} - if app.Spec.HasMultipleSources() && len(revisions) > 0 && len(sourcePositions) > 0 { - numOfSources := int64(len(app.Spec.GetSources())) - for _, pos := range sourcePositions { - if pos <= 0 || pos > numOfSources { - log.Fatal("source-position cannot be less than 1 or more than number of sources in the app. Counting starts at 1.") - } - } - - q := application.ApplicationManifestQuery{ - Name: &appName, - AppNamespace: &appNs, - Revisions: revisions, - SourcePositions: sourcePositions, - } - res, err := appIf.GetManifests(ctx, &q) - errors.CheckError(err) - - diffOption.res = res - diffOption.revisions = revisions - diffOption.sourcePositions = sourcePositions - } else if revision != "" { + if revision != "" { q := application.ApplicationManifestQuery{ Name: &appName, Revision: &revision, @@ -1254,23 +1139,18 @@ 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") - command.Flags().StringArrayVar(&revisions, "revisions", []string{}, "Show manifests at specific revisions for source position in source-positions") - command.Flags().Int64SliceVar(&sourcePositions, "source-positions", []int64{}, "List of source positions. Default is empty array. Counting start at 1.") command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout") return command } // DifferenceOption struct to store diff options type DifferenceOption struct { - local string - localRepoRoot string - revision string - cluster *argoappv1.Cluster - res *repoapiclient.ManifestResponse - serversideRes *repoapiclient.ManifestResponse - revisions []string - sourcePositions []int64 + local string + localRepoRoot string + revision string + cluster *argoappv1.Cluster + res *repoapiclient.ManifestResponse + serversideRes *repoapiclient.ManifestResponse } // 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 @@ -1282,7 +1162,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *arg if diffOptions.local != "" { 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 != "" || len(diffOptions.revisions) > 0 { + } else if diffOptions.revision != "" { var unstructureds []*unstructured.Unstructured for _, mfst := range diffOptions.res.Manifests { obj, err := argoappv1.UnmarshalToUnstructured(mfst) @@ -1303,11 +1183,11 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *arg } else { for i := range resources.Items { res := resources.Items[i] - live := &unstructured.Unstructured{} + var live = &unstructured.Unstructured{} err := json.Unmarshal([]byte(res.NormalizedLiveState), &live) errors.CheckError(err) - target := &unstructured.Unstructured{} + var target = &unstructured.Unstructured{} err = json.Unmarshal([]byte(res.TargetState), &target) errors.CheckError(err) @@ -1332,7 +1212,6 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *arg WithDiffSettings(app.Spec.IgnoreDifferences, overrides, ignoreAggregatedRoles, ignoreNormalizerOpts). WithTracking(argoSettings.AppLabelKey, argoSettings.TrackingMethod). WithNoCache(). - WithLogger(logutils.NewLogrusLogger(logutils.NewWithCurrentConfig())). Build() errors.CheckError(err) diffRes, err := argodiff.StateDiff(item.live, item.target, diffConfig) @@ -1363,7 +1242,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *arg 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 { - live := &unstructured.Unstructured{} + var live = &unstructured.Unstructured{} err := json.Unmarshal([]byte(res.NormalizedLiveState), &live) errors.CheckError(err) @@ -1401,10 +1280,8 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. noPrompt bool propagationPolicy string selector string - wait bool - appNamespace string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "delete APPNAME", Short: "Delete an application", Example: ` # Delete an app @@ -1426,13 +1303,12 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. c.HelpFunc()(c, args) os.Exit(1) } - acdClient := headless.NewClientOrDie(clientOpts, c) - conn, appIf := acdClient.NewApplicationClientOrDie() + conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) var isTerminal bool = isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) var isConfirmAll bool = false - numOfApps := len(args) - promptFlag := c.Flag("yes") + var numOfApps = len(args) + var promptFlag = c.Flag("yes") if promptFlag.Changed && promptFlag.Value.String() == "true" { noPrompt = true } @@ -1445,7 +1321,7 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. } for _, appFullName := range appNames { - appName, appNs := argo.ParseFromQualifiedName(appFullName, appNamespace) + appName, appNs := argo.ParseFromQualifiedName(appFullName, "") appDeleteReq := application.ApplicationDeleteRequest{ Name: &appName, AppNamespace: &appNs, @@ -1474,9 +1350,6 @@ 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.") @@ -1484,10 +1357,6 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. } else { _, err := appIf.Delete(ctx, &appDeleteReq) errors.CheckError(err) - - if wait { - checkForDeleteEvent(ctx, acdClient, appFullName) - } } } }, @@ -1496,20 +1365,9 @@ 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 { @@ -1558,7 +1416,7 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co appNamespace string cluster string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "list", Short: "List applications", Example: ` # List all apps @@ -1576,7 +1434,7 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) apps, err := appIf.List(ctx, &application.ApplicationQuery{ - Selector: ptr.To(selector), + Selector: pointer.String(selector), AppNamespace: &appNamespace, }) @@ -1617,7 +1475,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 "Manual" + return "" } policy := "Auto" if app.Spec.SyncPolicy.Automated.Prune { @@ -1721,14 +1579,13 @@ 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 - output string - appNamespace string + watch watchOpts + timeout uint + selector string + resources []string + output string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "wait [APPNAME.. | -l selector]", Short: "Wait for an application to reach a synced and healthy state", Example: ` # Wait for an app @@ -1768,17 +1625,13 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co closer, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(closer) if selector != "" { - list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: ptr.To(selector)}) + list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: pointer.String(selector)}) errors.CheckError(err) for _, i := range list.Items { - appNames = append(appNames, i.QualifiedName()) + appNames = append(appNames, i.Name) } } for _, appName := range appNames { - // Construct QualifiedName - if appNamespace != "" && !strings.Contains(appName, "/") { - appName = appNamespace + "/" + appName - } _, _, err := waitOnApplicationStatus(ctx, acdClient, appName, timeout, watch, selectedResources, output) errors.CheckError(err) } @@ -1788,12 +1641,10 @@ 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 } @@ -1828,8 +1679,6 @@ func printTreeViewDetailed(nodeMapping map[string]argoappv1.ResourceNode, parent func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( revision string - revisions []string - sourcePositions []int64 resources []string labels []string selector string @@ -1853,10 +1702,9 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co diffChangesConfirm bool projects []string output string - appNamespace string ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "sync [APPNAME... | -l selector | --project project-name]", Short: "Sync an application to its target state", Example: ` # Sync an app @@ -1872,9 +1720,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co argocd app sync -l '!app.kubernetes.io/instance' argocd app sync -l 'app.kubernetes.io/instance notin (my-app,other-app)' - # Sync a multi-source application for specific revision of specific sources - argocd app manifests my-app --revisions 0.0.1 --source-positions 1 --revisions 0.0.2 --source-positions 2 - # Sync a specific resource # Resource should be formatted as GROUP:KIND:NAME. If no GROUP is specified then :KIND:NAME argocd app sync my-app --resource :Service:my-service @@ -1893,21 +1738,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co if len(args) > 1 && selector != "" { log.Fatal("Cannot use selector option when application name(s) passed as argument(s)") } - - if len(args) != 1 && (len(revisions) > 0 || len(sourcePositions) > 0) { - log.Fatal("Cannot use --revisions and --source-positions options when 0 or more than 1 application names are passed as argument(s)") - } - - if len(revisions) != len(sourcePositions) { - log.Fatal("While using --revisions and --source-positions, length of values for both flags should be same.") - } - - for _, pos := range sourcePositions { - if pos <= 0 { - log.Fatal("source-position cannot be less than or equal to 0, Counting starts at 1") - } - } - acdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) @@ -1917,11 +1747,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co appNames := args if selector != "" || len(projects) > 0 { - list, err := appIf.List(ctx, &application.ApplicationQuery{ - Selector: ptr.To(selector), - AppNamespace: &appNamespace, - Projects: projects, - }) + list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: pointer.String(selector), Projects: projects}) errors.CheckError(err) // unlike list, we'd want to fail if nothing was found @@ -1933,7 +1759,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co if len(projects) != 0 { errMsg += fmt.Sprintf(" projects %v", projects) } - log.Fatal(errMsg) + log.Fatalf(errMsg) } for _, i := range list.Items { @@ -1942,19 +1768,13 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } for _, appQualifiedName := range appNames { - // Construct QualifiedName - if appNamespace != "" && !strings.Contains(appQualifiedName, "/") { - appQualifiedName = appNamespace + "/" + appQualifiedName - } appName, appNs := argo.ParseFromQualifiedName(appQualifiedName, "") if len(selectedLabels) > 0 { q := application.ApplicationManifestQuery{ - Name: &appName, - AppNamespace: &appNs, - Revision: &revision, - Revisions: revisions, - SourcePositions: sourcePositions, + Name: &appName, + AppNamespace: &appNs, + Revision: &revision, } res, err := appIf.GetManifests(ctx, &q) @@ -1997,7 +1817,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co if app.Spec.HasMultipleSources() { if revision != "" { - log.Fatal("argocd cli does not work on multi-source app with --revision flag. Use --revisions and --source-position instead.") + log.Fatal("argocd cli does not work on multi-source app with --revision flag") return } @@ -2062,17 +1882,15 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } syncReq := application.ApplicationSyncRequest{ - Name: &appName, - AppNamespace: &appNs, - DryRun: &dryRun, - Revision: &revision, - Resources: filteredResources, - Prune: &prune, - Manifests: localObjsStrings, - Infos: getInfos(infos), - SyncOptions: syncOptionsFactory(), - Revisions: revisions, - SourcePositions: sourcePositions, + Name: &appName, + AppNamespace: &appNs, + DryRun: &dryRun, + Revision: &revision, + Resources: filteredResources, + Prune: &prune, + Manifests: localObjsStrings, + Infos: getInfos(infos), + SyncOptions: syncOptionsFactory(), } switch strategy { @@ -2091,7 +1909,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co Backoff: &argoappv1.Backoff{ Duration: retryBackoffDuration.String(), MaxDuration: retryBackoffMaxDuration.String(), - Factor: ptr.To(retryBackoffFactor), + Factor: pointer.Int64(retryBackoffFactor), }, } } @@ -2119,8 +1937,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } } else { fmt.Printf("====== No Differences found ======\n") - // if no differences found, then no need to sync - return } } _, err = appIf.Sync(ctx, &syncReq) @@ -2169,17 +1985,14 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co 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") command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout") - command.Flags().StringArrayVar(&revisions, "revisions", []string{}, "Show manifests at specific revisions for source position in source-positions") - command.Flags().Int64SliceVar(&sourcePositions, "source-positions", []int64{}, "List of source positions. Default is empty array. Counting start at 1.") return command } func getAppNamesBySelector(ctx context.Context, appIf application.ApplicationServiceClient, selector string) ([]string, error) { appNames := []string{} if selector != "" { - list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: ptr.To(selector)}) + list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: pointer.String(selector)}) if err != nil { return []string{}, err } @@ -2188,13 +2001,13 @@ func getAppNamesBySelector(ctx context.Context, appIf application.ApplicationSer return []string{}, fmt.Errorf("no apps match selector %v", selector) } for _, i := range list.Items { - appNames = append(appNames, i.QualifiedName()) + appNames = append(appNames, i.Name) } } return appNames, nil } -// ResourceState tracks the state of a resource when waiting on an application status. +// ResourceDiff tracks the state of a resource when waiting on an application status. type resourceState struct { Group string Kind string @@ -2256,8 +2069,7 @@ func getResourceStates(app *argoappv1.Application, selectedResources []*argoappv sync = string(resource.Status) } states = append(states, &resourceState{ - Group: res.Group, Kind: res.Kind, Namespace: res.Namespace, Name: res.Name, Status: sync, Health: health, Hook: string(res.HookType), Message: res.Message, - }) + Group: res.Group, Kind: res.Kind, Namespace: res.Namespace, Name: res.Name, Status: sync, Health: health, Hook: string(res.HookType), Message: res.Message}) delete(resourceByKey, kube.NewResourceKey(res.Group, res.Kind, res.Namespace, res.Name)) } } @@ -2276,8 +2088,7 @@ func getResourceStates(app *argoappv1.Application, selectedResources []*argoappv health = string(res.Health.Status) } states = append(states, &resourceState{ - Group: res.Group, Kind: res.Kind, Namespace: res.Namespace, Name: res.Name, Status: string(res.Status), Health: health, Hook: "", Message: "", - }) + Group: res.Group, Kind: res.Kind, Namespace: res.Namespace, Name: res.Name, Status: string(res.Status), Health: health, Hook: "", Message: ""}) } // filter out not selected resources if len(selectedResources) > 0 { @@ -2326,9 +2137,6 @@ 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 { @@ -2363,7 +2171,7 @@ func checkResourceStatus(watch watchOpts, healthStatus string, syncStatus string 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: ptr.To(appName), AppNamespace: ptr.To(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) { @@ -2386,10 +2194,6 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, // time when the sync status lags behind when an operation completes refresh := false - // printSummary controls whether we print the app summary table, OperationState, and ResourceState - // We don't want to print these when output type is json or yaml, as the output would become unparsable. - printSummary := output != "json" && output != "yaml" - appRealName, appNs := argo.ParseFromQualifiedName(appName, "") printFinalStatus := func(app *argoappv1.Application) *argoappv1.Application { @@ -2406,13 +2210,11 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, _ = conn.Close() } - if printSummary { - fmt.Println() - printAppSummaryTable(app, appURL(ctx, acdClient, appName), nil) - fmt.Println() - if watch.operation { - printOperationResult(app.Status.OperationState) - } + fmt.Println() + printAppSummaryTable(app, appURL(ctx, acdClient, appName), nil) + fmt.Println() + if watch.operation { + printOperationResult(app.Status.OperationState) } switch output { @@ -2427,13 +2229,13 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, _ = w.Flush() } case "tree": - mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appRealName, appNs) + 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, appRealName, appNs) + mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs) if len(mapUidToNode) > 0 { fmt.Println() printTreeViewDetailed(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) @@ -2452,26 +2254,17 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, AppNamespace: &appNs, }) errors.CheckError(err) - - if printSummary { - fmt.Println() - fmt.Println("This is the state of the app after `wait` timed out:") - } - + fmt.Println() + fmt.Println("This is the state of the app after `wait` timed out:") printFinalStatus(app) cancel() - - if printSummary { - fmt.Println() - fmt.Println("The command timed out waiting for the conditions to be met.") - } + fmt.Println() + fmt.Println("The command timed out waiting for the conditions to be met.") }) } w := tabwriter.NewWriter(os.Stdout, 5, 0, 2, ' ', 0) - if printSummary { - _, _ = fmt.Fprintf(w, waitFormatString, "TIMESTAMP", "GROUP", "KIND", "NAMESPACE", "NAME", "STATUS", "HEALTH", "HOOK", "MESSAGE") - } + _, _ = fmt.Fprintf(w, waitFormatString, "TIMESTAMP", "GROUP", "KIND", "NAMESPACE", "NAME", "STATUS", "HEALTH", "HOOK", "MESSAGE") prevStates := make(map[string]*resourceState) conn, appClient := acdClient.NewApplicationClientOrDie() @@ -2496,12 +2289,6 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, 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 @@ -2555,7 +2342,7 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, prevStates[stateKey] = newState doPrint = true } - if doPrint && printSummary { + if doPrint { _, _ = fmt.Fprintf(w, waitFormatString, prevStates[stateKey].FormatItems()...) } } @@ -2568,18 +2355,20 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, // 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, sourcePosition int) { +func setParameterOverrides(app *argoappv1.Application, parameters []string) { if len(parameters) == 0 { return } - source := app.Spec.GetSourcePtrByPosition(sourcePosition) + source := app.Spec.GetSource() var sourceType argoappv1.ApplicationSourceType if st, _ := source.ExplicitType(); st != nil { sourceType = *st } else if app.Status.SourceType != "" { sourceType = app.Status.SourceType - } else if len(strings.SplitN(parameters[0], "=", 2)) == 2 { - sourceType = argoappv1.ApplicationSourceTypeHelm + } else { + if len(strings.SplitN(parameters[0], "=", 2)) == 2 { + sourceType = argoappv1.ApplicationSourceTypeHelm + } } switch sourceType { @@ -2609,56 +2398,14 @@ 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) - type history struct { - id int64 - date string - revision string - } - varHistory := map[string][]history{} - varHistoryKeys := []string{} + _, _ = fmt.Fprintf(w, "ID\tDATE\tREVISION\n") for _, depInfo := range revHistory { - 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") + rev := depInfo.Source.TargetRevision + if len(depInfo.Revision) >= 7 { + rev = fmt.Sprintf("%s (%s)", rev, depInfo.Revision[0:7]) } + _, _ = fmt.Fprintf(w, "%d\t%s\t%s\n", depInfo.ID, depInfo.DeployedAt, rev) } _ = w.Flush() } @@ -2666,10 +2413,9 @@ 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 - appNamespace string + output string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "history APPNAME", Short: "Show application deployment history", Run: func(c *cobra.Command, args []string) { @@ -2681,7 +2427,7 @@ func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra } conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + appName, appNs := argo.ParseFromQualifiedName(args[0], "") app, err := appIf.Get(ctx, &application.ApplicationQuery{ Name: &appName, AppNamespace: &appNs, @@ -2695,7 +2441,6 @@ 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 } @@ -2720,12 +2465,11 @@ 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 - output string - appNamespace string + prune bool + timeout uint + output string ) - command := &cobra.Command{ + 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) { @@ -2734,7 +2478,7 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + appName, appNs := argo.ParseFromQualifiedName(args[0], "") var err error depID := -1 if len(args) > 1 { @@ -2756,8 +2500,8 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr _, err = appIf.Rollback(ctx, &application.ApplicationRollbackRequest{ Name: &appName, AppNamespace: &appNs, - Id: ptr.To(depInfo.ID), - Prune: ptr.To(prune), + Id: pointer.Int64(depInfo.ID), + Prune: pointer.Bool(prune), }) errors.CheckError(err) @@ -2770,14 +2514,11 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr 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 } -const ( - printOpFmtStr = "%-20s%s\n" - defaultCheckTimeoutSeconds = 0 -) +const printOpFmtStr = "%-20s%s\n" +const defaultCheckTimeoutSeconds = 0 func printOperationResult(opState *argoappv1.OperationState) { if opState == nil { @@ -2785,11 +2526,7 @@ func printOperationResult(opState *argoappv1.OperationState) { } if opState.SyncResult != nil { fmt.Printf(printOpFmtStr, "Operation:", "Sync") - 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, "Sync Revision:", opState.SyncResult.Revision) } fmt.Printf(printOpFmtStr, "Phase:", opState.Phase) fmt.Printf(printOpFmtStr, "Start:", opState.StartedAt) @@ -2809,26 +2546,14 @@ func printOperationResult(opState *argoappv1.OperationState) { // NewApplicationManifestsCommand returns a new instance of an `argocd app manifests` command func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - source string - revision string - revisions []string - sourcePositions []int64 - local string - localRepoRoot string + source string + revision string + local string + localRepoRoot string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "manifests APPNAME", Short: "Print manifests of an application", - Example: templates.Examples(` - # Get manifests for an application - argocd app manifests my-app - - # Get manifests for an application at a specific revision - argocd app manifests my-app --revision 0.0.1 - - # Get manifests for a multi-source application at specific revisions for specific sources - argocd app manifests my-app --revisions 0.0.1 --source-positions 1 --revisions 0.0.2 --source-positions 2 - `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -2836,22 +2561,10 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob c.HelpFunc()(c, args) os.Exit(1) } - - if len(revisions) != len(sourcePositions) { - errors.CheckError(fmt.Errorf("While using revisions and source-positions, length of values for both flags should be same.")) - } - - for _, pos := range sourcePositions { - if pos <= 0 { - log.Fatal("source-position cannot be less than or equal to 0, Counting starts at 1") - } - } - appName, appNs := argo.ParseFromQualifiedName(args[0], "") clientset := headless.NewClientOrDie(clientOpts, c) conn, appIf := clientset.NewApplicationClientOrDie() defer argoio.Close(conn) - resources, err := appIf.ManagedResources(ctx, &application.ResourcesQuery{ ApplicationName: &appName, AppNamespace: &appNs, @@ -2876,29 +2589,12 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob errors.CheckError(err) proj := getProject(c, clientOpts, ctx, app.Spec.Project) - // nolint:staticcheck unstructureds = getLocalObjects(context.Background(), app, proj.Project, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod) - } else if len(revisions) > 0 && len(sourcePositions) > 0 { - q := application.ApplicationManifestQuery{ - Name: &appName, - AppNamespace: &appNs, - Revision: ptr.To(revision), - Revisions: revisions, - SourcePositions: sourcePositions, - } - res, err := appIf.GetManifests(ctx, &q) - errors.CheckError(err) - - for _, mfst := range res.Manifests { - obj, err := argoappv1.UnmarshalToUnstructured(mfst) - errors.CheckError(err) - unstructureds = append(unstructureds, obj) - } } else if revision != "" { q := application.ApplicationManifestQuery{ Name: &appName, AppNamespace: &appNs, - Revision: ptr.To(revision), + Revision: pointer.String(revision), } res, err := appIf.GetManifests(ctx, &q) errors.CheckError(err) @@ -2931,8 +2627,6 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob } command.Flags().StringVar(&source, "source", "git", "Source of manifests. One of: live|git") command.Flags().StringVar(&revision, "revision", "", "Show manifests at a specific revision") - command.Flags().StringArrayVar(&revisions, "revisions", []string{}, "Show manifests at specific revisions for the source at position in source-positions") - command.Flags().Int64SliceVar(&sourcePositions, "source-positions", []int64{}, "List of source positions. Default is empty array. Counting start at 1.") command.Flags().StringVar(&local, "local", "", "If set, show locally-generated manifests. Value is the absolute path to app manifests within the manifest repo. Example: '/home/username/apps/env/app-1'.") command.Flags().StringVar(&localRepoRoot, "local-repo-root", ".", "Path to the local repository root. Used together with --local allows setting the repository root. Example: '/home/username/apps'.") return command @@ -2940,7 +2634,7 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob // NewApplicationTerminateOpCommand returns a new instance of an `argocd app terminate-op` command func NewApplicationTerminateOpCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "terminate-op APPNAME", Short: "Terminate running operation of an application", Run: func(c *cobra.Command, args []string) { @@ -2965,8 +2659,7 @@ func NewApplicationTerminateOpCommand(clientOpts *argocdclient.ClientOptions) *c } func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var appNamespace string - command := &cobra.Command{ + var command = &cobra.Command{ Use: "edit APPNAME", Short: "Edit application", Run: func(c *cobra.Command, args []string) { @@ -2976,8 +2669,7 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co c.HelpFunc()(c, args) os.Exit(1) } - - appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + appName, appNs := argo.ParseFromQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) app, err := appIf.Get(ctx, &application.ApplicationQuery{ @@ -3003,11 +2695,7 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } var appOpts cmdutil.AppOptions - - // do not allow overrides for applications with multiple sources - if !app.Spec.HasMultipleSources() { - cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts, 0) - } + cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts) _, err = appIf.UpdateSpec(ctx, &application.ApplicationUpdateSpecRequest{ Name: &appName, Spec: &updatedSpec, @@ -3021,16 +2709,12 @@ 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 - patchType string - appNamespace string - ) + var patch string + var patchType string command := cobra.Command{ Use: "patch APPNAME", @@ -3047,7 +2731,7 @@ func NewApplicationPatchCommand(clientOpts *argocdclient.ClientOptions) *cobra.C c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + appName, appNs := argo.ParseFromQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) @@ -3065,137 +2749,8 @@ 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 - ) - 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()) - - // sourcePosition is the index at which new source will be appended to spec.Sources - sourcePosition := len(app.Spec.GetSources()) - app.Spec.Sources = append(app.Spec.Sources, *appSource) - - setParameterOverrides(app, appOpts.Parameters, sourcePosition) - - _, 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 ( - sourcePosition int - appNamespace string - ) - command := &cobra.Command{ - Use: "remove-source APPNAME", - Short: "Remove a source from multiple sources application. Counting starts with 1. Default value is -1.", - Example: ` # Remove the source at position 1 from application's sources. Counting starts at 1. - argocd app remove-source myapplication --source-position 1`, - Run: func(c *cobra.Command, args []string) { - ctx := c.Context() - - if len(args) != 1 { - c.HelpFunc()(c, args) - os.Exit(1) - } - - if sourcePosition <= 0 { - errors.CheckError(fmt.Errorf("Value of source-position 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()) < sourcePosition { - errors.CheckError(fmt.Errorf("Application does not have source at %d\n", sourcePosition)) - } - - app.Spec.Sources = append(app.Spec.Sources[:sourcePosition-1], app.Spec.Sources[sourcePosition:]...) - - _, 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(&sourcePosition, "source-position", -1, "Position of the source from the list of sources of the app. Counting starts at 1.") - return command -} diff --git a/cmd/argocd/commands/app_actions.go b/cmd/argocd/commands/app_actions.go index f795585d07b8c..866aed5ae349e 100644 --- a/cmd/argocd/commands/app_actions.go +++ b/cmd/argocd/commands/app_actions.go @@ -4,17 +4,16 @@ import ( "context" "encoding/json" "fmt" + "github.com/argoproj/argo-cd/v2/util/templates" "os" "strconv" "text/tabwriter" - "github.com/argoproj/argo-cd/v2/util/templates" - "github.com/argoproj/argo-cd/v2/cmd/util" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" @@ -35,17 +34,19 @@ type DisplayedAction struct { Disabled bool } -var appActionExample = templates.Examples(` +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 { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "actions", Short: "Manage Resource actions", Example: appActionExample, @@ -66,7 +67,7 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt var group string var resourceName string var output string - command := &cobra.Command{ + var command = &cobra.Command{ Use: "list APPNAME", Short: "Lists available actions on a resource", Example: templates.Examples(` @@ -95,11 +96,11 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt availActionsForResource, err := appIf.ListResourceActions(ctx, &applicationpkg.ApplicationResourceRequest{ Name: &appName, AppNamespace: &appNs, - Namespace: ptr.To(obj.GetNamespace()), - ResourceName: ptr.To(obj.GetName()), - Group: ptr.To(gvk.Group), - Kind: ptr.To(gvk.Kind), - Version: ptr.To(gvk.Version), + Namespace: pointer.String(obj.GetNamespace()), + ResourceName: pointer.String(obj.GetName()), + Group: pointer.String(gvk.Group), + Kind: pointer.String(gvk.Kind), + Version: pointer.String(gvk.Version), }) errors.CheckError(err) for _, action := range availActionsForResource.Actions { @@ -148,7 +149,7 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti var kind string var group string var all bool - command := &cobra.Command{ + var command = &cobra.Command{ Use: "run APPNAME ACTION", Short: "Runs an available action on resource(s)", Example: templates.Examples(` @@ -180,7 +181,7 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti errors.CheckError(err) filteredObjects, err := util.FilterResources(command.Flags().Changed("group"), resources, group, kind, namespace, resourceName, all) errors.CheckError(err) - resGroup := filteredObjects[0].GroupVersionKind().Group + var resGroup = filteredObjects[0].GroupVersionKind().Group for i := range filteredObjects[1:] { if filteredObjects[i].GroupVersionKind().Group != resGroup { log.Fatal("Ambiguous resource group. Use flag --group to specify resource group explicitly.") @@ -194,12 +195,12 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti _, err := appIf.RunResourceAction(ctx, &applicationpkg.ResourceActionRunRequest{ Name: &appName, AppNamespace: &appNs, - Namespace: ptr.To(obj.GetNamespace()), - ResourceName: ptr.To(objResourceName), - Group: ptr.To(gvk.Group), - Kind: ptr.To(gvk.Kind), - Version: ptr.To(gvk.GroupVersion().Version), - Action: ptr.To(actionName), + Namespace: pointer.String(obj.GetNamespace()), + ResourceName: pointer.String(objResourceName), + Group: pointer.String(gvk.Group), + Kind: pointer.String(gvk.Kind), + Version: pointer.String(gvk.GroupVersion().Version), + Action: pointer.String(actionName), }) errors.CheckError(err) } diff --git a/cmd/argocd/commands/app_resource_test.go b/cmd/argocd/commands/app_resource_test.go index 4a6cd50d6800f..5846065141e15 100644 --- a/cmd/argocd/commands/app_resource_test.go +++ b/cmd/argocd/commands/app_resource_test.go @@ -17,9 +17,9 @@ func TestPrintTreeViewAppResources(t *testing.T) { 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"} - nodeMapping := make(map[string]v1alpha1.ResourceNode) - mapParentToChild := make(map[string][]string) - parentNode := make(map[string]struct{}) + 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 { @@ -36,7 +36,7 @@ func TestPrintTreeViewAppResources(t *testing.T) { buf := &bytes.Buffer{} w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) - printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, w) + printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w) if err := w.Flush(); err != nil { t.Fatal(err) } @@ -58,9 +58,9 @@ func TestPrintTreeViewDetailedAppResources(t *testing.T) { Message: "Readiness Gate failed", } - nodeMapping := make(map[string]v1alpha1.ResourceNode) - mapParentToChild := make(map[string][]string) - parentNode := make(map[string]struct{}) + 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 { @@ -77,7 +77,7 @@ func TestPrintTreeViewDetailedAppResources(t *testing.T) { buf := &bytes.Buffer{} w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) - printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, w) + printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w) if err := w.Flush(); err != nil { t.Fatal(err) } diff --git a/cmd/argocd/commands/app_resources.go b/cmd/argocd/commands/app_resources.go index f86ab0669661b..4cffb706ff1bc 100644 --- a/cmd/argocd/commands/app_resources.go +++ b/cmd/argocd/commands/app_resources.go @@ -11,7 +11,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" @@ -71,14 +71,14 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) _, err = appIf.PatchResource(ctx, &applicationpkg.ApplicationResourcePatchRequest{ Name: &appName, AppNamespace: &appNs, - Namespace: ptr.To(obj.GetNamespace()), - ResourceName: ptr.To(obj.GetName()), - Version: ptr.To(gvk.Version), - Group: ptr.To(gvk.Group), - Kind: ptr.To(gvk.Kind), - Patch: ptr.To(patch), - PatchType: ptr.To(patchType), - Project: ptr.To(project), + Namespace: pointer.String(obj.GetNamespace()), + ResourceName: pointer.String(obj.GetName()), + Version: pointer.String(gvk.Version), + Group: pointer.String(gvk.Group), + 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()) @@ -108,8 +108,8 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) errors.CheckError(err) command.Flags().StringVar(&group, "group", "", "Group") command.Flags().StringVar(&namespace, "namespace", "", "Namespace") - command.Flags().BoolVar(&force, "force", false, "Indicates whether to force delete the resource") - command.Flags().BoolVar(&orphan, "orphan", false, "Indicates whether to orphan the dependents of the deleted resource") + 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) { @@ -136,14 +136,14 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) _, err = appIf.DeleteResource(ctx, &applicationpkg.ApplicationResourceDeleteRequest{ Name: &appName, AppNamespace: &appNs, - Namespace: ptr.To(obj.GetNamespace()), - ResourceName: ptr.To(obj.GetName()), - Version: ptr.To(gvk.Version), - Group: ptr.To(gvk.Group), - Kind: ptr.To(gvk.Kind), + Namespace: pointer.String(obj.GetNamespace()), + ResourceName: pointer.String(obj.GetName()), + Version: pointer.String(gvk.Version), + Group: pointer.String(gvk.Group), + Kind: pointer.String(gvk.Kind), Force: &force, Orphan: &orphan, - Project: ptr.To(project), + Project: pointer.String(project), }) errors.CheckError(err) log.Infof("Resource '%s' deleted", obj.GetName()) @@ -175,25 +175,27 @@ func parentChildInfo(nodes []v1alpha1.ResourceNode) (map[string]v1alpha1.Resourc return mapUidToNode, mapParentToChild, parentNode } -func printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, w *tabwriter.Writer) { +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{}, w *tabwriter.Writer) { +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{}, w *tabwriter.Writer) { +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{}, w *tabwriter.Writer) { +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) } @@ -206,26 +208,29 @@ func printResources(listAll bool, orphaned bool, appResourceTree *v1alpha1.Appli if !orphaned || listAll { mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.Nodes) - printDetailedTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, w) + printDetailedTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w) } if orphaned || listAll { mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes) - printDetailedTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, w) + 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, w) + printTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w) } if orphaned || listAll { mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes) - printTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, w) + 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...) @@ -241,15 +246,17 @@ func printResources(listAll bool, orphaned bool, appResourceTree *v1alpha1.Appli _, _ = 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 - command := &cobra.Command{ + var command = &cobra.Command{ Use: "resources APPNAME", Short: "List resource of application", Run: func(c *cobra.Command, args []string) { diff --git a/cmd/argocd/commands/app_test.go b/cmd/argocd/commands/app_test.go index ffd7d78329cd9..68983560999c8 100644 --- a/cmd/argocd/commands/app_test.go +++ b/cmd/argocd/commands/app_test.go @@ -1,56 +1,23 @@ package commands import ( - "context" - "encoding/json" "fmt" - "io" - "net/http" "os" - "slices" - "strings" "testing" "time" - "google.golang.org/grpc" - "k8s.io/apimachinery/pkg/watch" - - "github.com/argoproj/argo-cd/v2/reposerver/apiclient" - - v1 "k8s.io/api/core/v1" - - "sigs.k8s.io/yaml" - 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" - "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" - "github.com/stretchr/testify/require" - "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) { @@ -112,6 +79,7 @@ func Test_getRefreshType(t *testing.T) { } func TestFindRevisionHistoryWithoutPassedId(t *testing.T) { + histories := v1alpha1.RevisionHistories{} histories = append(histories, v1alpha1.RevisionHistory{ID: 1}) @@ -136,6 +104,7 @@ func TestFindRevisionHistoryWithoutPassedId(t *testing.T) { } history, err := findRevisionHistory(&application, -1) + if err != nil { t.Fatal("Find revision history should fail without errors") } @@ -143,6 +112,7 @@ func TestFindRevisionHistoryWithoutPassedId(t *testing.T) { if history == nil { t.Fatal("History should be found") } + } func TestPrintTreeViewAppGet(t *testing.T) { @@ -153,9 +123,9 @@ func TestPrintTreeViewAppGet(t *testing.T) { 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"} - nodeMapping := make(map[string]v1alpha1.ResourceNode) - mapParentToChild := make(map[string][]string) - parentNode := make(map[string]struct{}) + 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 @@ -192,9 +162,9 @@ func TestPrintTreeViewDetailedAppGet(t *testing.T) { 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"} - nodeMapping := make(map[string]v1alpha1.ResourceNode) - mapParentToChild := make(map[string][]string) - parentNode := make(map[string]struct{}) + 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 @@ -222,40 +192,7 @@ func TestPrintTreeViewDetailedAppGet(t *testing.T) { assert.Contains(t, output, "numalogic-rollout-demo-5dcd5457d5-6trpt") assert.Contains(t, output, "Degraded") assert.Contains(t, output, "Readiness Gate failed") -} - -func TestFindRevisionHistoryWithoutPassedIdWithMultipleSources(t *testing.T) { - histories := v1alpha1.RevisionHistories{} - - histories = append(histories, v1alpha1.RevisionHistory{ID: 1}) - histories = append(histories, v1alpha1.RevisionHistory{ID: 2}) - histories = append(histories, v1alpha1.RevisionHistory{ID: 3}) - - status := v1alpha1.ApplicationStatus{ - Resources: nil, - Sync: v1alpha1.SyncStatus{}, - Health: v1alpha1.HealthStatus{}, - History: histories, - Conditions: nil, - ReconciledAt: nil, - OperationState: nil, - ObservedAt: nil, - SourceType: "", - Summary: v1alpha1.ApplicationSummary{}, - } - - application := v1alpha1.Application{ - Status: status, - } - - history, err := findRevisionHistory(&application, -1) - if err != nil { - t.Fatal("Find revision history should fail without errors") - } - if history == nil { - t.Fatal("History should be found") - } } func TestDefaultWaitOptions(t *testing.T) { @@ -266,10 +203,10 @@ func TestDefaultWaitOptions(t *testing.T) { suspended: false, } opts := getWatchOpts(watch) - assert.True(t, opts.sync) - assert.True(t, opts.health) - assert.True(t, opts.operation) - assert.False(t, opts.suspended) + assert.Equal(t, true, opts.sync) + assert.Equal(t, true, opts.health) + assert.Equal(t, true, opts.operation) + assert.Equal(t, false, opts.suspended) } func TestOverrideWaitOptions(t *testing.T) { @@ -280,13 +217,14 @@ func TestOverrideWaitOptions(t *testing.T) { suspended: false, } opts := getWatchOpts(watch) - assert.True(t, opts.sync) - assert.False(t, opts.health) - assert.False(t, opts.operation) - assert.False(t, opts.suspended) + assert.Equal(t, true, opts.sync) + assert.Equal(t, false, opts.health) + assert.Equal(t, false, opts.operation) + assert.Equal(t, false, opts.suspended) } func TestFindRevisionHistoryWithoutPassedIdAndEmptyHistoryList(t *testing.T) { + histories := v1alpha1.RevisionHistories{} status := v1alpha1.ApplicationStatus{ @@ -319,9 +257,11 @@ func TestFindRevisionHistoryWithoutPassedIdAndEmptyHistoryList(t *testing.T) { if err.Error() != "Application '' should have at least two successful deployments" { t.Fatal("Find revision history should fail with correct error message") } + } func TestFindRevisionHistoryWithPassedId(t *testing.T) { + histories := v1alpha1.RevisionHistories{} histories = append(histories, v1alpha1.RevisionHistory{ID: 1}) @@ -346,6 +286,7 @@ func TestFindRevisionHistoryWithPassedId(t *testing.T) { } history, err := findRevisionHistory(&application, 3) + if err != nil { t.Fatal("Find revision history should fail without errors") } @@ -357,9 +298,11 @@ func TestFindRevisionHistoryWithPassedId(t *testing.T) { if history.Revision != "123" { t.Fatal("Failed to find correct history with correct revision") } + } func TestFindRevisionHistoryWithPassedIdThatNotExist(t *testing.T) { + histories := v1alpha1.RevisionHistories{} histories = append(histories, v1alpha1.RevisionHistory{ID: 1}) @@ -396,6 +339,7 @@ func TestFindRevisionHistoryWithPassedIdThatNotExist(t *testing.T) { if err.Error() != "Application '' does not have deployment id '4' in history\n" { t.Fatal("Find revision history should fail with correct error message") } + } func Test_groupObjsByKey(t *testing.T) { @@ -452,13 +396,14 @@ func Test_groupObjsByKey(t *testing.T) { } func TestFormatSyncPolicy(t *testing.T) { + t.Run("Policy not defined", func(t *testing.T) { app := v1alpha1.Application{} policy := formatSyncPolicy(app) - if policy != "Manual" { - t.Fatalf("Incorrect policy %q, should be Manual", policy) + if policy != "" { + t.Fatalf("Incorrect policy %q, should be ", policy) } }) @@ -495,6 +440,7 @@ func TestFormatSyncPolicy(t *testing.T) { t.Fatalf("Incorrect policy %q, should be Auto-Prune", policy) } }) + } func TestFormatConditionSummary(t *testing.T) { @@ -591,21 +537,18 @@ 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", }, }, } @@ -615,86 +558,7 @@ func TestPrintApplicationHistoryTable(t *testing.T) { 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", - }, - }, - }, - } - - output, _ := captureOutput(func() error { - printApplicationHistoryTable(histories) - return nil - }) - - 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" + 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" if output != expectation { t.Fatalf("Incorrect print operation output %q, should be %q", output, expectation) @@ -775,110 +639,11 @@ Project: default Server: local Namespace: argocd URL: url -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 +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) @@ -918,83 +683,35 @@ func TestPrintAppConditions(t *testing.T) { } func TestPrintParams(t *testing.T) { - testCases := []struct { - name string - app *v1alpha1.Application - sourcePosition int - expectedOutput string - }{ - { - name: "Single Source application with valid helm parameters", - app: &v1alpha1.Application{ - Spec: v1alpha1.ApplicationSpec{ - Source: &v1alpha1.ApplicationSource{ - Helm: &v1alpha1.ApplicationSourceHelm{ - Parameters: []v1alpha1.HelmParameter{ - { - Name: "name1", - Value: "value1", - }, - { - Name: "name2", - Value: "value2", - }, - { - Name: "name3", - Value: "value3", - }, + output, _ := captureOutput(func() error { + app := &v1alpha1.Application{ + Spec: v1alpha1.ApplicationSpec{ + Source: &v1alpha1.ApplicationSource{ + Helm: &v1alpha1.ApplicationSourceHelm{ + Parameters: []v1alpha1.HelmParameter{ + { + Name: "name1", + Value: "value1", }, - }, - }, - }, - }, - sourcePosition: -1, - expectedOutput: "\n\nNAME VALUE\nname1 value1\nname2 value2\nname3 value3\n", - }, - { - name: "Multi-source application with a valid Source Position", - app: &v1alpha1.Application{ - Spec: v1alpha1.ApplicationSpec{ - Sources: []v1alpha1.ApplicationSource{ - { - Helm: &v1alpha1.ApplicationSourceHelm{ - Parameters: []v1alpha1.HelmParameter{ - { - Name: "nameA", - Value: "valueA", - }, - }, + { + Name: "name2", + Value: "value2", }, - }, - { - Helm: &v1alpha1.ApplicationSourceHelm{ - Parameters: []v1alpha1.HelmParameter{ - { - Name: "nameB", - Value: "valueB", - }, - }, + { + Name: "name3", + Value: "value3", }, }, }, }, }, - sourcePosition: 1, - expectedOutput: "\n\nNAME VALUE\nnameA valueA\n", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - output, _ := captureOutput(func() error { - printParams(tc.app, tc.sourcePosition) - return nil - }) - - if output != tc.expectedOutput { - t.Fatalf("Incorrect print params output %q, should be %q\n", output, tc.expectedOutput) - } - }) + } + printParams(app) + return nil + }) + expectation := "\n\nNAME VALUE\nname1 value1\nname2 value2\nname3 value3\n" + if output != expectation { + t.Fatalf("Incorrect print params output %q, should be %q", output, expectation) } } @@ -1089,13 +806,6 @@ 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{ @@ -1199,36 +909,36 @@ func Test_unset(t *testing.T) { assert.False(t, updated) assert.False(t, nothingToUnset) - assert.Len(t, kustomizeSource.Kustomize.Images, 2) + assert.Equal(t, 2, len(kustomizeSource.Kustomize.Images)) updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeImages: []string{"old1=new:tag"}}) - assert.Len(t, kustomizeSource.Kustomize.Images, 1) + assert.Equal(t, 1, len(kustomizeSource.Kustomize.Images)) assert.True(t, updated) assert.False(t, nothingToUnset) updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeImages: []string{"old1=new:tag"}}) assert.False(t, updated) assert.False(t, nothingToUnset) - assert.Len(t, kustomizeSource.Kustomize.Replicas, 2) + assert.Equal(t, 2, len(kustomizeSource.Kustomize.Replicas)) updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeReplicas: []string{"my-deployment"}}) - assert.Len(t, kustomizeSource.Kustomize.Replicas, 1) + 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.Len(t, helmSource.Helm.Parameters, 2) + assert.Equal(t, 2, len(helmSource.Helm.Parameters)) updated, nothingToUnset = unset(helmSource, unsetOpts{parameters: []string{"name-1"}}) - assert.Len(t, helmSource.Helm.Parameters, 1) + assert.Equal(t, 1, len(helmSource.Helm.Parameters)) assert.True(t, updated) assert.False(t, nothingToUnset) updated, nothingToUnset = unset(helmSource, unsetOpts{parameters: []string{"name-1"}}) assert.False(t, updated) assert.False(t, nothingToUnset) - assert.Len(t, helmSource.Helm.ValueFiles, 2) + assert.Equal(t, 2, len(helmSource.Helm.ValueFiles)) updated, nothingToUnset = unset(helmSource, unsetOpts{valuesFiles: []string{"values-1.yaml"}}) - assert.Len(t, helmSource.Helm.ValueFiles, 1) + assert.Equal(t, 1, len(helmSource.Helm.ValueFiles)) assert.True(t, updated) assert.False(t, nothingToUnset) updated, nothingToUnset = unset(helmSource, unsetOpts{valuesFiles: []string{"values-1.yaml"}}) @@ -1244,27 +954,27 @@ func Test_unset(t *testing.T) { assert.False(t, updated) assert.False(t, nothingToUnset) - assert.True(t, helmSource.Helm.IgnoreMissingValueFiles) + assert.Equal(t, true, helmSource.Helm.IgnoreMissingValueFiles) updated, nothingToUnset = unset(helmSource, unsetOpts{ignoreMissingValueFiles: true}) - assert.False(t, helmSource.Helm.IgnoreMissingValueFiles) + assert.Equal(t, false, helmSource.Helm.IgnoreMissingValueFiles) assert.True(t, updated) assert.False(t, nothingToUnset) updated, nothingToUnset = unset(helmSource, unsetOpts{ignoreMissingValueFiles: true}) assert.False(t, updated) assert.False(t, nothingToUnset) - assert.True(t, helmSource.Helm.PassCredentials) + assert.Equal(t, true, helmSource.Helm.PassCredentials) updated, nothingToUnset = unset(helmSource, unsetOpts{passCredentials: true}) - assert.False(t, helmSource.Helm.PassCredentials) + assert.Equal(t, false, helmSource.Helm.PassCredentials) assert.True(t, updated) assert.False(t, nothingToUnset) updated, nothingToUnset = unset(helmSource, unsetOpts{passCredentials: true}) assert.False(t, updated) assert.False(t, nothingToUnset) - assert.Len(t, pluginSource.Plugin.Env, 2) + assert.Equal(t, 2, len(pluginSource.Plugin.Env)) updated, nothingToUnset = unset(pluginSource, unsetOpts{pluginEnvs: []string{"env-1"}}) - assert.Len(t, pluginSource.Plugin.Env, 1) + assert.Equal(t, 1, len(pluginSource.Plugin.Env)) assert.True(t, updated) assert.False(t, nothingToUnset) updated, nothingToUnset = unset(pluginSource, unsetOpts{pluginEnvs: []string{"env-1"}}) @@ -1338,8 +1048,7 @@ func TestFilterAppResources(t *testing.T) { app := v1alpha1.Application{ Status: v1alpha1.ApplicationStatus{ Resources: []v1alpha1.ResourceStatus{ - appReplicaSet1, appReplicaSet2, appJob, appService1, appService2, appDeployment, - }, + appReplicaSet1, appReplicaSet2, appJob, appService1, appService2, appDeployment}, }, } // Resource filters @@ -1349,64 +1058,49 @@ func TestFilterAppResources(t *testing.T) { Kind: "", Name: "", Namespace: "", - Exclude: false, - } + Exclude: false} // *:*:* includeAllResources = v1alpha1.SyncOperationResource{ Group: "*", Kind: "*", Name: "*", Namespace: "", - Exclude: false, - } + Exclude: false} // !*:*:* excludeAllResources = v1alpha1.SyncOperationResource{ Group: "*", Kind: "*", Name: "*", Namespace: "", - Exclude: true, - } + Exclude: true} // *:Service:* includeAllServiceResources = v1alpha1.SyncOperationResource{ Group: "*", Kind: "Service", Name: "*", Namespace: "", - Exclude: false, - } + Exclude: false} // !*:Service:* excludeAllServiceResources = v1alpha1.SyncOperationResource{ Group: "*", Kind: "Service", Name: "*", Namespace: "", - Exclude: true, - } - // apps:ReplicaSet:* - includeAllReplicaSetResource = v1alpha1.SyncOperationResource{ - Group: "apps", - Kind: "ReplicaSet", - Name: "*", - Namespace: "", - Exclude: false, - } + Exclude: true} // apps:ReplicaSet:replicaSet-name1 includeReplicaSet1Resource = v1alpha1.SyncOperationResource{ Group: "apps", Kind: "ReplicaSet", Name: "replicaSet-name1", Namespace: "", - Exclude: false, - } + Exclude: false} // !apps:ReplicaSet:replicaSet-name2 excludeReplicaSet2Resource = v1alpha1.SyncOperationResource{ Group: "apps", Kind: "ReplicaSet", Name: "replicaSet-name2", Namespace: "", - Exclude: true, - } + Exclude: true} ) // Filtered resources @@ -1454,72 +1148,55 @@ func TestFilterAppResources(t *testing.T) { expectedResult []*v1alpha1.SyncOperationResource }{ // --resource apps:ReplicaSet:replicaSet-name1 --resource *:Service:* - { - testName: "Include ReplicaSet replicaSet-name1 resource and all service resources", + {testName: "Include ReplicaSet replicaSet-name1 resouce and all service resources", selectedResources: []*v1alpha1.SyncOperationResource{&includeAllServiceResources, &includeReplicaSet1Resource}, expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &service1, &service2}, }, // --resource apps:ReplicaSet:replicaSet-name1 --resource !*:Service:* - { - testName: "Include ReplicaSet replicaSet-name1 resource and exclude all service resources", + {testName: "Include ReplicaSet replicaSet-name1 resouce and exclude all service resources", selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllServiceResources, &includeReplicaSet1Resource}, - expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1}, + expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment}, }, // --resource !apps:ReplicaSet:replicaSet-name2 --resource !*:Service:* - { - testName: "Exclude ReplicaSet replicaSet-name2 resource and all service resources", + {testName: "Exclude ReplicaSet replicaSet-name2 resouce and all service resources", selectedResources: []*v1alpha1.SyncOperationResource{&excludeReplicaSet2Resource, &excludeAllServiceResources}, - expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &job, &deployment}, + expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment}, }, // --resource !apps:ReplicaSet:replicaSet-name2 - { - testName: "Exclude ReplicaSet replicaSet-name2 resource", + {testName: "Exclude ReplicaSet replicaSet-name2 resouce", selectedResources: []*v1alpha1.SyncOperationResource{&excludeReplicaSet2Resource}, expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &job, &service1, &service2, &deployment}, }, // --resource apps:ReplicaSet:replicaSet-name1 - { - testName: "Include ReplicaSet replicaSet-name1 resource", + {testName: "Include ReplicaSet replicaSet-name1 resouce", selectedResources: []*v1alpha1.SyncOperationResource{&includeReplicaSet1Resource}, expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1}, }, - // --resource apps:ReplicaSet:* --resource !apps:ReplicaSet:replicaSet-name2 - { - testName: "Include All ReplicaSet resource and exclude replicaSet-name1 resource", - selectedResources: []*v1alpha1.SyncOperationResource{&includeAllReplicaSetResource, &excludeReplicaSet2Resource}, - expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1}, - }, // --resource !*:Service:* - { - testName: "Exclude Service resources", + {testName: "Exclude Service resouces", selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllServiceResources}, expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment}, }, // --resource *:Service:* - { - testName: "Include Service resources", + {testName: "Include Service resouces", selectedResources: []*v1alpha1.SyncOperationResource{&includeAllServiceResources}, expectedResult: []*v1alpha1.SyncOperationResource{&service1, &service2}, }, // --resource !*:*:* - { - testName: "Exclude all resources", + {testName: "Exclude all resouces", selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllResources}, expectedResult: nil, }, // --resource *:*:* - { - testName: "Include all resources", + {testName: "Include all resouces", selectedResources: []*v1alpha1.SyncOperationResource{&includeAllResources}, expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment}, }, - { - testName: "No Filters", + {testName: "No Filters", selectedResources: []*v1alpha1.SyncOperationResource{&blankValues}, expectedResult: nil, }, - { - testName: "Empty Filter", + {testName: "Empty Filter", selectedResources: []*v1alpha1.SyncOperationResource{}, expectedResult: nil, }, @@ -1534,49 +1211,47 @@ func TestFilterAppResources(t *testing.T) { } func TestParseSelectedResources(t *testing.T) { - resources := []string{ - "v1alpha:Application:test", + resources := []string{"v1alpha:Application:test", "v1alpha:Application:namespace/test", "!v1alpha:Application:test", "apps:Deployment:default/test", - "!*:*:*", - } + "!*:*:*"} operationResources, err := parseSelectedResources(resources) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, operationResources, 5) - assert.Equal(t, v1alpha1.SyncOperationResource{ + assert.Equal(t, *operationResources[0], v1alpha1.SyncOperationResource{ Namespace: "", Name: "test", Kind: application.ApplicationKind, Group: "v1alpha", - }, *operationResources[0]) - assert.Equal(t, v1alpha1.SyncOperationResource{ + }) + assert.Equal(t, *operationResources[1], v1alpha1.SyncOperationResource{ Namespace: "namespace", Name: "test", Kind: application.ApplicationKind, Group: "v1alpha", - }, *operationResources[1]) - assert.Equal(t, v1alpha1.SyncOperationResource{ + }) + assert.Equal(t, *operationResources[2], v1alpha1.SyncOperationResource{ Namespace: "", Name: "test", Kind: "Application", Group: "v1alpha", Exclude: true, - }, *operationResources[2]) - assert.Equal(t, v1alpha1.SyncOperationResource{ + }) + assert.Equal(t, *operationResources[3], v1alpha1.SyncOperationResource{ Namespace: "default", Name: "test", Kind: "Deployment", Group: "apps", Exclude: false, - }, *operationResources[3]) - assert.Equal(t, v1alpha1.SyncOperationResource{ + }) + assert.Equal(t, *operationResources[4], v1alpha1.SyncOperationResource{ Namespace: "", Name: "*", Kind: "*", Group: "*", Exclude: true, - }, *operationResources[4]) + }) } func TestParseSelectedResourcesIncorrect(t *testing.T) { @@ -1589,13 +1264,14 @@ func TestParseSelectedResourcesIncorrectNamespace(t *testing.T) { resources := []string{"v1alpha:Application:namespace/test/unknown"} _, err := parseSelectedResources(resources) assert.ErrorContains(t, err, "v1alpha:Application:namespace/test/unknown") + } func TestParseSelectedResourcesEmptyList(t *testing.T) { var resources []string operationResources, err := parseSelectedResources(resources) - require.NoError(t, err) - assert.Empty(t, operationResources) + assert.NoError(t, err) + assert.Len(t, operationResources, 0) } func TestPrintApplicationTableNotWide(t *testing.T) { @@ -1624,8 +1300,8 @@ func TestPrintApplicationTableNotWide(t *testing.T) { printApplicationTable([]v1alpha1.Application{*app, *app}, &output) return nil }) - require.NoError(t, err) - 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.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" assert.Equal(t, output, expectation) } @@ -1660,8 +1336,8 @@ func TestPrintApplicationTableWide(t *testing.T) { printApplicationTable([]v1alpha1.Application{*app, *app}, &output) return nil }) - require.NoError(t, err) - 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.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" assert.Equal(t, output, expectation) } @@ -1697,6 +1373,7 @@ func TestFormatItems(t *testing.T) { assert.Equal(t, "health", items[6]) assert.Equal(t, "hook", items[7]) assert.Equal(t, "message", items[8]) + } func TestMerge(t *testing.T) { @@ -1922,441 +1599,3 @@ func testApp(name, project string, labels map[string]string, annotations map[str }, } } - -func TestWaitOnApplicationStatus_JSON_YAML_WideOutput(t *testing.T) { - acdClient := &customAcdClient{&fakeAcdClient{}} - ctx := context.Background() - var selectResource []*v1alpha1.SyncOperationResource - watch := watchOpts{ - sync: false, - health: false, - operation: true, - suspended: false, - } - watch = getWatchOpts(watch) - - output, err := captureOutput(func() error { - _, _, _ = waitOnApplicationStatus(ctx, acdClient, "app-name", 0, watch, selectResource, "json") - return nil - }, - ) - require.NoError(t, err) - assert.True(t, json.Valid([]byte(output))) - - output, err = captureOutput(func() error { - _, _, _ = waitOnApplicationStatus(ctx, acdClient, "app-name", 0, watch, selectResource, "yaml") - return nil - }) - - require.NoError(t, err) - err = yaml.Unmarshal([]byte(output), &v1alpha1.Application{}) - require.NoError(t, err) - - output, _ = captureOutput(func() error { - _, _, _ = waitOnApplicationStatus(ctx, acdClient, "app-name", 0, watch, selectResource, "") - return nil - }) - timeStr := time.Now().Format("2006-01-02T15:04:05-07:00") - - expectation := `TIMESTAMP GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE -%s Service default service-name1 Synced Healthy -%s apps Deployment default test Synced Healthy - -Name: argocd/test -Project: default -Server: local -Namespace: argocd -URL: http://localhost:8080/applications/app-name -Source: -- Repo: test - Target: master - Path: /test - Helm Values: path1,path2 - Name Prefix: prefix -SyncWindow: Sync Allowed -Sync Policy: Automated (Prune) -Sync Status: OutOfSync from master -Health Status: Progressing (health-message) - -Operation: Sync -Sync Revision: revision -Phase: -Start: 0001-01-01 00:00:00 +0000 UTC -Finished: 2020-11-10 23:00:00 +0000 UTC -Duration: 2333448h16m18.871345152s -Message: test - -GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE - Service default service-name1 Synced Healthy -apps Deployment default test Synced Healthy -` - expectation = fmt.Sprintf(expectation, timeStr, timeStr) - expectationParts := strings.Split(expectation, "\n") - slices.Sort(expectationParts) - expectationSorted := strings.Join(expectationParts, "\n") - outputParts := strings.Split(output, "\n") - slices.Sort(outputParts) - outputSorted := strings.Join(outputParts, "\n") - // Need to compare sorted since map entries may not keep a specific order during serialization, leading to flakiness. - assert.Equalf(t, expectationSorted, outputSorted, "Incorrect output %q, should be %q (items order doesn't matter)", output, expectation) -} - -type customAcdClient struct { - *fakeAcdClient -} - -func (c *customAcdClient) WatchApplicationWithRetry(ctx context.Context, appName string, revision string) chan *v1alpha1.ApplicationWatchEvent { - appEventsCh := make(chan *v1alpha1.ApplicationWatchEvent) - _, appClient := c.NewApplicationClientOrDie() - app, _ := appClient.Get(ctx, &applicationpkg.ApplicationQuery{}) - - newApp := v1alpha1.Application{ - TypeMeta: app.TypeMeta, - ObjectMeta: app.ObjectMeta, - Spec: app.Spec, - Status: app.Status, - Operation: app.Operation, - } - - go func() { - appEventsCh <- &v1alpha1.ApplicationWatchEvent{ - Type: watch.Bookmark, - Application: newApp, - } - close(appEventsCh) - }() - - return appEventsCh -} - -func (c *customAcdClient) NewApplicationClientOrDie() (io.Closer, applicationpkg.ApplicationServiceClient) { - return &fakeConnection{}, &fakeAppServiceClient{} -} - -func (c *customAcdClient) NewSettingsClientOrDie() (io.Closer, settingspkg.SettingsServiceClient) { - return &fakeConnection{}, &fakeSettingsServiceClient{} -} - -type fakeConnection struct{} - -func (c *fakeConnection) Close() error { - return nil -} - -type fakeSettingsServiceClient struct{} - -func (f fakeSettingsServiceClient) Get(ctx context.Context, in *settingspkg.SettingsQuery, opts ...grpc.CallOption) (*settingspkg.Settings, error) { - return &settingspkg.Settings{ - URL: "http://localhost:8080", - }, nil -} - -func (f fakeSettingsServiceClient) GetPlugins(ctx context.Context, in *settingspkg.SettingsQuery, opts ...grpc.CallOption) (*settingspkg.SettingsPluginsResponse, error) { - return nil, nil -} - -type fakeAppServiceClient struct{} - -func (c *fakeAppServiceClient) Get(ctx context.Context, in *applicationpkg.ApplicationQuery, opts ...grpc.CallOption) (*v1alpha1.Application, error) { - time := metav1.Date(2020, time.November, 10, 23, 0, 0, 0, time.UTC) - return &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"}, - Source: &v1alpha1.ApplicationSource{ - RepoURL: "test", - TargetRevision: "master", - Path: "/test", - Helm: &v1alpha1.ApplicationSourceHelm{ - ValueFiles: []string{"path1", "path2"}, - }, - Kustomize: &v1alpha1.ApplicationSourceKustomize{NamePrefix: "prefix"}, - }, - }, - Status: v1alpha1.ApplicationStatus{ - Resources: []v1alpha1.ResourceStatus{ - { - Group: "", - Kind: "Service", - Namespace: "default", - Name: "service-name1", - Status: "Synced", - Health: &v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "health-message", - }, - }, - { - Group: "apps", - Kind: "Deployment", - Namespace: "default", - Name: "test", - Status: "Synced", - Health: &v1alpha1.HealthStatus{ - Status: health.HealthStatusHealthy, - Message: "health-message", - }, - }, - }, - OperationState: &v1alpha1.OperationState{ - SyncResult: &v1alpha1.SyncOperationResult{ - Revision: "revision", - }, - FinishedAt: &time, - Message: "test", - }, - Sync: v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeOutOfSync, - }, - Health: v1alpha1.HealthStatus{ - Status: health.HealthStatusProgressing, - Message: "health-message", - }, - }, - }, nil -} - -func (c *fakeAppServiceClient) List(ctx context.Context, in *applicationpkg.ApplicationQuery, opts ...grpc.CallOption) (*v1alpha1.ApplicationList, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) ListResourceEvents(ctx context.Context, in *applicationpkg.ApplicationResourceEventsQuery, opts ...grpc.CallOption) (*v1.EventList, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) Watch(ctx context.Context, in *applicationpkg.ApplicationQuery, opts ...grpc.CallOption) (applicationpkg.ApplicationService_WatchClient, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) Create(ctx context.Context, in *applicationpkg.ApplicationCreateRequest, opts ...grpc.CallOption) (*v1alpha1.Application, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) GetApplicationSyncWindows(ctx context.Context, in *applicationpkg.ApplicationSyncWindowsQuery, opts ...grpc.CallOption) (*applicationpkg.ApplicationSyncWindowsResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) RevisionMetadata(ctx context.Context, in *applicationpkg.RevisionMetadataQuery, opts ...grpc.CallOption) (*v1alpha1.RevisionMetadata, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) RevisionChartDetails(ctx context.Context, in *applicationpkg.RevisionMetadataQuery, opts ...grpc.CallOption) (*v1alpha1.ChartDetails, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) GetManifests(ctx context.Context, in *applicationpkg.ApplicationManifestQuery, opts ...grpc.CallOption) (*apiclient.ManifestResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) GetManifestsWithFiles(ctx context.Context, opts ...grpc.CallOption) (applicationpkg.ApplicationService_GetManifestsWithFilesClient, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) Update(ctx context.Context, in *applicationpkg.ApplicationUpdateRequest, opts ...grpc.CallOption) (*v1alpha1.Application, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) UpdateSpec(ctx context.Context, in *applicationpkg.ApplicationUpdateSpecRequest, opts ...grpc.CallOption) (*v1alpha1.ApplicationSpec, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) Patch(ctx context.Context, in *applicationpkg.ApplicationPatchRequest, opts ...grpc.CallOption) (*v1alpha1.Application, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) Delete(ctx context.Context, in *applicationpkg.ApplicationDeleteRequest, opts ...grpc.CallOption) (*applicationpkg.ApplicationResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) Sync(ctx context.Context, in *applicationpkg.ApplicationSyncRequest, opts ...grpc.CallOption) (*v1alpha1.Application, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) ManagedResources(ctx context.Context, in *applicationpkg.ResourcesQuery, opts ...grpc.CallOption) (*applicationpkg.ManagedResourcesResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) ResourceTree(ctx context.Context, in *applicationpkg.ResourcesQuery, opts ...grpc.CallOption) (*v1alpha1.ApplicationTree, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) WatchResourceTree(ctx context.Context, in *applicationpkg.ResourcesQuery, opts ...grpc.CallOption) (applicationpkg.ApplicationService_WatchResourceTreeClient, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) Rollback(ctx context.Context, in *applicationpkg.ApplicationRollbackRequest, opts ...grpc.CallOption) (*v1alpha1.Application, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) TerminateOperation(ctx context.Context, in *applicationpkg.OperationTerminateRequest, opts ...grpc.CallOption) (*applicationpkg.OperationTerminateResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) GetResource(ctx context.Context, in *applicationpkg.ApplicationResourceRequest, opts ...grpc.CallOption) (*applicationpkg.ApplicationResourceResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) PatchResource(ctx context.Context, in *applicationpkg.ApplicationResourcePatchRequest, opts ...grpc.CallOption) (*applicationpkg.ApplicationResourceResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) ListResourceActions(ctx context.Context, in *applicationpkg.ApplicationResourceRequest, opts ...grpc.CallOption) (*applicationpkg.ResourceActionsListResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) RunResourceAction(ctx context.Context, in *applicationpkg.ResourceActionRunRequest, opts ...grpc.CallOption) (*applicationpkg.ApplicationResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) DeleteResource(ctx context.Context, in *applicationpkg.ApplicationResourceDeleteRequest, opts ...grpc.CallOption) (*applicationpkg.ApplicationResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) PodLogs(ctx context.Context, in *applicationpkg.ApplicationPodLogsQuery, opts ...grpc.CallOption) (applicationpkg.ApplicationService_PodLogsClient, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) ListLinks(ctx context.Context, in *applicationpkg.ListAppLinksRequest, opts ...grpc.CallOption) (*applicationpkg.LinksResponse, error) { - return nil, nil -} - -func (c *fakeAppServiceClient) ListResourceLinks(ctx context.Context, in *applicationpkg.ApplicationResourceRequest, opts ...grpc.CallOption) (*applicationpkg.LinksResponse, error) { - return nil, nil -} - -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 4bed5aea8a992..b38f8837598fb 100644 --- a/cmd/argocd/commands/applicationset.go +++ b/cmd/argocd/commands/applicationset.go @@ -11,7 +11,6 @@ import ( "github.com/spf13/cobra" "google.golang.org/grpc/codes" - "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/admin" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" @@ -25,7 +24,8 @@ import ( "github.com/argoproj/argo-cd/v2/util/templates" ) -var appSetExample = templates.Examples(` +var ( + appSetExample = templates.Examples(` # Get an ApplicationSet. argocd appset get APPSETNAME @@ -38,10 +38,11 @@ var appSetExample = templates.Examples(` # Delete an ApplicationSet argocd appset delete APPSETNAME (APPSETNAME...) `) +) // NewAppSetCommand returns a new instance of an `argocd appset` command func NewAppSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "appset", Short: "Manage ApplicationSets", Example: appSetExample, @@ -54,7 +55,6 @@ func NewAppSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { command.AddCommand(NewApplicationSetCreateCommand(clientOpts)) command.AddCommand(NewApplicationSetListCommand(clientOpts)) command.AddCommand(NewApplicationSetDeleteCommand(clientOpts)) - command.AddCommand(NewApplicationSetGenerateCommand(clientOpts)) return command } @@ -64,7 +64,7 @@ func NewApplicationSetGetCommand(clientOpts *argocdclient.ClientOptions) *cobra. output string showParams bool ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "get APPSETNAME", Short: "Get ApplicationSet details", Example: templates.Examples(` @@ -116,17 +116,13 @@ func NewApplicationSetGetCommand(clientOpts *argocdclient.ClientOptions) *cobra. // NewApplicationSetCreateCommand returns a new instance of an `argocd appset create` command func NewApplicationSetCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - var upsert, dryRun bool - command := &cobra.Command{ + var upsert bool + var command = &cobra.Command{ Use: "create", Short: "Create one or more ApplicationSets", Example: templates.Examples(` # Create ApplicationSets argocd appset create (...) - - # Dry-run AppSet creation to see what applications would be managed - argocd appset create --dry-run -o json | jq -r '.status.resources[].name' `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -163,16 +159,10 @@ func NewApplicationSetCreateCommand(clientOpts *argocdclient.ClientOptions) *cob appSetCreateRequest := applicationset.ApplicationSetCreateRequest{ Applicationset: appset, Upsert: upsert, - DryRun: dryRun, } created, err := appIf.Create(ctx, &appSetCreateRequest) errors.CheckError(err) - dryRunMsg := "" - if dryRun { - dryRunMsg = " (dry-run)" - } - var action string if existing == nil { action = "created" @@ -182,100 +172,11 @@ func NewApplicationSetCreateCommand(clientOpts *argocdclient.ClientOptions) *cob action = "updated" } - c.PrintErrf("ApplicationSet '%s' %s%s\n", created.ObjectMeta.Name, action, dryRunMsg) - - switch output { - case "yaml", "json": - err := PrintResource(created, output) - errors.CheckError(err) - case "wide", "": - printAppSetSummaryTable(created) - - if len(created.Status.Conditions) > 0 { - fmt.Println() - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - printAppSetConditions(w, created) - _ = w.Flush() - fmt.Println() - } - default: - errors.CheckError(fmt.Errorf("unknown output format: %s", output)) - } + fmt.Printf("ApplicationSet '%s' %s\n", created.ObjectMeta.Name, action) } }, } command.Flags().BoolVar(&upsert, "upsert", false, "Allows to override ApplicationSet with the same name even if supplied ApplicationSet spec is different from existing spec") - command.Flags().BoolVar(&dryRun, "dry-run", false, "Allows to evaluate the ApplicationSet template on the server to get a preview of the applications that would be created") - command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide") - return command -} - -// NewApplicationSetGenerateCommand returns a new instance of an `argocd appset generate` command -func NewApplicationSetGenerateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ - Use: "generate", - Short: "Generate apps of ApplicationSet rendered templates", - Example: templates.Examples(` - # Generate apps of ApplicationSet rendered templates - argocd appset generate (...) -`), - Run: func(c *cobra.Command, args []string) { - ctx := c.Context() - - if len(args) == 0 { - c.HelpFunc()(c, args) - os.Exit(1) - } - argocdClient := headless.NewClientOrDie(clientOpts, c) - fileUrl := args[0] - appsets, err := cmdutil.ConstructApplicationSet(fileUrl) - errors.CheckError(err) - - if len(appsets) != 1 { - fmt.Printf("Input file must contain one ApplicationSet") - os.Exit(1) - } - appset := appsets[0] - if appset.Name == "" { - err := fmt.Errorf("Error generating apps for ApplicationSet %s. ApplicationSet does not have Name field set", appset) - errors.CheckError(err) - } - - conn, appIf := argocdClient.NewApplicationSetClientOrDie() - defer argoio.Close(conn) - - req := applicationset.ApplicationSetGenerateRequest{ - ApplicationSet: appset, - } - resp, err := appIf.Generate(ctx, &req) - errors.CheckError(err) - - var appsList []arogappsetv1.Application - for i := range resp.Applications { - appsList = append(appsList, *resp.Applications[i]) - } - - switch output { - case "yaml", "json": - var resources []interface{} - for i := range appsList { - app := appsList[i] - // backfill api version and kind because k8s client always return empty values for these fields - app.APIVersion = arogappsetv1.ApplicationSchemaGroupVersionKind.GroupVersion().String() - app.Kind = arogappsetv1.ApplicationSchemaGroupVersionKind.Kind - resources = append(resources, app) - } - - cobra.CheckErr(admin.PrintResources(output, os.Stdout, resources...)) - case "wide", "": - printApplicationTable(appsList, &output) - default: - errors.CheckError(fmt.Errorf("unknown output format: %s", output)) - } - }, - } - command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide") return command } @@ -287,10 +188,10 @@ func NewApplicationSetListCommand(clientOpts *argocdclient.ClientOptions) *cobra projects []string appSetNamespace string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "list", Short: "List ApplicationSets", - Example: templates.Examples(` + Example: templates.Examples(` # List all ApplicationSets argocd appset list `), @@ -327,11 +228,13 @@ func NewApplicationSetListCommand(clientOpts *argocdclient.ClientOptions) *cobra // NewApplicationSetDeleteCommand returns a new instance of an `argocd appset delete` command func NewApplicationSetDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var noPrompt bool - command := &cobra.Command{ + var ( + noPrompt bool + ) + var command = &cobra.Command{ Use: "delete", Short: "Delete one or more ApplicationSets", - Example: templates.Examples(` + Example: templates.Examples(` # Delete an applicationset argocd appset delete APPSETNAME (APPSETNAME...) `), @@ -346,12 +249,13 @@ func NewApplicationSetDeleteCommand(clientOpts *argocdclient.ClientOptions) *cob defer argoio.Close(conn) var isTerminal bool = isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) var isConfirmAll bool = false - numOfApps := len(args) - promptFlag := c.Flag("yes") + var numOfApps = len(args) + var promptFlag = c.Flag("yes") if promptFlag.Changed && promptFlag.Value.String() == "true" { noPrompt = true } for _, appSetQualifiedName := range args { + appSetName, appSetNs := argo.ParseFromQualifiedName(appSetQualifiedName, "") appsetDeleteReq := applicationset.ApplicationSetDeleteRequest{ @@ -446,11 +350,9 @@ func printAppSetSummaryTable(appSet *arogappsetv1.ApplicationSet) { 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) - if !appSet.Spec.Template.Spec.HasMultipleSources() { - fmt.Println("Source:") - } else { - fmt.Println("Sources:") - } + fmt.Printf(printOpFmtStr, "Repo:", source.RepoURL) + fmt.Printf(printOpFmtStr, "Target:", source.TargetRevision) + fmt.Printf(printOpFmtStr, "Path:", source.Path) printAppSourceDetails(&source) var ( @@ -466,6 +368,7 @@ func printAppSetSummaryTable(appSet *arogappsetv1.ApplicationSet) { syncPolicyStr = "" } fmt.Printf(printOpFmtStr, "SyncPolicy:", syncPolicyStr) + } func printAppSetConditions(w io.Writer, appSet *arogappsetv1.ApplicationSet) { diff --git a/cmd/argocd/commands/applicationset_test.go b/cmd/argocd/commands/applicationset_test.go index e5034e05f9f9b..18e5f85feebbc 100644 --- a/cmd/argocd/commands/applicationset_test.go +++ b/cmd/argocd/commands/applicationset_test.go @@ -5,11 +5,9 @@ import ( "os" "testing" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) func TestPrintApplicationSetNames(t *testing.T) { @@ -108,7 +106,7 @@ func TestPrintApplicationSetTable(t *testing.T) { printApplicationSetTable([]v1alpha1.ApplicationSet{*app, *app2}, &output) return nil }) - require.NoError(t, err) + assert.NoError(t, err) 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) } @@ -182,9 +180,9 @@ func TestPrintAppSetSummaryTable(t *testing.T) { Project: default Server: Namespace: -Source: -- Repo: - Target: +Repo: +Target: +Path: SyncPolicy: `, }, @@ -195,9 +193,9 @@ SyncPolicy: Project: default Server: Namespace: -Source: -- Repo: - Target: +Repo: +Target: +Path: SyncPolicy: Automated `, }, @@ -208,9 +206,9 @@ SyncPolicy: Automated Project: default Server: Namespace: -Source: -- Repo: - Target: +Repo: +Target: +Path: SyncPolicy: Automated `, }, @@ -228,7 +226,7 @@ SyncPolicy: Automated w.Close() out, err := io.ReadAll(r) - require.NoError(t, err) + 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 d2557ef4111e1..6d8f87fd447a5 100644 --- a/cmd/argocd/commands/bcrypt.go +++ b/cmd/argocd/commands/bcrypt.go @@ -10,8 +10,10 @@ import ( // NewBcryptCmd represents the bcrypt command func NewBcryptCmd() *cobra.Command { - var password string - bcryptCmd := &cobra.Command{ + var ( + password string + ) + var bcryptCmd = &cobra.Command{ Use: "bcrypt", Short: "Generate bcrypt hash for any password", Example: `# Generate bcrypt hash for any password diff --git a/cmd/argocd/commands/cert.go b/cmd/argocd/commands/cert.go index 0a8204b89d9e0..d443d57e337d4 100644 --- a/cmd/argocd/commands/cert.go +++ b/cmd/argocd/commands/cert.go @@ -21,7 +21,7 @@ import ( // NewCertCommand returns a new instance of an `argocd repo` command func NewCertCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "cert", Short: "Manage repository certificates and SSH known hosts entries", Run: func(c *cobra.Command, args []string) { @@ -60,7 +60,7 @@ func NewCertAddTLSCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command fromFile string upsert bool ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "add-tls SERVERNAME", Short: "Add TLS certificate data for connecting to repository server SERVERNAME", Run: func(c *cobra.Command, args []string) { @@ -144,7 +144,7 @@ func NewCertAddSSHCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command certificates []appsv1.RepositoryCertificate ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "add-ssh --batch", Short: "Add SSH known host entries for repository servers", Run: func(c *cobra.Command, args []string) { @@ -214,7 +214,7 @@ func NewCertRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command certSubType string certQuery certificatepkg.RepositoryCertificateQuery ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "rm REPOSERVER", Short: "Remove certificate of TYPE for REPOSERVER", Run: func(c *cobra.Command, args []string) { @@ -265,7 +265,7 @@ func NewCertListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { sortOrder string output string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "list", Short: "List configured certificates", Run: func(c *cobra.Command, args []string) { @@ -295,6 +295,7 @@ func NewCertListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { default: errors.CheckError(fmt.Errorf("unknown output format: %s", output)) } + }, } diff --git a/cmd/argocd/commands/cluster.go b/cmd/argocd/commands/cluster.go index 729848de78634..3df4be6632d85 100644 --- a/cmd/argocd/commands/cluster.go +++ b/cmd/argocd/commands/cluster.go @@ -34,17 +34,13 @@ const ( clusterFieldName = "name" // cluster field is 'namespaces' clusterFieldNamespaces = "namespaces" - // cluster field is 'labels' - clusterFieldLabel = "labels" - // cluster field is 'annotations' - clusterFieldAnnotation = "annotations" // indicates managing all namespaces allNamespaces = "*" ) // NewClusterCommand returns a new instance of an `argocd cluster` command func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientcmd.PathOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "cluster", Short: "Manage cluster credentials", Run: func(c *cobra.Command, args []string) { @@ -85,7 +81,7 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie labels []string annotations []string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "add CONTEXT", Short: fmt.Sprintf("%s cluster add CONTEXT", cliName), Run: func(c *cobra.Command, args []string) { @@ -115,7 +111,6 @@ 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{ @@ -224,10 +219,8 @@ func NewClusterSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command var ( clusterOptions cmdutil.ClusterOptions clusterName string - labels []string - annotations []string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "set NAME", Short: "Set cluster information", Example: ` # Set cluster information @@ -244,25 +237,17 @@ func NewClusterSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command conn, clusterIf := headless.NewClientOrDie(clientOpts, c).NewClusterClientOrDie() defer io.Close(conn) // checks the fields that needs to be updated - updatedFields := checkFieldsToUpdate(clusterOptions, labels, annotations) + updatedFields := checkFieldsToUpdate(clusterOptions) namespaces := clusterOptions.Namespaces // check if all namespaces have to be considered if len(namespaces) == 1 && strings.EqualFold(namespaces[0], allNamespaces) { namespaces[0] = "" } - // parse the labels you're receiving from the label flag - labelsMap, err := label.Parse(labels) - errors.CheckError(err) - // parse the annotations you're receiving from the annotation flag - annotationsMap, err := label.Parse(annotations) - errors.CheckError(err) if updatedFields != nil { clusterUpdateRequest := clusterpkg.ClusterUpdateRequest{ Cluster: &argoappv1.Cluster{ - Name: clusterOptions.Name, - Namespaces: namespaces, - Labels: labelsMap, - Annotations: annotationsMap, + Name: clusterOptions.Name, + Namespaces: namespaces, }, UpdatedFields: updatedFields, Id: &clusterpkg.ClusterID{ @@ -280,13 +265,11 @@ func NewClusterSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command } command.Flags().StringVar(&clusterOptions.Name, "name", "", "Overwrite the cluster name") command.Flags().StringArrayVar(&clusterOptions.Namespaces, "namespace", nil, "List of namespaces which are allowed to manage. Specify '*' to manage all namespaces") - command.Flags().StringArrayVar(&labels, "label", nil, "Set metadata labels (e.g. --label key=value)") - command.Flags().StringArrayVar(&annotations, "annotation", nil, "Set metadata annotations (e.g. --annotation key=value)") return command } // checkFieldsToUpdate returns the fields that needs to be updated -func checkFieldsToUpdate(clusterOptions cmdutil.ClusterOptions, labels []string, annotations []string) []string { +func checkFieldsToUpdate(clusterOptions cmdutil.ClusterOptions) []string { var updatedFields []string if clusterOptions.Name != "" { updatedFields = append(updatedFields, clusterFieldName) @@ -294,19 +277,15 @@ func checkFieldsToUpdate(clusterOptions cmdutil.ClusterOptions, labels []string, if clusterOptions.Namespaces != nil { updatedFields = append(updatedFields, clusterFieldNamespaces) } - if labels != nil { - updatedFields = append(updatedFields, clusterFieldLabel) - } - if annotations != nil { - updatedFields = append(updatedFields, clusterFieldAnnotation) - } return updatedFields } // NewClusterGetCommand returns a new instance of an `argocd cluster get` command func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ + var ( + output string + ) + var command = &cobra.Command{ Use: "get SERVER/NAME", Short: "Get cluster information", Example: `argocd cluster get https://12.34.567.89 @@ -363,7 +342,6 @@ func printClusterDetails(clusters []argoappv1.Cluster) { fmt.Printf("Cluster information\n\n") fmt.Printf(" Server URL: %s\n", cluster.Server) fmt.Printf(" Server Name: %s\n", strWithDefault(cluster.Name, "-")) - // nolint:staticcheck fmt.Printf(" Server Version: %s\n", cluster.ServerVersion) fmt.Printf(" Namespaces: %s\n", formatNamespaces(cluster)) fmt.Printf("\nTLS configuration\n\n") @@ -380,7 +358,7 @@ func printClusterDetails(clusters []argoappv1.Cluster) { // NewClusterRemoveCommand returns a new instance of an `argocd cluster rm` command func NewClusterRemoveCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientcmd.PathOptions) *cobra.Command { var noPrompt bool - command := &cobra.Command{ + var command = &cobra.Command{ Use: "rm SERVER/NAME", Short: "Remove cluster credentials", Example: `argocd cluster rm https://12.34.567.89 @@ -394,7 +372,7 @@ argocd cluster rm cluster-name`, } conn, clusterIf := headless.NewClientOrDie(clientOpts, c).NewClusterClientOrDie() defer io.Close(conn) - numOfClusters := len(args) + var numOfClusters = len(args) var isConfirmAll bool = false for _, clusterSelector := range args { @@ -456,7 +434,6 @@ func printClusterTable(clusters []argoappv1.Cluster) { if len(c.Namespaces) > 0 { server = fmt.Sprintf("%s (%d namespaces)", c.Server, len(c.Namespaces)) } - // nolint:staticcheck _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", server, c.Name, c.ServerVersion, c.ConnectionState.Status, c.ConnectionState.Message, c.Project) } _ = w.Flush() @@ -483,8 +460,10 @@ func printClusterServers(clusters []argoappv1.Cluster) { // NewClusterListCommand returns a new instance of an `argocd cluster rm` command func NewClusterListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ + var ( + output string + ) + var command = &cobra.Command{ Use: "list", Short: "List configured clusters", Run: func(c *cobra.Command, args []string) { @@ -510,7 +489,7 @@ func NewClusterListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman # List Clusters in Default "Wide" Format argocd cluster list -# List Cluster via specifying the server +# List Cluster via specifing the server argocd cluster list --server # List Clusters in JSON Format @@ -530,7 +509,7 @@ argocd cluster list -o server // NewClusterRotateAuthCommand returns a new instance of an `argocd cluster rotate-auth` command func NewClusterRotateAuthCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "rotate-auth SERVER/NAME", Short: fmt.Sprintf("%s cluster rotate-auth SERVER/NAME", cliName), Example: `argocd cluster rotate-auth https://12.34.567.89 diff --git a/cmd/argocd/commands/cluster_test.go b/cmd/argocd/commands/cluster_test.go index d0bc485ace252..24e54ea122fc4 100644 --- a/cmd/argocd/commands/cluster_test.go +++ b/cmd/argocd/commands/cluster_test.go @@ -3,27 +3,26 @@ package commands import ( "testing" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) func Test_getQueryBySelector(t *testing.T) { query := getQueryBySelector("my-cluster") - assert.Equal(t, "my-cluster", query.Name) - assert.Equal(t, "", query.Server) + assert.Equal(t, query.Name, "my-cluster") + assert.Equal(t, query.Server, "") query = getQueryBySelector("http://my-server") - assert.Equal(t, "", query.Name) - assert.Equal(t, "http://my-server", query.Server) + assert.Equal(t, query.Name, "") + assert.Equal(t, query.Server, "http://my-server") query = getQueryBySelector("https://my-server") - assert.Equal(t, "", query.Name) - assert.Equal(t, "https://my-server", query.Server) + assert.Equal(t, query.Name, "") + assert.Equal(t, query.Server, "https://my-server") } func Test_printClusterTable(t *testing.T) { @@ -98,9 +97,9 @@ func Test_getRestConfig(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got, err := getRestConfig(tt.args.pathOpts, tt.args.ctxName); err == nil { - require.Equal(t, tt.expected, got) + require.Equal(t, got, tt.expected) } else if tt.wantErr { - require.Equal(t, tt.expectedErr, err.Error()) + require.Equal(t, err.Error(), tt.expectedErr) } else { t.Errorf("An unexpected error occurred during test %s:\n%s", tt.name, err.Error()) } diff --git a/cmd/argocd/commands/common_test.go b/cmd/argocd/commands/common_test.go index 24ab6ebcf7fd9..c86429b32e0c8 100644 --- a/cmd/argocd/commands/common_test.go +++ b/cmd/argocd/commands/common_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) // Be careful with tabs vs. spaces in the following expected formats. Indents @@ -22,7 +21,6 @@ const expectJsonSingle = `{ "foo": "bar" } ` - const expectYamlList = `one: bar: "" baz: foo @@ -80,18 +78,18 @@ func Test_PrintResource(t *testing.T) { err := PrintResource(testResource, "yaml") return err }) - require.NoError(t, err) - assert.Equal(t, expectYamlSingle, str) + assert.NoError(t, err) + assert.Equal(t, str, expectYamlSingle) str, err = captureOutput(func() error { err := PrintResource(testResource, "json") return err }) - require.NoError(t, err) - assert.Equal(t, expectJsonSingle, str) + assert.NoError(t, err) + assert.Equal(t, str, expectJsonSingle) err = PrintResource(testResource, "unknown") - require.Error(t, err) + assert.Error(t, err) } func Test_PrintResourceList(t *testing.T) { @@ -115,30 +113,30 @@ func Test_PrintResourceList(t *testing.T) { err := PrintResourceList(testResource, "yaml", false) return err }) - require.NoError(t, err) - assert.Equal(t, expectYamlList, str) + assert.NoError(t, err) + assert.Equal(t, str, expectYamlList) str, err = captureOutput(func() error { err := PrintResourceList(testResource, "json", false) return err }) - require.NoError(t, err) - assert.Equal(t, expectJsonList, str) + assert.NoError(t, err) + assert.Equal(t, str, expectJsonList) str, err = captureOutput(func() error { err := PrintResourceList(testResource2, "yaml", true) return err }) - require.NoError(t, err) - assert.Equal(t, expectYamlSingle, str) + assert.NoError(t, err) + assert.Equal(t, str, expectYamlSingle) str, err = captureOutput(func() error { err := PrintResourceList(testResource2, "json", true) return err }) - require.NoError(t, err) - assert.Equal(t, expectJsonSingle, str) + assert.NoError(t, err) + assert.Equal(t, str, expectJsonSingle) err = PrintResourceList(testResource, "unknown", false) - require.Error(t, err) + assert.Error(t, err) } diff --git a/cmd/argocd/commands/completion.go b/cmd/argocd/commands/completion.go index 106a9992572d1..7d3f5675ee95e 100644 --- a/cmd/argocd/commands/completion.go +++ b/cmd/argocd/commands/completion.go @@ -194,10 +194,10 @@ __argocd_custom_func() { ) func NewCompletionCommand() *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "completion SHELL", - Short: "output shell completion code for the specified shell (bash, zsh or fish)", - Long: `Write bash, zsh or fish shell completion code to standard output. + Short: "output shell completion code for the specified shell (bash or zsh)", + Long: `Write bash or zsh shell completion code to standard output. For bash, ensure you have bash completions installed and enabled. To access completions in your current shell, run @@ -218,11 +218,6 @@ $ source <(argocd completion bash) # For zsh $ argocd completion zsh > _argocd $ source _argocd - -# For fish -$ argocd completion fish > ~/.config/fish/completions/argocd.fish -$ source ~/.config/fish/completions/argocd.fish - `, Run: func(cmd *cobra.Command, args []string) { if len(args) != 1 { @@ -232,17 +227,16 @@ $ source ~/.config/fish/completions/argocd.fish shell := args[0] rootCommand := NewCommand() rootCommand.BashCompletionFunction = bashCompletionFunc - availableCompletions := map[string]func(out io.Writer, cmd *cobra.Command) error{ - "bash": runCompletionBash, - "zsh": runCompletionZsh, - "fish": runCompletionFish, + availableCompletions := map[string]func(io.Writer) error{ + "bash": rootCommand.GenBashCompletion, + "zsh": rootCommand.GenZshCompletion, } completion, ok := availableCompletions[shell] if !ok { - fmt.Printf("Invalid shell '%s'. The supported shells are bash, zsh and fish.\n", shell) + fmt.Printf("Invalid shell '%s'. The supported shells are bash and zsh.\n", shell) os.Exit(1) } - if err := completion(os.Stdout, rootCommand); err != nil { + if err := completion(os.Stdout); err != nil { log.Fatal(err) } }, @@ -250,15 +244,3 @@ $ source ~/.config/fish/completions/argocd.fish return command } - -func runCompletionBash(out io.Writer, cmd *cobra.Command) error { - return cmd.GenBashCompletion(out) -} - -func runCompletionZsh(out io.Writer, cmd *cobra.Command) error { - return cmd.GenZshCompletion(out) -} - -func runCompletionFish(out io.Writer, cmd *cobra.Command) error { - return cmd.GenFishCompletion(out, true) -} diff --git a/cmd/argocd/commands/context.go b/cmd/argocd/commands/context.go index 2c4b37ba2fa81..51d003b4df9df 100644 --- a/cmd/argocd/commands/context.go +++ b/cmd/argocd/commands/context.go @@ -18,7 +18,7 @@ import ( // NewContextCommand returns a new instance of an `argocd ctx` command func NewContextCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var delete bool - command := &cobra.Command{ + var command = &cobra.Command{ Use: "context [CONTEXT]", Aliases: []string{"ctx"}, Short: "Switch between contexts", @@ -31,6 +31,7 @@ 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) errors.CheckError(err) @@ -72,7 +73,7 @@ argocd context cd.argoproj.io --delete`, err = localconfig.WriteLocalConfig(*localCfg, clientOpts.ConfigPath) errors.CheckError(err) - err = os.WriteFile(prevCtxFile, []byte(prevCtx), 0o644) + err = os.WriteFile(prevCtxFile, []byte(prevCtx), 0644) errors.CheckError(err) fmt.Printf("Switched to context '%s'\n", localCfg.CurrentContext) }, @@ -82,6 +83,7 @@ argocd context cd.argoproj.io --delete`, } func deleteContext(context, configPath string) error { + localCfg, err := localconfig.ReadLocalConfig(configPath) errors.CheckError(err) if localCfg == nil { diff --git a/cmd/argocd/commands/context_test.go b/cmd/argocd/commands/context_test.go index e9f953a22cd0f..c258485b8181f 100644 --- a/cmd/argocd/commands/context_test.go +++ b/cmd/argocd/commands/context_test.go @@ -4,10 +4,9 @@ import ( "os" "testing" + "github.com/argoproj/argo-cd/v2/util/localconfig" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/argoproj/argo-cd/v2/util/localconfig" ) const testConfig = `contexts: @@ -41,23 +40,23 @@ const testConfigFilePath = "./testdata/local.config" func TestContextDelete(t *testing.T) { // Write the test config file err := os.WriteFile(testConfigFilePath, []byte(testConfig), os.ModePerm) - require.NoError(t, err) + assert.NoError(t, err) defer os.Remove(testConfigFilePath) - err = os.Chmod(testConfigFilePath, 0o600) + err = os.Chmod(testConfigFilePath, 0600) require.NoError(t, err, "Could not change the file permission to 0600 %v", err) localConfig, err := localconfig.ReadLocalConfig(testConfigFilePath) - require.NoError(t, err) - assert.Equal(t, "localhost:8080", localConfig.CurrentContext) + assert.NoError(t, err) + assert.Equal(t, localConfig.CurrentContext, "localhost:8080") assert.Contains(t, localConfig.Contexts, localconfig.ContextRef{Name: "localhost:8080", Server: "localhost:8080", User: "localhost:8080"}) // Delete a non-current context err = deleteContext("argocd1.example.com:443", testConfigFilePath) - require.NoError(t, err) + assert.NoError(t, err) localConfig, err = localconfig.ReadLocalConfig(testConfigFilePath) - require.NoError(t, err) - assert.Equal(t, "localhost:8080", localConfig.CurrentContext) + assert.NoError(t, err) + assert.Equal(t, localConfig.CurrentContext, "localhost:8080") assert.NotContains(t, localConfig.Contexts, localconfig.ContextRef{Name: "argocd1.example.com:443", Server: "argocd1.example.com:443", User: "argocd1.example.com:443"}) assert.NotContains(t, localConfig.Servers, localconfig.Server{Server: "argocd1.example.com:443"}) assert.NotContains(t, localConfig.Users, localconfig.User{AuthToken: "vErrYS3c3tReFRe$hToken", Name: "argocd1.example.com:443"}) @@ -66,11 +65,11 @@ func TestContextDelete(t *testing.T) { // Delete the current context err = deleteContext("localhost:8080", testConfigFilePath) - require.NoError(t, err) + assert.NoError(t, err) localConfig, err = localconfig.ReadLocalConfig(testConfigFilePath) - require.NoError(t, err) - assert.Equal(t, "", localConfig.CurrentContext) + assert.NoError(t, err) + assert.Equal(t, localConfig.CurrentContext, "") assert.NotContains(t, localConfig.Contexts, localconfig.ContextRef{Name: "localhost:8080", Server: "localhost:8080", User: "localhost:8080"}) assert.NotContains(t, localConfig.Servers, localconfig.Server{PlainText: true, Server: "localhost:8080"}) assert.NotContains(t, localConfig.Users, localconfig.User{AuthToken: "vErrYS3c3tReFRe$hToken", Name: "localhost:8080"}) diff --git a/cmd/argocd/commands/gpg.go b/cmd/argocd/commands/gpg.go index d68c2918f1ccb..73768fc18a324 100644 --- a/cmd/argocd/commands/gpg.go +++ b/cmd/argocd/commands/gpg.go @@ -19,7 +19,7 @@ import ( // NewGPGCommand returns a new instance of an `argocd repo` command func NewGPGCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "gpg", Short: "Manage GPG keys used for signature verification", Run: func(c *cobra.Command, args []string) { @@ -37,8 +37,10 @@ func NewGPGCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { // NewGPGListCommand lists all configured public keys from the server func NewGPGListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ + var ( + output string + ) + var command = &cobra.Command{ Use: "list", Short: "List configured GPG public keys", Example: templates.Examples(` @@ -76,8 +78,10 @@ func NewGPGListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { // NewGPGGetCommand retrieves a single public key from the server func NewGPGGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ + var ( + output string + ) + var command = &cobra.Command{ Use: "get KEYID", Short: "Get the GPG public key with ID from the server", Example: templates.Examples(` @@ -122,8 +126,10 @@ func NewGPGGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { // NewGPGAddCommand adds a public key to the server's configuration func NewGPGAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var fromFile string - command := &cobra.Command{ + var ( + fromFile string + ) + var command = &cobra.Command{ Use: "add", Short: "Adds a GPG public key to the server's keyring", Example: templates.Examples(` @@ -154,11 +160,12 @@ func NewGPGAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { } command.Flags().StringVarP(&fromFile, "from", "f", "", "Path to the file that contains the GPG public key to import") return command + } // NewGPGDeleteCommand removes a key from the server's keyring func NewGPGDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "rm KEYID", Short: "Removes a GPG public key from the server's keyring", Run: func(c *cobra.Command, args []string) { @@ -175,6 +182,7 @@ func NewGPGDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command }, } return command + } // Print table of certificate info diff --git a/cmd/argocd/commands/headless/headless.go b/cmd/argocd/commands/headless/headless.go index f4d4503a9b723..5c9828fc9f131 100644 --- a/cmd/argocd/commands/headless/headless.go +++ b/cmd/argocd/commands/headless/headless.go @@ -8,23 +8,22 @@ import ( "sync" "time" + "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/golang/protobuf/ptypes/empty" "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" "github.com/spf13/pflag" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" cache2 "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" - "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client" + "k8s.io/utils/pointer" - "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize" - "github.com/argoproj/argo-cd/v2/common" "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" @@ -48,7 +47,6 @@ type forwardCacheClient struct { err error redisHaProxyName string redisName string - redisPassword string } func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error { @@ -65,7 +63,7 @@ func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) return } - redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort), Password: c.redisPassword}) + redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort)}) c.client = cache.NewRedisCache(redisClient, time.Hour, c.compression) }) if c.err != nil { @@ -80,12 +78,6 @@ 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) @@ -117,7 +109,6 @@ type forwardRepoClientset struct { repoClientset repoapiclient.Clientset err error repoServerName string - kubeClientset kubernetes.Interface } func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.RepoServerServiceClient, error) { @@ -125,27 +116,14 @@ func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.R overrides := clientcmd.ConfigOverrides{ CurrentContext: c.context, } - 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 + repoServerPodLabelSelector := common.LabelKeyAppName + "=" + c.repoServerName repoServerPort, err := kubeutil.PortForward(8081, c.namespace, &overrides, repoServerPodLabelSelector) if err != nil { c.err = err return } c.repoClientset = repoapiclient.NewRepoServerClientset(fmt.Sprintf("localhost:%d", repoServerPort), 60, repoapiclient.TLSConfiguration{ - DisableTLS: false, StrictValidation: false, - }) + DisableTLS: false, StrictValidation: false}) }) if c.err != nil { return nil, nil, c.err @@ -207,7 +185,7 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti log.SetLevel(log.ErrorLevel) os.Setenv(v1alpha1.EnvVarFakeInClusterConfig, "true") if address == nil { - address = ptr.To("localhost") + address = pointer.String("localhost") } if port == nil || *port == 0 { addr := fmt.Sprintf("%s:0", *address) @@ -232,17 +210,6 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti return fmt.Errorf("error creating kubernetes clientset: %w", err) } - dynamicClientset, err := dynamic.NewForConfig(restConfig) - if err != nil { - return fmt.Errorf("error creating kubernetes dynamic clientset: %w", err) - } - - controllerClientset, err := client.New(restConfig, client.Options{}) - if err != nil { - return fmt.Errorf("error creating kubernetes controller clientset: %w", err) - } - controllerClientset = client.NewDryRunClient(controllerClientset) - namespace, _, err := clientConfig.Namespace() if err != nil { return fmt.Errorf("error getting namespace: %w", err) @@ -252,28 +219,21 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti if err != nil { return fmt.Errorf("error running miniredis: %w", err) } - redisOptions := &redis.Options{Addr: mr.Addr()} - if err = common.SetOptionalRedisPasswordFromKubeConfig(ctx, kubeClientset, namespace, redisOptions); err != nil { - log.Warnf("Failed to fetch & set redis password for namespace %s: %v", namespace, err) - } - - appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr, compression: compression, redisHaProxyName: clientOpts.RedisHaProxyName, redisName: clientOpts.RedisName, redisPassword: redisOptions.Password}), 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, - ListenPort: *port, - AppClientset: appClientset, - DisableAuth: true, - RedisClient: redis.NewClient(redisOptions), - Cache: servercache.NewCache(appstateCache, 0, 0, 0), - KubeClientset: kubeClientset, - DynamicClientset: dynamicClientset, - KubeControllerClientset: controllerClientset, - Insecure: true, - ListenHost: *address, - RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr, repoServerName: clientOpts.RepoServerName, kubeClientset: kubeClientset}, - EnableProxyExtension: false, - }, server.ApplicationSetOpts{}) + EnableGZip: false, + Namespace: namespace, + ListenPort: *port, + AppClientset: appClientset, + DisableAuth: true, + RedisClient: redis.NewClient(&redis.Options{Addr: mr.Addr()}), + Cache: servercache.NewCache(appstateCache, 0, 0, 0), + KubeClientset: kubeClientset, + Insecure: true, + ListenHost: *address, + RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr, repoServerName: clientOpts.RepoServerName}, + EnableProxyExtension: false, + }) srv.Init(ctx) lns, err := srv.Listen() diff --git a/cmd/argocd/commands/login.go b/cmd/argocd/commands/login.go index 72b89dae1771c..abb2b004291c2 100644 --- a/cmd/argocd/commands/login.go +++ b/cmd/argocd/commands/login.go @@ -31,21 +31,19 @@ import ( "github.com/argoproj/argo-cd/v2/util/localconfig" oidcutil "github.com/argoproj/argo-cd/v2/util/oidc" "github.com/argoproj/argo-cd/v2/util/rand" - oidcconfig "github.com/argoproj/argo-cd/v2/util/settings" ) // NewLoginCommand returns a new instance of `argocd login` command func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - ctxName string - username string - password string - sso bool - ssoPort int - skipTestTLS bool - ssoLaunchBrowser bool + ctxName string + username string + password string + sso bool + ssoPort int + skipTestTLS bool ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "login SERVER", Short: "Log in to Argo CD", Long: "Log in to Argo CD", @@ -136,7 +134,7 @@ argocd login cd.argoproj.io --core`, errors.CheckError(err) oauth2conf, provider, err := acdClient.OIDCConfig(ctx, acdSet) errors.CheckError(err) - tokenString, refreshToken = oauth2Login(ctx, ssoPort, acdSet.GetOIDCConfig(), oauth2conf, provider, ssoLaunchBrowser) + tokenString, refreshToken = oauth2Login(ctx, ssoPort, acdSet.GetOIDCConfig(), oauth2conf, provider) } parser := jwt.NewParser(jwt.WithoutClaimsValidation()) claims := jwt.MapClaims{} @@ -185,7 +183,6 @@ argocd login cd.argoproj.io --core`, 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)") - command.Flags().BoolVar(&ssoLaunchBrowser, "sso-launch-browser", true, "Automatically launch the system default browser when performing SSO login") return command } @@ -207,7 +204,6 @@ func oauth2Login( oidcSettings *settingspkg.OIDCConfig, oauth2conf *oauth2.Config, provider *oidc.Provider, - ssoLaunchBrowser bool, ) (string, string) { oauth2conf.RedirectURL = fmt.Sprintf("http://localhost:%d/auth/callback", port) oidcConf, err := oidcutil.ParseConfig(provider) @@ -307,8 +303,9 @@ func oauth2Login( http.HandleFunc("/auth/callback", callbackHandler) // Redirect user to login & consent page to ask for permission for the scopes specified above. + fmt.Printf("Opening browser for authentication\n") + var url string - var oidcconfig oidcconfig.OIDCConfig grantType := oidcutil.InferGrantType(oidcConf) opts := []oauth2.AuthCodeOption{oauth2.AccessTypeOffline} if claimsRequested := oidcSettings.GetIDTokenClaims(); claimsRequested != nil { @@ -319,9 +316,6 @@ func oauth2Login( case oidcutil.GrantTypeAuthorizationCode: opts = append(opts, oauth2.SetAuthURLParam("code_challenge", codeChallenge)) opts = append(opts, oauth2.SetAuthURLParam("code_challenge_method", "S256")) - if oidcconfig.DomainHint != "" { - opts = append(opts, oauth2.SetAuthURLParam("domain_hint", oidcconfig.DomainHint)) - } url = oauth2conf.AuthCodeURL(stateNonce, opts...) case oidcutil.GrantTypeImplicit: url, err = oidcutil.ImplicitFlowURL(oauth2conf, stateNonce, opts...) @@ -331,7 +325,8 @@ func oauth2Login( } fmt.Printf("Performing %s flow login: %s\n", grantType, url) time.Sleep(1 * time.Second) - ssoAuthFlow(url, ssoLaunchBrowser) + err = open.Start(url) + errors.CheckError(err) go func() { log.Debugf("Listen: %s", srv.Addr) if err := srv.ListenAndServe(); err != http.ErrServerClosed { @@ -363,13 +358,3 @@ func passwordLogin(ctx context.Context, acdClient argocdclient.Client, username, errors.CheckError(err) return createdSession.Token } - -func ssoAuthFlow(url string, ssoLaunchBrowser bool) { - if ssoLaunchBrowser { - fmt.Printf("Opening system default browser for authentication\n") - err := open.Start(url) - errors.CheckError(err) - } else { - fmt.Printf("To authenticate, copy-and-paste the following URL into your preferred browser: %s\n", url) - } -} diff --git a/cmd/argocd/commands/login_test.go b/cmd/argocd/commands/login_test.go index 420b484674901..3a7411b4b7fa3 100644 --- a/cmd/argocd/commands/login_test.go +++ b/cmd/argocd/commands/login_test.go @@ -1,39 +1,12 @@ package commands import ( - "io" - "os" "testing" - utils "github.com/argoproj/argo-cd/v2/util/io" - "github.com/golang-jwt/jwt/v4" "github.com/stretchr/testify/assert" ) -func captureStdout(callback func()) (string, error) { - oldStdout := os.Stdout - oldStderr := os.Stderr - r, w, err := os.Pipe() - if err != nil { - return "", err - } - os.Stdout = w - defer func() { - os.Stdout = oldStdout - os.Stderr = oldStderr - }() - - callback() - utils.Close(w) - - data, err := io.ReadAll(r) - if err != nil { - return "", err - } - return string(data), err -} - 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) @@ -54,11 +27,3 @@ func Test_userDisplayName_sub(t *testing.T) { expectedName := "foo" assert.Equal(t, expectedName, actualName) } - -func Test_ssoAuthFlow_ssoLaunchBrowser_false(t *testing.T) { - out, _ := captureStdout(func() { - ssoAuthFlow("http://test-sso-browser-flow.com", false) - }) - - assert.Contains(t, out, "To authenticate, copy-and-paste the following URL into your preferred browser: http://test-sso-browser-flow.com") -} diff --git a/cmd/argocd/commands/logout.go b/cmd/argocd/commands/logout.go index ec532a81ed1ef..f64c57ccc89cc 100644 --- a/cmd/argocd/commands/logout.go +++ b/cmd/argocd/commands/logout.go @@ -14,7 +14,7 @@ import ( // NewLogoutCommand returns a new instance of `argocd logout` command func NewLogoutCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "logout CONTEXT", Short: "Log out from Argo CD", Long: "Log out from Argo CD", diff --git a/cmd/argocd/commands/logout_test.go b/cmd/argocd/commands/logout_test.go index 2a5c2da7b88f8..f70992c17bb93 100644 --- a/cmd/argocd/commands/logout_test.go +++ b/cmd/argocd/commands/logout_test.go @@ -12,25 +12,26 @@ import ( ) func TestLogout(t *testing.T) { + // Write the test config file err := os.WriteFile(testConfigFilePath, []byte(testConfig), os.ModePerm) - require.NoError(t, err) + assert.NoError(t, err) defer os.Remove(testConfigFilePath) - err = os.Chmod(testConfigFilePath, 0o600) + err = os.Chmod(testConfigFilePath, 0600) require.NoError(t, err) localConfig, err := localconfig.ReadLocalConfig(testConfigFilePath) - require.NoError(t, err) - assert.Equal(t, "localhost:8080", localConfig.CurrentContext) + assert.NoError(t, err) + assert.Equal(t, localConfig.CurrentContext, "localhost:8080") assert.Contains(t, localConfig.Contexts, localconfig.ContextRef{Name: "localhost:8080", Server: "localhost:8080", User: "localhost:8080"}) command := NewLogoutCommand(&argocdclient.ClientOptions{ConfigPath: testConfigFilePath}) command.Run(nil, []string{"localhost:8080"}) localConfig, err = localconfig.ReadLocalConfig(testConfigFilePath) - require.NoError(t, err) - assert.Equal(t, "localhost:8080", localConfig.CurrentContext) + assert.NoError(t, err) + assert.Equal(t, localConfig.CurrentContext, "localhost:8080") assert.NotContains(t, localConfig.Users, localconfig.User{AuthToken: "vErrYS3c3tReFRe$hToken", Name: "localhost:8080"}) assert.Contains(t, localConfig.Contexts, localconfig.ContextRef{Name: "argocd1.example.com:443", Server: "argocd1.example.com:443", User: "argocd1.example.com:443"}) assert.Contains(t, localConfig.Contexts, localconfig.ContextRef{Name: "argocd2.example.com:443", Server: "argocd2.example.com:443", User: "argocd2.example.com:443"}) diff --git a/cmd/argocd/commands/project.go b/cmd/argocd/commands/project.go index 827af5e536c33..32fb9e779e8ed 100644 --- a/cmd/argocd/commands/project.go +++ b/cmd/argocd/commands/project.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "os" - "slices" "strings" "text/tabwriter" "time" @@ -38,7 +37,7 @@ type policyOpts struct { // NewProjectCommand returns a new instance of an `argocd proj` command func NewProjectCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "proj", Short: "Manage projects", Example: templates.Examples(` @@ -79,10 +78,6 @@ 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)) - command.AddCommand(NewProjectAddDestinationServiceAccountCommand(clientOpts)) - command.AddCommand(NewProjectRemoveDestinationServiceAccountCommand(clientOpts)) return command } @@ -104,7 +99,7 @@ func NewProjectCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm fileURL string upsert bool ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "create PROJECT", Short: "Create a project", Example: templates.Examples(` @@ -138,8 +133,10 @@ func NewProjectCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm // NewProjectSetCommand returns a new instance of an `argocd proj set` command func NewProjectSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var opts cmdutil.ProjectOpts - command := &cobra.Command{ + var ( + opts cmdutil.ProjectOpts + ) + var command = &cobra.Command{ Use: "set PROJECT", Short: "Set project parameters", Example: templates.Examples(` @@ -179,7 +176,7 @@ func NewProjectSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command // NewProjectAddSignatureKeyCommand returns a new instance of an `argocd proj add-signature-key` command func NewProjectAddSignatureKeyCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "add-signature-key PROJECT KEY-ID", Short: "Add GnuPG signature key to project", Example: templates.Examples(` @@ -221,7 +218,7 @@ func NewProjectAddSignatureKeyCommand(clientOpts *argocdclient.ClientOptions) *c // NewProjectRemoveSignatureKeyCommand returns a new instance of an `argocd proj remove-signature-key` command func NewProjectRemoveSignatureKeyCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "remove-signature-key PROJECT KEY-ID", Short: "Remove GnuPG signature key from project", Example: templates.Examples(` @@ -275,7 +272,7 @@ func NewProjectAddDestinationCommand(clientOpts *argocdclient.ClientOptions) *co return v1alpha1.ApplicationDestination{Server: destination, Namespace: namespace} } - command := &cobra.Command{ + var command = &cobra.Command{ Use: "add-destination PROJECT SERVER/NAME NAMESPACE", Short: "Add project destination", Example: templates.Examples(` @@ -319,7 +316,7 @@ func NewProjectAddDestinationCommand(clientOpts *argocdclient.ClientOptions) *co // NewProjectRemoveDestinationCommand returns a new instance of an `argocd proj remove-destination` command func NewProjectRemoveDestinationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "remove-destination PROJECT SERVER NAMESPACE", Short: "Remove project destination", Example: templates.Examples(` @@ -364,8 +361,10 @@ func NewProjectRemoveDestinationCommand(clientOpts *argocdclient.ClientOptions) // NewProjectAddOrphanedIgnoreCommand returns a new instance of an `argocd proj add-orphaned-ignore` command func NewProjectAddOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var name string - command := &cobra.Command{ + var ( + name string + ) + var command = &cobra.Command{ Use: "add-orphaned-ignore PROJECT GROUP KIND", Short: "Add a resource to orphaned ignore list", Example: templates.Examples(` @@ -414,8 +413,10 @@ func NewProjectAddOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOptions) // NewProjectRemoveOrphanedIgnoreCommand returns a new instance of an `argocd proj remove-orphaned-ignore` command func NewProjectRemoveOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var name string - command := &cobra.Command{ + var ( + name string + ) + var command = &cobra.Command{ Use: "remove-orphaned-ignore PROJECT GROUP KIND", Short: "Remove a resource from orphaned ignore list", Example: templates.Examples(` @@ -468,7 +469,7 @@ func NewProjectRemoveOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOption // NewProjectAddSourceCommand returns a new instance of an `argocd proj add-src` command func NewProjectAddSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "add-source PROJECT URL", Short: "Add project source repository", Example: templates.Examples(` @@ -508,88 +509,6 @@ 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 { - 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 { - 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 { @@ -629,7 +548,7 @@ func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdc } else { defaultList = "allow" } - command := &cobra.Command{ + var command = &cobra.Command{ Use: cmdUse, Short: cmdDesc, Example: templates.Examples(examples), @@ -723,7 +642,7 @@ func NewProjectAllowClusterResourceCommand(clientOpts *argocdclient.ClientOption // NewProjectRemoveSourceCommand returns a new instance of an `argocd proj remove-src` command func NewProjectRemoveSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "remove-source PROJECT URL", Short: "Remove project source repository", Example: templates.Examples(` @@ -767,7 +686,7 @@ func NewProjectRemoveSourceCommand(clientOpts *argocdclient.ClientOptions) *cobr // NewProjectDeleteCommand returns a new instance of an `argocd proj delete` command func NewProjectDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "delete PROJECT", Short: "Delete project", Example: templates.Examples(` @@ -802,7 +721,7 @@ func printProjectNames(projects []v1alpha1.AppProject) { // Print table of project info func printProjectTable(projects []v1alpha1.AppProject) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - fmt.Fprintf(w, "NAME\tDESCRIPTION\tDESTINATIONS\tSOURCES\tCLUSTER-RESOURCE-WHITELIST\tNAMESPACE-RESOURCE-BLACKLIST\tSIGNATURE-KEYS\tORPHANED-RESOURCES\tDESTINATION-SERVICE-ACCOUNTS\n") + fmt.Fprintf(w, "NAME\tDESCRIPTION\tDESTINATIONS\tSOURCES\tCLUSTER-RESOURCE-WHITELIST\tNAMESPACE-RESOURCE-BLACKLIST\tSIGNATURE-KEYS\tORPHANED-RESOURCES\n") for _, p := range projects { printProjectLine(w, &p) } @@ -811,8 +730,10 @@ func printProjectTable(projects []v1alpha1.AppProject) { // NewProjectListCommand returns a new instance of an `argocd proj list` command func NewProjectListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ + var ( + output string + ) + var command = &cobra.Command{ Use: "list", Short: "List projects", Example: templates.Examples(` @@ -858,7 +779,7 @@ func formatOrphanedResources(p *v1alpha1.AppProject) string { } func printProjectLine(w io.Writer, p *v1alpha1.AppProject) { - var destinations, destinationServiceAccounts, sourceRepos, clusterWhitelist, namespaceBlacklist, signatureKeys string + var destinations, sourceRepos, clusterWhitelist, namespaceBlacklist, signatureKeys string switch len(p.Spec.Destinations) { case 0: destinations = "" @@ -867,14 +788,6 @@ func printProjectLine(w io.Writer, p *v1alpha1.AppProject) { default: destinations = fmt.Sprintf("%d destinations", len(p.Spec.Destinations)) } - switch len(p.Spec.DestinationServiceAccounts) { - case 0: - destinationServiceAccounts = "" - case 1: - destinationServiceAccounts = fmt.Sprintf("%s,%s,%s", p.Spec.DestinationServiceAccounts[0].Server, p.Spec.DestinationServiceAccounts[0].Namespace, p.Spec.DestinationServiceAccounts[0].DefaultServiceAccount) - default: - destinationServiceAccounts = fmt.Sprintf("%d destinationServiceAccounts", len(p.Spec.DestinationServiceAccounts)) - } switch len(p.Spec.SourceRepos) { case 0: sourceRepos = "" @@ -903,7 +816,7 @@ func printProjectLine(w io.Writer, p *v1alpha1.AppProject) { default: signatureKeys = fmt.Sprintf("%d key(s)", len(p.Spec.SignatureKeys)) } - fmt.Fprintf(w, "%s\t%s\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", p.Name, p.Spec.Description, destinations, sourceRepos, clusterWhitelist, namespaceBlacklist, signatureKeys, formatOrphanedResources(p), destinationServiceAccounts) + fmt.Fprintf(w, "%s\t%s\t%v\t%v\t%v\t%v\t%v\t%v\n", p.Name, p.Spec.Description, destinations, sourceRepos, clusterWhitelist, namespaceBlacklist, signatureKeys, formatOrphanedResources(p)) } func printProject(p *v1alpha1.AppProject, scopedRepositories []*v1alpha1.Repository, scopedClusters []*v1alpha1.Cluster) { @@ -932,17 +845,7 @@ func printProject(p *v1alpha1.AppProject, scopedRepositories []*v1alpha1.Reposit fmt.Printf(printProjFmtStr, "", p.Spec.SourceRepos[i]) } - // Print source namespaces - ns0 := "" - if len(p.Spec.SourceNamespaces) > 0 { - ns0 = p.Spec.SourceNamespaces[0] - } - fmt.Printf(printProjFmtStr, "Source Namespaces:", ns0) - for i := 1; i < len(p.Spec.SourceNamespaces); i++ { - fmt.Printf(printProjFmtStr, "", p.Spec.SourceNamespaces[i]) - } - - // Print scoped repositories + //Print scoped repositories scr0 := "" if len(scopedRepositories) > 0 { scr0 = scopedRepositories[0].Repo @@ -962,7 +865,7 @@ func printProject(p *v1alpha1.AppProject, scopedRepositories []*v1alpha1.Reposit fmt.Printf(printProjFmtStr, "", fmt.Sprintf("%s/%s", p.Spec.ClusterResourceWhitelist[i].Group, p.Spec.ClusterResourceWhitelist[i].Kind)) } - // Print scoped clusters + //Print scoped clusters scc0 := "" if len(scopedClusters) > 0 { scc0 = scopedClusters[0].Server @@ -994,12 +897,15 @@ func printProject(p *v1alpha1.AppProject, scopedRepositories []*v1alpha1.Reposit fmt.Printf(printProjFmtStr, "Signature keys:", signatureKeysStr) fmt.Printf(printProjFmtStr, "Orphaned Resources:", formatOrphanedResources(p)) + } // NewProjectGetCommand returns a new instance of an `argocd proj get` command func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ + var ( + output string + ) + var command = &cobra.Command{ Use: "get PROJECT", Short: "Get project details", Example: templates.Examples(` @@ -1044,7 +950,7 @@ func getProject(c *cobra.Command, clientOpts *argocdclient.ClientOptions, ctx co } func NewProjectEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "edit PROJECT", Short: "Edit project", Example: templates.Examples(` @@ -1093,122 +999,3 @@ func NewProjectEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman } return command } - -// NewProjectAddDestinationServiceAccountCommand returns a new instance of an `argocd proj add-destination-service-account` command -func NewProjectAddDestinationServiceAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var serviceAccountNamespace string - - buildApplicationDestinationServiceAccount := func(destination string, namespace string, serviceAccount string, serviceAccountNamespace string) v1alpha1.ApplicationDestinationServiceAccount { - if serviceAccountNamespace != "" { - return v1alpha1.ApplicationDestinationServiceAccount{ - Server: destination, - Namespace: namespace, - DefaultServiceAccount: fmt.Sprintf("%s:%s", serviceAccountNamespace, serviceAccount), - } - } else { - return v1alpha1.ApplicationDestinationServiceAccount{ - Server: destination, - Namespace: namespace, - DefaultServiceAccount: serviceAccount, - } - } - } - - command := &cobra.Command{ - Use: "add-destination-service-account PROJECT SERVER NAMESPACE SERVICE_ACCOUNT", - Short: "Add project destination's default service account", - Example: templates.Examples(` - # Add project destination service account (SERVICE_ACCOUNT) for a server URL (SERVER) in the specified namespace (NAMESPACE) on the project with name PROJECT - argocd proj add-destination-service-account PROJECT SERVER NAMESPACE SERVICE_ACCOUNT - - # Add project destination service account (SERVICE_ACCOUNT) from a different namespace - argocd proj add-destination PROJECT SERVER NAMESPACE SERVICE_ACCOUNT --service-account-namespace - `), - Run: func(c *cobra.Command, args []string) { - ctx := c.Context() - - if len(args) != 4 { - c.HelpFunc()(c, args) - os.Exit(1) - } - projName := args[0] - server := args[1] - namespace := args[2] - serviceAccount := args[3] - - if strings.Contains(serviceAccountNamespace, "*") { - log.Fatal("service-account-namespace for DestinationServiceAccount must not contain wildcards") - } - - if strings.Contains(serviceAccount, "*") { - log.Fatal("ServiceAccount for DestinationServiceAccount must not contain wildcards") - } - - destinationServiceAccount := buildApplicationDestinationServiceAccount(server, namespace, serviceAccount, serviceAccountNamespace) - conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie() - defer argoio.Close(conn) - - proj, err := projIf.Get(ctx, &projectpkg.ProjectQuery{Name: projName}) - errors.CheckError(err) - - for _, dest := range proj.Spec.DestinationServiceAccounts { - dstServerExist := destinationServiceAccount.Server != "" && dest.Server == destinationServiceAccount.Server - dstServiceAccountExist := destinationServiceAccount.DefaultServiceAccount != "" && dest.DefaultServiceAccount == destinationServiceAccount.DefaultServiceAccount - if dest.Namespace == destinationServiceAccount.Namespace && dstServerExist && dstServiceAccountExist { - log.Fatal("Specified destination service account is already defined in project") - } - } - proj.Spec.DestinationServiceAccounts = append(proj.Spec.DestinationServiceAccounts, destinationServiceAccount) - _, err = projIf.Update(ctx, &projectpkg.ProjectUpdateRequest{Project: proj}) - errors.CheckError(err) - }, - } - command.Flags().StringVar(&serviceAccountNamespace, "service-account-namespace", "", "Use service-account-namespace as namespace where the service account is present") - return command -} - -// NewProjectRemoveDestinationCommand returns a new instance of an `argocd proj remove-destination-service-account` command -func NewProjectRemoveDestinationServiceAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ - Use: "remove-destination-service-account PROJECT SERVER NAMESPACE SERVICE_ACCOUNT", - Short: "Remove default destination service account from the project", - Example: templates.Examples(` - # Remove the destination service account (SERVICE_ACCOUNT) from the specified destination (SERVER and NAMESPACE combination) on the project with name PROJECT - argocd proj remove-destination-service-account PROJECT SERVER NAMESPACE SERVICE_ACCOUNT - `), - Run: func(c *cobra.Command, args []string) { - ctx := c.Context() - - if len(args) != 4 { - c.HelpFunc()(c, args) - os.Exit(1) - } - projName := args[0] - server := args[1] - namespace := args[2] - serviceAccount := args[3] - conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie() - defer argoio.Close(conn) - - proj, err := projIf.Get(ctx, &projectpkg.ProjectQuery{Name: projName}) - errors.CheckError(err) - - originalLength := len(proj.Spec.DestinationServiceAccounts) - proj.Spec.DestinationServiceAccounts = slices.DeleteFunc(proj.Spec.DestinationServiceAccounts, - func(destServiceAccount v1alpha1.ApplicationDestinationServiceAccount) bool { - return destServiceAccount.Namespace == namespace && - destServiceAccount.Server == server && - destServiceAccount.DefaultServiceAccount == serviceAccount - }, - ) - if originalLength != len(proj.Spec.DestinationServiceAccounts) { - _, err = projIf.Update(ctx, &projectpkg.ProjectUpdateRequest{Project: proj}) - errors.CheckError(err) - } else { - log.Fatal("Specified destination service account does not exist in project") - } - }, - } - - return command -} diff --git a/cmd/argocd/commands/project_role.go b/cmd/argocd/commands/project_role.go index 023bf18fba700..5920bac0dc8e4 100644 --- a/cmd/argocd/commands/project_role.go +++ b/cmd/argocd/commands/project_role.go @@ -51,8 +51,10 @@ func NewProjectRoleCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman // NewProjectRoleAddPolicyCommand returns a new instance of an `argocd proj role add-policy` command func NewProjectRoleAddPolicyCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var opts policyOpts - command := &cobra.Command{ + var ( + opts policyOpts + ) + var command = &cobra.Command{ Use: "add-policy PROJECT ROLE-NAME", Short: "Add a policy to a project role", Example: `# Before adding new policy @@ -110,8 +112,10 @@ ID ISSUED-AT EXPIRES-AT // NewProjectRoleRemovePolicyCommand returns a new instance of an `argocd proj role remove-policy` command func NewProjectRoleRemovePolicyCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var opts policyOpts - command := &cobra.Command{ + var ( + opts policyOpts + ) + 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 @@ -179,8 +183,10 @@ ID ISSUED-AT EXPIRES-AT // NewProjectRoleCreateCommand returns a new instance of an `argocd proj role create` command func NewProjectRoleCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var description string - command := &cobra.Command{ + var ( + description string + ) + var command = &cobra.Command{ Use: "create PROJECT ROLE-NAME", Short: "Create a project role", Example: templates.Examples(` @@ -221,7 +227,7 @@ 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 { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "delete PROJECT ROLE-NAME", Short: "Delete a project role", Example: `$ argocd proj role delete test-project test-role`, @@ -271,7 +277,7 @@ func NewProjectRoleCreateTokenCommand(clientOpts *argocdclient.ClientOptions) *c outputTokenOnly bool tokenID string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "create-token PROJECT ROLE-NAME", Short: "Create a project token", Example: `$ argocd proj role create-token test-project test-role @@ -308,7 +314,7 @@ Create token succeeded for proj:test-project:test-role. token, err := jwtgo.Parse(tokenResponse.Token, nil) if token == nil { - err = fmt.Errorf("received malformed token %w", err) + err = fmt.Errorf("received malformed token %v", err) errors.CheckError(err) return } @@ -340,8 +346,10 @@ Create token succeeded for proj:test-project:test-role. } func NewProjectRoleListTokensCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var useUnixTime bool - command := &cobra.Command{ + var ( + useUnixTime bool + ) + var command = &cobra.Command{ Use: "list-tokens PROJECT ROLE-NAME", Short: "List tokens for a given role.", Example: `$ argocd proj role list-tokens test-project test-role @@ -397,7 +405,7 @@ fa9d3517-c52d-434c-9bff-215b38508842 2023-10-08T11:08:18+01:00 Never // NewProjectRoleDeleteTokenCommand returns a new instance of an `argocd proj role delete-token` command func NewProjectRoleDeleteTokenCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "delete-token PROJECT ROLE-NAME ISSUED-AT", Short: "Delete a project token", Example: `#Create project test-project @@ -469,8 +477,10 @@ func printProjectRoleListTable(roles []v1alpha1.ProjectRole) { // NewProjectRoleListCommand returns a new instance of an `argocd proj roles list` command func NewProjectRoleListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ + var ( + output string + ) + var command = &cobra.Command{ Use: "list PROJECT", Short: "List all the roles in a project", Example: templates.Examples(` @@ -514,7 +524,7 @@ func NewProjectRoleListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co // NewProjectRoleGetCommand returns a new instance of an `argocd proj roles get` command func NewProjectRoleGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + 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 @@ -569,7 +579,7 @@ ID ISSUED-AT EXPIRES-AT // NewProjectRoleAddGroupCommand returns a new instance of an `argocd proj role add-group` command func NewProjectRoleAddGroupCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "add-group PROJECT ROLE-NAME GROUP-CLAIM", Short: "Add a group claim to a project role", Run: func(c *cobra.Command, args []string) { @@ -600,7 +610,7 @@ func NewProjectRoleAddGroupCommand(clientOpts *argocdclient.ClientOptions) *cobr // NewProjectRoleRemoveGroupCommand returns a new instance of an `argocd proj role remove-group` command func NewProjectRoleRemoveGroupCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "remove-group PROJECT ROLE-NAME GROUP-CLAIM", Short: "Remove a group claim from a role within a project", Run: func(c *cobra.Command, args []string) { diff --git a/cmd/argocd/commands/projectwindows.go b/cmd/argocd/commands/projectwindows.go index d824222306419..93843130ebb13 100644 --- a/cmd/argocd/commands/projectwindows.go +++ b/cmd/argocd/commands/projectwindows.go @@ -50,7 +50,7 @@ argocd proj windows list `, // NewProjectWindowsDisableManualSyncCommand returns a new instance of an `argocd proj windows disable-manual-sync` command func NewProjectWindowsDisableManualSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ 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\"", @@ -58,7 +58,7 @@ func NewProjectWindowsDisableManualSyncCommand(clientOpts *argocdclient.ClientOp #Disable manual sync for a sync window for the Project argocd proj windows disable-manual-sync PROJECT ID -#Disabling manual sync for a windows set on the default project with Id 0 +#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() @@ -93,7 +93,7 @@ argocd proj windows disable-manual-sync default 0`, // NewProjectWindowsEnableManualSyncCommand returns a new instance of an `argocd proj windows enable-manual-sync` command func NewProjectWindowsEnableManualSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ 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\"", @@ -149,7 +149,7 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) * manualSync bool timeZone string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "add PROJECT", Short: "Add a sync window to a project", Example: ` @@ -205,7 +205,7 @@ argocd proj windows add PROJECT \ // NewProjectWindowsDeleteCommand returns a new instance of an `argocd proj windows delete` command func NewProjectWindowsDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &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: ` @@ -252,7 +252,7 @@ func NewProjectWindowsUpdateCommand(clientOpts *argocdclient.ClientOptions) *cob clusters []string timeZone string ) - command := &cobra.Command{ + var command = &cobra.Command{ 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\"", @@ -302,8 +302,10 @@ argocd proj windows update PROJECT ID \ // NewProjectWindowsListCommand returns a new instance of an `argocd proj windows list` command func NewProjectWindowsListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ + var ( + output string + ) + var command = &cobra.Command{ Use: "list PROJECT", Short: "List project sync windows", Example: ` @@ -379,7 +381,6 @@ func formatListOutput(list []string) string { } return o } - func formatBoolOutput(active bool) string { var o string if active { @@ -389,7 +390,6 @@ func formatBoolOutput(active bool) string { } return o } - func formatManualOutput(active bool) string { var o string if active { diff --git a/cmd/argocd/commands/relogin.go b/cmd/argocd/commands/relogin.go index effb0239c051b..92affe05b2e5b 100644 --- a/cmd/argocd/commands/relogin.go +++ b/cmd/argocd/commands/relogin.go @@ -20,11 +20,10 @@ import ( // NewReloginCommand returns a new instance of `argocd relogin` command func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - password string - ssoPort int - ssoLaunchBrowser bool + password string + ssoPort int ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "relogin", Short: "Refresh an expired authenticate token", Long: "Refresh an expired authenticate token", @@ -73,7 +72,7 @@ func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comm errors.CheckError(err) oauth2conf, provider, err := acdClient.OIDCConfig(ctx, acdSet) errors.CheckError(err) - tokenString, refreshToken = oauth2Login(ctx, ssoPort, acdSet.GetOIDCConfig(), oauth2conf, provider, ssoLaunchBrowser) + tokenString, refreshToken = oauth2Login(ctx, ssoPort, acdSet.GetOIDCConfig(), oauth2conf, provider) } localCfg.UpsertUser(localconfig.User{ @@ -100,6 +99,5 @@ argocd login cd.argoproj.io --core } 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().BoolVar(&ssoLaunchBrowser, "sso-launch-browser", true, "Automatically launch the default browser when performing SSO login") return command } diff --git a/cmd/argocd/commands/relogin_test.go b/cmd/argocd/commands/relogin_test.go index 9b6e1e7d83b47..eb6c4cd2d2f2d 100644 --- a/cmd/argocd/commands/relogin_test.go +++ b/cmd/argocd/commands/relogin_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" ) @@ -29,7 +28,7 @@ func TestNewReloginCommand(t *testing.T) { ssoPortFlag := cmd.Flags().Lookup("sso-port") port, err := strconv.Atoi(ssoPortFlag.Value.String()) assert.NotNil(t, ssoPortFlag, "Expected flag --sso-port to be defined") - require.NoError(t, err, "Failed to convert sso-port flag value to integer") + 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") } @@ -60,6 +59,6 @@ func TestNewReloginCommandWithGlobalClientOptions(t *testing.T) { ssoPortFlag := cmd.Flags().Lookup("sso-port") port, err := strconv.Atoi(ssoPortFlag.Value.String()) assert.NotNil(t, ssoPortFlag, "Expected flag --sso-port to be defined") - require.NoError(t, err, "Failed to convert sso-port flag value to integer") + 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 f58204ea76c3a..2bf9714a06f11 100644 --- a/cmd/argocd/commands/repo.go +++ b/cmd/argocd/commands/repo.go @@ -22,7 +22,7 @@ import ( // NewRepoCommand returns a new instance of an `argocd repo` command func NewRepoCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "repo", Short: "Manage repository connection parameters", Run: func(c *cobra.Command, args []string) { @@ -53,21 +53,17 @@ argocd repo rm https://github.com/yourusername/your-repo.git // NewRepoAddCommand returns a new instance of an `argocd repo add` command func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var repoOpts cmdutil.RepoOptions + var ( + repoOpts cmdutil.RepoOptions + ) // For better readability and easier formatting - repoAddExamples := ` # Add a Git repository via SSH using a private key for authentication, ignoring the server's host key: + var repoAddExamples = ` # Add a Git repository via SSH using a private key for authentication, ignoring the server's host key: argocd repo add git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa # 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 @@ -93,7 +89,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { argocd repo add https://source.developers.google.com/p/my-google-cloud-project/r/my-repo --gcp-service-account-key-path service-account-key.json ` - command := &cobra.Command{ + var command = &cobra.Command{ Use: "add REPOURL", Short: "Add git repository connection parameters", Example: repoAddExamples, @@ -178,7 +174,6 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { repoOpts.Repo.GithubAppInstallationId = repoOpts.GithubAppInstallationId repoOpts.Repo.GitHubAppEnterpriseBaseURL = repoOpts.GitHubAppEnterpriseBaseURL repoOpts.Repo.Proxy = repoOpts.Proxy - repoOpts.Repo.NoProxy = repoOpts.NoProxy repoOpts.Repo.ForceHttpBasicAuth = repoOpts.ForceHttpBasicAuth if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" { @@ -241,8 +236,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { // NewRepoRemoveCommand returns a new instance of an `argocd repo remove` command func NewRepoRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var project string - command := &cobra.Command{ + var command = &cobra.Command{ Use: "rm REPO", Short: "Remove repository credentials", Run: func(c *cobra.Command, args []string) { @@ -255,13 +249,12 @@ func NewRepoRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command conn, repoIf := headless.NewClientOrDie(clientOpts, c).NewRepoClientOrDie() defer io.Close(conn) for _, repoURL := range args { - _, err := repoIf.DeleteRepository(ctx, &repositorypkg.RepoQuery{Repo: repoURL, AppProject: project}) + _, err := repoIf.DeleteRepository(ctx, &repositorypkg.RepoQuery{Repo: repoURL}) errors.CheckError(err) fmt.Printf("Repository '%s' removed\n", repoURL) } }, } - command.Flags().StringVar(&project, "project", "", "project of the repository") return command } @@ -295,7 +288,7 @@ func NewRepoListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { output string refresh string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "list", Short: "List configured repositories", Run: func(c *cobra.Command, args []string) { @@ -338,9 +331,8 @@ func NewRepoGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( output string refresh string - project string ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: "get", Short: "Get a configured repository by URL", Run: func(c *cobra.Command, args []string) { @@ -364,7 +356,7 @@ func NewRepoGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { err := fmt.Errorf("--refresh must be one of: 'hard'") errors.CheckError(err) } - repo, err := repoIf.Get(ctx, &repositorypkg.RepoQuery{Repo: repoURL, ForceRefresh: forceRefresh, AppProject: project}) + repo, err := repoIf.Get(ctx, &repositorypkg.RepoQuery{Repo: repoURL, ForceRefresh: forceRefresh}) errors.CheckError(err) switch output { case "yaml", "json": @@ -380,8 +372,6 @@ func NewRepoGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { } }, } - - command.Flags().StringVar(&project, "project", "", "project of the repository") command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|url") command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status , must be one of: 'hard'") return command diff --git a/cmd/argocd/commands/repocreds.go b/cmd/argocd/commands/repocreds.go index 21ebca795cdfb..e43b9713a2927 100644 --- a/cmd/argocd/commands/repocreds.go +++ b/cmd/argocd/commands/repocreds.go @@ -22,7 +22,7 @@ import ( // NewRepoCredsCommand returns a new instance of an `argocd repocreds` command func NewRepoCredsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "repocreds", Short: "Manage repository connection parameters", Example: templates.Examples(` @@ -60,7 +60,7 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma ) // For better readability and easier formatting - repocredsAddExamples := ` # Add credentials with user/pass authentication to use for all repositories under https://git.example.com/repos + var repocredsAddExamples = ` # Add credentials with user/pass authentication to use for all repositories under https://git.example.com/repos argocd repocreds add https://git.example.com/repos/ --username git --password secret # Add credentials with SSH private key authentication to use for all repositories under ssh://git@git.example.com/repos @@ -79,7 +79,7 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma argocd repocreds add https://source.developers.google.com/p/my-google-cloud-project/r/ --gcp-service-account-key-path service-account-key.json ` - command := &cobra.Command{ + var command = &cobra.Command{ Use: "add REPOURL", Short: "Add git repository connection parameters", Example: repocredsAddExamples, @@ -187,13 +187,12 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma 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") - command.Flags().StringVar(&repo.Proxy, "proxy-url", "", "If provided, this URL will be used to connect via proxy") return command } // NewRepoCredsRemoveCommand returns a new instance of an `argocd repocreds rm` command func NewRepoCredsRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "rm CREDSURL", Short: "Remove repository credentials", Example: templates.Examples(` @@ -241,8 +240,10 @@ func printRepoCredsUrls(repos []appsv1.RepoCreds) { // NewRepoCredsListCommand returns a new instance of an `argocd repo list` command func NewRepoCredsListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var output string - command := &cobra.Command{ + var ( + output string + ) + var command = &cobra.Command{ Use: "list", Short: "List configured repository credentials", Example: templates.Examples(` diff --git a/cmd/argocd/commands/root.go b/cmd/argocd/commands/root.go index 4386b8febd6c4..5c3b984e5bff5 100644 --- a/cmd/argocd/commands/root.go +++ b/cmd/argocd/commands/root.go @@ -34,7 +34,7 @@ func NewCommand() *cobra.Command { pathOpts = clientcmd.NewDefaultPathOptions() ) - command := &cobra.Command{ + var command = &cobra.Command{ Use: cliName, Short: "argocd controls a Argo CD server", Run: func(c *cobra.Command, args []string) { @@ -70,17 +70,16 @@ func NewCommand() *cobra.Command { command.PersistentFlags().StringVar(&clientOpts.CertFile, "server-crt", config.GetFlag("server-crt", ""), "Server certificate file") command.PersistentFlags().StringVar(&clientOpts.ClientCertFile, "client-crt", config.GetFlag("client-crt", ""), "Client certificate file") command.PersistentFlags().StringVar(&clientOpts.ClientCertKeyFile, "client-crt-key", config.GetFlag("client-crt-key", ""), "Client certificate key file") - command.PersistentFlags().StringVar(&clientOpts.AuthToken, "auth-token", config.GetFlag("auth-token", env.StringFromEnv(common.EnvAuthToken, "")), fmt.Sprintf("Authentication token; set this or the %s environment variable", common.EnvAuthToken)) + command.PersistentFlags().StringVar(&clientOpts.AuthToken, "auth-token", config.GetFlag("auth-token", ""), "Authentication token") command.PersistentFlags().BoolVar(&clientOpts.GRPCWeb, "grpc-web", config.GetBoolFlag("grpc-web"), "Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2.") command.PersistentFlags().StringVar(&clientOpts.GRPCWebRootPath, "grpc-web-root-path", config.GetFlag("grpc-web-root-path", ""), "Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root.") command.PersistentFlags().StringVar(&cmdutil.LogFormat, "logformat", config.GetFlag("logformat", "text"), "Set the logging format. One of: text|json") command.PersistentFlags().StringVar(&cmdutil.LogLevel, "loglevel", config.GetFlag("loglevel", "info"), "Set the logging level. One of: debug|info|warn|error") - command.PersistentFlags().StringSliceVarP(&clientOpts.Headers, "header", "H", config.GetStringSliceFlag("header", []string{}), "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)") + command.PersistentFlags().StringSliceVarP(&clientOpts.Headers, "header", "H", []string{}, "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)") command.PersistentFlags().BoolVar(&clientOpts.PortForward, "port-forward", config.GetBoolFlag("port-forward"), "Connect to a random argocd-server port using port forwarding") 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", config.GetIntFlag("http-retry-max", 0), "Maximum number of retries to establish http connection to Argo CD server") - command.PersistentFlags().BoolVar(&clientOpts.Core, "core", config.GetBoolFlag("core"), "If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server") - command.PersistentFlags().StringVar(&clientOpts.Context, "argocd-context", "", "The name of the Argo-CD server context to use") + 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)) diff --git a/cmd/argocd/commands/tree.go b/cmd/argocd/commands/tree.go index 904b2e7d689ef..5261adb5b7f4a 100644 --- a/cmd/argocd/commands/tree.go +++ b/cmd/argocd/commands/tree.go @@ -6,10 +6,9 @@ import ( "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" - - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) const ( @@ -46,11 +45,12 @@ func treeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode } 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) - age := "" + var age = "" if parent.CreatedAt != nil { age = duration.HumanDuration(time.Since(parent.CreatedAt.Time)) } @@ -60,6 +60,7 @@ func detailedTreeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.Reso _, _ = 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 { @@ -107,9 +108,10 @@ func treeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha } 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) - age := "" + var age = "" if parent.CreatedAt != nil { age = duration.HumanDuration(time.Since(parent.CreatedAt.Time)) } @@ -130,7 +132,7 @@ func detailedTreeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[str func detailedTreeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) { healthStatus, reason := extractHealthStatusAndReason(parent) - age := "" + var age = "" if parent.CreatedAt != nil { age = duration.HumanDuration(time.Since(parent.CreatedAt.Time)) } @@ -150,6 +152,7 @@ func detailedTreeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string } func printPrefix(p string) string { + if strings.HasSuffix(p, firstElemPrefix) { p = strings.Replace(p, firstElemPrefix, pipe, strings.Count(p, firstElemPrefix)-1) } else { diff --git a/cmd/argocd/commands/tree_test.go b/cmd/argocd/commands/tree_test.go index 70f7a86ae759e..91ffb9b963d01 100644 --- a/cmd/argocd/commands/tree_test.go +++ b/cmd/argocd/commands/tree_test.go @@ -5,9 +5,8 @@ import ( "testing" "text/tabwriter" - "github.com/stretchr/testify/assert" - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/stretchr/testify/assert" ) func TestTreeViewAppGet(t *testing.T) { diff --git a/cmd/argocd/commands/version_test.go b/cmd/argocd/commands/version_test.go index 54bfb21b18b61..3312e5ad958b6 100644 --- a/cmd/argocd/commands/version_test.go +++ b/cmd/argocd/commands/version_test.go @@ -4,10 +4,9 @@ import ( "bytes" "testing" - "github.com/stretchr/testify/assert" - argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" "github.com/argoproj/argo-cd/v2/pkg/apiclient/version" + "github.com/stretchr/testify/assert" ) func TestShortVersionClient(t *testing.T) { @@ -20,7 +19,7 @@ func TestShortVersionClient(t *testing.T) { t.Fatal("Failed to execute short version command") } output := buf.String() - assert.Equal(t, "argocd: v99.99.99+unknown\n", output) + assert.Equal(t, output, "argocd: v99.99.99+unknown\n") } func TestShortVersion(t *testing.T) { @@ -34,5 +33,5 @@ func TestShortVersion(t *testing.T) { t.Fatal("Failed to execute short version command") } output := buf.String() - assert.Equal(t, "argocd: v99.99.99+unknown\nargocd-server: v99.99.99+unknown\n", output) + assert.Equal(t, output, "argocd: v99.99.99+unknown\nargocd-server: v99.99.99+unknown\n") } diff --git a/cmd/main.go b/cmd/main.go index e6c94c40f7c85..d972863992bce 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -6,8 +6,6 @@ import ( "github.com/spf13/cobra" - _ "go.uber.org/automaxprocs" - appcontroller "github.com/argoproj/argo-cd/v2/cmd/argocd-application-controller/commands" applicationset "github.com/argoproj/argo-cd/v2/cmd/argocd-applicationset-controller/commands" cmpserver "github.com/argoproj/argo-cd/v2/cmd/argocd-cmp-server/commands" diff --git a/cmd/util/app.go b/cmd/util/app.go index 56b48fee82131..e08ee80305c48 100644 --- a/cmd/util/app.go +++ b/cmd/util/app.go @@ -17,7 +17,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" "github.com/argoproj/argo-cd/v2/pkg/apis/application" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -48,9 +48,6 @@ type AppOptions struct { helmVersion string helmPassCredentials bool helmSkipCrds bool - helmNamespace string - helmKubeVersion string - helmApiVersions []string project string syncPolicy string syncOptions []string @@ -71,12 +68,9 @@ type AppOptions struct { kustomizeVersion string kustomizeCommonLabels []string kustomizeCommonAnnotations []string - kustomizeLabelWithoutSelector bool kustomizeForceCommonLabels bool kustomizeForceCommonAnnotations bool kustomizeNamespace string - kustomizeKubeVersion string - kustomizeApiVersions []string pluginEnvs []string Validate bool directoryExclude string @@ -85,7 +79,6 @@ type AppOptions struct { retryBackoffDuration time.Duration retryBackoffMaxDuration time.Duration retryBackoffFactor int64 - ref string } func AddAppFlags(command *cobra.Command, opts *AppOptions) { @@ -109,11 +102,8 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) { command.Flags().StringArrayVar(&opts.helmSetStrings, "helm-set-string", []string{}, "Helm set STRING values on the command line (can be repeated to set several values: --helm-set-string key1=val1 --helm-set-string key2=val2)") command.Flags().StringArrayVar(&opts.helmSetFiles, "helm-set-file", []string{}, "Helm set values from respective files specified via the command line (can be repeated to set several values: --helm-set-file key1=path1 --helm-set-file key2=path2)") command.Flags().BoolVar(&opts.helmSkipCrds, "helm-skip-crds", false, "Skip helm crd installation step") - command.Flags().StringVar(&opts.helmNamespace, "helm-namespace", "", "Helm namespace to use when running helm template. If not set, use app.spec.destination.namespace") - command.Flags().StringVar(&opts.helmKubeVersion, "helm-kube-version", "", "Helm kube-version to use when running helm template. If not set, use the kube version from the destination cluster") - command.Flags().StringArrayVar(&opts.helmApiVersions, "helm-api-versions", []string{}, "Helm api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster") command.Flags().StringVar(&opts.project, "project", "", "Application project name") - 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().StringVar(&opts.syncPolicy, "sync-policy", "", "Set the sync policy (one of: 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") @@ -134,49 +124,90 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) { 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.kustomizeKubeVersion, "kustomize-kube-version", "", "kube-version to use when running helm template. If not set, use the kube version from the destination cluster. Only applicable when Helm is enabled for Kustomize builds") - command.Flags().StringArrayVar(&opts.kustomizeApiVersions, "kustomize-api-versions", nil, "api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster. Only applicable when Helm is enabled for Kustomize builds") 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, sourcePosition int) int { +func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, appOpts *AppOptions) int { visited := 0 if flags == nil { return visited } - source := spec.GetSourcePtrByPosition(sourcePosition) - if source == nil { - source = &argoappv1.ApplicationSource{} - } - source, visited = ConstructSource(source, *appOpts, flags) - if spec.HasMultipleSources() { - if sourcePosition == 0 { - spec.Sources[sourcePosition] = *source - } else if sourcePosition > 0 { - spec.Sources[sourcePosition-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": @@ -185,9 +216,45 @@ 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-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-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", "manual": + case "none": if spec.SyncPolicy != nil { spec.SyncPolicy.Automated = nil } @@ -228,7 +295,7 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap Backoff: &argoappv1.Backoff{ Duration: appOpts.retryBackoffDuration.String(), MaxDuration: appOpts.retryBackoffMaxDuration.String(), - Factor: ptr.To(appOpts.retryBackoffFactor), + Factor: pointer.Int64(appOpts.retryBackoffFactor), }, } } else if appOpts.retryLimit == 0 { @@ -241,6 +308,7 @@ 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 { @@ -272,12 +340,9 @@ type kustomizeOpts struct { version string commonLabels map[string]string commonAnnotations map[string]string - labelWithoutSelector bool forceCommonLabels bool forceCommonAnnotations bool namespace string - kubeVersion string - apiVersions []string } func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) { @@ -296,21 +361,12 @@ func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) { if opts.namespace != "" { src.Kustomize.Namespace = opts.namespace } - if opts.kubeVersion != "" { - src.Kustomize.KubeVersion = opts.kubeVersion - } - if len(opts.apiVersions) > 0 { - src.Kustomize.APIVersions = opts.apiVersions - } 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 } @@ -358,9 +414,6 @@ type helmOpts struct { helmSetFiles []string passCredentials bool skipCrds bool - namespace string - kubeVersion string - apiVersions []string } func setHelmOpt(src *argoappv1.ApplicationSource, opts helmOpts) { @@ -391,15 +444,6 @@ func setHelmOpt(src *argoappv1.ApplicationSource, opts helmOpts) { if opts.skipCrds { src.Helm.SkipCrds = opts.skipCrds } - if opts.namespace != "" { - src.Helm.Namespace = opts.namespace - } - if opts.kubeVersion != "" { - src.Helm.KubeVersion = opts.kubeVersion - } - if len(opts.apiVersions) > 0 { - src.Helm.APIVersions = opts.apiVersions - } for _, text := range opts.helmSets { p, err := argoappv1.NewHelmParameter(text, false) if err != nil { @@ -454,18 +498,20 @@ 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, index int) { +func SetParameterOverrides(app *argoappv1.Application, parameters []string) { if len(parameters) == 0 { return } - source := app.Spec.GetSourcePtrByIndex(index) + source := app.Spec.GetSource() var sourceType argoappv1.ApplicationSourceType if st, _ := source.ExplicitType(); st != nil { sourceType = *st } else if app.Status.SourceType != "" { sourceType = app.Status.SourceType - } else if len(strings.SplitN(parameters[0], "=", 2)) == 2 { - sourceType = argoappv1.ApplicationSourceTypeHelm + } else { + if len(strings.SplitN(parameters[0], "=", 2)) == 2 { + sourceType = argoappv1.ApplicationSourceTypeHelm + } } switch sourceType { @@ -511,12 +557,13 @@ func readAppsFromStdin(apps *[]*argoappv1.Application) error { } err = readApps(data, apps) if err != nil { - return fmt.Errorf("unable to read manifest from stdin: %w", err) + return fmt.Errorf("unable to read manifest from stdin: %v", err) } return nil } func readAppsFromURI(fileURL string, apps *[]*argoappv1.Application) error { + readFilePayload := func() ([]byte, error) { parsedURL, err := url.ParseRequestURI(fileURL) if err != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") { @@ -567,8 +614,8 @@ func constructAppsBaseOnName(appName string, labels, annotations, args []string, Source: &argoappv1.ApplicationSource{}, }, } - SetAppSpecOptions(flags, &app.Spec, &appOpts, 0) - SetParameterOverrides(app, appOpts.Parameters, 0) + SetAppSpecOptions(flags, &app.Spec, &appOpts) + SetParameterOverrides(app, appOpts.Parameters) mergeLabels(app, labels) setAnnotations(app, annotations) return []*argoappv1.Application{ @@ -593,15 +640,10 @@ 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 } @@ -612,127 +654,9 @@ 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 "helm-namespace": - setHelmOpt(source, helmOpts{namespace: appOpts.helmNamespace}) - case "helm-kube-version": - setHelmOpt(source, helmOpts{kubeVersion: appOpts.helmKubeVersion}) - case "helm-api-versions": - setHelmOpt(source, helmOpts{apiVersions: appOpts.helmApiVersions}) - 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-kube-version": - setKustomizeOpt(source, kustomizeOpts{kubeVersion: appOpts.kustomizeKubeVersion}) - case "kustomize-api-versions": - setKustomizeOpt(source, kustomizeOpts{apiVersions: appOpts.kustomizeApiVersions}) - 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) diff --git a/cmd/util/app_test.go b/cmd/util/app_test.go index 595b9be46563e..2f49a3cc4c8c4 100644 --- a/cmd/util/app_test.go +++ b/cmd/util/app_test.go @@ -7,7 +7,6 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -28,7 +27,7 @@ func Test_setHelmOpt(t *testing.T) { t.Run("IgnoreMissingValueFiles", func(t *testing.T) { src := v1alpha1.ApplicationSource{} setHelmOpt(&src, helmOpts{ignoreMissingValueFiles: true}) - assert.True(t, src.Helm.IgnoreMissingValueFiles) + assert.Equal(t, true, src.Helm.IgnoreMissingValueFiles) }) t.Run("ReleaseName", func(t *testing.T) { src := v1alpha1.ApplicationSource{} @@ -58,27 +57,12 @@ func Test_setHelmOpt(t *testing.T) { t.Run("HelmPassCredentials", func(t *testing.T) { src := v1alpha1.ApplicationSource{} setHelmOpt(&src, helmOpts{passCredentials: true}) - assert.True(t, src.Helm.PassCredentials) + assert.Equal(t, true, src.Helm.PassCredentials) }) t.Run("HelmSkipCrds", func(t *testing.T) { src := v1alpha1.ApplicationSource{} setHelmOpt(&src, helmOpts{skipCrds: true}) - assert.True(t, src.Helm.SkipCrds) - }) - t.Run("HelmNamespace", func(t *testing.T) { - src := v1alpha1.ApplicationSource{} - setHelmOpt(&src, helmOpts{namespace: "custom-namespace"}) - assert.Equal(t, "custom-namespace", src.Helm.Namespace) - }) - t.Run("HelmKubeVersion", func(t *testing.T) { - src := v1alpha1.ApplicationSource{} - setHelmOpt(&src, helmOpts{kubeVersion: "v1.16.0"}) - assert.Equal(t, "v1.16.0", src.Helm.KubeVersion) - }) - t.Run("HelmApiVersions", func(t *testing.T) { - src := v1alpha1.ApplicationSource{} - setHelmOpt(&src, helmOpts{apiVersions: []string{"v1", "v2"}}) - assert.Equal(t, []string{"v1", "v2"}, src.Helm.APIVersions) + assert.Equal(t, true, src.Helm.SkipCrds) }) } @@ -129,16 +113,6 @@ func Test_setKustomizeOpt(t *testing.T) { setKustomizeOpt(&src, kustomizeOpts{namespace: "custom-namespace"}) assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{Namespace: "custom-namespace"}, src.Kustomize) }) - t.Run("KubeVersion", func(t *testing.T) { - src := v1alpha1.ApplicationSource{} - setKustomizeOpt(&src, kustomizeOpts{kubeVersion: "999.999.999"}) - assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{KubeVersion: "999.999.999"}, src.Kustomize) - }) - t.Run("ApiVersions", func(t *testing.T) { - src := v1alpha1.ApplicationSource{} - setKustomizeOpt(&src, kustomizeOpts{apiVersions: []string{"v1", "v2"}}) - assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{APIVersions: []string{"v1", "v2"}}, src.Kustomize) - }) t.Run("Common labels", func(t *testing.T) { src := v1alpha1.ApplicationSource{} setKustomizeOpt(&src, kustomizeOpts{commonLabels: map[string]string{"foo1": "bar1", "foo2": "bar2"}}) @@ -149,11 +123,6 @@ 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) { @@ -196,16 +165,7 @@ func (f *appOptionsFixture) SetFlag(key, value string) error { if err != nil { return err } - _ = SetAppSpecOptions(f.command.Flags(), f.spec, f.options, 0) - return err -} - -func (f *appOptionsFixture) SetFlagWithSourcePosition(key, value string, sourcePosition int) error { - err := f.command.Flags().Set(key, value) - if err != nil { - return err - } - _ = SetAppSpecOptions(f.command.Flags(), f.spec, f.options, sourcePosition) + _ = SetAppSpecOptions(f.command.Flags(), f.spec, f.options) return err } @@ -224,113 +184,39 @@ func newAppOptionsFixture() *appOptionsFixture { func Test_setAppSpecOptions(t *testing.T) { f := newAppOptionsFixture() t.Run("SyncPolicy", func(t *testing.T) { - require.NoError(t, f.SetFlag("sync-policy", "automated")) + assert.NoError(t, f.SetFlag("sync-policy", "automated")) assert.NotNil(t, f.spec.SyncPolicy.Automated) f.spec.SyncPolicy = nil - require.NoError(t, f.SetFlag("sync-policy", "automatic")) + assert.NoError(t, f.SetFlag("sync-policy", "automatic")) assert.NotNil(t, f.spec.SyncPolicy.Automated) f.spec.SyncPolicy = nil - require.NoError(t, f.SetFlag("sync-policy", "auto")) + assert.NoError(t, f.SetFlag("sync-policy", "auto")) assert.NotNil(t, f.spec.SyncPolicy.Automated) - require.NoError(t, f.SetFlag("sync-policy", "none")) + assert.NoError(t, f.SetFlag("sync-policy", "none")) assert.Nil(t, f.spec.SyncPolicy) }) t.Run("SyncOptions", func(t *testing.T) { - require.NoError(t, f.SetFlag("sync-option", "a=1")) + assert.NoError(t, f.SetFlag("sync-option", "a=1")) assert.True(t, f.spec.SyncPolicy.SyncOptions.HasOption("a=1")) // remove the options using ! - require.NoError(t, f.SetFlag("sync-option", "!a=1")) + assert.NoError(t, f.SetFlag("sync-option", "!a=1")) assert.Nil(t, f.spec.SyncPolicy) }) t.Run("RetryLimit", func(t *testing.T) { - require.NoError(t, f.SetFlag("sync-retry-limit", "5")) - assert.Equal(t, int64(5), f.spec.SyncPolicy.Retry.Limit) + assert.NoError(t, f.SetFlag("sync-retry-limit", "5")) + assert.True(t, f.spec.SyncPolicy.Retry.Limit == 5) - require.NoError(t, f.SetFlag("sync-retry-limit", "0")) + assert.NoError(t, f.SetFlag("sync-retry-limit", "0")) assert.Nil(t, f.spec.SyncPolicy.Retry) }) t.Run("Kustomize", func(t *testing.T) { - require.NoError(t, f.SetFlag("kustomize-replica", "my-deployment=2")) - require.NoError(t, f.SetFlag("kustomize-replica", "my-statefulset=4")) - assert.Equal(t, v1alpha1.KustomizeReplicas{{Name: "my-deployment", Count: intstr.FromInt(2)}, {Name: "my-statefulset", Count: intstr.FromInt(4)}}, f.spec.Source.Kustomize.Replicas) - }) - t.Run("Kustomize Namespace", func(t *testing.T) { - require.NoError(t, f.SetFlag("kustomize-namespace", "override-namespace")) - assert.Equal(t, "override-namespace", f.spec.Source.Kustomize.Namespace) - }) - t.Run("Kustomize Kube Version", func(t *testing.T) { - require.NoError(t, f.SetFlag("kustomize-kube-version", "999.999.999")) - assert.Equal(t, "999.999.999", f.spec.Source.Kustomize.KubeVersion) - }) - t.Run("Kustomize API Versions", func(t *testing.T) { - require.NoError(t, f.SetFlag("kustomize-api-versions", "v1")) - require.NoError(t, f.SetFlag("kustomize-api-versions", "v2")) - assert.Equal(t, []string{"v1", "v2"}, f.spec.Source.Kustomize.APIVersions) - }) - t.Run("Helm Namespace", func(t *testing.T) { - require.NoError(t, f.SetFlag("helm-namespace", "override-namespace")) - assert.Equal(t, "override-namespace", f.spec.Source.Helm.Namespace) - }) - t.Run("Helm Kube Version", func(t *testing.T) { - require.NoError(t, f.SetFlag("kustomize-kube-version", "999.999.999")) - assert.Equal(t, "999.999.999", f.spec.Source.Kustomize.KubeVersion) - }) - t.Run("Helm API Versions", func(t *testing.T) { - require.NoError(t, f.SetFlag("helm-api-versions", "v1")) - require.NoError(t, f.SetFlag("helm-api-versions", "v2")) - assert.Equal(t, []string{"v1", "v2"}, f.spec.Source.Helm.APIVersions) - }) -} - -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() - sourcePosition := 0 - sourcePosition1 := 1 - sourcePosition2 := 2 - t.Run("SyncPolicy", func(t *testing.T) { - require.NoError(t, f.SetFlagWithSourcePosition("sync-policy", "automated", sourcePosition1)) - assert.NotNil(t, f.spec.SyncPolicy.Automated) - - f.spec.SyncPolicy = nil - require.NoError(t, f.SetFlagWithSourcePosition("sync-policy", "automatic", sourcePosition1)) - assert.NotNil(t, f.spec.SyncPolicy.Automated) - }) - t.Run("Helm - SourcePosition 0", func(t *testing.T) { - require.NoError(t, f.SetFlagWithSourcePosition("helm-version", "v2", sourcePosition)) - assert.Len(t, f.spec.GetSources(), 2) - assert.Equal(t, "v2", f.spec.GetSources()[sourcePosition].Helm.Version) - }) - t.Run("Kustomize", func(t *testing.T) { - require.NoError(t, f.SetFlagWithSourcePosition("kustomize-replica", "my-deployment=2", sourcePosition1)) - assert.Equal(t, v1alpha1.KustomizeReplicas{{Name: "my-deployment", Count: intstr.FromInt(2)}}, f.spec.Sources[sourcePosition1-1].Kustomize.Replicas) - require.NoError(t, f.SetFlagWithSourcePosition("kustomize-replica", "my-deployment=4", sourcePosition2)) - assert.Equal(t, v1alpha1.KustomizeReplicas{{Name: "my-deployment", Count: intstr.FromInt(4)}}, f.spec.Sources[sourcePosition2-1].Kustomize.Replicas) - }) - t.Run("Helm", func(t *testing.T) { - require.NoError(t, f.SetFlagWithSourcePosition("helm-version", "v2", sourcePosition1)) - require.NoError(t, f.SetFlagWithSourcePosition("helm-version", "v3", sourcePosition2)) - assert.Len(t, f.spec.GetSources(), 2) - assert.Equal(t, "v2", f.spec.GetSources()[sourcePosition1-1].Helm.Version) - assert.Equal(t, "v3", f.spec.GetSources()[sourcePosition2-1].Helm.Version) + 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)}}) }) } @@ -409,11 +295,12 @@ func TestReadAppsFromURI(t *testing.T) { apps := make([]*v1alpha1.Application, 0) err = readAppsFromURI(file.Name(), &apps) - require.NoError(t, err) - assert.Len(t, apps, 2) + assert.NoError(t, err) + assert.Equal(t, 2, len(apps)) assert.Equal(t, "sth1", apps[0].Name) assert.Equal(t, "sth2", apps[1].Name) + } func TestConstructAppFromStdin(t *testing.T) { @@ -439,22 +326,25 @@ func TestConstructAppFromStdin(t *testing.T) { if err := file.Close(); err != nil { log.Fatal(err) } - require.NoError(t, err) - assert.Len(t, apps, 2) + assert.NoError(t, err) + assert.Equal(t, 2, len(apps)) assert.Equal(t, "sth1", apps[0].Name) assert.Equal(t, "sth2", apps[1].Name) + } func TestConstructBasedOnName(t *testing.T) { apps, err := ConstructApps("", "test", []string{}, []string{}, []string{}, AppOptions{}, nil) - require.NoError(t, err) - assert.Len(t, apps, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(apps)) assert.Equal(t, "test", apps[0].Name) } func TestFilterResources(t *testing.T) { + t.Run("Filter by ns", func(t *testing.T) { + resources := []*v1alpha1.ResourceDiff{ { LiveState: "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"name\":\"test-helm-guestbook\",\"namespace\":\"argocd\"},\"spec\":{\"selector\":{\"app\":\"helm-guestbook\",\"release\":\"test\"},\"sessionAffinity\":\"None\",\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}", @@ -465,11 +355,12 @@ func TestFilterResources(t *testing.T) { } filteredResources, err := FilterResources(false, resources, "g", "Service", "ns", "test-helm-guestbook", true) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, filteredResources, 1) }) t.Run("Filter by kind", func(t *testing.T) { + resources := []*v1alpha1.ResourceDiff{ { LiveState: "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"name\":\"test-helm-guestbook\",\"namespace\":\"argocd\"},\"spec\":{\"selector\":{\"app\":\"helm-guestbook\",\"release\":\"test\"},\"sessionAffinity\":\"None\",\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}", @@ -480,11 +371,12 @@ func TestFilterResources(t *testing.T) { } filteredResources, err := FilterResources(false, resources, "g", "Deployment", "argocd", "test-helm-guestbook", true) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, filteredResources, 1) }) t.Run("Filter by name", func(t *testing.T) { + resources := []*v1alpha1.ResourceDiff{ { LiveState: "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"name\":\"test-helm-guestbook\",\"namespace\":\"argocd\"},\"spec\":{\"selector\":{\"app\":\"helm-guestbook\",\"release\":\"test\"},\"sessionAffinity\":\"None\",\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}", @@ -495,7 +387,7 @@ func TestFilterResources(t *testing.T) { } filteredResources, err := FilterResources(false, resources, "g", "Service", "argocd", "test-helm", true) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, filteredResources, 1) }) @@ -510,7 +402,7 @@ func TestFilterResources(t *testing.T) { } filteredResources, err := FilterResources(false, resources, "g", "Service", "argocd-unknown", "test-helm", true) - require.ErrorContains(t, err, "No matching resource found") + assert.ErrorContains(t, err, "No matching resource found") assert.Nil(t, filteredResources) }) @@ -525,7 +417,7 @@ func TestFilterResources(t *testing.T) { } filteredResources, err := FilterResources(false, resources, "g", "Service", "argocd", "test-helm", false) - require.ErrorContains(t, err, "Use the --all flag") + assert.ErrorContains(t, err, "Use the --all flag") assert.Nil(t, filteredResources) }) } diff --git a/cmd/util/applicationset.go b/cmd/util/applicationset.go index 6f6fd1e3de4ed..2b096aa6aa036 100644 --- a/cmd/util/applicationset.go +++ b/cmd/util/applicationset.go @@ -5,10 +5,9 @@ import ( "net/url" "os" - "github.com/argoproj/gitops-engine/pkg/utils/kube" - argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/config" + "github.com/argoproj/gitops-engine/pkg/utils/kube" ) func ConstructApplicationSet(fileURL string) ([]*argoprojiov1alpha1.ApplicationSet, error) { @@ -23,13 +22,14 @@ func constructAppsetFromFileUrl(fileURL string) ([]*argoprojiov1alpha1.Applicati // read uri err := readAppsetFromURI(fileURL, &appset) if err != nil { - return nil, fmt.Errorf("error reading applicationset from file %s: %w", fileURL, err) + return nil, fmt.Errorf("error reading applicationset from file %s: %s", fileURL, err) } return appset, nil } func readAppsetFromURI(fileURL string, appset *[]*argoprojiov1alpha1.ApplicationSet) error { + readFilePayload := func() ([]byte, error) { parsedURL, err := url.ParseRequestURI(fileURL) if err != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") { @@ -59,6 +59,7 @@ func readAppset(yml []byte, appsets *[]*argoprojiov1alpha1.ApplicationSet) error return fmt.Errorf("error unmarshalling appset: %w", err) } *appsets = append(*appsets, &appset) + } // 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 0fdc0a9f899a1..c15e58a61af14 100644 --- a/cmd/util/applicationset_test.go +++ b/cmd/util/applicationset_test.go @@ -3,9 +3,8 @@ package util import ( "testing" - "github.com/stretchr/testify/assert" - argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/stretchr/testify/assert" ) var appSet = `apiVersion: argoproj.io/v1alpha1 @@ -37,5 +36,5 @@ func TestReadAppSet(t *testing.T) { if err != nil { t.Logf("Failed reading appset file") } - assert.Len(t, appSets, 1) + assert.Equal(t, len(appSets), 1) } diff --git a/cmd/util/cluster.go b/cmd/util/cluster.go index e56048660d83f..95c071c882b12 100644 --- a/cmd/util/cluster.go +++ b/cmd/util/cluster.go @@ -130,7 +130,7 @@ func GetKubePublicEndpoint(client kubernetes.Interface) (string, error) { config := &clientcmdapiv1.Config{} err = yaml.Unmarshal([]byte(kubeconfig), config) if err != nil { - return "", fmt.Errorf("failed to parse cluster-info kubeconfig: %w", err) + 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") @@ -144,7 +144,6 @@ type ClusterOptions struct { Upsert bool ServiceAccount string AwsRoleArn string - AwsProfile string AwsClusterName string SystemNamespace string Namespaces []string @@ -170,7 +169,6 @@ 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") diff --git a/cmd/util/cluster_test.go b/cmd/util/cluster_test.go index 24b46765ca686..37e05bf6e58cb 100644 --- a/cmd/util/cluster_test.go +++ b/cmd/util/cluster_test.go @@ -1,6 +1,7 @@ package util import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -52,8 +53,8 @@ func Test_newCluster(t *testing.T) { &v1alpha1.AWSAuthConfig{}, &v1alpha1.ExecProviderConfig{}, labels, nil) - assert.Contains(t, string(clusterWithFiles.Config.CertData), "test-cert-data") - assert.Contains(t, string(clusterWithFiles.Config.KeyData), "test-key-data") + assert.True(t, strings.Contains(string(clusterWithFiles.Config.CertData), "test-cert-data")) + assert.True(t, strings.Contains(string(clusterWithFiles.Config.KeyData), "test-key-data")) assert.Equal(t, "", clusterWithFiles.Config.BearerToken) assert.Equal(t, labels, clusterWithFiles.Labels) assert.Nil(t, clusterWithFiles.Annotations) @@ -159,6 +160,7 @@ func TestGetKubePublicEndpoint(t *testing.T) { } }) } + } func kubeconfigFixture(endpoint string) string { diff --git a/cmd/util/project.go b/cmd/util/project.go index b0a82883dc0bb..fa446ceb3b41c 100644 --- a/cmd/util/project.go +++ b/cmd/util/project.go @@ -11,7 +11,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" "github.com/argoproj/argo-cd/v2/pkg/apis/application" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -20,12 +20,11 @@ import ( ) type ProjectOpts struct { - Description string - destinations []string - destinationServiceAccounts []string - Sources []string - SignatureKeys []string - SourceNamespaces []string + Description string + destinations []string + Sources []string + SignatureKeys []string + SourceNamespaces []string orphanedResourcesEnabled bool orphanedResourcesWarn bool @@ -48,6 +47,7 @@ func AddProjFlags(command *cobra.Command, opts *ProjectOpts) { command.Flags().StringArrayVar(&opts.allowedNamespacedResources, "allow-namespaced-resource", []string{}, "List of allowed namespaced resources") command.Flags().StringArrayVar(&opts.deniedNamespacedResources, "deny-namespaced-resource", []string{}, "List of denied namespaced resources") command.Flags().StringSliceVar(&opts.SourceNamespaces, "source-namespaces", []string{}, "List of source namespaces for applications") + } func getGroupKindList(values []string) []v1.GroupKind { @@ -94,23 +94,6 @@ func (opts *ProjectOpts) GetDestinations() []v1alpha1.ApplicationDestination { return destinations } -func (opts *ProjectOpts) GetDestinationServiceAccounts() []v1alpha1.ApplicationDestinationServiceAccount { - destinationServiceAccounts := make([]v1alpha1.ApplicationDestinationServiceAccount, 0) - for _, destStr := range opts.destinationServiceAccounts { - parts := strings.Split(destStr, ",") - if len(parts) != 2 { - log.Fatalf("Expected destination of the form: server,namespace. Received: %s", destStr) - } else { - destinationServiceAccounts = append(destinationServiceAccounts, v1alpha1.ApplicationDestinationServiceAccount{ - Server: parts[0], - Namespace: parts[1], - DefaultServiceAccount: parts[2], - }) - } - } - return destinationServiceAccounts -} - // 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) @@ -132,7 +115,7 @@ func GetOrphanedResourcesSettings(flagSet *pflag.FlagSet, opts ProjectOpts) *v1a if opts.orphanedResourcesEnabled || warnChanged { settings := v1alpha1.OrphanedResourcesMonitorSettings{} if warnChanged { - settings.Warn = ptr.To(opts.orphanedResourcesWarn) + settings.Warn = pointer.Bool(opts.orphanedResourcesWarn) } return &settings } @@ -143,7 +126,7 @@ func readProjFromStdin(proj *v1alpha1.AppProject) error { reader := bufio.NewReader(os.Stdin) err := config.UnmarshalReader(reader, &proj) if err != nil { - return fmt.Errorf("unable to read manifest from stdin: %w", err) + return fmt.Errorf("unable to read manifest from stdin: %v", err) } return nil } @@ -184,8 +167,6 @@ func SetProjSpecOptions(flags *pflag.FlagSet, spec *v1alpha1.AppProjectSpec, pro spec.NamespaceResourceBlacklist = projOpts.GetDeniedNamespacedResources() case "source-namespaces": spec.SourceNamespaces = projOpts.GetSourceNamespaces() - case "dest-service-accounts": - spec.DestinationServiceAccounts = projOpts.GetDestinationServiceAccounts() } }) if flags.Changed("orphaned-resources") || flags.Changed("orphaned-resources-warn") { @@ -196,7 +177,7 @@ func SetProjSpecOptions(flags *pflag.FlagSet, spec *v1alpha1.AppProjectSpec, pro } func ConstructAppProj(fileURL string, args []string, opts ProjectOpts, c *cobra.Command) (*v1alpha1.AppProject, error) { - proj := v1alpha1.AppProject{ + var proj = v1alpha1.AppProject{ TypeMeta: v1.TypeMeta{ Kind: application.AppProjectKind, APIVersion: application.Group + "/v1alpha1", diff --git a/cmd/util/repo.go b/cmd/util/repo.go index 6b822c6309f70..b60c30a071311 100644 --- a/cmd/util/repo.go +++ b/cmd/util/repo.go @@ -22,7 +22,6 @@ type RepoOptions struct { GithubAppPrivateKeyPath string GitHubAppEnterpriseBaseURL string Proxy string - NoProxy string GCPServiceAccountKeyPath string ForceHttpBasicAuth bool } @@ -45,7 +44,6 @@ func AddRepoFlags(command *cobra.Command, opts *RepoOptions) { command.Flags().StringVar(&opts.GithubAppPrivateKeyPath, "github-app-private-key-path", "", "private key of the GitHub Application") 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.Proxy, "no-proxy", "", "don't access these targets via proxy") 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 8e3ff9b8565e3..025625ff8092e 100644 --- a/cmpserver/apiclient/clientset.go +++ b/cmpserver/apiclient/clientset.go @@ -2,12 +2,8 @@ package apiclient import ( "context" - "math" "time" - "github.com/argoproj/argo-cd/v2/common" - "github.com/argoproj/argo-cd/v2/util/env" - grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" log "github.com/sirupsen/logrus" @@ -18,8 +14,10 @@ import ( "github.com/argoproj/argo-cd/v2/util/io" ) -// MaxGRPCMessageSize contains max grpc message size -var MaxGRPCMessageSize = env.ParseNumFromEnv(common.EnvGRPCMaxSizeMB, 100, 0, math.MaxInt32) * 1024 * 1024 +const ( + // MaxGRPCMessageSize contains max grpc message size + MaxGRPCMessageSize = 100 * 1024 * 1024 +) // Clientset represents config management plugin server api clients type Clientset interface { diff --git a/cmpserver/apiclient/plugin.pb.go b/cmpserver/apiclient/plugin.pb.go index b6fb8fca109b9..29ebca3ae3afc 100644 --- a/cmpserver/apiclient/plugin.pb.go +++ b/cmpserver/apiclient/plugin.pb.go @@ -11,7 +11,6 @@ import ( grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" - emptypb "google.golang.org/protobuf/types/known/emptypb" io "io" math "math" math_bits "math/bits" @@ -468,54 +467,6 @@ func (m *File) GetChunk() []byte { return nil } -// CheckPluginConfigurationResponse contains a list of plugin configuration flags. -type CheckPluginConfigurationResponse struct { - IsDiscoveryConfigured bool `protobuf:"varint,1,opt,name=isDiscoveryConfigured,proto3" json:"isDiscoveryConfigured,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CheckPluginConfigurationResponse) Reset() { *m = CheckPluginConfigurationResponse{} } -func (m *CheckPluginConfigurationResponse) String() string { return proto.CompactTextString(m) } -func (*CheckPluginConfigurationResponse) ProtoMessage() {} -func (*CheckPluginConfigurationResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b21875a7079a06ed, []int{7} -} -func (m *CheckPluginConfigurationResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *CheckPluginConfigurationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_CheckPluginConfigurationResponse.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 *CheckPluginConfigurationResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_CheckPluginConfigurationResponse.Merge(m, src) -} -func (m *CheckPluginConfigurationResponse) XXX_Size() int { - return m.Size() -} -func (m *CheckPluginConfigurationResponse) XXX_DiscardUnknown() { - xxx_messageInfo_CheckPluginConfigurationResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_CheckPluginConfigurationResponse proto.InternalMessageInfo - -func (m *CheckPluginConfigurationResponse) GetIsDiscoveryConfigured() bool { - if m != nil { - return m.IsDiscoveryConfigured - } - return false -} - func init() { proto.RegisterType((*AppStreamRequest)(nil), "plugin.AppStreamRequest") proto.RegisterType((*ManifestRequestMetadata)(nil), "plugin.ManifestRequestMetadata") @@ -524,54 +475,48 @@ func init() { proto.RegisterType((*RepositoryResponse)(nil), "plugin.RepositoryResponse") proto.RegisterType((*ParametersAnnouncementResponse)(nil), "plugin.ParametersAnnouncementResponse") proto.RegisterType((*File)(nil), "plugin.File") - proto.RegisterType((*CheckPluginConfigurationResponse)(nil), "plugin.CheckPluginConfigurationResponse") } func init() { proto.RegisterFile("cmpserver/plugin/plugin.proto", fileDescriptor_b21875a7079a06ed) } var fileDescriptor_b21875a7079a06ed = []byte{ - // 650 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0xc1, 0x6e, 0xd3, 0x4c, - 0x10, 0x8e, 0x9b, 0xb4, 0x4d, 0x26, 0x95, 0xfe, 0x68, 0xf5, 0x53, 0x4c, 0x68, 0x43, 0xf0, 0x01, - 0xe5, 0x82, 0x23, 0x85, 0x5e, 0x91, 0x68, 0x4b, 0x68, 0x05, 0x0a, 0x8a, 0xb6, 0x1c, 0x80, 0x03, - 0xd2, 0xc6, 0x99, 0x24, 0x4b, 0xed, 0xdd, 0x65, 0xbd, 0x8e, 0x14, 0xb8, 0xf0, 0x36, 0xbc, 0x0a, - 0x47, 0x1e, 0x01, 0xf5, 0x35, 0xb8, 0x20, 0xaf, 0xed, 0x24, 0xa2, 0x69, 0x7b, 0xf2, 0xcc, 0x7c, - 0xb3, 0xdf, 0x7e, 0xb3, 0x33, 0x63, 0x38, 0x0c, 0x22, 0x15, 0xa3, 0x9e, 0xa3, 0xee, 0xaa, 0x30, - 0x99, 0x72, 0x91, 0x7f, 0x7c, 0xa5, 0xa5, 0x91, 0x64, 0x27, 0xf3, 0x9a, 0xfd, 0x29, 0x37, 0xb3, - 0x64, 0xe4, 0x07, 0x32, 0xea, 0x32, 0x3d, 0x95, 0x4a, 0xcb, 0xcf, 0xd6, 0x78, 0x1a, 0x8c, 0xbb, - 0xf3, 0x5e, 0x57, 0xa3, 0x92, 0x39, 0x8d, 0x35, 0xb9, 0x91, 0x7a, 0xb1, 0x66, 0x66, 0x74, 0xcd, - 0x87, 0x53, 0x29, 0xa7, 0x21, 0x76, 0xad, 0x37, 0x4a, 0x26, 0x5d, 0x8c, 0x94, 0xc9, 0x41, 0xef, - 0xbb, 0x03, 0x8d, 0x63, 0xa5, 0x2e, 0x8c, 0x46, 0x16, 0x51, 0xfc, 0x92, 0x60, 0x6c, 0xc8, 0x73, - 0xa8, 0x46, 0x68, 0xd8, 0x98, 0x19, 0xe6, 0x3a, 0x6d, 0xa7, 0x53, 0xef, 0x3d, 0xf2, 0x73, 0x85, - 0x03, 0x26, 0xf8, 0x04, 0x63, 0x93, 0xa7, 0x0e, 0xf2, 0xb4, 0xf3, 0x12, 0x5d, 0x1e, 0x21, 0x1e, - 0x54, 0x26, 0x3c, 0x44, 0x77, 0xcb, 0x1e, 0xdd, 0x2b, 0x8e, 0xbe, 0xe2, 0x21, 0x9e, 0x97, 0xa8, - 0xc5, 0x4e, 0x6a, 0xb0, 0xab, 0x33, 0x0a, 0xef, 0x87, 0x03, 0xf7, 0x6f, 0xa0, 0x25, 0x2e, 0xec, - 0x32, 0xa5, 0xde, 0xb2, 0x08, 0xad, 0x90, 0x1a, 0x2d, 0x5c, 0xd2, 0x02, 0x60, 0x4a, 0x51, 0x0c, - 0x87, 0xcc, 0xcc, 0xec, 0x55, 0x35, 0xba, 0x16, 0x21, 0x4d, 0xa8, 0x06, 0x33, 0x0c, 0x2e, 0xe3, - 0x24, 0x72, 0xcb, 0x16, 0x5d, 0xfa, 0x84, 0x40, 0x25, 0xe6, 0x5f, 0xd1, 0xad, 0xb4, 0x9d, 0x4e, - 0x99, 0x5a, 0x9b, 0x78, 0x50, 0x46, 0x31, 0x77, 0xb7, 0xdb, 0xe5, 0x4e, 0xbd, 0xd7, 0x28, 0x34, - 0xf7, 0xc5, 0xbc, 0x2f, 0x8c, 0x5e, 0xd0, 0x14, 0xf4, 0x8e, 0xa0, 0x5a, 0x04, 0x52, 0x0e, 0xb1, - 0x92, 0x65, 0x6d, 0xf2, 0x3f, 0x6c, 0xcf, 0x59, 0x98, 0x60, 0x2e, 0x27, 0x73, 0xbc, 0x21, 0x34, - 0x56, 0xe5, 0xc5, 0x4a, 0x8a, 0x18, 0xc9, 0x01, 0xd4, 0xa2, 0x3c, 0x16, 0xbb, 0x4e, 0xbb, 0xdc, - 0xa9, 0xd1, 0x55, 0x20, 0xad, 0x2d, 0x96, 0x89, 0x0e, 0xf0, 0xdd, 0x42, 0x15, 0x64, 0x6b, 0x11, - 0x6f, 0x02, 0x84, 0x2e, 0xbb, 0xbc, 0xe4, 0x6c, 0x43, 0x9d, 0xc7, 0x17, 0x89, 0x52, 0x52, 0x1b, - 0x1c, 0x5b, 0x61, 0x55, 0xba, 0x1e, 0x22, 0x3e, 0x10, 0x1e, 0xbf, 0xe4, 0x71, 0x20, 0xe7, 0xa8, - 0x17, 0x7d, 0xc1, 0x46, 0x21, 0x8e, 0x2d, 0x7f, 0x95, 0x6e, 0x40, 0xbc, 0x6f, 0xd0, 0x1a, 0x32, - 0xcd, 0x22, 0x34, 0xa8, 0xe3, 0x63, 0x21, 0x64, 0x22, 0x02, 0x8c, 0x50, 0xac, 0xea, 0xf8, 0x00, - 0xfb, 0xaa, 0xc8, 0x58, 0x4f, 0xc8, 0x8a, 0xaa, 0xf7, 0x1e, 0xfb, 0x6b, 0xe3, 0x38, 0xdc, 0x94, - 0x49, 0x6f, 0x20, 0xf0, 0x0e, 0xa0, 0x92, 0x4e, 0x4c, 0xfa, 0xa8, 0xc1, 0x2c, 0x11, 0x97, 0xb6, - 0xa0, 0x3d, 0x9a, 0x39, 0xde, 0x7b, 0x68, 0x9f, 0xa6, 0xed, 0x1c, 0xda, 0x3e, 0x9d, 0x4a, 0x31, - 0xe1, 0xd3, 0x44, 0x33, 0xc3, 0xa5, 0x58, 0x8a, 0x3b, 0x82, 0x7b, 0x6b, 0x45, 0x15, 0x39, 0xcb, - 0xa7, 0xd9, 0x0c, 0xf6, 0xfe, 0x6c, 0xc1, 0x61, 0xe6, 0x0e, 0x98, 0x60, 0x53, 0xab, 0x26, 0xbb, - 0xe5, 0x02, 0xf5, 0x9c, 0x07, 0x48, 0x5e, 0x43, 0xe3, 0x0c, 0x05, 0x6a, 0x66, 0xb0, 0x68, 0x2c, - 0x71, 0x8b, 0x89, 0xf9, 0x77, 0x99, 0x9a, 0xee, 0xf5, 0xd5, 0xc9, 0xf4, 0x79, 0xa5, 0x8e, 0x43, - 0x3e, 0x81, 0x7b, 0x53, 0x1d, 0x64, 0xdf, 0xcf, 0x36, 0xd7, 0x2f, 0x36, 0xd7, 0xef, 0xa7, 0x9b, - 0xdb, 0xec, 0x14, 0x8c, 0x77, 0xbd, 0x80, 0x57, 0x22, 0x6f, 0xe0, 0xbf, 0x01, 0x33, 0xc1, 0x6c, - 0x35, 0x2f, 0xb7, 0x48, 0x6d, 0x16, 0xc8, 0xf5, 0xe9, 0xb2, 0x62, 0x19, 0x3c, 0x38, 0x43, 0xb3, - 0x79, 0x24, 0x6e, 0xa1, 0x7d, 0x52, 0x20, 0xb7, 0x0f, 0x53, 0x7a, 0xc5, 0xc9, 0x8b, 0x9f, 0x57, - 0x2d, 0xe7, 0xd7, 0x55, 0xcb, 0xf9, 0x7d, 0xd5, 0x72, 0x3e, 0xf6, 0xee, 0xf8, 0x03, 0xae, 0xfe, - 0xa3, 0x4c, 0xf1, 0x20, 0xe4, 0x28, 0xcc, 0x68, 0xc7, 0xbe, 0xd6, 0xb3, 0xbf, 0x01, 0x00, 0x00, - 0xff, 0xff, 0xb5, 0x6b, 0xca, 0xa6, 0x65, 0x05, 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. @@ -589,9 +534,6 @@ type ConfigManagementPluginServiceClient interface { // GenerateManifests receive a stream containing a tgz archive with all required files necessary // to generate manifests GenerateManifest(ctx context.Context, opts ...grpc.CallOption) (ConfigManagementPluginService_GenerateManifestClient, error) - // CheckPluginConfiguration is a pre-flight request to check the plugin configuration - // without sending the whole repo. - CheckPluginConfiguration(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*CheckPluginConfigurationResponse, error) // MatchRepository returns whether or not the given application is supported by the plugin MatchRepository(ctx context.Context, opts ...grpc.CallOption) (ConfigManagementPluginService_MatchRepositoryClient, error) // GetParametersAnnouncement gets a list of parameter announcements for the given app @@ -640,15 +582,6 @@ func (x *configManagementPluginServiceGenerateManifestClient) CloseAndRecv() (*M return m, nil } -func (c *configManagementPluginServiceClient) CheckPluginConfiguration(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*CheckPluginConfigurationResponse, error) { - out := new(CheckPluginConfigurationResponse) - err := c.cc.Invoke(ctx, "/plugin.ConfigManagementPluginService/CheckPluginConfiguration", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *configManagementPluginServiceClient) MatchRepository(ctx context.Context, opts ...grpc.CallOption) (ConfigManagementPluginService_MatchRepositoryClient, error) { stream, err := c.cc.NewStream(ctx, &_ConfigManagementPluginService_serviceDesc.Streams[1], "/plugin.ConfigManagementPluginService/MatchRepository", opts...) if err != nil { @@ -722,9 +655,6 @@ type ConfigManagementPluginServiceServer interface { // GenerateManifests receive a stream containing a tgz archive with all required files necessary // to generate manifests GenerateManifest(ConfigManagementPluginService_GenerateManifestServer) error - // CheckPluginConfiguration is a pre-flight request to check the plugin configuration - // without sending the whole repo. - CheckPluginConfiguration(context.Context, *emptypb.Empty) (*CheckPluginConfigurationResponse, error) // MatchRepository returns whether or not the given application is supported by the plugin MatchRepository(ConfigManagementPluginService_MatchRepositoryServer) error // GetParametersAnnouncement gets a list of parameter announcements for the given app @@ -738,9 +668,6 @@ type UnimplementedConfigManagementPluginServiceServer struct { func (*UnimplementedConfigManagementPluginServiceServer) GenerateManifest(srv ConfigManagementPluginService_GenerateManifestServer) error { return status.Errorf(codes.Unimplemented, "method GenerateManifest not implemented") } -func (*UnimplementedConfigManagementPluginServiceServer) CheckPluginConfiguration(ctx context.Context, req *emptypb.Empty) (*CheckPluginConfigurationResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CheckPluginConfiguration not implemented") -} func (*UnimplementedConfigManagementPluginServiceServer) MatchRepository(srv ConfigManagementPluginService_MatchRepositoryServer) error { return status.Errorf(codes.Unimplemented, "method MatchRepository not implemented") } @@ -778,24 +705,6 @@ func (x *configManagementPluginServiceGenerateManifestServer) Recv() (*AppStream return m, nil } -func _ConfigManagementPluginService_CheckPluginConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(emptypb.Empty) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ConfigManagementPluginServiceServer).CheckPluginConfiguration(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/plugin.ConfigManagementPluginService/CheckPluginConfiguration", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ConfigManagementPluginServiceServer).CheckPluginConfiguration(ctx, req.(*emptypb.Empty)) - } - return interceptor(ctx, in, info, handler) -} - func _ConfigManagementPluginService_MatchRepository_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(ConfigManagementPluginServiceServer).MatchRepository(&configManagementPluginServiceMatchRepositoryServer{stream}) } @@ -851,12 +760,7 @@ func (x *configManagementPluginServiceGetParametersAnnouncementServer) Recv() (* var _ConfigManagementPluginService_serviceDesc = grpc.ServiceDesc{ ServiceName: "plugin.ConfigManagementPluginService", HandlerType: (*ConfigManagementPluginServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "CheckPluginConfiguration", - Handler: _ConfigManagementPluginService_CheckPluginConfiguration_Handler, - }, - }, + Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "GenerateManifest", @@ -1228,43 +1132,6 @@ func (m *File) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *CheckPluginConfigurationResponse) 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 *CheckPluginConfigurationResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *CheckPluginConfigurationResponse) 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.IsDiscoveryConfigured { - i-- - if m.IsDiscoveryConfigured { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - func encodeVarintPlugin(dAtA []byte, offset int, v uint64) int { offset -= sovPlugin(v) base := offset @@ -1442,21 +1309,6 @@ func (m *File) Size() (n int) { return n } -func (m *CheckPluginConfigurationResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.IsDiscoveryConfigured { - n += 2 - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - func sovPlugin(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2275,77 +2127,6 @@ func (m *File) Unmarshal(dAtA []byte) error { } return nil } -func (m *CheckPluginConfigurationResponse) 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 ErrIntOverflowPlugin - } - 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: CheckPluginConfigurationResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CheckPluginConfigurationResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field IsDiscoveryConfigured", 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.IsDiscoveryConfigured = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipPlugin(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthPlugin - } - 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 skipPlugin(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/cmpserver/plugin/config_test.go b/cmpserver/plugin/config_test.go index db08e92a2f563..9e22dab1d3741 100644 --- a/cmpserver/plugin/config_test.go +++ b/cmpserver/plugin/config_test.go @@ -155,13 +155,13 @@ spec: require.NoError(t, err) err = tempFile.Close() require.NoError(t, err) - err = os.WriteFile(tempFile.Name(), []byte(tcc.fileContents), 0o644) + err = os.WriteFile(tempFile.Name(), []byte(tcc.fileContents), 0644) require.NoError(t, err) config, err := ReadPluginConfig(tempDir) if tcc.expectedErr != "" { - require.EqualError(t, err, tcc.expectedErr) + assert.EqualError(t, err, tcc.expectedErr) } else { - require.NoError(t, err) + assert.NoError(t, err) } assert.Equal(t, tcc.expected, config) }) diff --git a/cmpserver/plugin/plugin.go b/cmpserver/plugin/plugin.go index ee00f66ad9ecf..ca1e7592218ea 100644 --- a/cmpserver/plugin/plugin.go +++ b/cmpserver/plugin/plugin.go @@ -9,22 +9,22 @@ import ( "os" "os/exec" "path/filepath" + "strconv" "strings" "time" + "unicode" "github.com/argoproj/pkg/rand" - "github.com/golang/protobuf/ptypes/empty" "github.com/argoproj/argo-cd/v2/cmpserver/apiclient" "github.com/argoproj/argo-cd/v2/common" repoclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/util/buffered_context" "github.com/argoproj/argo-cd/v2/util/cmp" - argoexec "github.com/argoproj/argo-cd/v2/util/exec" "github.com/argoproj/argo-cd/v2/util/io/files" "github.com/argoproj/gitops-engine/pkg/utils/kube" - securejoin "github.com/cyphar/filepath-securejoin" + "github.com/cyphar/filepath-securejoin" "github.com/mattn/go-zglob" log "github.com/sirupsen/logrus" ) @@ -54,7 +54,7 @@ func (s *Service) Init(workDir string) error { if err != nil { return fmt.Errorf("error removing workdir %q: %w", workDir, err) } - err = os.MkdirAll(workDir, 0o700) + err = os.MkdirAll(workDir, 0700) if err != nil { return fmt.Errorf("error creating workdir %q: %w", workDir, err) } @@ -76,7 +76,7 @@ func runCommand(ctx context.Context, command Command, path string, env []string) } logCtx := log.WithFields(log.Fields{"execID": execId}) - argsToLog := argoexec.GetCommandArgsToLog(cmd) + argsToLog := getCommandArgsToLog(cmd) logCtx.WithFields(log.Fields{"dir": cmd.Dir}).Info(argsToLog) var stdout bytes.Buffer @@ -128,13 +128,35 @@ func runCommand(ctx context.Context, command Command, path string, env []string) if len(output) == 0 { logCtx.Warn("Plugin command returned zero output") } else { - // Log stderr even on successful commands to help develop plugins - logCtx.Info("Plugin command successful") + // 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 @@ -218,9 +240,6 @@ func (s *Service) generateManifestGeneric(stream GenerateManifestStream) error { if err != nil { return fmt.Errorf("error generating manifests: %w", err) } - - log.Tracef("Generated manifests result: %s", response.Manifests) - err = stream.SendAndClose(response) if err != nil { return fmt.Errorf("error sending manifest response: %w", err) @@ -424,15 +443,3 @@ func getParametersAnnouncement(ctx context.Context, appDir string, announcements } return repoResponse, nil } - -func (s *Service) CheckPluginConfiguration(ctx context.Context, _ *empty.Empty) (*apiclient.CheckPluginConfigurationResponse, error) { - isDiscoveryConfigured := s.isDiscoveryConfigured() - response := &apiclient.CheckPluginConfigurationResponse{IsDiscoveryConfigured: isDiscoveryConfigured} - - return response, nil -} - -func (s *Service) isDiscoveryConfigured() (isDiscoveryConfigured bool) { - config := s.initConstants.PluginConfig - return config.Spec.Discover.FileName != "" || config.Spec.Discover.Find.Glob != "" || len(config.Spec.Discover.Find.Command.Command) > 0 -} diff --git a/cmpserver/plugin/plugin.proto b/cmpserver/plugin/plugin.proto index 6f5b0d0cbf7b6..16d4268d5939f 100644 --- a/cmpserver/plugin/plugin.proto +++ b/cmpserver/plugin/plugin.proto @@ -4,7 +4,6 @@ option go_package = "github.com/argoproj/argo-cd/v2/cmpserver/apiclient"; package plugin; import "github.com/argoproj/argo-cd/v2/reposerver/repository/repository.proto"; -import "google/protobuf/empty.proto"; // AppStreamRequest is the request object used to send the application's // files over a stream. @@ -58,11 +57,6 @@ message File { bytes chunk = 1; } -// CheckPluginConfigurationResponse contains a list of plugin configuration flags. -message CheckPluginConfigurationResponse { - bool isDiscoveryConfigured = 1; -} - // ConfigManagementPlugin Service service ConfigManagementPluginService { // GenerateManifests receive a stream containing a tgz archive with all required files necessary @@ -70,11 +64,6 @@ service ConfigManagementPluginService { rpc GenerateManifest(stream AppStreamRequest) returns (ManifestResponse) { } - // CheckPluginConfiguration is a pre-flight request to check the plugin configuration - // without sending the whole repo. - rpc CheckPluginConfiguration(google.protobuf.Empty) returns (CheckPluginConfigurationResponse) { - } - // MatchRepository returns whether or not the given application is supported by the plugin rpc MatchRepository(stream AppStreamRequest) returns (RepositoryResponse) { } diff --git a/cmpserver/plugin/plugin_test.go b/cmpserver/plugin/plugin_test.go index 05001c31b3837..b253dc414cbdc 100644 --- a/cmpserver/plugin/plugin_test.go +++ b/cmpserver/plugin/plugin_test.go @@ -6,12 +6,12 @@ import ( "fmt" "io" "os" + "os/exec" "path" "path/filepath" "testing" "time" - "github.com/golang/protobuf/ptypes/empty" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/metadata" @@ -103,7 +103,7 @@ func TestMatchRepository(t *testing.T) { match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, match) assert.True(t, discovery) }) @@ -118,7 +118,7 @@ func TestMatchRepository(t *testing.T) { match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, match) assert.True(t, discovery) }) @@ -133,7 +133,7 @@ func TestMatchRepository(t *testing.T) { _, _, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.ErrorContains(t, err, "syntax error") + assert.ErrorContains(t, err, "syntax error") }) t.Run("will match plugin by glob", func(t *testing.T) { // given @@ -148,7 +148,7 @@ func TestMatchRepository(t *testing.T) { match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, match) assert.True(t, discovery) }) @@ -165,7 +165,7 @@ func TestMatchRepository(t *testing.T) { match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, match) assert.True(t, discovery) }) @@ -182,7 +182,7 @@ func TestMatchRepository(t *testing.T) { _, _, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.ErrorContains(t, err, "error finding glob match for pattern") + assert.ErrorContains(t, err, "error finding glob match for pattern") }) t.Run("will match plugin by command when returns any output", func(t *testing.T) { // given @@ -199,7 +199,7 @@ func TestMatchRepository(t *testing.T) { match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, match) assert.True(t, discovery) }) @@ -217,7 +217,7 @@ func TestMatchRepository(t *testing.T) { // when match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, match) assert.True(t, discovery) }) @@ -236,7 +236,7 @@ func TestMatchRepository(t *testing.T) { match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, match) assert.True(t, discovery) }) @@ -256,7 +256,7 @@ func TestMatchRepository(t *testing.T) { match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, match) assert.True(t, discovery) }) @@ -275,7 +275,7 @@ func TestMatchRepository(t *testing.T) { match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.Error(t, err) + assert.Error(t, err) assert.False(t, match) assert.True(t, discovery) }) @@ -288,7 +288,7 @@ func TestMatchRepository(t *testing.T) { match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, match) assert.False(t, discovery) }) @@ -323,7 +323,7 @@ func TestGenerateManifest(t *testing.T) { service.WithGenerateCommand(Command{Command: []string{"bad-command"}}) res, err := service.generateManifest(context.Background(), "testdata/kustomize", nil) - require.ErrorContains(t, err, "executable file not found") + assert.ErrorContains(t, err, "executable file not found") assert.Nil(t, res.Manifests) }) t.Run("bad yaml output", func(t *testing.T) { @@ -332,7 +332,7 @@ func TestGenerateManifest(t *testing.T) { service.WithGenerateCommand(Command{Command: []string{"echo", "invalid yaml: }"}}) res, err := service.generateManifest(context.Background(), "testdata/kustomize", nil) - require.ErrorContains(t, err, "failed to unmarshal manifest") + assert.ErrorContains(t, err, "failed to unmarshal manifest") assert.Nil(t, res.Manifests) }) } @@ -345,7 +345,7 @@ func TestGenerateManifest_deadline_exceeded(t *testing.T) { expiredCtx, cancel := context.WithTimeout(context.Background(), time.Second*0) defer cancel() _, err = service.generateManifest(expiredCtx, "", nil) - require.ErrorContains(t, err, "context deadline exceeded") + assert.ErrorContains(t, err, "context deadline exceeded") } // TestRunCommandContextTimeout makes sure the command dies at timeout rather than sleeping past the timeout. @@ -360,16 +360,16 @@ func TestRunCommandContextTimeout(t *testing.T) { before := time.Now() _, err := runCommand(ctx, command, "", []string{}) after := time.Now() - require.Error(t, err) // The command should time out, causing an error. + assert.Error(t, err) // The command should time out, causing an error. assert.Less(t, after.Sub(before), 1*time.Second) } func TestRunCommandEmptyCommand(t *testing.T) { _, err := runCommand(context.Background(), Command{}, "", nil) - require.ErrorContains(t, err, "Command is empty") + assert.ErrorContains(t, err, "Command is empty") } -// TestRunCommandContextTimeoutWithCleanup makes sure that the process is given enough time to cleanup before sending SIGKILL. +// 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() @@ -385,7 +385,7 @@ func TestRunCommandContextTimeoutWithCleanup(t *testing.T) { output, err := runCommand(ctx, command, "", []string{}) after := time.Now() - require.Error(t, err) // The command should time out, causing an error. + 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") @@ -451,7 +451,7 @@ func Test_getParametersAnnouncement_invalid_json(t *testing.T) { Args: []string{`[`}, } _, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command, []*apiclient.EnvEntry{}) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "unexpected end of JSON input") } @@ -461,7 +461,7 @@ func Test_getParametersAnnouncement_bad_command(t *testing.T) { Args: []string{"1"}, } _, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command, []*apiclient.EnvEntry{}) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "error executing dynamic parameter output command") } @@ -469,12 +469,12 @@ func Test_getTempDirMustCleanup(t *testing.T) { tempDir := t.TempDir() // Induce a directory create error to verify error handling. - err := os.Chmod(tempDir, 0o000) + err := os.Chmod(tempDir, 0000) require.NoError(t, err) _, _, err = getTempDirMustCleanup(path.Join(tempDir, "test")) - require.ErrorContains(t, err, "error creating temp dir") + assert.ErrorContains(t, err, "error creating temp dir") - err = os.Chmod(tempDir, 0o700) + err = os.Chmod(tempDir, 0700) require.NoError(t, err) workDir, cleanup, err := getTempDirMustCleanup(tempDir) require.NoError(t, err) @@ -487,7 +487,7 @@ func TestService_Init(t *testing.T) { // Set up a base directory containing a test directory and a test file. tempDir := t.TempDir() workDir := path.Join(tempDir, "workDir") - err := os.MkdirAll(workDir, 0o700) + err := os.MkdirAll(workDir, 0700) require.NoError(t, err) testfile := path.Join(workDir, "testfile") file, err := os.Create(testfile) @@ -496,17 +496,17 @@ func TestService_Init(t *testing.T) { require.NoError(t, err) // Make the base directory read-only so Init's cleanup fails. - err = os.Chmod(tempDir, 0o000) + err = os.Chmod(tempDir, 0000) require.NoError(t, err) s := NewService(CMPServerInitConstants{PluginConfig: PluginConfig{}}) err = s.Init(workDir) - require.ErrorContains(t, err, "error removing workdir", "Init must throw an error if it can't remove the work directory") + assert.ErrorContains(t, err, "error removing workdir", "Init must throw an error if it can't remove the work directory") // Make the base directory writable so Init's cleanup succeeds. - err = os.Chmod(tempDir, 0o700) + err = os.Chmod(tempDir, 0700) require.NoError(t, err) err = s.Init(workDir) - require.NoError(t, err) + assert.NoError(t, err) assert.DirExists(t, workDir) assert.NoFileExists(t, testfile) } @@ -532,76 +532,6 @@ func TestEnviron(t *testing.T) { }) } -func TestIsDiscoveryConfigured(t *testing.T) { - type fixture struct { - service *Service - } - setup := func(t *testing.T, opts ...pluginOpt) *fixture { - t.Helper() - cic := buildPluginConfig(opts...) - s := NewService(*cic) - return &fixture{ - service: s, - } - } - t.Run("discovery is enabled when is configured by FileName", func(t *testing.T) { - // given - d := Discover{ - FileName: "kustomization.yaml", - } - f := setup(t, withDiscover(d)) - - // when - isDiscoveryConfigured := f.service.isDiscoveryConfigured() - - // then - assert.True(t, isDiscoveryConfigured) - }) - t.Run("discovery is enabled when is configured by Glob", func(t *testing.T) { - // given - d := Discover{ - Find: Find{ - Glob: "**/*/plugin.yaml", - }, - } - f := setup(t, withDiscover(d)) - - // when - isDiscoveryConfigured := f.service.isDiscoveryConfigured() - - // then - assert.True(t, isDiscoveryConfigured) - }) - t.Run("discovery is enabled when is configured by Command", func(t *testing.T) { - // given - d := Discover{ - Find: Find{ - Command: Command{ - Command: []string{"echo", "test"}, - }, - }, - } - f := setup(t, withDiscover(d)) - - // when - isDiscoveryConfigured := f.service.isDiscoveryConfigured() - - // then - assert.True(t, isDiscoveryConfigured) - }) - t.Run("discovery is disabled when discover is not configured", func(t *testing.T) { - // given - d := Discover{} - f := setup(t, withDiscover(d)) - - // when - isDiscoveryConfigured := f.service.isDiscoveryConfigured() - - // then - assert.False(t, isDiscoveryConfigured) - }) -} - type MockGenerateManifestStream struct { metadataSent bool fileSent bool @@ -849,43 +779,29 @@ func TestService_GetParametersAnnouncement(t *testing.T) { }) } -func TestService_CheckPluginConfiguration(t *testing.T) { - type fixture struct { - service *Service - } - setup := func(t *testing.T, opts ...pluginOpt) *fixture { - t.Helper() - cic := buildPluginConfig(opts...) - s := NewService(*cic) - return &fixture{ - service: s, - } +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\""`, + }, } - t.Run("discovery is enabled when is configured", func(t *testing.T) { - // given - d := Discover{ - FileName: "kustomization.yaml", - } - f := setup(t, withDiscover(d)) - - // when - resp, err := f.service.CheckPluginConfiguration(context.Background(), &empty.Empty{}) - // then - require.NoError(t, err) - assert.True(t, resp.IsDiscoveryConfigured) - }) - - t.Run("discovery is disabled when is not configured", func(t *testing.T) { - // given - d := Discover{} - f := setup(t, withDiscover(d)) - - // when - resp, err := f.service.CheckPluginConfiguration(context.Background(), &empty.Empty{}) - - // then - require.NoError(t, err) - assert.False(t, resp.IsDiscoveryConfigured) - }) + 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/server.go b/cmpserver/server.go index 5d7eacd2fd35f..13abb1c02aed0 100644 --- a/cmpserver/server.go +++ b/cmpserver/server.go @@ -18,8 +18,6 @@ import ( "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/reflection" - "google.golang.org/grpc/keepalive" - "github.com/argoproj/argo-cd/v2/cmpserver/apiclient" "github.com/argoproj/argo-cd/v2/cmpserver/plugin" "github.com/argoproj/argo-cd/v2/common" @@ -27,6 +25,7 @@ import ( "github.com/argoproj/argo-cd/v2/server/version" "github.com/argoproj/argo-cd/v2/util/errors" grpc_util "github.com/argoproj/argo-cd/v2/util/grpc" + "google.golang.org/grpc/keepalive" ) // ArgoCDCMPServer is the config management plugin server implementation @@ -111,7 +110,7 @@ func (a *ArgoCDCMPServer) CreateGRPC() (*grpc.Server, error) { pluginService := plugin.NewService(a.initConstants) err := pluginService.Init(common.GetCMPWorkDir()) if err != nil { - return nil, fmt.Errorf("error initializing plugin service: %w", err) + return nil, fmt.Errorf("error initializing plugin service: %s", err) } apiclient.RegisterConfigManagementPluginServiceServer(server, pluginService) diff --git a/common/common.go b/common/common.go index 79fcdba195eb6..2f053d7a28198 100644 --- a/common/common.go +++ b/common/common.go @@ -1,20 +1,15 @@ package common import ( - "context" - "fmt" + "errors" "os" "path/filepath" "strconv" "time" - "github.com/pkg/errors" - "github.com/redis/go-redis/v9" "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" ) // Component names @@ -46,7 +41,6 @@ const ( ArgoCDGPGKeysConfigMapName = "argocd-gpg-keys-cm" // ArgoCDAppControllerShardConfigMapName contains the application controller to shard mapping ArgoCDAppControllerShardConfigMapName = "argocd-app-controller-shard-cm" - ArgoCDCmdParamsConfigMapName = "argocd-cmd-params-cm" ) // Some default configurables @@ -119,17 +113,11 @@ const ( // 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 across all shards + // 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 - - // ConsistentHashingWithBoundedLoadsAlgorithm uses an algorithm that tries to use an equal distribution across - // all shards but is optimised to handle sharding and/or cluster addition or removal. In case of sharding or - // cluster changes, this algorithm minimises the changes between shard and clusters assignments. - ConsistentHashingWithBoundedLoadsAlgorithm = "consistent-hashing" - - DefaultShardingAlgorithm = LegacyShardingAlgorithm + DefaultShardingAlgorithm = LegacyShardingAlgorithm ) // Dex related constants @@ -161,14 +149,10 @@ const ( 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 @@ -200,10 +184,6 @@ const ( // 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 @@ -218,11 +198,11 @@ const ( EnvVarTLSDataPath = "ARGOCD_TLS_DATA_PATH" // EnvGitAttemptsCount specifies number of git remote operations attempts count EnvGitAttemptsCount = "ARGOCD_GIT_ATTEMPTS_COUNT" - // EnvGitRetryMaxDuration specifies max duration of git remote operation retry + // EnvGitRetryMaxDuration specifices max duration of git remote operation retry EnvGitRetryMaxDuration = "ARGOCD_GIT_RETRY_MAX_DURATION" // EnvGitRetryDuration specifies duration of git remote operation retry EnvGitRetryDuration = "ARGOCD_GIT_RETRY_DURATION" - // EnvGitRetryFactor specifies factor of git remote operation retry + // EnvGitRetryFactor specifies fator of git remote operation retry EnvGitRetryFactor = "ARGOCD_GIT_RETRY_FACTOR" // EnvGitSubmoduleEnabled overrides git submodule support, true by default EnvGitSubmoduleEnabled = "ARGOCD_GIT_MODULES_ENABLED" @@ -244,7 +224,7 @@ const ( 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 enables dynamic sharding (ALPHA) EnvEnableDynamicClusterDistribution = "ARGOCD_ENABLE_DYNAMIC_CLUSTER_DISTRIBUTION" // EnvEnableGRPCTimeHistogramEnv enables gRPC metrics collection EnvEnableGRPCTimeHistogramEnv = "ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM" @@ -254,14 +234,10 @@ const ( EnvHelmIndexCacheDuration = "ARGOCD_HELM_INDEX_CACHE_DURATION" // EnvAppConfigPath allows to override the configuration path for repo server EnvAppConfigPath = "ARGOCD_APP_CONF_PATH" - // EnvAuthToken is the environment variable name for the auth token used by the CLI - EnvAuthToken = "ARGOCD_AUTH_TOKEN" // 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 @@ -287,8 +263,6 @@ const ( // 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" - // EnvGRPCMaxSizeMB is the environment variable to look for a max GRPC message size - EnvGRPCMaxSizeMB = "ARGOCD_GRPC_MAX_SIZE_MB" ) // Config Management Plugin related constants @@ -367,7 +341,7 @@ func GetCMPChunkSize() int { } // GetCMPWorkDir will return the full path of the work directory used by the CMP server. -// This directory and all it's contents will be deleted during CMP bootstrap. +// This directory and all it's contents will be deleted durring CMP bootstrap. func GetCMPWorkDir() string { if workDir := os.Getenv(EnvCMPWorkDir); workDir != "" { return filepath.Join(workDir, DefaultCMPWorkDirName) @@ -422,30 +396,3 @@ const TokenVerificationError = "failed to verify the token" var TokenVerificationErr = errors.New(TokenVerificationError) var PermissionDeniedAPIError = status.Error(codes.PermissionDenied, "permission denied") - -// Redis password consts -const ( - DefaultRedisInitialPasswordSecretName = "argocd-redis" - DefaultRedisInitialPasswordKey = "auth" -) - -/* -SetOptionalRedisPasswordFromKubeConfig sets the optional Redis password if it exists in the k8s namespace's secrets. - -We specify kubeClient as kubernetes.Interface to allow for mocking in tests, but this should be treated as a kubernetes.Clientset param. -*/ -func SetOptionalRedisPasswordFromKubeConfig(ctx context.Context, kubeClient kubernetes.Interface, namespace string, redisOptions *redis.Options) error { - secret, err := kubeClient.CoreV1().Secrets(namespace).Get(ctx, DefaultRedisInitialPasswordSecretName, v1.GetOptions{}) - if err != nil { - return fmt.Errorf("failed to get secret %s/%s: %w", namespace, DefaultRedisInitialPasswordSecretName, err) - } - if secret == nil { - return fmt.Errorf("failed to get secret %s/%s: secret is nil", namespace, DefaultRedisInitialPasswordSecretName) - } - _, ok := secret.Data[DefaultRedisInitialPasswordKey] - if !ok { - return fmt.Errorf("secret %s/%s does not contain key %s", namespace, DefaultRedisInitialPasswordSecretName, DefaultRedisInitialPasswordKey) - } - redisOptions.Password = string(secret.Data[DefaultRedisInitialPasswordKey]) - return nil -} diff --git a/common/common_test.go b/common/common_test.go index 1021a30a14f60..5632c1e7a78cc 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -1,18 +1,12 @@ package common import ( - "context" "fmt" "os" "testing" "time" - "github.com/redis/go-redis/v9" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kubefake "k8s.io/client-go/kubernetes/fake" ) // Test env var not set for EnvGRPCKeepAliveMin @@ -50,63 +44,3 @@ func Test_GRPCKeepAliveMinIncorrectlySet(t *testing.T) { grpcKeepAliveTime := GetGRPCKeepAliveTime() assert.Equal(t, 2*grpcKeepAliveExpectedMin, grpcKeepAliveTime) } - -func TestSetOptionalRedisPasswordFromKubeConfig(t *testing.T) { - t.Parallel() - testCases := []struct { - name, namespace, expectedPassword, expectedErr string - secret *corev1.Secret - }{ - { - name: "Secret exists with correct key", - namespace: "default", - expectedPassword: "password123", - expectedErr: "", - secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: DefaultRedisInitialPasswordSecretName}, - Data: map[string][]byte{DefaultRedisInitialPasswordKey: []byte("password123")}, - }, - }, - { - name: "Secret does not exist", - namespace: "default", - expectedPassword: "", - expectedErr: fmt.Sprintf("failed to get secret default/%s", DefaultRedisInitialPasswordSecretName), - secret: nil, - }, - { - name: "Secret exists without correct key", - namespace: "default", - expectedPassword: "", - expectedErr: fmt.Sprintf("secret default/%s does not contain key %s", DefaultRedisInitialPasswordSecretName, DefaultRedisInitialPasswordKey), - secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: DefaultRedisInitialPasswordSecretName}, - Data: map[string][]byte{}, - }, - }, - } - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - var ( - ctx = context.TODO() - kubeClient = kubefake.NewSimpleClientset() - redisOptions = &redis.Options{} - ) - if tc.secret != nil { - if _, err := kubeClient.CoreV1().Secrets(tc.namespace).Create(ctx, tc.secret, metav1.CreateOptions{}); err != nil { - t.Fatalf("Failed to create secret: %v", err) - } - } - err := SetOptionalRedisPasswordFromKubeConfig(ctx, kubeClient, tc.namespace, redisOptions) - if tc.expectedErr != "" { - require.Error(t, err) - require.Contains(t, err.Error(), tc.expectedErr) - } else { - require.NoError(t, err) - } - require.Equal(t, tc.expectedPassword, redisOptions.Password) - }) - } -} diff --git a/controller/appcontroller.go b/controller/appcontroller.go index e607771de586d..de24205063800 100644 --- a/controller/appcontroller.go +++ b/controller/appcontroller.go @@ -48,6 +48,7 @@ import ( "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" + argov1alpha "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/client/informers/externalversions/application/v1alpha1" applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" @@ -56,7 +57,6 @@ import ( argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff" "github.com/argoproj/argo-cd/v2/util/argo/normalizers" "github.com/argoproj/argo-cd/v2/util/env" - "github.com/argoproj/argo-cd/v2/util/stats" kubeerrors "k8s.io/apimachinery/pkg/api/errors" @@ -98,15 +98,6 @@ func (a CompareWith) Pointer() *CompareWith { return &a } -func getAppLog(app *appv1.Application) *log.Entry { - return log.WithFields(log.Fields{ - "application": app.Name, - "app-namespace": app.Namespace, - "app-qualified-name": app.QualifiedName(), - "project": app.Spec.Project, - }) -} - // ApplicationController is the controller for application resources. type ApplicationController struct { cache *appstatecache.Cache @@ -116,11 +107,11 @@ type ApplicationController struct { applicationClientset appclientset.Interface auditLogger *argo.AuditLogger // queue contains app namespace/name - appRefreshQueue workqueue.TypedRateLimitingInterface[string] + appRefreshQueue workqueue.RateLimitingInterface // queue contains app namespace/name/comparisonType and used to request app refresh with the predefined comparison type - appComparisonTypeRefreshQueue workqueue.TypedRateLimitingInterface[string] - appOperationQueue workqueue.TypedRateLimitingInterface[string] - projectRefreshQueue workqueue.TypedRateLimitingInterface[string] + appComparisonTypeRefreshQueue workqueue.RateLimitingInterface + appOperationQueue workqueue.RateLimitingInterface + projectRefreshQueue workqueue.RateLimitingInterface appInformer cache.SharedIndexInformer appLister applisters.ApplicationLister projInformer cache.SharedIndexInformer @@ -164,7 +155,6 @@ func NewApplicationController( metricsPort int, metricsCacheExpiration time.Duration, metricsApplicationLabels []string, - metricsApplicationConditions []string, kubectlParallelismLimit int64, persistResourceHealth bool, clusterSharding sharding.ClusterShardingCache, @@ -173,7 +163,6 @@ func NewApplicationController( serverSideDiff bool, dynamicClusterDistributionEnabled bool, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts, - enableK8sEvent []string, ) (*ApplicationController, error) { log.Infof("appResyncPeriod=%v, appHardResyncPeriod=%v, appResyncJitter=%v", appResyncPeriod, appHardResyncPeriod, appResyncJitter) db := db.NewDB(namespace, settingsMgr, kubeClientset) @@ -188,17 +177,17 @@ func NewApplicationController( kubectl: kubectl, applicationClientset: applicationClientset, repoClientset: repoClientset, - appRefreshQueue: workqueue.NewTypedRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), workqueue.TypedRateLimitingQueueConfig[string]{Name: "app_reconciliation_queue"}), - appOperationQueue: workqueue.NewTypedRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), workqueue.TypedRateLimitingQueueConfig[string]{Name: "app_operation_processing_queue"}), - projectRefreshQueue: workqueue.NewTypedRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), workqueue.TypedRateLimitingQueueConfig[string]{Name: "project_reconciliation_queue"}), - appComparisonTypeRefreshQueue: workqueue.NewTypedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig)), + 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, enableK8sEvent), + auditLogger: argo.NewAuditLogger(namespace, kubeClientset, common.ApplicationController), settingsMgr: settingsMgr, selfHealTimeout: selfHealTimeout, clusterSharding: clusterSharding, @@ -222,6 +211,7 @@ func NewApplicationController( if projMeta, ok := obj.(metav1.Object); ok { ctrl.InvalidateProjectsCache(projMeta.GetName()) } + } }, UpdateFunc: func(old, new interface{}) { @@ -263,7 +253,7 @@ func NewApplicationController( if kubeerrors.IsNotFound(err) { appControllerDeployment = nil } else { - return fmt.Errorf("error retrieving Application Controller Deployment: %w", err) + return fmt.Errorf("error retrieving Application Controller Deployment: %s", err) } } if appControllerDeployment != nil { @@ -272,7 +262,7 @@ func NewApplicationController( } 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: %w", err) + return fmt.Errorf("error while updating the heartbeat for to the Shard Mapping ConfigMap: %s", err) } } } @@ -281,7 +271,7 @@ func NewApplicationController( metricsAddr := fmt.Sprintf("0.0.0.0:%d", metricsPort) - ctrl.metricsServer, err = metrics.NewMetricsServer(metricsAddr, appLister, ctrl.canProcessApp, readinessHealthCheck, metricsApplicationLabels, metricsApplicationConditions) + ctrl.metricsServer, err = metrics.NewMetricsServer(metricsAddr, appLister, ctrl.canProcessApp, readinessHealthCheck, metricsApplicationLabels) if err != nil { return nil, err } @@ -308,11 +298,13 @@ func (ctrl *ApplicationController) InvalidateProjectsCache(names ...string) { for _, name := range names { ctrl.projByNameCache.Delete(name) } - } else if ctrl != nil { - ctrl.projByNameCache.Range(func(key, _ interface{}) bool { - ctrl.projByNameCache.Delete(key) - return true - }) + } else { + if ctrl != nil { + ctrl.projByNameCache.Range(func(key, _ interface{}) bool { + ctrl.projByNameCache.Delete(key) + return true + }) + } } } @@ -382,7 +374,7 @@ func (ctrl *ApplicationController) getAppProj(app *appv1.Application) (*appv1.Ap if apierr.IsNotFound(err) { return nil, err } else { - return nil, fmt.Errorf("could not retrieve AppProject '%s' from cache: %w", app.Spec.Project, err) + return nil, fmt.Errorf("could not retrieve AppProject '%s' from cache: %v", app.Spec.Project, err) } } if !proj.IsAppNamespacePermitted(app, ctrl.namespace) { @@ -422,11 +414,10 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b continue } - logCtx := getAppLog(app) // Enforce application's permission for the source namespace _, err = ctrl.getAppProj(app) if err != nil { - logCtx.Errorf("Unable to determine project for app '%s': %v", app.QualifiedName(), err) + log.Errorf("Unable to determine project for app '%s': %v", app.QualifiedName(), err) continue } @@ -439,14 +430,15 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b if ref.Namespace == "" { namespace = "(cluster-scoped)" } - logCtx.WithFields(log.Fields{ - "comparison-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, + 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) @@ -456,34 +448,21 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b // setAppManagedResources will build a list of ResourceDiff based on the provided comparisonResult // and persist app resources related data in the cache. Will return the persisted ApplicationTree. func (ctrl *ApplicationController) setAppManagedResources(a *appv1.Application, comparisonResult *comparisonResult) (*appv1.ApplicationTree, error) { - ts := stats.NewTimingStats() - defer func() { - logCtx := getAppLog(a) - for k, v := range ts.Timings() { - logCtx = logCtx.WithField(k, v.Milliseconds()) - } - logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds()) - logCtx.Debug("Finished setting app managed resources") - }() managedResources, err := ctrl.hideSecretData(a, comparisonResult) - ts.AddCheckpoint("hide_secret_data_ms") if err != nil { - return nil, fmt.Errorf("error getting managed resources: %w", err) + return nil, fmt.Errorf("error getting managed resources: %s", err) } tree, err := ctrl.getResourceTree(a, managedResources) - ts.AddCheckpoint("get_resource_tree_ms") if err != nil { - return nil, fmt.Errorf("error getting resource tree: %w", err) + return nil, fmt.Errorf("error getting resource tree: %s", err) } err = ctrl.cache.SetAppResourcesTree(a.InstanceName(ctrl.namespace), tree) - ts.AddCheckpoint("set_app_resources_tree_ms") if err != nil { - return nil, fmt.Errorf("error setting app resource tree: %w", err) + return nil, fmt.Errorf("error setting app resource tree: %s", err) } err = ctrl.cache.SetAppManagedResources(a.InstanceName(ctrl.namespace), managedResources) - ts.AddCheckpoint("set_app_managed_resources_ms") if err != nil { - return nil, fmt.Errorf("error setting app managed resources: %w", err) + return nil, fmt.Errorf("error setting app managed resources: %s", err) } return tree, nil } @@ -513,18 +492,8 @@ func isKnownOrphanedResourceExclusion(key kube.ResourceKey, proj *appv1.AppProje } func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managedResources []*appv1.ResourceDiff) (*appv1.ApplicationTree, error) { - ts := stats.NewTimingStats() - defer func() { - logCtx := getAppLog(a) - for k, v := range ts.Timings() { - logCtx = logCtx.WithField(k, v.Milliseconds()) - } - logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds()) - logCtx.Debug("Finished getting resource tree") - }() nodes := make([]appv1.ResourceNode, 0) proj, err := ctrl.getAppProj(a) - ts.AddCheckpoint("get_app_proj_ms") if err != nil { return nil, fmt.Errorf("failed to get project: %w", err) } @@ -538,23 +507,21 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed } warnOrphaned = proj.Spec.OrphanedResources.IsWarn() } - ts.AddCheckpoint("get_orphaned_resources_ms") - managedResourcesKeys := make([]kube.ResourceKey, 0) for i := range managedResources { managedResource := managedResources[i] delete(orphanedNodesMap, kube.NewResourceKey(managedResource.Group, managedResource.Kind, managedResource.Namespace, managedResource.Name)) - live := &unstructured.Unstructured{} + var live = &unstructured.Unstructured{} err := json.Unmarshal([]byte(managedResource.LiveState), &live) if err != nil { return nil, fmt.Errorf("failed to unmarshal live state of managed resources: %w", err) } + 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 { - 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, @@ -565,61 +532,56 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed }, }) } else { - managedResourcesKeys = append(managedResourcesKeys, kube.GetResourceKey(live)) - } - } - err = ctrl.stateCache.IterateHierarchyV2(a.Spec.Destination.Server, managedResourcesKeys, func(child appv1.ResourceNode, appName string) bool { - permitted, _ := proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination, func(project string) ([]*appv1.Cluster, error) { - clusters, err := ctrl.db.GetProjectClusters(context.TODO(), project) + err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, kube.GetResourceKey(live), func(child appv1.ResourceNode, appName string) bool { + permitted, _ := proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination, func(project string) ([]*appv1.Cluster, error) { + clusters, err := ctrl.db.GetProjectClusters(context.TODO(), project) + if err != nil { + return nil, fmt.Errorf("failed to get project clusters: %w", err) + } + return clusters, nil + }) + if !permitted { + return false + } + nodes = append(nodes, child) + return true + }) if err != nil { - return nil, fmt.Errorf("failed to get project clusters: %w", err) + return nil, fmt.Errorf("failed to iterate resource hierarchy: %w", err) } - return clusters, nil - }) - if !permitted { - return false } - nodes = append(nodes, child) - return true - }) - if err != nil { - return nil, fmt.Errorf("failed to iterate resource hierarchy v2: %w", err) } - ts.AddCheckpoint("process_managed_resources_ms") orphanedNodes := make([]appv1.ResourceNode, 0) - orphanedNodesKeys := make([]kube.ResourceKey, 0) for k := range orphanedNodesMap { if k.Namespace != "" && proj.IsGroupKindPermitted(k.GroupKind(), true) && !isKnownOrphanedResourceExclusion(k, proj) { - orphanedNodesKeys = append(orphanedNodesKeys, k) - } - } - err = ctrl.stateCache.IterateHierarchyV2(a.Spec.Destination.Server, orphanedNodesKeys, func(child appv1.ResourceNode, appName string) bool { - belongToAnotherApp := false - if appName != "" { - appKey := ctrl.toAppKey(appName) - if _, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey); exists && err == nil { - belongToAnotherApp = true - } - } + err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, k, func(child appv1.ResourceNode, appName string) bool { + belongToAnotherApp := false + if appName != "" { + appKey := ctrl.toAppKey(appName) + if _, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey); exists && err == nil { + belongToAnotherApp = true + } + } - if belongToAnotherApp { - return false - } + if belongToAnotherApp { + return false + } - permitted, _ := proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination, func(project string) ([]*appv1.Cluster, error) { - return ctrl.db.GetProjectClusters(context.TODO(), project) - }) + permitted, _ := proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination, func(project string) ([]*appv1.Cluster, error) { + return ctrl.db.GetProjectClusters(context.TODO(), project) + }) - if !permitted { - return false + if !permitted { + return false + } + orphanedNodes = append(orphanedNodes, child) + return true + }) + if err != nil { + return nil, err + } } - orphanedNodes = append(orphanedNodes, child) - return true - }) - if err != nil { - return nil, err } - var conditions []appv1.ApplicationCondition if len(orphanedNodes) > 0 && warnOrphaned { conditions = []appv1.ApplicationCondition{{ @@ -631,26 +593,15 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed sort.Slice(orphanedNodes, func(i, j int) bool { return orphanedNodes[i].ResourceRef.String() < orphanedNodes[j].ResourceRef.String() }) - ts.AddCheckpoint("process_orphaned_resources_ms") hosts, err := ctrl.getAppHosts(a, nodes) if err != nil { return nil, fmt.Errorf("failed to get app hosts: %w", err) } - ts.AddCheckpoint("get_app_hosts_ms") return &appv1.ApplicationTree{Nodes: nodes, OrphanedNodes: orphanedNodes, Hosts: hosts}, nil } func (ctrl *ApplicationController) getAppHosts(a *appv1.Application, appNodes []appv1.ResourceNode) ([]appv1.HostInfo, error) { - ts := stats.NewTimingStats() - defer func() { - logCtx := getAppLog(a) - for k, v := range ts.Timings() { - logCtx = logCtx.WithField(k, v.Milliseconds()) - } - logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds()) - logCtx.Debug("Finished getting app hosts") - }() supportedResourceNames := map[v1.ResourceName]bool{ v1.ResourceCPU: true, v1.ResourceStorage: true, @@ -680,7 +631,6 @@ func (ctrl *ApplicationController) getAppHosts(a *appv1.Application, appNodes [] } } }) - ts.AddCheckpoint("iterate_resources_ms") if err != nil { return nil, err } @@ -736,7 +686,6 @@ func (ctrl *ApplicationController) getAppHosts(a *appv1.Application, appNodes [] }) hosts = append(hosts, appv1.HostInfo{Name: nodeName, SystemInfo: node.SystemInfo, ResourcesInfo: resourcesInfo}) } - ts.AddCheckpoint("process_app_pods_by_node_ms") return hosts, nil } @@ -760,28 +709,28 @@ func (ctrl *ApplicationController) hideSecretData(app *appv1.Application, compar var err error target, live, err = diff.HideSecretData(res.Target, res.Live) if err != nil { - return nil, fmt.Errorf("error hiding secret data: %w", err) + return nil, fmt.Errorf("error hiding secret data: %s", err) } compareOptions, err := ctrl.settingsMgr.GetResourceCompareOptions() if err != nil { - return nil, fmt.Errorf("error getting resource compare options: %w", err) + return nil, fmt.Errorf("error getting resource compare options: %s", err) } resourceOverrides, err := ctrl.settingsMgr.GetResourceOverrides() if err != nil { - return nil, fmt.Errorf("error getting resource overrides: %w", err) + return nil, fmt.Errorf("error getting resource overrides: %s", err) } appLabelKey, err := ctrl.settingsMgr.GetAppInstanceLabelKey() if err != nil { - return nil, fmt.Errorf("error getting app instance label key: %w", err) + return nil, fmt.Errorf("error getting app instance label key: %s", err) } trackingMethod, err := ctrl.settingsMgr.GetTrackingMethod() if err != nil { - return nil, fmt.Errorf("error getting tracking method: %w", err) + return nil, fmt.Errorf("error getting tracking method: %s", err) } clusterCache, err := ctrl.stateCache.GetClusterCache(app.Spec.Destination.Server) if err != nil { - return nil, fmt.Errorf("error getting cluster cache: %w", err) + return nil, fmt.Errorf("error getting cluster cache: %s", err) } diffConfig, err := argodiff.NewDiffConfigBuilder(). WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles, ctrl.ignoreNormalizerOpts). @@ -791,12 +740,12 @@ func (ctrl *ApplicationController) hideSecretData(app *appv1.Application, compar WithGVKParser(clusterCache.GetGVKParser()). Build() if err != nil { - return nil, fmt.Errorf("appcontroller error building diff config: %w", err) + return nil, fmt.Errorf("appcontroller error building diff config: %s", err) } diffResult, err := argodiff.StateDiff(live, target, diffConfig) if err != nil { - return nil, fmt.Errorf("error applying diff: %w", err) + return nil, fmt.Errorf("error applying diff: %s", err) } resDiff = diffResult } @@ -804,7 +753,7 @@ func (ctrl *ApplicationController) hideSecretData(app *appv1.Application, compar if live != nil { data, err := json.Marshal(live) if err != nil { - return nil, fmt.Errorf("error marshaling live json: %w", err) + return nil, fmt.Errorf("error marshaling live json: %s", err) } item.LiveState = string(data) } else { @@ -814,7 +763,7 @@ func (ctrl *ApplicationController) hideSecretData(app *appv1.Application, compar if target != nil { data, err := json.Marshal(target) if err != nil { - return nil, fmt.Errorf("error marshaling target json: %w", err) + return nil, fmt.Errorf("error marshaling target json: %s", err) } item.TargetState = string(data) } else { @@ -852,13 +801,7 @@ func (ctrl *ApplicationController) Run(ctx context.Context, statusProcessors int 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) - } + ctrl.clusterSharding.Init(clusters) } errors.CheckError(ctrl.stateCache.Init()) @@ -912,8 +855,10 @@ func (ctrl *ApplicationController) requestAppRefresh(appName string, compareWith } if after != nil { ctrl.appRefreshQueue.AddAfter(key, *after) + ctrl.appOperationQueue.AddAfter(key, *after) } else { ctrl.appRefreshQueue.AddRateLimited(key) + ctrl.appOperationQueue.AddRateLimited(key) } } } @@ -942,7 +887,7 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b ctrl.appOperationQueue.Done(appKey) }() - obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey) + obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey.(string)) if err != nil { log.Errorf("Failed to get application '%s' from informer index: %+v", appKey, err) return @@ -957,15 +902,6 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b return } app := origApp.DeepCopy() - logCtx := getAppLog(app) - ts := stats.NewTimingStats() - defer func() { - for k, v := range ts.Timings() { - logCtx = logCtx.WithField(k, v.Milliseconds()) - } - logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds()) - logCtx.Debug("Finished processing app operation queue item") - }() if app.Operation != nil { // If we get here, we are about to process an operation, but we cannot rely on informer since it might have stale data. @@ -973,16 +909,14 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b // We cannot rely on informer since applications might be updated by both application controller and api server. freshApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.ObjectMeta.Namespace).Get(context.Background(), app.ObjectMeta.Name, metav1.GetOptions{}) if err != nil { - logCtx.Errorf("Failed to retrieve latest application state: %v", err) + log.Errorf("Failed to retrieve latest application state: %v", err) return } app = freshApp } - ts.AddCheckpoint("get_fresh_app_ms") if app.Operation != nil { ctrl.processRequestedAppOperation(app) - ts.AddCheckpoint("process_requested_app_operation_ms") } else if app.DeletionTimestamp != nil { if err = ctrl.finalizeApplicationDeletion(app, func(project string) ([]*appv1.Cluster, error) { return ctrl.db.GetProjectClusters(context.Background(), project) @@ -992,9 +926,8 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b Message: err.Error(), }) message := fmt.Sprintf("Unable to delete application resources: %v", err.Error()) - ctrl.logAppEvent(app, argo.EventInfo{Reason: argo.EventReasonStatusRefreshed, Type: v1.EventTypeWarning}, message, context.TODO()) + ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonStatusRefreshed, Type: v1.EventTypeWarning}, message, "") } - ts.AddCheckpoint("finalize_application_deletion_ms") } return } @@ -1014,8 +947,8 @@ func (ctrl *ApplicationController) processAppComparisonTypeQueueItem() (processN return } - if parts := strings.Split(key, "/"); len(parts) != 3 { - log.Warnf("Unexpected key format in appComparisonTypeRefreshTypeQueue. Key should consists of namespace/name/comparisonType but got: %s", key) + if parts := strings.Split(key.(string), "/"); len(parts) != 3 { + log.Warnf("Unexpected key format in appComparisonTypeRefreshTypeQueue. Key should consists of namespace/name/comparisonType but got: %s", key.(string)) } else { if compareWith, err := strconv.Atoi(parts[2]); err != nil { log.Warnf("Unable to parse comparison type: %v", err) @@ -1041,7 +974,7 @@ func (ctrl *ApplicationController) processProjectQueueItem() (processNext bool) processNext = false return } - obj, exists, err := ctrl.projInformer.GetIndexer().GetByKey(key) + obj, exists, err := ctrl.projInformer.GetIndexer().GetByKey(key.(string)) if err != nil { log.Errorf("Failed to get project '%s' from informer index: %+v", key, err) return @@ -1110,6 +1043,7 @@ func (ctrl *ApplicationController) getPermittedAppLiveObjects(app *appv1.Applica // Don't delete live resources which are not permitted in the app project for k, v := range objsMap { permitted, err := proj.IsLiveResourcePermitted(v, app.Spec.Destination.Server, app.Spec.Destination.Name, projectClusters) + if err != nil { return nil, err } @@ -1121,25 +1055,24 @@ func (ctrl *ApplicationController) getPermittedAppLiveObjects(app *appv1.Applica return objsMap, nil } -func (ctrl *ApplicationController) isValidDestination(app *appv1.Application) (bool, *appv1.Cluster) { - logCtx := getAppLog(app) +func (ctrl *ApplicationController) isValidDestination(app *appv1.Application) (bool, *argov1alpha.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 { - logCtx.Warnf("Unable to validate destination of the Application being deleted: %v", err) + 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 { - logCtx.Warnf("Unable to locate cluster URL for Application being deleted: %v", err) + 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 := getAppLog(app) + logCtx := log.WithField("application", app.QualifiedName()) // 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 { @@ -1287,7 +1220,6 @@ func (ctrl *ApplicationController) updateFinalizers(app *appv1.Application) erro } func (ctrl *ApplicationController) setAppCondition(app *appv1.Application, condition appv1.ApplicationCondition) { - logCtx := getAppLog(app) // do nothing if app already has same condition for _, c := range app.Status.Conditions { if c.Message == condition.Message && c.Type == condition.Type { @@ -1307,12 +1239,12 @@ func (ctrl *ApplicationController) setAppCondition(app *appv1.Application, condi _, err = ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) } if err != nil { - logCtx.Errorf("Unable to set application condition: %v", err) + log.Errorf("Unable to set application condition: %v", err) } } func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Application) { - logCtx := getAppLog(app) + logCtx := log.WithField("application", app.QualifiedName()) var state *appv1.OperationState // Recover from any unexpected panics and automatically set the status to be failed defer func() { @@ -1327,14 +1259,6 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli ctrl.setOperationState(app, state) } }() - ts := stats.NewTimingStats() - defer func() { - for k, v := range ts.Timings() { - logCtx = logCtx.WithField(k, v.Milliseconds()) - } - logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds()) - logCtx.Debug("Finished processing requested app operation") - }() terminating := false if isOperationInProgress(app) { state = app.Status.OperationState.DeepCopy() @@ -1369,7 +1293,6 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli ctrl.setOperationState(app, state) logCtx.Infof("Initialized new operation: %v", *app.Operation) } - ts.AddCheckpoint("initial_operation_stage_ms") if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil { state.Phase = synccommon.OperationFailed @@ -1377,11 +1300,9 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli } else { ctrl.appStateManager.SyncAppState(app, state) } - ts.AddCheckpoint("validate_and_sync_app_state_ms") // Check whether application is allowed to use project _, err := ctrl.getAppProj(app) - ts.AddCheckpoint("get_app_proj_ms") if err != nil { state.Phase = synccommon.OperationError state.Message = err.Error() @@ -1421,25 +1342,25 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli } else if state.RetryCount > 0 { state.Message = fmt.Sprintf("%s (retried %d times).", state.Message, state.RetryCount) } + } ctrl.setOperationState(app, state) - ts.AddCheckpoint("final_set_operation_state") if state.Phase.Completed() && (app.Operation.Sync != nil && !app.Operation.Sync.DryRun) { // if we just completed an operation, force a refresh so that UI will report up-to-date // sync/health information if _, err := cache.MetaNamespaceKeyFunc(app); err == nil { // force app refresh with using CompareWithLatest comparison type and trigger app reconciliation loop - ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatestForceResolve.Pointer(), nil) + ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatest.Pointer(), nil) } else { logCtx.Warnf("Fails to requeue application: %v", err) } } - ts.AddCheckpoint("request_app_refresh_ms") } func (ctrl *ApplicationController) setOperationState(app *appv1.Application, state *appv1.OperationState) { - logCtx := getAppLog(app) + 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") @@ -1509,7 +1430,7 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta eventInfo.Type = v1.EventTypeWarning messages = append(messages, "failed:", state.Message) } - ctrl.logAppEvent(app, eventInfo, strings.Join(messages, " "), context.TODO()) + ctrl.auditLogger.LogAppEvent(app, eventInfo, strings.Join(messages, " "), "") ctrl.metricsServer.IncSync(app, state) } } @@ -1517,7 +1438,7 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta // 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 := getAppLog(app).WithField("informer-writeBack", true) + 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) @@ -1548,12 +1469,9 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo if r := recover(); r != nil { log.Errorf("Recovered from panic: %+v\n%s", r, debug.Stack()) } - // We want to have app operation update happen after the sync, so there's no race condition - // and app updates not proceeding. See https://github.com/argoproj/argo-cd/issues/18500. - ctrl.appOperationQueue.AddRateLimited(appKey) ctrl.appRefreshQueue.Done(appKey) }() - obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey) + obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey.(string)) if err != nil { log.Errorf("Failed to get application '%s' from informer index: %+v", appKey, err) return @@ -1574,21 +1492,18 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo return } app := origApp.DeepCopy() - logCtx := getAppLog(app).WithFields(log.Fields{ - "comparison-level": comparisonLevel, - "dest-server": origApp.Spec.Destination.Server, - "dest-name": origApp.Spec.Destination.Name, - "dest-namespace": origApp.Spec.Destination.Namespace, + 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() - ts := stats.NewTimingStats() defer func() { reconcileDuration := time.Since(startTime) ctrl.metricsServer.IncReconcile(origApp, reconcileDuration) - for k, v := range ts.Timings() { - logCtx = logCtx.WithField(k, v.Milliseconds()) - } logCtx.WithFields(log.Fields{ "time_ms": reconcileDuration.Milliseconds(), "patch_ms": patchMs.Milliseconds(), @@ -1614,22 +1529,19 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo return } } - ts.AddCheckpoint("comparison_with_nothing_ms") project, hasErrors := ctrl.refreshAppConditions(app) - ts.AddCheckpoint("refresh_app_conditions_ms") if hasErrors { app.Status.Sync.Status = appv1.SyncStatusCodeUnknown app.Status.Health.Status = health.HealthStatusUnknown patchMs = ctrl.persistAppStatus(origApp, &app.Status) if err := ctrl.cache.SetAppResourcesTree(app.InstanceName(ctrl.namespace), &appv1.ApplicationTree{}); err != nil { - logCtx.Warnf("failed to set app resource tree: %v", err) + log.Warnf("failed to set app resource tree: %v", err) } if err := ctrl.cache.SetAppManagedResources(app.InstanceName(ctrl.namespace), nil); err != nil { - logCtx.Warnf("failed to set app managed resources tree: %v", err) + log.Warnf("failed to set app managed resources tree: %v", err) } - ts.AddCheckpoint("process_refresh_app_conditions_errors_ms") return } @@ -1668,8 +1580,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo compareResult, err := ctrl.appStateManager.CompareAppState(app, project, revisions, sources, refreshType == appv1.RefreshTypeHard, - comparisonLevel == CompareWithLatestForceResolve, localManifests, hasMultipleSources, false) - ts.AddCheckpoint("compare_app_state_ms") + comparisonLevel == CompareWithLatestForceResolve, localManifests, hasMultipleSources) if goerrors.Is(err, CompareStateRepoError) { logCtx.Warnf("Ignoring temporary failed attempt to compare app state against repo: %v", err) @@ -1681,10 +1592,8 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo } ctrl.normalizeApplication(origApp, app) - ts.AddCheckpoint("normalize_application_ms") tree, err := ctrl.setAppManagedResources(app, compareResult) - ts.AddCheckpoint("set_app_managed_resources_ms") if err != nil { logCtx.Errorf("Failed to cache app resources: %v", err) } else { @@ -1692,7 +1601,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo } if project.Spec.SyncWindows.Matches(app).CanSync(false) { - syncErrCond, opMS := ctrl.autoSync(app, compareResult.syncStatus, compareResult.resources, compareResult.revisionUpdated) + syncErrCond, opMS := ctrl.autoSync(app, compareResult.syncStatus, compareResult.resources) setOpMs = opMS if syncErrCond != nil { app.Status.SetConditions( @@ -1708,7 +1617,6 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo } else { logCtx.Info("Sync prevented by sync window") } - ts.AddCheckpoint("auto_sync_ms") if app.Status.ReconciledAt == nil || comparisonLevel >= CompareWithLatest { app.Status.ReconciledAt = &now @@ -1722,10 +1630,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo app.Status.SourceType = compareResult.appSourceType app.Status.SourceTypes = compareResult.appSourceTypes app.Status.ControllerNamespace = ctrl.namespace - ts.AddCheckpoint("app_status_update_ms") patchMs = ctrl.persistAppStatus(origApp, &app.Status) - // This is a partly a duplicate of patch_ms, but more descriptive and allows to have measurement for the next step. - ts.AddCheckpoint("persist_app_status_ms") if (compareResult.hasPostDeleteHooks != app.HasPostDeleteFinalizer() || compareResult.hasPostDeleteHooks != app.HasPostDeleteFinalizer("cleanup")) && app.GetDeletionTimestamp() == nil { if compareResult.hasPostDeleteHooks { @@ -1740,7 +1645,6 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo logCtx.Errorf("Failed to update finalizers: %v", err) } } - ts.AddCheckpoint("process_finalizers_ms") return } @@ -1760,7 +1664,7 @@ func currentSourceEqualsSyncedSource(app *appv1.Application) bool { // 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 := getAppLog(app) + logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) var reason string compareWith := CompareWithLatest refreshType := appv1.RefreshTypeNormal @@ -1837,8 +1741,8 @@ func (ctrl *ApplicationController) refreshAppConditions(app *appv1.Application) // normalizeApplication normalizes an application.spec and additionally persists updates if it changed func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Application) { + logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) app.Spec = *argo.NormalizeApplicationSpec(&app.Spec) - logCtx := getAppLog(app) patch, modified, err := diff.CreateTwoWayMergePatch(orig, app, appv1.Application{}) @@ -1872,14 +1776,14 @@ func createMergePatch(orig, new interface{}) ([]byte, bool, error) { // 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) (patchMs time.Duration) { - logCtx := getAppLog(orig) + 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.logAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message, context.TODO()) + 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.logAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message, context.TODO()) + ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message, "") } var newAnnotations map[string]string if orig.GetAnnotations() != nil { @@ -1915,19 +1819,11 @@ func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, new } // 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, revisionUpdated bool) (*appv1.ApplicationCondition, time.Duration) { - logCtx := getAppLog(app) - ts := stats.NewTimingStats() - defer func() { - for k, v := range ts.Timings() { - logCtx = logCtx.WithField(k, v.Milliseconds()) - } - logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds()) - logCtx.Debug("Finished auto sync") - }() +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, 0 } + logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) if app.Operation != nil { logCtx.Infof("Skipping auto-sync: another operation is in progress") @@ -1959,18 +1855,10 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus * } } - selfHeal := app.Spec.SyncPolicy.Automated.SelfHeal - // Multi-Source Apps with selfHeal disabled should not trigger an autosync if - // the last sync revision and the new sync revision is the same. - if app.Spec.HasMultipleSources() && !selfHeal && reflect.DeepEqual(app.Status.Sync.Revisions, syncStatus.Revisions) { - logCtx.Infof("Skipping auto-sync: selfHeal disabled and sync caused by object update") - return nil, 0 - } - desiredCommitSHA := syncStatus.Revision desiredCommitSHAsMS := syncStatus.Revisions - alreadyAttempted, attemptPhase := alreadyAttemptedSync(app, desiredCommitSHA, desiredCommitSHAsMS, app.Spec.HasMultipleSources(), revisionUpdated) - ts.AddCheckpoint("already_attempted_sync_ms") + alreadyAttempted, attemptPhase := alreadyAttemptedSync(app, desiredCommitSHA, desiredCommitSHAsMS, app.Spec.HasMultipleSources()) + selfHeal := app.Spec.SyncPolicy.Automated.SelfHeal op := appv1.Operation{ Sync: &appv1.SyncOperation{ Revision: desiredCommitSHA, @@ -2012,8 +1900,8 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus * ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatest.Pointer(), &retryAfter) return nil, 0 } + } - ts.AddCheckpoint("already_attempted_check_ms") if app.Spec.SyncPolicy.Automated.Prune && !app.Spec.SyncPolicy.Automated.AllowEmpty { bAllNeedPrune := true @@ -2024,16 +1912,14 @@ 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.Warn(message) + logCtx.Warnf(message) return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: message}, 0 } } appIf := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace) - ts.AddCheckpoint("get_applications_ms") start := time.Now() updatedApp, err := argo.SetAppOperation(appIf, app.Name, &op) - ts.AddCheckpoint("set_app_operation_ms") setOpTime := time.Since(start) if err != nil { if goerrors.Is(err, argo.ErrAnotherOperationInProgress) { @@ -2048,42 +1934,25 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus * } else { ctrl.writeBackToInformer(updatedApp) } - ts.AddCheckpoint("write_back_to_informer_ms") - - var target string - if updatedApp.Spec.HasMultipleSources() { - target = strings.Join(desiredCommitSHAsMS, ", ") - } else { - target = desiredCommitSHA - } - message := fmt.Sprintf("Initiated automated sync to '%s'", target) - ctrl.logAppEvent(app, argo.EventInfo{Reason: argo.EventReasonOperationStarted, Type: v1.EventTypeNormal}, message, context.TODO()) + message := fmt.Sprintf("Initiated automated sync to '%s'", desiredCommitSHA) + ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonOperationStarted, Type: v1.EventTypeNormal}, message, "") logCtx.Info(message) return nil, setOpTime } // 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, revisionUpdated bool) (bool, synccommon.OperationPhase) { +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 { return false, "" } if hasMultipleSources { - if revisionUpdated { - if !reflect.DeepEqual(app.Status.OperationState.SyncResult.Revisions, commitSHAsMS) { - return false, "" - } - } else { - log.WithField("application", app.Name).Debugf("Skipping auto-sync: commitSHA %s has no changes", commitSHA) + if !reflect.DeepEqual(app.Status.OperationState.SyncResult.Revisions, commitSHAsMS) { + return false, "" } } else { - if revisionUpdated { - log.WithField("application", app.Name).Infof("Executing compare of syncResult.Revision and commitSha because manifest changed: %v", commitSHA) - if app.Status.OperationState.SyncResult.Revision != commitSHA { - return false, "" - } - } else { - log.WithField("application", app.Name).Debugf("Skipping auto-sync: commitSHA %s has no changes", commitSHA) + if app.Status.OperationState.SyncResult.Revision != commitSHA { + return false, "" } } @@ -2127,7 +1996,7 @@ func (ctrl *ApplicationController) shouldSelfHeal(app *appv1.Application) (bool, // 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, glob.REGEXP) + return app.Namespace == ctrl.namespace || glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) } func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool { @@ -2144,7 +2013,7 @@ func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool { if annotations := app.GetAnnotations(); annotations != nil { if skipVal, ok := annotations[common.AnnotationKeyAppSkipReconcile]; ok { - logCtx := getAppLog(app) + 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) @@ -2256,10 +2125,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar key, err := cache.MetaNamespaceKeyFunc(obj) if err == nil { ctrl.appRefreshQueue.AddRateLimited(key) - } - newApp, newOK := obj.(*appv1.Application) - if err == nil && newOK { - ctrl.clusterSharding.AddApp(newApp) + ctrl.appOperationQueue.AddRateLimited(key) } }, UpdateFunc: func(old, new interface{}) { @@ -2279,7 +2145,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar newApp, newOK := new.(*appv1.Application) if oldOK && newOK { if automatedSyncEnabled(oldApp, newApp) { - getAppLog(newApp).Info("Enabled automated sync") + log.WithField("application", newApp.QualifiedName()).Info("Enabled automated sync") compareWith = CompareWithLatest.Pointer() } if ctrl.statusRefreshJitter != 0 && oldApp.ResourceVersion == newApp.ResourceVersion { @@ -2290,10 +2156,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar } ctrl.requestAppRefresh(newApp.QualifiedName(), compareWith, delay) - if !newOK || (delay != nil && *delay != time.Duration(0)) { - ctrl.appOperationQueue.AddRateLimited(key) - } - ctrl.clusterSharding.UpdateApp(newApp) + ctrl.appOperationQueue.AddRateLimited(key) }, DeleteFunc: func(obj interface{}) { if !ctrl.canProcessApp(obj) { @@ -2306,10 +2169,6 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar // 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) - } }, }, ) @@ -2385,31 +2244,4 @@ func (ctrl *ApplicationController) toAppQualifiedName(appName, appNamespace stri 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 -} - -func (ctrl *ApplicationController) logAppEvent(a *appv1.Application, eventInfo argo.EventInfo, message string, ctx context.Context) { - eventLabels := argo.GetAppEventLabels(a, applisters.NewAppProjectLister(ctrl.projInformer.GetIndexer()), ctrl.namespace, ctrl.settingsMgr, ctrl.db, ctx) - ctrl.auditLogger.LogAppEvent(a, eventInfo, message, "", eventLabels) -} - -type ClusterFilterFunction func(c *appv1.Cluster, distributionFunction sharding.DistributionFunction) bool +type ClusterFilterFunction func(c *argov1alpha.Cluster, distributionFunction sharding.DistributionFunction) bool diff --git a/controller/appcontroller_test.go b/controller/appcontroller_test.go index 50fb08042719d..a8bcf9fede0ed 100644 --- a/controller/appcontroller_test.go +++ b/controller/appcontroller_test.go @@ -19,6 +19,7 @@ import ( 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" @@ -35,38 +36,32 @@ import ( "k8s.io/client-go/tools/cache" "sigs.k8s.io/yaml" - dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" - mockstatecache "github.com/argoproj/argo-cd/v2/controller/cache/mocks" "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" "github.com/argoproj/argo-cd/v2/test" - "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/argo/normalizers" 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/settings" ) -var testEnableEventList []string = argo.DefaultEnableEventList() - type namespacedResource struct { 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 - updateRevisionForPathsResponse *apiclient.UpdateRevisionForPathsResponse + 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 { @@ -112,8 +107,6 @@ func newFakeController(data *fakeData, repoErr error) *ApplicationController { } } - mockRepoClient.On("UpdateRevisionForPaths", mock.Anything, mock.Anything).Return(data.updateRevisionForPathsResponse, nil) - mockRepoClientset := mockrepoclient.Clientset{RepoServerServiceClient: &mockRepoClient} secret := corev1.Secret{ @@ -158,7 +151,6 @@ func newFakeController(data *fakeData, repoErr error) *ApplicationController { common.DefaultPortArgoCDMetrics, data.metricsCacheExpiration, []string{}, - []string{}, 0, true, nil, @@ -167,7 +159,6 @@ func newFakeController(data *fakeData, repoErr error) *ApplicationController { false, false, normalizers.IgnoreNormalizerOpts{}, - testEnableEventList, ) db := &dbmocks.ArgoDB{} db.On("GetApplicationControllerReplicas").Return(1) @@ -198,16 +189,14 @@ func newFakeController(data *fakeData, repoErr error) *ApplicationController { mockStateCache.On("GetNamespaceTopLevelResources", mock.Anything, mock.Anything).Return(response, nil) mockStateCache.On("IterateResources", mock.Anything, mock.Anything).Return(nil) mockStateCache.On("GetClusterCache", mock.Anything).Return(&clusterCacheMock, nil) - mockStateCache.On("IterateHierarchyV2", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - keys := args[1].([]kube.ResourceKey) + mockStateCache.On("IterateHierarchy", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + key := args[1].(kube.ResourceKey) action := args[2].(func(child v1alpha1.ResourceNode, appName string) bool) - for _, key := range keys { - appName := "" - if res, ok := data.namespacedResources[key]; ok { - appName = res.AppName - } - _ = action(v1alpha1.ResourceNode{ResourceRef: v1alpha1.ResourceRef{Kind: key.Kind, Group: key.Group, Namespace: key.Namespace, Name: key.Name}}, appName) + appName := "" + if res, ok := data.namespacedResources[key]; ok { + appName = res.AppName } + _ = action(v1alpha1.ResourceNode{ResourceRef: v1alpha1.ResourceRef{Kind: key.Kind, Group: key.Group, Namespace: key.Namespace, Name: key.Name}}, appName) }).Return(nil) return ctrl } @@ -229,7 +218,6 @@ metadata: namespace: ` + test.FakeArgoCDNamespace + ` type: Opaque ` - var fakeApp = ` apiVersion: argoproj.io/v1alpha1 kind: Application @@ -381,8 +369,8 @@ data: var fakePostDeleteHook = ` { - "apiVersion": "batch/v1", - "kind": "Job", + "apiVersion": "v1", + "kind": "Pod", "metadata": { "name": "post-delete-hook", "namespace": "default", @@ -395,93 +383,22 @@ var fakePostDeleteHook = ` } }, "spec": { - "template": { - "metadata": { - "name": "post-delete-hook" - }, - "spec": { - "containers": [ - { - "name": "post-delete-hook", - "image": "busybox", - "command": [ - "/bin/sh", - "-c", - "sleep 5 && echo hello from the post-delete-hook job" - ] - } - ], - "restartPolicy": "Never" + "containers": [ + { + "name": "post-delete-hook", + "image": "busybox", + "restartPolicy": "Never", + "command": [ + "/bin/sh", + "-c", + "sleep 5 && echo hello from the post-delete-hook pod" + ] } - } + ] } } ` -var fakeServiceAccount = ` -{ - "apiVersion": "v1", - "kind": "ServiceAccount", - "metadata": { - "name": "hook-serviceaccount", - "namespace": "default", - "annotations": { - "argocd.argoproj.io/hook": "PostDelete", - "argocd.argoproj.io/hook-delete-policy": "BeforeHookCreation,HookSucceeded" - } - } -} -` - -var fakeRole = ` -{ - "apiVersion": "rbac.authorization.k8s.io/v1", - "kind": "Role", - "metadata": { - "name": "hook-role", - "namespace": "default", - "annotations": { - "argocd.argoproj.io/hook": "PostDelete", - "argocd.argoproj.io/hook-delete-policy": "BeforeHookCreation,HookSucceeded" - } - }, - "rules": [ - { - "apiGroups": [""], - "resources": ["secrets"], - "verbs": ["get", "delete", "list"] - } - ] -} -` - -var fakeRoleBinding = ` -{ - "apiVersion": "rbac.authorization.k8s.io/v1", - "kind": "RoleBinding", - "metadata": { - "name": "hook-rolebinding", - "namespace": "default", - "annotations": { - "argocd.argoproj.io/hook": "PostDelete", - "argocd.argoproj.io/hook-delete-policy": "BeforeHookCreation,HookSucceeded" - } - }, - "roleRef": { - "apiGroup": "rbac.authorization.k8s.io", - "kind": "Role", - "name": "hook-role" - }, - "subjects": [ - { - "kind": "ServiceAccount", - "name": "hook-serviceaccount", - "namespace": "default" - } - ] -} -` - func newFakeApp() *v1alpha1.Application { return createFakeApp(fakeApp) } @@ -517,39 +434,12 @@ func newFakeCM() map[string]interface{} { } func newFakePostDeleteHook() map[string]interface{} { - var hook map[string]interface{} - err := yaml.Unmarshal([]byte(fakePostDeleteHook), &hook) - if err != nil { - panic(err) - } - return hook -} - -func newFakeRoleBinding() map[string]interface{} { - var roleBinding map[string]interface{} - err := yaml.Unmarshal([]byte(fakeRoleBinding), &roleBinding) - if err != nil { - panic(err) - } - return roleBinding -} - -func newFakeRole() map[string]interface{} { - var role map[string]interface{} - err := yaml.Unmarshal([]byte(fakeRole), &role) - if err != nil { - panic(err) - } - return role -} - -func newFakeServiceAccount() map[string]interface{} { - var serviceAccount map[string]interface{} - err := yaml.Unmarshal([]byte(fakeServiceAccount), &serviceAccount) + var cm map[string]interface{} + err := yaml.Unmarshal([]byte(fakePostDeleteHook), &cm) if err != nil { panic(err) } - return serviceAccount + return cm } func TestAutoSync(t *testing.T) { @@ -559,51 +449,15 @@ func TestAutoSync(t *testing.T) { Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true) + 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{}) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, app.Operation) assert.NotNil(t, app.Operation.Sync) assert.False(t, app.Operation.Sync.Prune) } -func TestMultiSourceSelfHeal(t *testing.T) { - // Simulate OutOfSync caused by object change in cluster - // So our Sync Revisions and SyncStatus Revisions should deep equal - t.Run("ClusterObjectChangeShouldNotTriggerAutoSync", func(t *testing.T) { - app := newFakeMultiSourceApp() - app.Spec.SyncPolicy.Automated.SelfHeal = false - app.Status.Sync.Revisions = []string{"z", "x", "v"} - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) - syncStatus := v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeOutOfSync, - Revisions: []string{"z", "x", "v"}, - } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook-1", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true) - assert.Nil(t, cond) - app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) - require.NoError(t, err) - assert.Nil(t, app.Operation) - }) - - t.Run("NewRevisionChangeShouldTriggerAutoSync", func(t *testing.T) { - app := newFakeMultiSourceApp() - app.Spec.SyncPolicy.Automated.SelfHeal = false - app.Status.Sync.Revisions = []string{"a", "b", "c"} - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) - syncStatus := v1alpha1.SyncStatus{ - Status: v1alpha1.SyncStatusCodeOutOfSync, - Revisions: []string{"z", "x", "v"}, - } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook-1", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true) - assert.Nil(t, cond) - app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) - require.NoError(t, err) - assert.NotNil(t, app.Operation) - }) -} - func TestAutoSyncNotAllowEmpty(t *testing.T) { app := newFakeApp() app.Spec.SyncPolicy.Automated.Prune = true @@ -612,7 +466,7 @@ func TestAutoSyncNotAllowEmpty(t *testing.T) { Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}, true) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}) assert.NotNil(t, cond) } @@ -625,7 +479,7 @@ func TestAutoSyncAllowEmpty(t *testing.T) { Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}, true) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}) assert.Nil(t, cond) } @@ -639,10 +493,10 @@ func TestSkipAutoSync(t *testing.T) { Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}, true) + 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{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Nil(t, app.Operation) }) @@ -654,10 +508,10 @@ func TestSkipAutoSync(t *testing.T) { Status: v1alpha1.SyncStatusCodeSynced, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}, true) + 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{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Nil(t, app.Operation) }) @@ -670,10 +524,10 @@ func TestSkipAutoSync(t *testing.T) { Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}, true) + 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{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Nil(t, app.Operation) }) @@ -687,10 +541,10 @@ func TestSkipAutoSync(t *testing.T) { Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}, true) + 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{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Nil(t, app.Operation) }) @@ -713,10 +567,10 @@ func TestSkipAutoSync(t *testing.T) { Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true) + 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{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Nil(t, app.Operation) }) @@ -729,10 +583,10 @@ func TestSkipAutoSync(t *testing.T) { } cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{ {Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync, RequiresPruning: true}, - }, true) + }) assert.Nil(t, cond) app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Nil(t, app.Operation) }) } @@ -765,10 +619,10 @@ func TestAutoSyncIndicateError(t *testing.T) { Source: *app.Spec.Source.DeepCopy(), }, } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true) + 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{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Nil(t, app.Operation) } @@ -808,10 +662,10 @@ func TestAutoSyncParameterOverrides(t *testing.T) { Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", }, } - cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true) + 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{}) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, app.Operation) } @@ -856,13 +710,13 @@ func TestFinalizeAppDeletion(t *testing.T) { err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, patched) }) // 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(t *testing.T) { + t.Run("ProjectRestrictionEnforced", func(*testing.T) { restrictedProj := v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{ Name: "restricted", @@ -907,11 +761,11 @@ func TestFinalizeAppDeletion(t *testing.T) { err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, patched) objsMap, err := ctrl.stateCache.GetManagedLiveObjs(app, []*unstructured.Unstructured{}) if err != nil { - require.NoError(t, err) + assert.NoError(t, err) } // Managed objects must be empty assert.Empty(t, objsMap) @@ -943,13 +797,14 @@ func TestFinalizeAppDeletion(t *testing.T) { err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, patched) }) // Create an Application with a cluster that doesn't exist // Ensure it can be deleted. t.Run("DeleteWithInvalidClusterName", func(t *testing.T) { + appTemplate := newFakeAppWithDestName() testShouldDelete := func(app *v1alpha1.Application) { @@ -967,7 +822,7 @@ func TestFinalizeAppDeletion(t *testing.T) { err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) - require.NoError(t, err) + assert.NoError(t, err) } app1 := appTemplate.DeepCopy() @@ -982,6 +837,7 @@ func TestFinalizeAppDeletion(t *testing.T) { app3.Spec.Destination.Name = "invalid" app3.Spec.Destination.Server = "https://invalid" testShouldDelete(app3) + }) t.Run("PostDelete_HookIsCreated", func(t *testing.T) { @@ -993,8 +849,7 @@ func TestFinalizeAppDeletion(t *testing.T) { Manifests: []string{fakePostDeleteHook}, }}, apps: []runtime.Object{app, &defaultProj}, - managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{}, - }, nil) + managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{}}, nil) patched := false fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) @@ -1010,7 +865,7 @@ func TestFinalizeAppDeletion(t *testing.T) { err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) - require.NoError(t, err) + assert.NoError(t, err) // finalizer is not deleted assert.False(t, patched) // post-delete hook is created @@ -1023,13 +878,7 @@ func TestFinalizeAppDeletion(t *testing.T) { app.SetPostDeleteFinalizer() app.Spec.Destination.Namespace = test.FakeArgoCDNamespace liveHook := &unstructured.Unstructured{Object: newFakePostDeleteHook()} - conditions := []interface{}{ - map[string]interface{}{ - "type": "Complete", - "status": "True", - }, - } - require.NoError(t, unstructured.SetNestedField(liveHook.Object, conditions, "status", "conditions")) + require.NoError(t, unstructured.SetNestedField(liveHook.Object, "Succeeded", "status", "phase")) ctrl := newFakeController(&fakeData{ manifestResponses: []*apiclient.ManifestResponse{{ Manifests: []string{fakePostDeleteHook}, @@ -1037,8 +886,7 @@ func TestFinalizeAppDeletion(t *testing.T) { apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ kube.GetResourceKey(liveHook): liveHook, - }, - }, nil) + }}, nil) patched := false fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) @@ -1054,7 +902,7 @@ func TestFinalizeAppDeletion(t *testing.T) { err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) - require.NoError(t, err) + assert.NoError(t, err) // finalizer is removed assert.True(t, patched) }) @@ -1063,29 +911,16 @@ func TestFinalizeAppDeletion(t *testing.T) { app := newFakeApp() app.SetPostDeleteFinalizer("cleanup") app.Spec.Destination.Namespace = test.FakeArgoCDNamespace - liveRoleBinding := &unstructured.Unstructured{Object: newFakeRoleBinding()} - liveRole := &unstructured.Unstructured{Object: newFakeRole()} - liveServiceAccount := &unstructured.Unstructured{Object: newFakeServiceAccount()} liveHook := &unstructured.Unstructured{Object: newFakePostDeleteHook()} - conditions := []interface{}{ - map[string]interface{}{ - "type": "Complete", - "status": "True", - }, - } - require.NoError(t, unstructured.SetNestedField(liveHook.Object, conditions, "status", "conditions")) + require.NoError(t, unstructured.SetNestedField(liveHook.Object, "Succeeded", "status", "phase")) ctrl := newFakeController(&fakeData{ manifestResponses: []*apiclient.ManifestResponse{{ - Manifests: []string{fakeRoleBinding, fakeRole, fakeServiceAccount, fakePostDeleteHook}, + Manifests: []string{fakePostDeleteHook}, }}, apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ - kube.GetResourceKey(liveRoleBinding): liveRoleBinding, - kube.GetResourceKey(liveRole): liveRole, - kube.GetResourceKey(liveServiceAccount): liveServiceAccount, - kube.GetResourceKey(liveHook): liveHook, - }, - }, nil) + kube.GetResourceKey(liveHook): liveHook, + }}, nil) patched := false fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) @@ -1101,15 +936,10 @@ func TestFinalizeAppDeletion(t *testing.T) { err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) - require.NoError(t, err) - // post-delete hooks are deleted - require.Len(t, ctrl.kubectl.(*MockKubectl).DeletedResources, 4) - deletedResources := []string{} - for _, res := range ctrl.kubectl.(*MockKubectl).DeletedResources { - deletedResources = append(deletedResources, res.Name) - } - expectedNames := []string{"hook-rolebinding", "hook-role", "hook-serviceaccount", "post-delete-hook"} - require.ElementsMatch(t, expectedNames, deletedResources, "Deleted resources should match expected names") + 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) }) @@ -1267,9 +1097,9 @@ func TestGetResourceTree_HasOrphanedResources(t *testing.T) { TargetState: test.DeploymentManifest, }}) - require.NoError(t, err) - assert.Equal(t, []v1alpha1.ResourceNode{managedDeploy}, tree.Nodes) - assert.Equal(t, []v1alpha1.ResourceNode{orphanedDeploy1, orphanedDeploy2}, tree.OrphanedNodes) + assert.NoError(t, err) + assert.Equal(t, tree.Nodes, []v1alpha1.ResourceNode{managedDeploy}) + assert.Equal(t, tree.OrphanedNodes, []v1alpha1.ResourceNode{orphanedDeploy1, orphanedDeploy2}) } func TestSetOperationStateOnDeletedApp(t *testing.T) { @@ -1576,7 +1406,7 @@ func TestRefreshAppConditions(t *testing.T) { _, hasErrors := ctrl.refreshAppConditions(app) assert.False(t, hasErrors) - assert.Empty(t, app.Status.Conditions) + assert.Len(t, app.Status.Conditions, 0) }) t.Run("PreserveExistingWarningCondition", func(t *testing.T) { @@ -1627,7 +1457,7 @@ func TestUpdateReconciledAt(t *testing.T) { receivedPatch := map[string]interface{}{} fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { if patchAction, ok := action.(kubetesting.PatchAction); ok { - require.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) + assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) } return true, &v1alpha1.Application{}, nil }) @@ -1640,11 +1470,11 @@ func TestUpdateReconciledAt(t *testing.T) { ctrl.processAppRefreshQueueItem() _, updated, err := unstructured.NestedString(receivedPatch, "status", "reconciledAt") - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, updated) _, updated, err = unstructured.NestedString(receivedPatch, "status", "observedAt") - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, updated) }) @@ -1656,13 +1486,14 @@ func TestUpdateReconciledAt(t *testing.T) { ctrl.processAppRefreshQueueItem() _, updated, err := unstructured.NestedString(receivedPatch, "status", "reconciledAt") - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, updated) _, updated, err = unstructured.NestedString(receivedPatch, "status", "observedAt") - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, updated) }) + } func TestProjectErrorToCondition(t *testing.T) { @@ -1686,7 +1517,7 @@ func TestProjectErrorToCondition(t *testing.T) { obj, ok, err := ctrl.appInformer.GetIndexer().GetByKey(key) assert.True(t, ok) - require.NoError(t, err) + assert.NoError(t, err) 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) @@ -1706,7 +1537,7 @@ func TestFinalizeProjectDeletion_HasApplications(t *testing.T) { }) err := ctrl.finalizeProjectDeletion(proj) - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, patched) } @@ -1718,13 +1549,13 @@ func TestFinalizeProjectDeletion_DoesNotHaveApplications(t *testing.T) { 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 { - require.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) + assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) } return true, &v1alpha1.AppProject{}, nil }) err := ctrl.finalizeProjectDeletion(proj) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ "metadata": map[string]interface{}{ "finalizers": nil, @@ -1743,7 +1574,7 @@ func TestProcessRequestedAppOperation_FailedNoRetries(t *testing.T) { 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 { - require.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) + assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) } return true, &v1alpha1.Application{}, nil }) @@ -1771,7 +1602,7 @@ func TestProcessRequestedAppOperation_InvalidDestination(t *testing.T) { defer fakeAppCs.Unlock() fakeAppCs.PrependReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { if patchAction, ok := action.(kubetesting.PatchAction); ok { - require.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) + assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) } return true, &v1alpha1.Application{}, nil }) @@ -1797,7 +1628,7 @@ func TestProcessRequestedAppOperation_FailedHasRetries(t *testing.T) { 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 { - require.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) + assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) } return true, &v1alpha1.Application{}, nil }) @@ -1809,7 +1640,7 @@ func TestProcessRequestedAppOperation_FailedHasRetries(t *testing.T) { message, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "message") assert.Contains(t, message, "Retrying attempt #1") retryCount, _, _ := unstructured.NestedFloat64(receivedPatch, "status", "operationState", "retryCount") - assert.InEpsilon(t, float64(1), retryCount, 0.0001) + assert.Equal(t, float64(1), retryCount) } func TestProcessRequestedAppOperation_RunningPreviouslyFailed(t *testing.T) { @@ -1840,7 +1671,7 @@ func TestProcessRequestedAppOperation_RunningPreviouslyFailed(t *testing.T) { 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 { - require.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) + assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) } return true, &v1alpha1.Application{}, nil }) @@ -1873,7 +1704,7 @@ func TestProcessRequestedAppOperation_HasRetriesTerminated(t *testing.T) { 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 { - require.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) + assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) } return true, &v1alpha1.Application{}, nil }) @@ -1884,36 +1715,6 @@ func TestProcessRequestedAppOperation_HasRetriesTerminated(t *testing.T) { assert.Equal(t, string(synccommon.OperationFailed), phase) } -func TestProcessRequestedAppOperation_Successful(t *testing.T) { - app := newFakeApp() - app.Spec.Project = "default" - app.Operation = &v1alpha1.Operation{ - Sync: &v1alpha1.SyncOperation{}, - } - ctrl := newFakeController(&fakeData{ - apps: []runtime.Object{app, &defaultProj}, - manifestResponses: []*apiclient.ManifestResponse{{ - Manifests: []string{}, - }}, - }, 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 { - require.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) - } - return true, &v1alpha1.Application{}, nil - }) - - ctrl.processRequestedAppOperation(app) - - phase, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "phase") - assert.Equal(t, string(synccommon.OperationSucceeded), phase) - ok, level := ctrl.isRefreshRequested(ctrl.toAppKey(app.Name)) - assert.True(t, ok) - assert.Equal(t, CompareWithLatestForceResolve, level) -} - func TestGetAppHosts(t *testing.T) { app := newFakeApp() data := &fakeData{ @@ -1963,16 +1764,13 @@ func TestGetAppHosts(t *testing.T) { }}, }}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, []v1alpha1.HostInfo{{ Name: "minikube", SystemInfo: corev1.NodeSystemInfo{OSImage: "debian"}, - ResourcesInfo: []v1alpha1.HostResourceInfo{ - { - ResourceName: corev1.ResourceCPU, Capacity: 5000, RequestedByApp: 1000, RequestedByNeighbors: 2000, - }, - }, - }}, hosts) + ResourcesInfo: []v1alpha1.HostResourceInfo{{ + ResourceName: corev1.ResourceCPU, Capacity: 5000, RequestedByApp: 1000, RequestedByNeighbors: 2000}, + }}}, hosts) } func TestMetricsExpiration(t *testing.T) { @@ -2090,7 +1888,7 @@ func TestAddControllerNamespace(t *testing.T) { ctrl.processAppRefreshQueueItem() updatedApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).Get(context.Background(), app.Name, metav1.GetOptions{}) - require.NoError(t, err) + 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) { @@ -2109,7 +1907,7 @@ func TestAddControllerNamespace(t *testing.T) { ctrl.processAppRefreshQueueItem() updatedApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(appNamespace).Get(context.Background(), app.Name, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, test.FakeArgoCDNamespace, updatedApp.Status.ControllerNamespace) }) } @@ -2175,16 +1973,3 @@ func TestAppStatusIsReplaced(t *testing.T) { require.True(t, has) require.Nil(t, val) } - -func TestAlreadyAttemptSync(t *testing.T) { - app := newFakeApp() - t.Run("same manifest with sync result", func(t *testing.T) { - attempted, _ := alreadyAttemptedSync(app, "sha", []string{}, false, false) - assert.True(t, attempted) - }) - - t.Run("different manifest with sync result", func(t *testing.T) { - attempted, _ := alreadyAttemptedSync(app, "sha", []string{}, false, true) - assert.False(t, attempted) - }) -} diff --git a/controller/cache/cache.go b/controller/cache/cache.go index 170f5118b521a..df58a5955c5f4 100644 --- a/controller/cache/cache.go +++ b/controller/cache/cache.go @@ -9,7 +9,6 @@ import ( "net/url" "os/exec" "reflect" - "strconv" "strings" "sync" "syscall" @@ -68,10 +67,6 @@ const ( // EnvClusterCacheRetryUseBackoff is the env variable to control whether to use a backoff strategy with the retry during cluster cache sync EnvClusterCacheRetryUseBackoff = "ARGOCD_CLUSTER_CACHE_RETRY_USE_BACKOFF" - - // AnnotationIgnoreResourceUpdates when set to true on an untracked resource, - // argo will apply `ignoreResourceUpdates` configuration on it. - AnnotationIgnoreResourceUpdates = "argocd.argoproj.io/ignore-resource-updates" ) // GitOps engine cluster cache tuning options @@ -125,8 +120,6 @@ type LiveStateCache interface { GetClusterCache(server string) (clustercache.ClusterCache, error) // Executes give callback against resource specified by the key and all its children IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string) bool) error - // Executes give callback against resources specified by the keys and all its children - IterateHierarchyV2(server string, keys []kube.ResourceKey, action func(child appv1.ResourceNode, appName string) bool) error // Returns state of live nodes which correspond for target nodes of specified application. GetManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error) // IterateResources iterates all resource stored in cache @@ -178,8 +171,8 @@ func NewLiveStateCache( metricsServer *metrics.MetricsServer, onObjectUpdated ObjectUpdatedHandler, clusterSharding sharding.ClusterShardingCache, - resourceTracking argo.ResourceTracking, -) LiveStateCache { + resourceTracking argo.ResourceTracking) LiveStateCache { + return &liveStateCache{ appInformer: appInformer, db: db, @@ -336,9 +329,11 @@ func getAppRecursive(r *clustercache.Resource, ns map[kube.ResourceKey]*clusterc return "", true } -var ignoredRefreshResources = map[string]bool{ - "/" + kube.EndpointsKind: true, -} +var ( + ignoredRefreshResources = map[string]bool{ + "/" + kube.EndpointsKind: true, + } +) // skipAppRequeuing checks if the object is an API type which we want to skip requeuing against. // We ignore API types which have a high churn rate, and/or whose updates are irrelevant to the app @@ -358,30 +353,13 @@ func skipResourceUpdate(oldInfo, newInfo *ResourceInfo) bool { // 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, un *unstructured.Unstructured) bool { - // Only hash if the resource belongs to an app OR argocd.argoproj.io/ignore-resource-updates is present and set to true +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 - isTrackedResource := appName != "" || (gvk.Group == application.Group && gvk.Kind == application.ApplicationKind) - - // If the resource is not a tracked resource, we will look up argocd.argoproj.io/ignore-resource-updates and decide - // whether we generate hash or not. - // If argocd.argoproj.io/ignore-resource-updates is presented and is true, return true - // Else return false - if !isTrackedResource { - if val, ok := un.GetAnnotations()[AnnotationIgnoreResourceUpdates]; ok { - applyResourcesUpdate, err := strconv.ParseBool(val) - if err != nil { - applyResourcesUpdate = false - } - return applyResourcesUpdate - } - return false - } - - return isTrackedResource + return appName != "" || (gvk.Group == application.Group && gvk.Kind == application.ApplicationKind) } // isRetryableError is a helper method to see whether an error @@ -401,14 +379,9 @@ 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") } @@ -418,17 +391,12 @@ func isResourceQuotaConflictErr(err error) bool { } func isTransientNetworkErr(err error) bool { - var netErr net.Error - switch { - case errors.As(err, &netErr): - var dnsErr *net.DNSError - var opErr *net.OpError - var unknownNetworkErr net.UnknownNetworkError - var urlErr *url.Error - switch { - case errors.As(err, &dnsErr), errors.As(err, &opErr), errors.As(err, &unknownNetworkErr): + switch err.(type) { + case net.Error: + switch err.(type) { + case *net.DNSError, *net.OpError, net.UnknownNetworkError: return true - case errors.As(err, &urlErr): + case *url.Error: // For a URL error, where it replies "connection closed" // retry again. return strings.Contains(err.Error(), "Connection closed by foreign host") @@ -436,8 +404,7 @@ func isTransientNetworkErr(err error) bool { } errorString := err.Error() - var exitErr *exec.ExitError - if errors.As(err, &exitErr) { + if exitErr, ok := err.(*exec.ExitError); ok { errorString = fmt.Sprintf("%s %s", errorString, exitErr.Stderr) } if strings.Contains(errorString, "net/http: TLS handshake timeout") || @@ -472,10 +439,6 @@ 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) } @@ -530,7 +493,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e gvk := un.GroupVersionKind() - if cacheSettings.ignoreResourceUpdatesEnabled && shouldHashManifest(appName, gvk, un) { + if cacheSettings.ignoreResourceUpdatesEnabled && shouldHashManifest(appName, gvk) { hash, err := generateManifestHash(un, nil, cacheSettings.resourceOverrides, c.ignoreNormalizerOpts) if err != nil { log.Errorf("Failed to generate manifest hash: %v", err) @@ -649,17 +612,6 @@ func (c *liveStateCache) IterateHierarchy(server string, key kube.ResourceKey, a return nil } -func (c *liveStateCache) IterateHierarchyV2(server string, keys []kube.ResourceKey, action func(child appv1.ResourceNode, appName string) bool) error { - clusterInfo, err := c.getSyncedCluster(server) - if err != nil { - return err - } - clusterInfo.IterateHierarchyV2(keys, func(resource *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) bool { - return action(asResourceNode(resource), getApp(resource, namespaceResources)) - }) - return nil -} - func (c *liveStateCache) IterateResources(server string, callback func(res *clustercache.Resource, info *ResourceInfo)) error { clusterInfo, err := c.getSyncedCluster(server) if err != nil { @@ -844,6 +796,7 @@ func (c *liveStateCache) handleModEvent(oldCluster *appv1.Cluster, newCluster *a }() } } + } func (c *liveStateCache) handleDeleteEvent(clusterServer string) { diff --git a/controller/cache/cache_test.go b/controller/cache/cache_test.go index 63935a1e453f4..584f311f2ee30 100644 --- a/controller/cache/cache_test.go +++ b/controller/cache/cache_test.go @@ -9,8 +9,6 @@ import ( "testing" "time" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" apierr "k8s.io/apimachinery/pkg/api/errors" @@ -27,7 +25,6 @@ import ( "github.com/argoproj/argo-cd/v2/common" "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" dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" argosettings "github.com/argoproj/argo-cd/v2/util/settings" @@ -130,7 +127,7 @@ func TestHandleAddEvent_ClusterExcluded(t *testing.T) { Config: appv1.ClusterConfig{Username: "bar"}, }) - assert.Empty(t, clustersCache.clusters) + assert.Len(t, clustersCache.clusters, 0) } func TestHandleDeleteEvent_CacheDeadlock(t *testing.T) { @@ -592,8 +589,7 @@ func TestSkipResourceUpdate(t *testing.T) { assert.False(t, skipResourceUpdate(&ResourceInfo{ manifestHash: hash1_x, Health: &health.HealthStatus{ - Status: health.HealthStatusHealthy, - }, + Status: health.HealthStatusHealthy}, }, &ResourceInfo{ manifestHash: hash3_x, Health: nil, @@ -656,79 +652,3 @@ func TestSkipResourceUpdate(t *testing.T) { })) }) } - -func TestShouldHashManifest(t *testing.T) { - tests := []struct { - name string - appName string - gvk schema.GroupVersionKind - un *unstructured.Unstructured - annotations map[string]string - want bool - }{ - { - name: "appName not empty gvk matches", - appName: "MyApp", - gvk: schema.GroupVersionKind{Group: application.Group, Kind: application.ApplicationKind}, - un: &unstructured.Unstructured{}, - want: true, - }, - { - name: "appName empty", - appName: "", - gvk: schema.GroupVersionKind{Group: application.Group, Kind: application.ApplicationKind}, - un: &unstructured.Unstructured{}, - want: true, - }, - { - name: "appName empty group not match", - appName: "", - gvk: schema.GroupVersionKind{Group: "group1", Kind: application.ApplicationKind}, - un: &unstructured.Unstructured{}, - want: false, - }, - { - name: "appName empty kind not match", - appName: "", - gvk: schema.GroupVersionKind{Group: application.Group, Kind: "kind1"}, - un: &unstructured.Unstructured{}, - want: false, - }, - { - name: "argocd.argoproj.io/ignore-resource-updates=true", - appName: "", - gvk: schema.GroupVersionKind{Group: application.Group, Kind: "kind1"}, - un: &unstructured.Unstructured{}, - annotations: map[string]string{"argocd.argoproj.io/ignore-resource-updates": "true"}, - want: true, - }, - { - name: "argocd.argoproj.io/ignore-resource-updates=invalid", - appName: "", - gvk: schema.GroupVersionKind{Group: application.Group, Kind: "kind1"}, - un: &unstructured.Unstructured{}, - annotations: map[string]string{"argocd.argoproj.io/ignore-resource-updates": "invalid"}, - want: false, - }, - { - name: "argocd.argoproj.io/ignore-resource-updates=false", - appName: "", - gvk: schema.GroupVersionKind{Group: application.Group, Kind: "kind1"}, - un: &unstructured.Unstructured{}, - annotations: map[string]string{"argocd.argoproj.io/ignore-resource-updates": "false"}, - want: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if test.annotations != nil { - test.un.SetAnnotations(test.annotations) - } - got := shouldHashManifest(test.appName, test.gvk, test.un) - if test.want != got { - t.Fatalf("test=%v want %v got %v", test.name, test.want, got) - } - }) - } -} diff --git a/controller/cache/info_test.go b/controller/cache/info_test.go index da47f8e498c63..d0d67244ca4f9 100644 --- a/controller/cache/info_test.go +++ b/controller/cache/info_test.go @@ -314,7 +314,7 @@ status: func TestGetServiceInfo(t *testing.T) { info := &ResourceInfo{} populateNodeInfo(testService, info, []string{}) - assert.Empty(t, info.Info) + assert.Equal(t, 0, len(info.Info)) assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ TargetLabels: map[string]string{"app": "guestbook"}, Ingress: []v1.LoadBalancerIngress{{Hostname: "localhost"}}, @@ -324,7 +324,7 @@ func TestGetServiceInfo(t *testing.T) { func TestGetLinkAnnotatedServiceInfo(t *testing.T) { info := &ResourceInfo{} populateNodeInfo(testLinkAnnotatedService, info, []string{}) - assert.Empty(t, info.Info) + assert.Equal(t, 0, len(info.Info)) assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ TargetLabels: map[string]string{"app": "guestbook"}, Ingress: []v1.LoadBalancerIngress{{Hostname: "localhost"}}, @@ -335,7 +335,7 @@ func TestGetLinkAnnotatedServiceInfo(t *testing.T) { func TestGetIstioVirtualServiceInfo(t *testing.T) { info := &ResourceInfo{} populateNodeInfo(testIstioVirtualService, info, []string{}) - assert.Empty(t, info.Info) + assert.Equal(t, 0, len(info.Info)) require.NotNil(t, info.NetworkingInfo) require.NotNil(t, info.NetworkingInfo.TargetRefs) assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{ @@ -356,7 +356,7 @@ func TestGetIstioVirtualServiceInfo(t *testing.T) { } func TestGetIngressInfo(t *testing.T) { - tests := []struct { + var tests = []struct { Ingress *unstructured.Unstructured }{ {testIngress}, @@ -365,7 +365,7 @@ func TestGetIngressInfo(t *testing.T) { for _, tc := range tests { info := &ResourceInfo{} populateNodeInfo(tc.Ingress, info, []string{}) - assert.Empty(t, info.Info) + assert.Equal(t, 0, len(info.Info)) sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool { return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0 }) @@ -390,7 +390,7 @@ func TestGetIngressInfo(t *testing.T) { func TestGetLinkAnnotatedIngressInfo(t *testing.T) { info := &ResourceInfo{} populateNodeInfo(testLinkAnnotatedIngress, info, []string{}) - assert.Empty(t, info.Info) + assert.Equal(t, 0, len(info.Info)) sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool { return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0 }) @@ -414,7 +414,7 @@ func TestGetLinkAnnotatedIngressInfo(t *testing.T) { func TestGetIngressInfoWildCardPath(t *testing.T) { info := &ResourceInfo{} populateNodeInfo(testIngressWildCardPath, info, []string{}) - assert.Empty(t, info.Info) + assert.Equal(t, 0, len(info.Info)) sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool { return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0 }) @@ -438,7 +438,7 @@ func TestGetIngressInfoWildCardPath(t *testing.T) { func TestGetIngressInfoWithoutTls(t *testing.T) { info := &ResourceInfo{} populateNodeInfo(testIngressWithoutTls, info, []string{}) - assert.Empty(t, info.Info) + assert.Equal(t, 0, len(info.Info)) sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool { return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0 }) @@ -495,7 +495,6 @@ func TestGetIngressInfoWithHost(t *testing.T) { ExternalURLs: []string{"https://107.178.210.11/"}, }, info.NetworkingInfo) } - func TestGetIngressInfoNoHost(t *testing.T) { ingress := strToUnstructured(` apiVersion: extensions/v1beta1 @@ -526,9 +525,8 @@ func TestGetIngressInfoNoHost(t *testing.T) { Name: "helm-guestbook", }}, }, info.NetworkingInfo) - assert.Empty(t, info.NetworkingInfo.ExternalURLs) + assert.Equal(t, len(info.NetworkingInfo.ExternalURLs), 0) } - func TestExternalUrlWithSubPath(t *testing.T) { ingress := strToUnstructured(` apiVersion: networking.k8s.io/v1 @@ -557,7 +555,6 @@ func TestExternalUrlWithSubPath(t *testing.T) { expectedExternalUrls := []string{"https://107.178.210.11/my/sub/path/"} assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs) } - func TestExternalUrlWithMultipleSubPaths(t *testing.T) { ingress := strToUnstructured(` apiVersion: networking.k8s.io/v1 @@ -597,7 +594,6 @@ func TestExternalUrlWithMultipleSubPaths(t *testing.T) { sort.Strings(actualURLs) assert.Equal(t, expectedExternalUrls, actualURLs) } - func TestExternalUrlWithNoSubPath(t *testing.T) { ingress := strToUnstructured(` apiVersion: networking.k8s.io/v1 @@ -664,7 +660,7 @@ func TestCustomLabel(t *testing.T) { info := &ResourceInfo{} populateNodeInfo(configmap, info, []string{"my-label"}) - assert.Empty(t, info.Info) + assert.Equal(t, 0, len(info.Info)) configmap = strToUnstructured(` apiVersion: v1 @@ -677,7 +673,7 @@ func TestCustomLabel(t *testing.T) { info = &ResourceInfo{} populateNodeInfo(configmap, info, []string{"my-label", "other-label"}) - assert.Len(t, info.Info, 1) + assert.Equal(t, 1, len(info.Info)) assert.Equal(t, "my-label", info.Info[0].Name) assert.Equal(t, "value", info.Info[0].Value) @@ -693,7 +689,7 @@ func TestCustomLabel(t *testing.T) { info = &ResourceInfo{} populateNodeInfo(configmap, info, []string{"my-label", "other-label"}) - assert.Len(t, info.Info, 2) + assert.Equal(t, 2, len(info.Info)) assert.Equal(t, "my-label", info.Info[0].Name) assert.Equal(t, "value", info.Info[0].Value) assert.Equal(t, "other-label", info.Info[1].Name) @@ -756,5 +752,5 @@ func TestManifestHash(t *testing.T) { hash, err := generateManifestHash(manifest, ignores, nil, normalizers.IgnoreNormalizerOpts{}) assert.Equal(t, expected, hash) - assert.NoError(t, err) + assert.Nil(t, err) } diff --git a/controller/cache/mocks/LiveStateCache.go b/controller/cache/mocks/LiveStateCache.go index 85a4a298ba4c2..7dc4d6b7710e2 100644 --- a/controller/cache/mocks/LiveStateCache.go +++ b/controller/cache/mocks/LiveStateCache.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v1.0.0. DO NOT EDIT. package mocks @@ -29,15 +29,7 @@ type LiveStateCache struct { func (_m *LiveStateCache) GetClusterCache(server string) (cache.ClusterCache, error) { ret := _m.Called(server) - if len(ret) == 0 { - panic("no return value specified for GetClusterCache") - } - var r0 cache.ClusterCache - var r1 error - if rf, ok := ret.Get(0).(func(string) (cache.ClusterCache, error)); ok { - return rf(server) - } if rf, ok := ret.Get(0).(func(string) cache.ClusterCache); ok { r0 = rf(server) } else { @@ -46,6 +38,7 @@ func (_m *LiveStateCache) GetClusterCache(server string) (cache.ClusterCache, er } } + var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(server) } else { @@ -59,10 +52,6 @@ func (_m *LiveStateCache) GetClusterCache(server string) (cache.ClusterCache, er func (_m *LiveStateCache) GetClustersInfo() []cache.ClusterInfo { ret := _m.Called() - if len(ret) == 0 { - panic("no return value specified for GetClustersInfo") - } - var r0 []cache.ClusterInfo if rf, ok := ret.Get(0).(func() []cache.ClusterInfo); ok { r0 = rf() @@ -79,15 +68,7 @@ func (_m *LiveStateCache) GetClustersInfo() []cache.ClusterInfo { func (_m *LiveStateCache) GetManagedLiveObjs(a *v1alpha1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error) { ret := _m.Called(a, targetObjs) - if len(ret) == 0 { - panic("no return value specified for GetManagedLiveObjs") - } - var r0 map[kube.ResourceKey]*unstructured.Unstructured - var r1 error - if rf, ok := ret.Get(0).(func(*v1alpha1.Application, []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error)); ok { - return rf(a, targetObjs) - } if rf, ok := ret.Get(0).(func(*v1alpha1.Application, []*unstructured.Unstructured) map[kube.ResourceKey]*unstructured.Unstructured); ok { r0 = rf(a, targetObjs) } else { @@ -96,6 +77,7 @@ func (_m *LiveStateCache) GetManagedLiveObjs(a *v1alpha1.Application, targetObjs } } + var r1 error if rf, ok := ret.Get(1).(func(*v1alpha1.Application, []*unstructured.Unstructured) error); ok { r1 = rf(a, targetObjs) } else { @@ -109,15 +91,7 @@ func (_m *LiveStateCache) GetManagedLiveObjs(a *v1alpha1.Application, targetObjs func (_m *LiveStateCache) GetNamespaceTopLevelResources(server string, namespace string) (map[kube.ResourceKey]v1alpha1.ResourceNode, error) { ret := _m.Called(server, namespace) - if len(ret) == 0 { - panic("no return value specified for GetNamespaceTopLevelResources") - } - var r0 map[kube.ResourceKey]v1alpha1.ResourceNode - var r1 error - if rf, ok := ret.Get(0).(func(string, string) (map[kube.ResourceKey]v1alpha1.ResourceNode, error)); ok { - return rf(server, namespace) - } if rf, ok := ret.Get(0).(func(string, string) map[kube.ResourceKey]v1alpha1.ResourceNode); ok { r0 = rf(server, namespace) } else { @@ -126,6 +100,7 @@ func (_m *LiveStateCache) GetNamespaceTopLevelResources(server string, namespace } } + var r1 error if rf, ok := ret.Get(1).(func(string, string) error); ok { r1 = rf(server, namespace) } else { @@ -139,22 +114,14 @@ func (_m *LiveStateCache) GetNamespaceTopLevelResources(server string, namespace func (_m *LiveStateCache) GetVersionsInfo(serverURL string) (string, []kube.APIResourceInfo, error) { ret := _m.Called(serverURL) - if len(ret) == 0 { - panic("no return value specified for GetVersionsInfo") - } - var r0 string - var r1 []kube.APIResourceInfo - var r2 error - if rf, ok := ret.Get(0).(func(string) (string, []kube.APIResourceInfo, error)); ok { - return rf(serverURL) - } if rf, ok := ret.Get(0).(func(string) string); ok { r0 = rf(serverURL) } else { r0 = ret.Get(0).(string) } + var r1 []kube.APIResourceInfo if rf, ok := ret.Get(1).(func(string) []kube.APIResourceInfo); ok { r1 = rf(serverURL) } else { @@ -163,6 +130,7 @@ func (_m *LiveStateCache) GetVersionsInfo(serverURL string) (string, []kube.APIR } } + var r2 error if rf, ok := ret.Get(2).(func(string) error); ok { r2 = rf(serverURL) } else { @@ -176,10 +144,6 @@ func (_m *LiveStateCache) GetVersionsInfo(serverURL string) (string, []kube.APIR func (_m *LiveStateCache) Init() error { ret := _m.Called() - if len(ret) == 0 { - panic("no return value specified for Init") - } - var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -194,21 +158,14 @@ func (_m *LiveStateCache) Init() error { func (_m *LiveStateCache) IsNamespaced(server string, gk schema.GroupKind) (bool, error) { ret := _m.Called(server, gk) - if len(ret) == 0 { - panic("no return value specified for IsNamespaced") - } - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(string, schema.GroupKind) (bool, error)); ok { - return rf(server, gk) - } if rf, ok := ret.Get(0).(func(string, schema.GroupKind) bool); ok { r0 = rf(server, gk) } else { r0 = ret.Get(0).(bool) } + var r1 error if rf, ok := ret.Get(1).(func(string, schema.GroupKind) error); ok { r1 = rf(server, gk) } else { @@ -222,10 +179,6 @@ func (_m *LiveStateCache) IsNamespaced(server string, gk schema.GroupKind) (bool func (_m *LiveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(v1alpha1.ResourceNode, string) bool) error { ret := _m.Called(server, key, action) - if len(ret) == 0 { - panic("no return value specified for IterateHierarchy") - } - var r0 error if rf, ok := ret.Get(0).(func(string, kube.ResourceKey, func(v1alpha1.ResourceNode, string) bool) error); ok { r0 = rf(server, key, action) @@ -236,32 +189,10 @@ func (_m *LiveStateCache) IterateHierarchy(server string, key kube.ResourceKey, return r0 } -// IterateHierarchyV2 provides a mock function with given fields: server, keys, action -func (_m *LiveStateCache) IterateHierarchyV2(server string, keys []kube.ResourceKey, action func(v1alpha1.ResourceNode, string) bool) error { - ret := _m.Called(server, keys, action) - - if len(ret) == 0 { - panic("no return value specified for IterateHierarchyV2") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, []kube.ResourceKey, func(v1alpha1.ResourceNode, string) bool) error); ok { - r0 = rf(server, keys, action) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // IterateResources provides a mock function with given fields: server, callback func (_m *LiveStateCache) IterateResources(server string, callback func(*cache.Resource, *controllercache.ResourceInfo)) error { ret := _m.Called(server, callback) - if len(ret) == 0 { - panic("no return value specified for IterateResources") - } - var r0 error if rf, ok := ret.Get(0).(func(string, func(*cache.Resource, *controllercache.ResourceInfo)) error); ok { r0 = rf(server, callback) @@ -276,10 +207,6 @@ func (_m *LiveStateCache) IterateResources(server string, callback func(*cache.R func (_m *LiveStateCache) Run(ctx context.Context) error { ret := _m.Called(ctx) - if len(ret) == 0 { - panic("no return value specified for Run") - } - var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -289,17 +216,3 @@ func (_m *LiveStateCache) Run(ctx context.Context) error { return r0 } - -// NewLiveStateCache creates a new instance of LiveStateCache. 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 NewLiveStateCache(t interface { - mock.TestingT - Cleanup(func()) -}) *LiveStateCache { - mock := &LiveStateCache{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/controller/clusterinfoupdater.go b/controller/clusterinfoupdater.go index 655ff6a59b759..a2f488534aeb0 100644 --- a/controller/clusterinfoupdater.go +++ b/controller/clusterinfoupdater.go @@ -5,16 +5,13 @@ import ( "fmt" "time" - "github.com/argoproj/argo-cd/v2/common" - + "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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - "github.com/argoproj/argo-cd/v2/util/env" - "github.com/argoproj/argo-cd/v2/controller/metrics" appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" @@ -29,7 +26,9 @@ const ( EnvClusterInfoTimeout = "ARGO_CD_UPDATE_CLUSTER_INFO_TIMEOUT" ) -var clusterInfoTimeout = env.ParseDurationFromEnv(EnvClusterInfoTimeout, defaultSecretUpdateInterval, defaultSecretUpdateInterval, 1*time.Minute) +var ( + clusterInfoTimeout = env.ParseDurationFromEnv(EnvClusterInfoTimeout, defaultSecretUpdateInterval, defaultSecretUpdateInterval, 1*time.Minute) +) type clusterInfoUpdater struct { infoSource metrics.HasClustersInfo @@ -49,8 +48,8 @@ func NewClusterInfoUpdater( cache *appstatecache.Cache, clusterFilter func(cluster *appv1.Cluster) bool, projGetter func(app *appv1.Application) (*appv1.AppProject, error), - namespace string, -) *clusterInfoUpdater { + namespace string) *clusterInfoUpdater { + return &clusterInfoUpdater{infoSource, db, appLister, cache, clusterFilter, projGetter, namespace, time.Time{}} } @@ -102,11 +101,8 @@ func (c *clusterInfoUpdater) updateClusters() { } _ = kube.RunAllAsync(len(clustersFiltered), func(i int) error { cluster := clustersFiltered[i] - 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) + if err := c.updateClusterInfo(ctx, cluster, infoByServer[cluster.Server]); err != nil { + log.Warnf("Failed to save clusters info: %v", err) } return nil }) @@ -118,12 +114,6 @@ func (c *clusterInfoUpdater) updateClusterInfo(ctx context.Context, cluster appv 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 { @@ -139,6 +129,7 @@ func (c *clusterInfoUpdater) getUpdatedClusterInfo(ctx context.Context, apps []* appCount += 1 } } + now := metav1.Now() clusterInfo := appv1.ClusterInfo{ ConnectionState: appv1.ConnectionState{ModifiedAt: &now}, ApplicationsCount: appCount, @@ -165,15 +156,5 @@ func (c *clusterInfoUpdater) getUpdatedClusterInfo(ctx context.Context, apps []* } } - 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 + return c.cache.SetClusterInfo(cluster.Server, &clusterInfo) } diff --git a/controller/clusterinfoupdater_test.go b/controller/clusterinfoupdater_test.go index 989ac630d528a..bac0bb56cbe08 100644 --- a/controller/clusterinfoupdater_test.go +++ b/controller/clusterinfoupdater_test.go @@ -2,7 +2,6 @@ package controller import ( "context" - "errors" "fmt" "testing" "time" @@ -23,7 +22,6 @@ import ( clustercache "github.com/argoproj/gitops-engine/pkg/cache" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/cache" ) @@ -34,7 +32,7 @@ func TestClusterSecretUpdater(t *testing.T) { const updatedK8sVersion = "1.0" now := time.Now() - tests := []struct { + var tests = []struct { LastCacheSyncTime *time.Time SyncError error ExpectedStatus v1alpha1.ConnectionStatus @@ -77,7 +75,7 @@ func TestClusterSecretUpdater(t *testing.T) { appCache := appstate.NewCache(cacheutil.NewCache(cacheutil.NewInMemoryCache(time.Minute)), time.Minute) cluster, err := argoDB.CreateCluster(ctx, &v1alpha1.Cluster{Server: "http://minikube"}) - require.NoError(t, err, "Test prepare test data create cluster failed") + assert.NoError(t, err, "Test prepare test data create cluster failed") for _, test := range tests { info := &clustercache.ClusterInfo{ @@ -91,101 +89,12 @@ func TestClusterSecretUpdater(t *testing.T) { updater := NewClusterInfoUpdater(nil, argoDB, lister, appCache, nil, nil, fakeNamespace) err = updater.updateClusterInfo(context.Background(), *cluster, info) - require.NoError(t, err, "Invoking updateClusterInfo failed.") + assert.NoError(t, err, "Invoking updateClusterInfo failed.") var clusterInfo v1alpha1.ClusterInfo err = appCache.GetClusterInfo(cluster.Server, &clusterInfo) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, updatedK8sVersion, clusterInfo.ServerVersion) 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") - require.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, "1.28", cluster.Labels["argocd.argoproj.io/kubernetes-version"]) - 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, "1.28", cluster.Labels["argocd.argoproj.io/kubernetes-version"]) - 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 f713a574f57d3..b1acac8ac5b9b 100644 --- a/controller/health.go +++ b/controller/health.go @@ -80,7 +80,7 @@ func setApplicationHealth(resources []managedResource, statuses []appv1.Resource app.Status.ResourceHealthSource = appv1.ResourceHealthLocationAppTree } if savedErr != nil && errCount > 1 { - savedErr = fmt.Errorf("see application-controller logs for %d other errors; most recent error was: %w", errCount-1, savedErr) + 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 efaf4b2a8fc80..caa53b446f733 100644 --- a/controller/health_test.go +++ b/controller/health_test.go @@ -8,7 +8,6 @@ import ( synccommon "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -47,23 +46,22 @@ func TestSetApplicationHealth(t *testing.T) { runningPod := resourceFromFile("./testdata/pod-running-restart-always.yaml") resources := []managedResource{{ - Group: "", Version: "v1", Kind: "Pod", Live: &runningPod, - }, { + Group: "", Version: "v1", Kind: "Pod", Live: &runningPod}, { Group: "batch", Version: "v1", Kind: "Job", Live: &failedJob, }} resourceStatuses := initStatuses(resources) healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{}, app, true) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, health.HealthStatusDegraded, healthStatus.Status) - assert.Equal(t, health.HealthStatusHealthy, resourceStatuses[0].Health.Status) - assert.Equal(t, health.HealthStatusDegraded, resourceStatuses[1].Health.Status) + assert.Equal(t, resourceStatuses[0].Health.Status, health.HealthStatusHealthy) + assert.Equal(t, resourceStatuses[1].Health.Status, health.HealthStatusDegraded) // now mark the job as a hook and retry. it should ignore the hook and consider the app healthy failedJob.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"}) healthStatus, err = setApplicationHealth(resources, resourceStatuses, nil, app, true) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status) } @@ -76,7 +74,7 @@ func TestSetApplicationHealth_ResourceHealthNotPersisted(t *testing.T) { resourceStatuses := initStatuses(resources) healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{}, app, false) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, health.HealthStatusDegraded, healthStatus.Status) assert.Nil(t, resourceStatuses[0].Health) @@ -86,12 +84,11 @@ func TestSetApplicationHealth_MissingResource(t *testing.T) { pod := resourceFromFile("./testdata/pod-running-restart-always.yaml") resources := []managedResource{{ - Group: "", Version: "v1", Kind: "Pod", Target: &pod, - }, {}} + Group: "", Version: "v1", Kind: "Pod", Target: &pod}, {}} resourceStatuses := initStatuses(resources) healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{}, app, true) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, health.HealthStatusMissing, healthStatus.Status) } @@ -99,15 +96,14 @@ func TestSetApplicationHealth_MissingResourceNoBuiltHealthCheck(t *testing.T) { cm := resourceFromFile("./testdata/configmap.yaml") resources := []managedResource{{ - Group: "", Version: "v1", Kind: "ConfigMap", Target: &cm, - }} + Group: "", Version: "v1", Kind: "ConfigMap", Target: &cm}} resourceStatuses := initStatuses(resources) t.Run("NoOverride", func(t *testing.T) { healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{}, app, true) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status) - assert.Equal(t, health.HealthStatusMissing, resourceStatuses[0].Health.Status) + assert.Equal(t, resourceStatuses[0].Health.Status, health.HealthStatusMissing) }) t.Run("HasOverride", func(t *testing.T) { @@ -116,7 +112,7 @@ func TestSetApplicationHealth_MissingResourceNoBuiltHealthCheck(t *testing.T) { HealthLua: "some health check", }, }, app, true) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, health.HealthStatusMissing, healthStatus.Status) }) } @@ -162,24 +158,22 @@ return hs`, t.Run("ChildAppDegraded", func(t *testing.T) { degradedApp := newAppLiveObj(health.HealthStatusDegraded) resources := []managedResource{{ - Group: application.Group, Version: "v1alpha1", Kind: application.ApplicationKind, Live: degradedApp, - }, {}} + Group: application.Group, Version: "v1alpha1", Kind: application.ApplicationKind, Live: degradedApp}, {}} resourceStatuses := initStatuses(resources) healthStatus, err := setApplicationHealth(resources, resourceStatuses, overrides, app, true) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, health.HealthStatusDegraded, healthStatus.Status) }) t.Run("ChildAppMissing", func(t *testing.T) { degradedApp := newAppLiveObj(health.HealthStatusMissing) resources := []managedResource{{ - Group: application.Group, Version: "v1alpha1", Kind: application.ApplicationKind, Live: degradedApp, - }, {}} + Group: application.Group, Version: "v1alpha1", Kind: application.ApplicationKind, Live: degradedApp}, {}} resourceStatuses := initStatuses(resources) healthStatus, err := setApplicationHealth(resources, resourceStatuses, overrides, app, true) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status) }) } diff --git a/controller/hook.go b/controller/hook.go index 5c391114ab9bb..0c019ac6a1e08 100644 --- a/controller/hook.go +++ b/controller/hook.go @@ -51,7 +51,7 @@ func (ctrl *ApplicationController) executePostDeleteHooks(app *v1alpha1.Applicat revisions = append(revisions, src.TargetRevision) } - targets, _, _, err := ctrl.appStateManager.GetRepoObjs(app, app.Spec.GetSources(), appLabelKey, revisions, false, false, false, proj, false) + targets, _, err := ctrl.appStateManager.GetRepoObjs(app, app.Spec.GetSources(), appLabelKey, revisions, false, false, false, proj) if err != nil { return false, err } @@ -98,18 +98,6 @@ func (ctrl *ApplicationController) executePostDeleteHooks(app *v1alpha1.Applicat if err != nil { return false, err } - if hookHealth == nil { - logCtx.WithFields(log.Fields{ - "group": obj.GroupVersionKind().Group, - "version": obj.GroupVersionKind().Version, - "kind": obj.GetKind(), - "name": obj.GetName(), - "namespace": obj.GetNamespace(), - }).Info("No health check defined for resource, considering it healthy") - hookHealth = &health.HealthStatus{ - Status: health.HealthStatusHealthy, - } - } if hookHealth.Status == health.HealthStatusProgressing { progressingHooksCnt++ } @@ -140,11 +128,6 @@ func (ctrl *ApplicationController) cleanupPostDeleteHooks(liveObjs map[kube.Reso if err != nil { return false, err } - if hookHealth == nil { - hookHealth = &health.HealthStatus{ - Status: health.HealthStatusHealthy, - } - } if health.IsWorse(aggregatedHealth, hookHealth.Status) { aggregatedHealth = hookHealth.Status } @@ -165,6 +148,7 @@ func (ctrl *ApplicationController) cleanupPostDeleteHooks(liveObjs map[kube.Reso } } } + } if pendingDeletionCount > 0 { logCtx.Infof("Waiting for %d post-delete hooks to be deleted", pendingDeletionCount) diff --git a/controller/metrics/clustercollector.go b/controller/metrics/clustercollector.go index edbe8c2581f18..bebbfef62d807 100644 --- a/controller/metrics/clustercollector.go +++ b/controller/metrics/clustercollector.go @@ -87,6 +87,7 @@ func (c *clusterCollector) Describe(ch chan<- *prometheus.Desc) { } func (c *clusterCollector) Collect(ch chan<- prometheus.Metric) { + now := time.Now() for _, c := range c.info { defaultValues := []string{c.Server} diff --git a/controller/metrics/metrics.go b/controller/metrics/metrics.go index a9df75aff8015..94405b51eac75 100644 --- a/controller/metrics/metrics.go +++ b/controller/metrics/metrics.go @@ -6,7 +6,7 @@ import ( "fmt" "net/http" "os" - "slices" + "regexp" "strconv" "time" @@ -22,7 +22,6 @@ import ( 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" - metricsutil "github.com/argoproj/argo-cd/v2/util/metrics" "github.com/argoproj/argo-cd/v2/util/profile" ctrl_metrics "sigs.k8s.io/controller-runtime/pkg/metrics" @@ -55,8 +54,7 @@ const ( var ( descAppDefaultLabels = []string{"namespace", "name", "project"} - descAppLabels *prometheus.Desc - descAppConditions *prometheus.Desc + descAppLabels *prometheus.Desc descAppInfo = prometheus.NewDesc( "argocd_app_info", @@ -64,22 +62,21 @@ var ( append(descAppDefaultLabels, "autosync_enabled", "repo", "dest_server", "dest_namespace", "sync_status", "health_status", "operation"), nil, ) - - // Deprecated + // DEPRECATED descAppCreated = prometheus.NewDesc( "argocd_app_created_time", "Creation time in unix timestamp for an application.", descAppDefaultLabels, nil, ) - // Deprecated: superseded by sync_status label in argocd_app_info + // DEPRECATED: superseded by sync_status label in argocd_app_info descAppSyncStatusCode = prometheus.NewDesc( "argocd_app_sync_status", "The application current sync status.", append(descAppDefaultLabels, "sync_status"), nil, ) - // Deprecated: superseded by health_status label in argocd_app_info + // DEPRECATED: superseded by health_status label in argocd_app_info descAppHealthStatus = prometheus.NewDesc( "argocd_app_health_status", "The application current health status.", @@ -116,7 +113,7 @@ var ( reconcileHistogram = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "argocd_app_reconcile", - Help: "Application reconciliation performance in seconds.", + Help: "Application reconciliation performance.", // Buckets chosen after observing a ~2100ms mean reconcile time Buckets: []float64{0.25, .5, 1, 2, 4, 8, 16}, }, @@ -147,14 +144,14 @@ var ( ) // NewMetricsServer returns a new prometheus server which collects application metrics -func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFilter func(obj interface{}) bool, healthCheck func(r *http.Request) error, appLabels []string, appConditions []string) (*MetricsServer, error) { +func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFilter func(obj interface{}) bool, healthCheck func(r *http.Request) error, appLabels []string) (*MetricsServer, error) { hostname, err := os.Hostname() if err != nil { return nil, err } if len(appLabels) > 0 { - normalizedLabels := metricsutil.NormalizeLabels("label", appLabels) + normalizedLabels := normalizeLabels("label", appLabels) descAppLabels = prometheus.NewDesc( "argocd_app_labels", "Argo Application labels converted to Prometheus labels", @@ -163,17 +160,8 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFil ) } - if len(appConditions) > 0 { - descAppConditions = prometheus.NewDesc( - "argocd_app_condition", - "Report application conditions.", - append(descAppDefaultLabels, "condition"), - nil, - ) - } - mux := http.NewServeMux() - registry := NewAppRegistry(appLister, appFilter, appLabels, appConditions) + registry := NewAppRegistry(appLister, appFilter, appLabels) mux.Handle(MetricsPath, promhttp.HandlerFor(prometheus.Gatherers{ // contains app controller specific metrics @@ -215,6 +203,20 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFil }, nil } +// Prometheus invalid labels, more info: https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels. +var invalidPromLabelChars = regexp.MustCompile(`[^a-zA-Z0-9_]`) + +func normalizeLabels(prefix string, appLabels []string) []string { + results := []string{} + for _, label := range appLabels { + //prometheus labels don't accept dash in their name + curr := invalidPromLabelChars.ReplaceAllString(label, "_") + result := fmt.Sprintf("%s_%s", prefix, curr) + results = append(results, result) + } + return results +} + func (m *MetricsServer) RegisterClustersInfoSource(ctx context.Context, source HasClustersInfo) { collector := &clusterCollector{infoSource: source} go collector.Run(ctx) @@ -305,26 +307,24 @@ func (m *MetricsServer) SetExpiration(cacheExpiration time.Duration) error { } type appCollector struct { - store applister.ApplicationLister - appFilter func(obj interface{}) bool - appLabels []string - appConditions []string + store applister.ApplicationLister + appFilter func(obj interface{}) bool + appLabels []string } // NewAppCollector returns a prometheus collector for application metrics -func NewAppCollector(appLister applister.ApplicationLister, appFilter func(obj interface{}) bool, appLabels []string, appConditions []string) prometheus.Collector { +func NewAppCollector(appLister applister.ApplicationLister, appFilter func(obj interface{}) bool, appLabels []string) prometheus.Collector { return &appCollector{ - store: appLister, - appFilter: appFilter, - appLabels: appLabels, - appConditions: appConditions, + store: appLister, + appFilter: appFilter, + appLabels: appLabels, } } // NewAppRegistry creates a new prometheus registry that collects applications -func NewAppRegistry(appLister applister.ApplicationLister, appFilter func(obj interface{}) bool, appLabels []string, appConditions []string) *prometheus.Registry { +func NewAppRegistry(appLister applister.ApplicationLister, appFilter func(obj interface{}) bool, appLabels []string) *prometheus.Registry { registry := prometheus.NewRegistry() - registry.MustRegister(NewAppCollector(appLister, appFilter, appLabels, appConditions)) + registry.MustRegister(NewAppCollector(appLister, appFilter, appLabels)) return registry } @@ -333,9 +333,6 @@ func (c *appCollector) Describe(ch chan<- *prometheus.Desc) { if len(c.appLabels) > 0 { ch <- descAppLabels } - if len(c.appConditions) > 0 { - ch <- descAppConditions - } ch <- descAppInfo ch <- descAppSyncStatusCode ch <- descAppHealthStatus @@ -400,19 +397,6 @@ func (c *appCollector) collectApps(ch chan<- prometheus.Metric, app *argoappv1.A addGauge(descAppLabels, 1, labelValues...) } - if len(c.appConditions) > 0 { - conditionCount := make(map[string]int) - for _, condition := range app.Status.Conditions { - if slices.Contains(c.appConditions, condition.Type) { - conditionCount[condition.Type]++ - } - } - - for conditionType, count := range conditionCount { - addGauge(descAppConditions, float64(count), conditionType) - } - } - // Deprecated controller metrics if os.Getenv(EnvVarLegacyControllerMetrics) == "true" { addGauge(descAppCreated, float64(app.CreationTimestamp.Unix())) diff --git a/controller/metrics/metrics_test.go b/controller/metrics/metrics_test.go index 44a6524ed7d85..23628c38347a5 100644 --- a/controller/metrics/metrics_test.go +++ b/controller/metrics/metrics_test.go @@ -2,6 +2,7 @@ package metrics import ( "context" + "fmt" "log" "net/http" "net/http/httptest" @@ -12,7 +13,6 @@ import ( gitopsCache "github.com/argoproj/gitops-engine/pkg/cache" "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/cache" @@ -116,41 +116,6 @@ status: status: Degraded ` -const fakeApp4 = ` -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: my-app-4 - namespace: argocd - labels: - team-name: my-team - team-bu: bu-id - argoproj.io/cluster: test-cluster -spec: - destination: - namespace: dummy-namespace - server: https://localhost:6443 - project: important-project - source: - path: some/path - repoURL: https://github.com/argoproj/argocd-example-apps.git -status: - sync: - status: OutOfSync - health: - status: Degraded - conditions: - - lastTransitionTime: "2024-08-07T12:25:40Z" - message: Application has 1 orphaned resources - type: OrphanedResourceWarning - - lastTransitionTime: "2024-08-07T12:25:40Z" - message: Resource Pod standalone-pod is excluded in the settings - type: ExcludedResourceWarning - - lastTransitionTime: "2024-08-07T12:25:40Z" - message: Resource Endpoint raw-endpoint is excluded in the settings - type: ExcludedResourceWarning -` - const fakeDefaultApp = ` apiVersion: argoproj.io/v1alpha1 kind: Application @@ -214,7 +179,7 @@ func newFakeLister(fakeAppYAMLs ...string) (context.CancelFunc, applister.Applic func testApp(t *testing.T, fakeAppYAMLs []string, expectedResponse string) { t.Helper() - testMetricServer(t, fakeAppYAMLs, expectedResponse, []string{}, []string{}) + testMetricServer(t, fakeAppYAMLs, expectedResponse, []string{}) } type fakeClusterInfo struct { @@ -229,17 +194,15 @@ type TestMetricServerConfig struct { FakeAppYAMLs []string ExpectedResponse string AppLabels []string - AppConditions []string ClustersInfo []gitopsCache.ClusterInfo } -func testMetricServer(t *testing.T, fakeAppYAMLs []string, expectedResponse string, appLabels []string, appConditions []string) { +func testMetricServer(t *testing.T, fakeAppYAMLs []string, expectedResponse string, appLabels []string) { t.Helper() cfg := TestMetricServerConfig{ FakeAppYAMLs: fakeAppYAMLs, ExpectedResponse: expectedResponse, AppLabels: appLabels, - AppConditions: appConditions, ClustersInfo: []gitopsCache.ClusterInfo{}, } runTest(t, cfg) @@ -249,8 +212,8 @@ func runTest(t *testing.T, cfg TestMetricServerConfig) { t.Helper() cancel, appLister := newFakeLister(cfg.FakeAppYAMLs...) defer cancel() - metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, cfg.AppLabels, cfg.AppConditions) - require.NoError(t, err) + metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, cfg.AppLabels) + assert.NoError(t, err) if len(cfg.ClustersInfo) > 0 { ci := &fakeClusterInfo{clustersInfo: cfg.ClustersInfo} @@ -262,10 +225,10 @@ func runTest(t *testing.T, cfg TestMetricServerConfig) { } req, err := http.NewRequest(http.MethodGet, "/metrics", nil) - require.NoError(t, err) + assert.NoError(t, err) rr := httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, rr.Code, http.StatusOK) body := rr.Body.String() assertMetricsPrinted(t, cfg.ExpectedResponse, body) } @@ -340,61 +303,7 @@ argocd_app_labels{label_non_existing="",name="my-app-3",namespace="argocd",proje for _, c := range cases { c := c t.Run(c.description, func(t *testing.T) { - testMetricServer(t, c.applications, c.responseContains, c.metricLabels, []string{}) - }) - } -} - -func TestMetricConditions(t *testing.T) { - type testCases struct { - testCombination - description string - metricConditions []string - } - cases := []testCases{ - { - description: "metric will only output OrphanedResourceWarning", - metricConditions: []string{"OrphanedResourceWarning"}, - testCombination: testCombination{ - applications: []string{fakeApp4}, - responseContains: ` -# HELP argocd_app_condition Report application conditions. -# TYPE argocd_app_condition gauge -argocd_app_condition{condition="OrphanedResourceWarning",name="my-app-4",namespace="argocd",project="important-project"} 1 -`, - }, - }, - { - description: "metric will only output ExcludedResourceWarning", - metricConditions: []string{"ExcludedResourceWarning"}, - testCombination: testCombination{ - applications: []string{fakeApp4}, - responseContains: ` -# HELP argocd_app_condition Report application conditions. -# TYPE argocd_app_condition gauge -argocd_app_condition{condition="ExcludedResourceWarning",name="my-app-4",namespace="argocd",project="important-project"} 2 -`, - }, - }, - { - description: "metric will only output both OrphanedResourceWarning and ExcludedResourceWarning", - metricConditions: []string{"ExcludedResourceWarning", "OrphanedResourceWarning"}, - testCombination: testCombination{ - applications: []string{fakeApp4}, - responseContains: ` -# HELP argocd_app_condition Report application conditions. -# TYPE argocd_app_condition gauge -argocd_app_condition{condition="OrphanedResourceWarning",name="my-app-4",namespace="argocd",project="important-project"} 1 -argocd_app_condition{condition="ExcludedResourceWarning",name="my-app-4",namespace="argocd",project="important-project"} 2 -`, - }, - }, - } - - for _, c := range cases { - c := c - t.Run(c.description, func(t *testing.T) { - testMetricServer(t, c.applications, c.responseContains, []string{}, c.metricConditions) + testMetricServer(t, c.applications, c.responseContains, c.metricLabels) }) } } @@ -426,8 +335,8 @@ argocd_app_sync_status{name="my-app",namespace="argocd",project="important-proje func TestMetricsSyncCounter(t *testing.T) { cancel, appLister := newFakeLister() defer cancel() - metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}) - require.NoError(t, err) + metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}) + assert.NoError(t, err) appSyncTotal := ` # HELP argocd_app_sync_total Number of application syncs. @@ -445,10 +354,10 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationSucceeded}) req, err := http.NewRequest(http.MethodGet, "/metrics", nil) - require.NoError(t, err) + assert.NoError(t, err) rr := httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, rr.Code, http.StatusOK) body := rr.Body.String() log.Println(body) assertMetricsPrinted(t, appSyncTotal, body) @@ -461,28 +370,28 @@ func assertMetricsPrinted(t *testing.T, expectedLines, body string) { if line == "" { continue } - assert.Contains(t, body, line, "expected metrics mismatch for line: %s", line) + assert.Contains(t, body, line, fmt.Sprintf("expected metrics mismatch for line: %s", line)) } } -// assertMetricsNotPrinted +// assertMetricNotPrinted func assertMetricsNotPrinted(t *testing.T, expectedLines, body string) { for _, line := range strings.Split(expectedLines, "\n") { if line == "" { continue } - assert.NotContains(t, body, expectedLines) + assert.False(t, strings.Contains(body, expectedLines)) } } func TestReconcileMetrics(t *testing.T) { cancel, appLister := newFakeLister() defer cancel() - metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}) - require.NoError(t, err) + metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}) + assert.NoError(t, err) appReconcileMetrics := ` -# HELP argocd_app_reconcile Application reconciliation performance in seconds. +# HELP argocd_app_reconcile Application reconciliation performance. # TYPE argocd_app_reconcile histogram argocd_app_reconcile_bucket{dest_server="https://localhost:6443",namespace="argocd",le="0.25"} 0 argocd_app_reconcile_bucket{dest_server="https://localhost:6443",namespace="argocd",le="0.5"} 0 @@ -499,10 +408,10 @@ argocd_app_reconcile_count{dest_server="https://localhost:6443",namespace="argoc metricsServ.IncReconcile(fakeApp, 5*time.Second) req, err := http.NewRequest(http.MethodGet, "/metrics", nil) - require.NoError(t, err) + assert.NoError(t, err) rr := httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, rr.Code, http.StatusOK) body := rr.Body.String() log.Println(body) assertMetricsPrinted(t, appReconcileMetrics, body) @@ -511,8 +420,8 @@ argocd_app_reconcile_count{dest_server="https://localhost:6443",namespace="argoc func TestMetricsReset(t *testing.T) { cancel, appLister := newFakeLister() defer cancel() - metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}) - require.NoError(t, err) + metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}) + assert.NoError(t, err) appSyncTotal := ` # HELP argocd_app_sync_total Number of application syncs. @@ -523,58 +432,58 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa ` req, err := http.NewRequest(http.MethodGet, "/metrics", nil) - require.NoError(t, err) + assert.NoError(t, err) rr := httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, rr.Code, http.StatusOK) body := rr.Body.String() assertMetricsPrinted(t, appSyncTotal, body) err = metricsServ.SetExpiration(time.Second) - require.NoError(t, err) + assert.NoError(t, err) time.Sleep(2 * time.Second) req, err = http.NewRequest(http.MethodGet, "/metrics", nil) - require.NoError(t, err) + assert.NoError(t, err) rr = httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, rr.Code, http.StatusOK) body = rr.Body.String() log.Println(body) assertMetricsNotPrinted(t, appSyncTotal, body) err = metricsServ.SetExpiration(time.Second) - require.Error(t, err) + assert.Error(t, err) } func TestWorkqueueMetrics(t *testing.T) { cancel, appLister := newFakeLister() defer cancel() - metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}) - require.NoError(t, err) + metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}) + assert.NoError(t, err) expectedMetrics := ` # TYPE workqueue_adds_total counter -workqueue_adds_total{controller="test",name="test"} +workqueue_adds_total{name="test"} # TYPE workqueue_depth gauge -workqueue_depth{controller="test",name="test"} +workqueue_depth{name="test"} # TYPE workqueue_longest_running_processor_seconds gauge -workqueue_longest_running_processor_seconds{controller="test",name="test"} +workqueue_longest_running_processor_seconds{name="test"} # TYPE workqueue_queue_duration_seconds histogram # TYPE workqueue_unfinished_work_seconds gauge -workqueue_unfinished_work_seconds{controller="test",name="test"} +workqueue_unfinished_work_seconds{name="test"} # TYPE workqueue_work_duration_seconds histogram ` workqueue.NewNamed("test") req, err := http.NewRequest(http.MethodGet, "/metrics", nil) - require.NoError(t, err) + assert.NoError(t, err) rr := httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, rr.Code, http.StatusOK) body := rr.Body.String() log.Println(body) assertMetricsPrinted(t, expectedMetrics, body) @@ -583,8 +492,8 @@ workqueue_unfinished_work_seconds{controller="test",name="test"} func TestGoMetrics(t *testing.T) { cancel, appLister := newFakeLister() defer cancel() - metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}) - require.NoError(t, err) + metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}) + assert.NoError(t, err) expectedMetrics := ` # TYPE go_gc_duration_seconds summary @@ -603,10 +512,10 @@ go_threads ` req, err := http.NewRequest(http.MethodGet, "/metrics", nil) - require.NoError(t, err) + assert.NoError(t, err) rr := httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, rr.Code, http.StatusOK) body := rr.Body.String() log.Println(body) assertMetricsPrinted(t, expectedMetrics, body) diff --git a/controller/sharding/cache.go b/controller/sharding/cache.go index 4a750e3545524..3818e7381f3ab 100644 --- a/controller/sharding/cache.go +++ b/controller/sharding/cache.go @@ -3,23 +3,18 @@ package sharding import ( "sync" - log "github.com/sirupsen/logrus" - "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) + Init(clusters *v1alpha1.ClusterList) 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 { @@ -27,7 +22,6 @@ type ClusterSharding struct { Replicas int Shards map[string]int Clusters map[string]*v1alpha1.Cluster - Apps map[string]*v1alpha1.Application lock sync.RWMutex getClusterShard DistributionFunction } @@ -39,12 +33,11 @@ func NewClusterSharding(_ db.ArgoDB, shard, replicas int, shardingAlgorithm stri 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) + distributionFunction = GetDistributionFunction(clusterSharding.GetClusterAccessor(), shardingAlgorithm, replicas) } else { log.Info("Processing all cluster shards") } @@ -52,7 +45,7 @@ func NewClusterSharding(_ db.ArgoDB, shard, replicas int, shardingAlgorithm stri return clusterSharding } -// IsManagedCluster returns whether or not the cluster should be processed by a given shard. +// 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() @@ -69,7 +62,7 @@ func (s *ClusterSharding) IsManagedCluster(c *v1alpha1.Cluster) bool { return clusterShard == s.Shard } -func (sharding *ClusterSharding) Init(clusters *v1alpha1.ClusterList, apps *v1alpha1.ApplicationList) { +func (sharding *ClusterSharding) Init(clusters *v1alpha1.ClusterList) { sharding.lock.Lock() defer sharding.lock.Unlock() newClusters := make(map[string]*v1alpha1.Cluster, len(clusters.Items)) @@ -78,13 +71,6 @@ func (sharding *ClusterSharding) Init(clusters *v1alpha1.ClusterList, apps *v1al 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() } @@ -187,8 +173,7 @@ func hasShardingUpdates(old, new *v1alpha1.Cluster) bool { 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 { +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)) @@ -198,68 +183,3 @@ func (d *ClusterSharding) getClusterAccessor() clusterAccessor { 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 index 34318ec259872..ed3da752e7279 100644 --- a/controller/sharding/cache_test.go +++ b/controller/sharding/cache_test.go @@ -3,10 +3,9 @@ package sharding import ( "testing" - "github.com/stretchr/testify/assert" - "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 { @@ -59,7 +58,7 @@ func TestClusterSharding_Add(t *testing.T) { assert.True(t, ok) assert.Equal(t, 0, myClusterDistribution) - assert.Len(t, distribution, 2) + assert.Equal(t, 2, len(distribution)) } func TestClusterSharding_AddRoundRobin_Redistributes(t *testing.T) { @@ -95,7 +94,7 @@ func TestClusterSharding_AddRoundRobin_Redistributes(t *testing.T) { assert.True(t, ok) assert.Equal(t, 1, clusterDistributionB) - assert.Len(t, distributionBefore, 2) + assert.Equal(t, 2, len(distributionBefore)) clusterC := v1alpha1.Cluster{ ID: "2", @@ -140,17 +139,11 @@ func TestClusterSharding_Delete(t *testing.T) { }, }, }, - &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.Len(t, distribution, 1) + assert.Equal(t, 1, len(distribution)) } func TestClusterSharding_Update(t *testing.T) { @@ -171,16 +164,10 @@ func TestClusterSharding_Update(t *testing.T) { }, }, }, - &v1alpha1.ApplicationList{ - Items: []v1alpha1.Application{ - createApp("app2", "https://127.0.0.1:6443"), - createApp("app1", "https://kubernetes.default.svc"), - }, - }, ) distributionBefore := sharding.GetDistribution() - assert.Len(t, distributionBefore, 2) + assert.Equal(t, 2, len(distributionBefore)) distributionA, ok := distributionBefore["https://kubernetes.default.svc"] assert.True(t, ok) @@ -195,7 +182,7 @@ func TestClusterSharding_Update(t *testing.T) { }) distributionAfter := sharding.GetDistribution() - assert.Len(t, distributionAfter, 2) + assert.Equal(t, 2, len(distributionAfter)) distributionA, ok = distributionAfter["https://kubernetes.default.svc"] assert.True(t, ok) @@ -220,16 +207,10 @@ func TestClusterSharding_UpdateServerName(t *testing.T) { }, }, }, - &v1alpha1.ApplicationList{ - Items: []v1alpha1.Application{ - createApp("app2", "https://127.0.0.1:6443"), - createApp("app1", "https://kubernetes.default.svc"), - }, - }, ) distributionBefore := sharding.GetDistribution() - assert.Len(t, distributionBefore, 2) + assert.Equal(t, 2, len(distributionBefore)) distributionA, ok := distributionBefore["https://kubernetes.default.svc"] assert.True(t, ok) @@ -244,7 +225,7 @@ func TestClusterSharding_UpdateServerName(t *testing.T) { }) distributionAfter := sharding.GetDistribution() - assert.Len(t, distributionAfter, 2) + 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 @@ -270,12 +251,6 @@ func TestClusterSharding_IsManagedCluster(t *testing.T) { }, }, }, - &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{ @@ -303,12 +278,6 @@ func TestClusterSharding_IsManagedCluster(t *testing.T) { }, }, }, - &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{ @@ -320,6 +289,7 @@ func TestClusterSharding_IsManagedCluster(t *testing.T) { ID: "2", Server: "https://127.0.0.1:6443", })) + } func TestClusterSharding_ClusterShardOfResourceShouldNotBeChanged(t *testing.T) { @@ -357,15 +327,9 @@ func TestClusterSharding_ClusterShardOfResourceShouldNotBeChanged(t *testing.T) *clusterWithToBigValue, }, }, - &v1alpha1.ApplicationList{ - Items: []v1alpha1.Application{ - createApp("app2", "https://127.0.0.1:6443"), - createApp("app1", "https://kubernetes.default.svc"), - }, - }, ) distribution := sharding.GetDistribution() - assert.Len(t, distribution, 3) + assert.Equal(t, 3, len(distribution)) assert.Nil(t, sharding.Clusters[clusterWithNil.Server].Shard) diff --git a/controller/sharding/consistent/consistent.go b/controller/sharding/consistent/consistent.go deleted file mode 100644 index bb9a5499264b2..0000000000000 --- a/controller/sharding/consistent/consistent.go +++ /dev/null @@ -1,275 +0,0 @@ -// An implementation of Consistent Hashing and -// Consistent Hashing With Bounded Loads. -// -// https://en.wikipedia.org/wiki/Consistent_hashing -// -// https://research.googleblog.com/2017/04/consistent-hashing-with-bounded-loads.html -package consistent - -import ( - "encoding/binary" - "errors" - "fmt" - "math" - "sync" - "sync/atomic" - - "github.com/google/btree" - - blake2b "github.com/minio/blake2b-simd" -) - -// OptimalExtraCapacityFactor extra factor capacity (1 + ε). The ideal balance -// between keeping the shards uniform while also keeping consistency when -// changing shard numbers. -const OptimalExtraCapacityFactor = 1.25 - -var ErrNoHosts = errors.New("no hosts added") - -type Host struct { - Name string - Load int64 -} - -type Consistent struct { - servers map[uint64]string - clients *btree.BTree - loadMap map[string]*Host - totalLoad int64 - replicationFactor int - - sync.RWMutex -} - -type item struct { - value uint64 -} - -func (i item) Less(than btree.Item) bool { - return i.value < than.(item).value -} - -func New() *Consistent { - return &Consistent{ - servers: map[uint64]string{}, - clients: btree.New(2), - loadMap: map[string]*Host{}, - replicationFactor: 1000, - } -} - -func NewWithReplicationFactor(replicationFactor int) *Consistent { - return &Consistent{ - servers: map[uint64]string{}, - clients: btree.New(2), - loadMap: map[string]*Host{}, - replicationFactor: replicationFactor, - } -} - -func (c *Consistent) Add(server string) { - c.Lock() - defer c.Unlock() - - if _, ok := c.loadMap[server]; ok { - return - } - - c.loadMap[server] = &Host{Name: server, Load: 0} - for i := 0; i < c.replicationFactor; i++ { - h := c.hash(fmt.Sprintf("%s%d", server, i)) - c.servers[h] = server - c.clients.ReplaceOrInsert(item{h}) - } -} - -// Get returns the server that owns the given client. -// As described in https://en.wikipedia.org/wiki/Consistent_hashing -// It returns ErrNoHosts if the ring has no servers in it. -func (c *Consistent) Get(client string) (string, error) { - c.RLock() - defer c.RUnlock() - - if c.clients.Len() == 0 { - return "", ErrNoHosts - } - - h := c.hash(client) - var foundItem btree.Item - c.clients.AscendGreaterOrEqual(item{h}, func(i btree.Item) bool { - foundItem = i - return false // stop the iteration - }) - - if foundItem == nil { - // If no host found, wrap around to the first one. - foundItem = c.clients.Min() - } - - host := c.servers[foundItem.(item).value] - - return host, nil -} - -// GetLeast returns the least loaded host that can serve the key. -// It uses Consistent Hashing With Bounded loads. -// https://research.googleblog.com/2017/04/consistent-hashing-with-bounded-loads.html -// It returns ErrNoHosts if the ring has no hosts in it. -func (c *Consistent) GetLeast(client string) (string, error) { - c.RLock() - defer c.RUnlock() - - if c.clients.Len() == 0 { - return "", ErrNoHosts - } - h := c.hash(client) - for { - var foundItem btree.Item - c.clients.AscendGreaterOrEqual(item{h}, func(bItem btree.Item) bool { - if h != bItem.(item).value { - foundItem = bItem - return false // stop the iteration - } - return true - }) - - if foundItem == nil { - // If no host found, wrap around to the first one. - foundItem = c.clients.Min() - } - key := c.clients.Get(foundItem) - if key != nil { - host := c.servers[key.(item).value] - if c.loadOK(host) { - return host, nil - } - h = key.(item).value - } else { - return client, nil - } - } -} - -// Sets the load of `server` to the given `load` -func (c *Consistent) UpdateLoad(server string, load int64) { - c.Lock() - defer c.Unlock() - - if _, ok := c.loadMap[server]; !ok { - return - } - c.totalLoad -= c.loadMap[server].Load - c.loadMap[server].Load = load - c.totalLoad += load -} - -// Increments the load of host by 1 -// -// should only be used with if you obtained a host with GetLeast -func (c *Consistent) Inc(server string) { - c.Lock() - defer c.Unlock() - - if _, ok := c.loadMap[server]; !ok { - return - } - atomic.AddInt64(&c.loadMap[server].Load, 1) - atomic.AddInt64(&c.totalLoad, 1) -} - -// Decrements the load of host by 1 -// -// should only be used with if you obtained a host with GetLeast -func (c *Consistent) Done(server string) { - c.Lock() - defer c.Unlock() - - if _, ok := c.loadMap[server]; !ok { - return - } - atomic.AddInt64(&c.loadMap[server].Load, -1) - atomic.AddInt64(&c.totalLoad, -1) -} - -// Deletes host from the ring -func (c *Consistent) Remove(server string) bool { - c.Lock() - defer c.Unlock() - - for i := 0; i < c.replicationFactor; i++ { - h := c.hash(fmt.Sprintf("%s%d", server, i)) - delete(c.servers, h) - c.delSlice(h) - } - delete(c.loadMap, server) - return true -} - -// Return the list of servers in the ring -func (c *Consistent) Servers() (servers []string) { - c.RLock() - defer c.RUnlock() - for k := range c.loadMap { - servers = append(servers, k) - } - return servers -} - -// Returns the loads of all the hosts -func (c *Consistent) GetLoads() map[string]int64 { - loads := map[string]int64{} - - for k, v := range c.loadMap { - loads[k] = v.Load - } - return loads -} - -// Returns the maximum load of the single host -// which is: -// (total_load/number_of_hosts)*1.25 -// total_load = is the total number of active requests served by hosts -// for more info: -// https://research.googleblog.com/2017/04/consistent-hashing-with-bounded-loads.html -func (c *Consistent) MaxLoad() int64 { - if c.totalLoad == 0 { - c.totalLoad = 1 - } - var avgLoadPerNode float64 - avgLoadPerNode = float64(c.totalLoad / int64(len(c.loadMap))) - if avgLoadPerNode == 0 { - avgLoadPerNode = 1 - } - avgLoadPerNode = math.Ceil(avgLoadPerNode * OptimalExtraCapacityFactor) - return int64(avgLoadPerNode) -} - -func (c *Consistent) loadOK(server string) bool { - // a safety check if someone performed c.Done more than needed - if c.totalLoad < 0 { - c.totalLoad = 0 - } - - var avgLoadPerNode float64 - avgLoadPerNode = float64((c.totalLoad + 1) / int64(len(c.loadMap))) - if avgLoadPerNode == 0 { - avgLoadPerNode = 1 - } - avgLoadPerNode = math.Ceil(avgLoadPerNode * 1.25) - - bserver, ok := c.loadMap[server] - if !ok { - panic(fmt.Sprintf("given host(%s) not in loadsMap", bserver.Name)) - } - - return float64(bserver.Load)+1 <= avgLoadPerNode -} - -func (c *Consistent) delSlice(val uint64) { - c.clients.Delete(item{val}) -} - -func (c *Consistent) hash(key string) uint64 { - out := blake2b.Sum512([]byte(key)) - return binary.LittleEndian.Uint64(out[:]) -} diff --git a/controller/sharding/sharding.go b/controller/sharding/sharding.go index e593547b00f8f..49d38711a74f6 100644 --- a/controller/sharding/sharding.go +++ b/controller/sharding/sharding.go @@ -2,7 +2,6 @@ package sharding import ( "context" - "encoding/json" "fmt" "hash/fnv" "math" @@ -12,22 +11,20 @@ import ( "strings" "time" - slices "golang.org/x/exp/slices" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" + "encoding/json" "github.com/argoproj/argo-cd/v2/common" - "github.com/argoproj/argo-cd/v2/controller/sharding/consistent" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - - log "github.com/sirupsen/logrus" - kubeerrors "k8s.io/apimachinery/pkg/api/errors" + 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 @@ -43,12 +40,9 @@ var ( const ShardControllerMappingKey = "shardControllerMapping" -type ( - DistributionFunction func(c *v1alpha1.Cluster) int - ClusterFilterFunction func(c *v1alpha1.Cluster) bool - clusterAccessor func() []*v1alpha1.Cluster - appAccessor func() []*v1alpha1.Application -) +type DistributionFunction func(c *v1alpha1.Cluster) int +type ClusterFilterFunction func(c *v1alpha1.Cluster) bool +type clusterAccessor func() []*v1alpha1.Cluster // 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. @@ -59,7 +53,7 @@ type shardApplicationControllerMapping struct { } // GetClusterFilter returns a ClusterFilterFunction which is a function taking a cluster as a parameter -// and returns whether or not the cluster should be processed by a given shard. It calls the distributionFunction +// 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 { @@ -81,7 +75,7 @@ func GetClusterFilter(db db.ArgoDB, distributionFunction DistributionFunction, r // 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 { +func GetDistributionFunction(clusters clusterAccessor, shardingAlgorithm string, replicasCount int) DistributionFunction { log.Debugf("Using filter function: %s", shardingAlgorithm) distributionFunction := LegacyDistributionFunction(replicasCount) switch shardingAlgorithm { @@ -89,8 +83,6 @@ func GetDistributionFunction(clusters clusterAccessor, apps appAccessor, shardin distributionFunction = RoundRobinDistributionFunction(clusters, replicasCount) case common.LegacyShardingAlgorithm: distributionFunction = LegacyDistributionFunction(replicasCount) - case common.ConsistentHashingWithBoundedLoadsAlgorithm: - distributionFunction = ConsistentHashingWithBoundedLoadsDistributionFunction(clusters, apps, replicasCount) default: log.Warnf("distribution type %s is not supported, defaulting to %s", shardingAlgorithm, common.DefaultShardingAlgorithm) } @@ -135,13 +127,13 @@ func LegacyDistributionFunction(replicas int) DistributionFunction { // 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 across shards in case of some changes +// 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 necessarily have a secret assigned. So we are receiving a nil cluster here. + 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, @@ -165,92 +157,6 @@ func RoundRobinDistributionFunction(clusters clusterAccessor, replicas int) Dist } } -// ConsistentHashingWithBoundedLoadsDistributionFunction returns a DistributionFunction using an almost homogeneous distribution algorithm: -// for a given cluster the function will return the shard number based on a consistent hashing with bounded loads algorithm. -// This function ensures an almost homogenous distribution: each shards got assigned the fairly similar number of -// clusters +/-10% , but with it is resilient to sharding and/or number of clusters changes. -func ConsistentHashingWithBoundedLoadsDistributionFunction(clusters clusterAccessor, apps appAccessor, replicas int) DistributionFunction { - return func(c *v1alpha1.Cluster) int { - if replicas > 0 { - if c == nil { // in-cluster does not necessarily 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 { - // if the cluster is not in the clusters list anymore, we should unassign it from any shard, so we - // return the reserved value of -1 - if !slices.Contains(clusters(), c) { - log.Warnf("Cluster with id=%s not found in cluster map.", c.ID) - return -1 - } - shardIndexedByCluster := createConsistentHashingWithBoundLoads(replicas, clusters, apps) - shard, ok := shardIndexedByCluster[c.ID] - if !ok { - log.Warnf("Cluster with id=%s not found in cluster map.", c.ID) - return -1 - } - 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 - } -} - -func createConsistentHashingWithBoundLoads(replicas int, getCluster clusterAccessor, getApp appAccessor) map[string]int { - clusters := getSortedClustersList(getCluster) - appDistribution := getAppDistribution(getCluster, getApp) - shardIndexedByCluster := make(map[string]int) - appsIndexedByShard := make(map[string]int64) - consistentHashing := consistent.New() - // Adding a shard with id "-1" as a reserved value for clusters that does not have an assigned shard - // this happens for clusters that are removed for the clusters list - // consistentHashing.Add("-1") - for i := 0; i < replicas; i++ { - shard := strconv.Itoa(i) - consistentHashing.Add(shard) - appsIndexedByShard[shard] = 0 - } - - for _, c := range clusters { - clusterIndex, err := consistentHashing.GetLeast(c.ID) - if err != nil { - log.Warnf("Cluster with id=%s not found in cluster map.", c.ID) - } - shardIndexedByCluster[c.ID], err = strconv.Atoi(clusterIndex) - if err != nil { - log.Errorf("Consistent Hashing was supposed to return a shard index but it returned %d", err) - } - numApps, ok := appDistribution[c.Server] - if !ok { - numApps = 0 - } - appsIndexedByShard[clusterIndex] += numApps - consistentHashing.UpdateLoad(clusterIndex, appsIndexedByShard[clusterIndex]) - } - - return shardIndexedByCluster -} - -func getAppDistribution(getCluster clusterAccessor, getApps appAccessor) map[string]int64 { - apps := getApps() - clusters := getCluster() - appDistribution := make(map[string]int64, 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 -} - // 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 { @@ -313,7 +219,7 @@ func GetOrUpdateShardFromConfigMap(kubeClient kubernetes.Interface, settingsMgr if err != nil { if !kubeerrors.IsNotFound(err) { - return -1, fmt.Errorf("error getting sharding config map: %w", 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) @@ -323,10 +229,10 @@ func GetOrUpdateShardFromConfigMap(kubeClient kubernetes.Interface, settingsMgr } shardMappingCM, err = generateDefaultShardMappingCM(settingsMgr.GetNamespace(), hostname, replicas, shard) if err != nil { - return -1, fmt.Errorf("error generating default shard mapping configmap %w", err) + 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 %w", err) + 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 @@ -336,13 +242,13 @@ func GetOrUpdateShardFromConfigMap(kubeClient kubernetes.Interface, settingsMgr var shardMappingData []shardApplicationControllerMapping err := json.Unmarshal([]byte(data), &shardMappingData) if err != nil { - return -1, fmt.Errorf("error unmarshalling shard config map data: %w", err) + 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: %w", err) + return -1, fmt.Errorf("error marshalling data of shard mapping ConfigMap: %s", err) } shardMappingCM.Data[ShardControllerMappingKey] = string(updatedShardMappingData) @@ -356,6 +262,7 @@ func GetOrUpdateShardFromConfigMap(kubeClient kubernetes.Interface, settingsMgr // 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 { @@ -420,6 +327,7 @@ func getOrUpdateShardNumberForController(shardMappingData []shardApplicationCont // 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, @@ -439,7 +347,7 @@ func generateDefaultShardMappingCM(namespace, hostname string, replicas, shard i data, err := json.Marshal(shardMappingData) if err != nil { - return nil, fmt.Errorf("error generating default ConfigMap: %w", err) + return nil, fmt.Errorf("error generating default ConfigMap: %s", err) } shardingCM.Data[ShardControllerMappingKey] = string(data) @@ -463,16 +371,18 @@ func GetClusterSharding(kubeClient kubernetes.Interface, settingsMgr *settings.S 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: %w", err) + return nil, fmt.Errorf("(dymanic 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") + return nil, fmt.Errorf("(dymanic cluster distribution) failed to get app controller deployment replica count") } + } else { replicasCount = env.ParseNumFromEnv(common.EnvControllerReplicas, 0, 0, math.MaxInt32) } @@ -487,7 +397,7 @@ func GetClusterSharding(kubeClient kubernetes.Interface, settingsMgr *settings.S 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: %w", 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) diff --git a/controller/sharding/sharding_test.go b/controller/sharding/sharding_test.go index ebd212062b199..15f834f190259 100644 --- a/controller/sharding/sharding_test.go +++ b/controller/sharding/sharding_test.go @@ -10,20 +10,17 @@ import ( "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" - "github.com/stretchr/testify/require" 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" - - "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" ) func TestGetShardByID_NotEmptyID(t *testing.T) { @@ -76,7 +73,7 @@ func TestGetShardByID_NoReplicasUsingHashDistributionFunctionWithClusters(t *tes } 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,...) + //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 @@ -89,7 +86,7 @@ func TestGetClusterFilterDefault(t *testing.T) { } 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,...) + //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) @@ -104,14 +101,13 @@ func TestGetClusterFilterLegacy(t *testing.T) { 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) + distributionFunction := GetDistributionFunction(clusterAccessor, "unknown", replicasCount) assert.Equal(t, 0, distributionFunction(nil)) assert.Equal(t, 0, distributionFunction(&cluster1)) assert.Equal(t, 1, distributionFunction(&cluster2)) @@ -120,13 +116,12 @@ func TestGetClusterFilterUnknown(t *testing.T) { } 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,...) + //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) + filter := GetDistributionFunction(clusterAccessor, common.DefaultShardingAlgorithm, replicasCount) assert.Equal(t, 0, filter(nil)) assert.Equal(t, 4, filter(&cluster1)) assert.Equal(t, 1, filter(&cluster2)) @@ -136,30 +131,29 @@ func TestLegacyGetClusterFilterWithFixedShard(t *testing.T) { 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) + filter = GetDistributionFunction(clusterAccessor, 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) + filter = GetDistributionFunction(clusterAccessor, 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,...) + //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, 0, filter(nil)) - assert.Equal(t, 0, filter(&cluster1)) - assert.Equal(t, 1, filter(&cluster2)) - assert.Equal(t, 2, filter(&cluster3)) - assert.Equal(t, 3, filter(&cluster4)) + filter := GetDistributionFunction(clusterAccessor, 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. @@ -167,14 +161,14 @@ func TestRoundRobinGetClusterFilterWithFixedShard(t *testing.T) { 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) + filter = GetDistributionFunction(clusterAccessor, 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) + filter = GetDistributionFunction(clusterAccessor, common.RoundRobinShardingAlgorithm, replicasCount) assert.Equal(t, int(fixedShard), filter(&v1alpha1.Cluster{Name: "cluster4", ID: "4", Shard: &fixedShard})) } @@ -277,108 +271,6 @@ func TestGetShardByIndexModuloReplicasCountDistributionFunctionWhenClusterIsAdde assert.Equal(t, -1, distributionFunction(&cluster6)) } -func TestConsistentHashingWhenClusterIsAddedAndRemoved(t *testing.T) { - db := dbmocks.ArgoDB{} - clusterCount := 133 - prefix := "cluster" - - clusters := []v1alpha1.Cluster{} - for i := 0; i < clusterCount; i++ { - id := fmt.Sprintf("%06d", i) - cluster := fmt.Sprintf("%s-%s", prefix, id) - clusters = append(clusters, createCluster(cluster, id)) - } - clusterAccessor := getClusterAccessor(clusters) - appAccessor, _, _, _, _, _ := createTestApps() - clusterList := &v1alpha1.ClusterList{Items: clusters} - db.On("ListClusters", mock.Anything).Return(clusterList, nil) - // Test with replicas set to 3 - replicasCount := 3 - db.On("GetApplicationControllerReplicas").Return(replicasCount) - distributionFunction := ConsistentHashingWithBoundedLoadsDistributionFunction(clusterAccessor, appAccessor, replicasCount) - assert.Equal(t, 0, distributionFunction(nil)) - distributionMap := map[int]int{} - assignementMap := map[string]int{} - for i := 0; i < clusterCount; i++ { - assignedShard := distributionFunction(&clusters[i]) - assignementMap[clusters[i].ID] = assignedShard - distributionMap[assignedShard]++ - } - - // We check that the distribution does not differ for more than 20% - var sum float64 - sum = 0 - for shard, count := range distributionMap { - if shard != -1 { - sum = (sum + float64(count)) - } - } - average := sum / float64(replicasCount) - failedTests := false - for shard, count := range distributionMap { - if shard != -1 { - if float64(count) > average*float64(1.1) || float64(count) < average*float64(0.9) { - fmt.Printf("Cluster distribution differs for more than 20%%: %d for shard %d (average: %f)\n", count, shard, average) - failedTests = true - } - if failedTests { - t.Fail() - } - } - } - - // Now we will decrease the number of replicas to 2, and we should see only clusters that were attached to shard 2 to be reassigned - replicasCount = 2 - distributionFunction = ConsistentHashingWithBoundedLoadsDistributionFunction(getClusterAccessor(clusterList.Items), appAccessor, replicasCount) - removedCluster := clusterList.Items[len(clusterList.Items)-1] - for i := 0; i < clusterCount; i++ { - c := &clusters[i] - assignedShard := distributionFunction(c) - prevıouslyAssignedShard := assignementMap[clusters[i].ID] - if prevıouslyAssignedShard != 2 && prevıouslyAssignedShard != assignedShard { - fmt.Printf("Previously assigned %s cluster has moved from replica %d to %d", c.ID, prevıouslyAssignedShard, assignedShard) - t.Fail() - } - } - // Now, we remove the last added cluster, it should be unassigned - removedCluster = clusterList.Items[len(clusterList.Items)-1] - clusterList.Items = clusterList.Items[:len(clusterList.Items)-1] - distributionFunction = ConsistentHashingWithBoundedLoadsDistributionFunction(getClusterAccessor(clusterList.Items), appAccessor, replicasCount) - assert.Equal(t, -1, distributionFunction(&removedCluster)) -} - -func TestConsistentHashingWhenClusterWithZeroReplicas(t *testing.T) { - db := dbmocks.ArgoDB{} - clusters := []v1alpha1.Cluster{createCluster("cluster-01", "01")} - clusterAccessor := getClusterAccessor(clusters) - clusterList := &v1alpha1.ClusterList{Items: clusters} - db.On("ListClusters", mock.Anything).Return(clusterList, nil) - appAccessor, _, _, _, _, _ := createTestApps() - // Test with replicas set to 0 - replicasCount := 0 - db.On("GetApplicationControllerReplicas").Return(replicasCount) - distributionFunction := ConsistentHashingWithBoundedLoadsDistributionFunction(clusterAccessor, appAccessor, replicasCount) - assert.Equal(t, -1, distributionFunction(nil)) -} - -func TestConsistentHashingWhenClusterWithFixedShard(t *testing.T) { - db := dbmocks.ArgoDB{} - var fixedShard int64 = 1 - cluster := &v1alpha1.Cluster{ID: "1", Shard: &fixedShard} - clusters := []v1alpha1.Cluster{*cluster} - - clusterAccessor := getClusterAccessor(clusters) - clusterList := &v1alpha1.ClusterList{Items: clusters} - db.On("ListClusters", mock.Anything).Return(clusterList, nil) - - // Test with replicas set to 5 - replicasCount := 5 - db.On("GetApplicationControllerReplicas").Return(replicasCount) - appAccessor, _, _, _, _, _ := createTestApps() - distributionFunction := ConsistentHashingWithBoundedLoadsDistributionFunction(clusterAccessor, appAccessor, replicasCount) - assert.Equal(t, fixedShard, int64(distributionFunction(cluster))) -} - func TestGetShardByIndexModuloReplicasCountDistributionFunction(t *testing.T) { clusters, db, cluster1, cluster2, _, _, _ := createTestClusters() replicasCount := 2 @@ -411,16 +303,16 @@ func TestInferShard(t *testing.T) { osHostnameError := errors.New("cannot resolve hostname") osHostnameFunction = func() (string, error) { return "exampleshard", osHostnameError } _, err := InferShard() - require.Error(t, err) + assert.NotNil(t, err) assert.Equal(t, err, osHostnameError) osHostnameFunction = func() (string, error) { return "exampleshard", nil } _, err = InferShard() - require.NoError(t, err) + assert.Nil(t, err) osHostnameFunction = func() (string, error) { return "example-shard", nil } _, err = InferShard() - require.NoError(t, err) + assert.Nil(t, err) } func createTestClusters() (clusterAccessor, *dbmocks.ArgoDB, v1alpha1.Cluster, v1alpha1.Cluster, v1alpha1.Cluster, v1alpha1.Cluster, v1alpha1.Cluster) { @@ -495,7 +387,7 @@ func Test_generateDefaultShardMappingCM_NoPredefinedShard(t *testing.T) { } expectedMappingCM, err := json.Marshal(expectedMapping) - require.NoError(t, err) + assert.NoError(t, err) expectedShadingCM := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -509,8 +401,9 @@ func Test_generateDefaultShardMappingCM_NoPredefinedShard(t *testing.T) { heartbeatCurrentTime = func() metav1.Time { return expectedTime } osHostnameFunction = func() (string, error) { return "test-example", nil } shardingCM, err := generateDefaultShardMappingCM("test", "test-example", replicas, -1) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, expectedShadingCM, shardingCM) + } func Test_generateDefaultShardMappingCM_PredefinedShard(t *testing.T) { @@ -530,7 +423,7 @@ func Test_generateDefaultShardMappingCM_PredefinedShard(t *testing.T) { } expectedMappingCM, err := json.Marshal(expectedMapping) - require.NoError(t, err) + assert.NoError(t, err) expectedShadingCM := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -544,8 +437,9 @@ func Test_generateDefaultShardMappingCM_PredefinedShard(t *testing.T) { heartbeatCurrentTime = func() metav1.Time { return expectedTime } osHostnameFunction = func() (string, error) { return "test-example", nil } shardingCM, err := generateDefaultShardMappingCM("test", "test-example", replicas, 1) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, expectedShadingCM, shardingCM) + } func Test_getOrUpdateShardNumberForController(t *testing.T) { @@ -948,7 +842,7 @@ func TestGetClusterSharding(t *testing.T) { useDynamicSharding: true, expectedShard: 0, expectedReplicas: 1, - expectedErr: fmt.Errorf("(dynamic cluster distribution) failed to get app controller deployment: deployments.apps \"missing-deployment\" not found"), + expectedErr: fmt.Errorf("(dymanic cluster distribution) failed to get app controller deployment: deployments.apps \"missing-deployment\" not found"), }, } @@ -971,86 +865,8 @@ func TestGetClusterSharding(t *testing.T) { t.Errorf("Expected error %v but got nil", tc.expectedErr) } } else { - require.NoError(t, err) + 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 { - 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 index 4c05a8c7aeebd..1cca783a2afe9 100644 --- a/controller/sharding/shuffle_test.go +++ b/controller/sharding/shuffle_test.go @@ -6,12 +6,11 @@ import ( "strconv" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "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) { @@ -19,7 +18,7 @@ func TestLargeShuffle(t *testing.T) { db := dbmocks.ArgoDB{} clusterList := &v1alpha1.ClusterList{Items: []v1alpha1.Cluster{}} for i := 0; i < math.MaxInt/4096; i += 256 { - // fmt.Fprintf(os.Stdout, "%d", i) + //fmt.Fprintf(os.Stdout, "%d", i) cluster := createCluster(fmt.Sprintf("cluster-%d", i), fmt.Sprintf("%d", i)) clusterList.Items = append(clusterList.Items, cluster) } @@ -32,6 +31,7 @@ func TestLargeShuffle(t *testing.T) { for i, c := range clusterList.Items { assert.Equal(t, i%2567, distributionFunction(&c)) } + } func TestShuffle(t *testing.T) { @@ -78,6 +78,7 @@ func TestShuffle(t *testing.T) { 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 { diff --git a/controller/state.go b/controller/state.go index 5b59f411dafb1..4be03900f268a 100644 --- a/controller/state.go +++ b/controller/state.go @@ -33,7 +33,6 @@ 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" - "github.com/argoproj/argo-cd/v2/util/app/path" "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/argo/normalizers" @@ -45,9 +44,12 @@ import ( "github.com/argoproj/argo-cd/v2/util/stats" ) -var CompareStateRepoError = errors.New("failed to get repo objects") +var ( + CompareStateRepoError = errors.New("failed to get repo objects") +) -type resourceInfoProviderStub struct{} +type resourceInfoProviderStub struct { +} func (r *resourceInfoProviderStub) IsNamespaced(_ schema.GroupKind) (bool, error) { return false, nil @@ -68,9 +70,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 *v1alpha1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localObjects []string, hasMultipleSources bool, rollback bool) (*comparisonResult, error) + 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, rollback bool) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, bool, error) + 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 @@ -88,7 +90,6 @@ type comparisonResult struct { timings map[string]time.Duration diffResultList *diff.DiffResultList hasPostDeleteHooks bool - revisionUpdated bool } func (res *comparisonResult) GetSyncStatus() *v1alpha1.SyncStatus { @@ -124,51 +125,51 @@ type appStateManager struct { // 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, rollback bool) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, bool, error) { +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, false, fmt.Errorf("failed to list Helm repositories: %w", 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, false, fmt.Errorf("failed to get permitted Helm repositories for project %q: %w", proj.Name, 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, false, fmt.Errorf("failed to get Helm credentials: %w", 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, false, fmt.Errorf("failed to get permitted Helm credentials for project %q: %w", proj.Name, err) + return nil, nil, fmt.Errorf("failed to get permitted Helm credentials for project %q: %w", proj.Name, err) } enabledSourceTypes, err := m.settingsMgr.GetEnabledSourceTypes() if err != nil { - return nil, nil, false, fmt.Errorf("failed to get enabled source types: %w", err) + return nil, nil, fmt.Errorf("failed to get enabled source types: %w", err) } ts.AddCheckpoint("plugins_ms") kustomizeSettings, err := m.settingsMgr.GetKustomizeSettings() if err != nil { - return nil, nil, false, fmt.Errorf("failed to get Kustomize settings: %w", err) + return nil, nil, fmt.Errorf("failed to get Kustomize settings: %w", err) } helmOptions, err := m.settingsMgr.GetHelmSettings() if err != nil { - return nil, nil, false, fmt.Errorf("failed to get Helm settings: %w", 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, false, fmt.Errorf("failed to get cluster version for cluster %q: %w", app.Spec.Destination.Server, 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, false, fmt.Errorf("failed to connect to repo server: %w", err) + return nil, nil, fmt.Errorf("failed to connect to repo server: %w", err) } defer io.Close(conn) @@ -176,128 +177,72 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp targetObjs := make([]*unstructured.Unstructured, 0) // Store the map of all sources having ref field into a map for applications with sources field - // If it's for a rollback process, the refSources[*].targetRevision fields are the desired - // revisions for the rollback - refSources, err := argo.GetRefSources(context.Background(), sources, app.Spec.Project, m.db.GetRepository, revisions, rollback) + refSources, err := argo.GetRefSources(context.Background(), app.Spec, m.db) if err != nil { - return nil, nil, false, fmt.Errorf("failed to get ref sources: %w", err) + return nil, nil, fmt.Errorf("failed to get ref sources: %v", err) } - revisionUpdated := false - - atLeastOneRevisionIsNotPossibleToBeUpdated := false - - keyManifestGenerateAnnotationVal, keyManifestGenerateAnnotationExists := app.Annotations[v1alpha1.AnnotationKeyManifestGeneratePaths] - for i, source := range sources { if len(revisions) < len(sources) || revisions[i] == "" { revisions[i] = source.TargetRevision } - repo, err := m.db.GetRepository(context.Background(), source.RepoURL, proj.Name) + ts.AddCheckpoint("helm_ms") + repo, err := m.db.GetRepository(context.Background(), source.RepoURL) if err != nil { - return nil, nil, false, fmt.Errorf("failed to get repo %q: %w", source.RepoURL, 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, false, fmt.Errorf("failed to get Kustomize options for source %d of %d: %w", i+1, len(sources), err) - } - - syncedRevision := app.Status.Sync.Revision - if app.Spec.HasMultipleSources() { - if i < len(app.Status.Sync.Revisions) { - syncedRevision = app.Status.Sync.Revisions[i] - } else { - syncedRevision = "" - } + return nil, nil, fmt.Errorf("failed to get Kustomize options for source %d of %d: %w", i+1, len(sources), err) } - revision := revisions[i] - - if !source.IsHelm() && syncedRevision != "" && keyManifestGenerateAnnotationExists && keyManifestGenerateAnnotationVal != "" { - // Validate the manifest-generate-path annotation to avoid generating manifests if it has not changed. - updateRevisionResult, err := repoClient.UpdateRevisionForPaths(context.Background(), &apiclient.UpdateRevisionForPathsRequest{ - Repo: repo, - Revision: revision, - SyncedRevision: syncedRevision, - NoRevisionCache: noRevisionCache, - Paths: path.GetAppRefreshPaths(app), - AppLabelKey: appLabelKey, - AppName: app.InstanceName(m.namespace), - Namespace: app.Spec.Destination.Namespace, - ApplicationSource: &source, - KubeVersion: serverVersion, - ApiVersions: argo.APIResourcesToStrings(apiResources, true), - TrackingMethod: string(argo.GetTrackingMethod(m.settingsMgr)), - RefSources: refSources, - HasMultipleSources: app.Spec.HasMultipleSources(), - }) - if err != nil { - return nil, nil, false, fmt.Errorf("failed to compare revisions for source %d of %d: %w", i+1, len(sources), err) - } - if updateRevisionResult.Changes { - revisionUpdated = true - } - - // Generate manifests should use same revision as updateRevisionForPaths, because HEAD revision may be different between these two calls - if updateRevisionResult.Revision != "" { - revision = updateRevisionResult.Revision - } - } else { - // revisionUpdated is set to true if at least one revision is not possible to be updated, - atLeastOneRevisionIsNotPossibleToBeUpdated = true - } - - log.Debugf("Generating Manifest for source %s revision %s", source, revision) + ts.AddCheckpoint("version_ms") + log.Debugf("Generating Manifest for source %s revision %s", source, revisions[i]) manifestInfo, err := repoClient.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ - Repo: repo, - Repos: permittedHelmRepos, - Revision: revision, - NoCache: noCache, - NoRevisionCache: noRevisionCache, - AppLabelKey: appLabelKey, - AppName: app.InstanceName(m.namespace), - Namespace: app.Spec.Destination.Namespace, - ApplicationSource: &source, - KustomizeOptions: kustomizeOptions, - KubeVersion: serverVersion, - ApiVersions: argo.APIResourcesToStrings(apiResources, true), - VerifySignature: verifySignature, - HelmRepoCreds: permittedHelmCredentials, - TrackingMethod: string(argo.GetTrackingMethod(m.settingsMgr)), - EnabledSourceTypes: enabledSourceTypes, - HelmOptions: helmOptions, - HasMultipleSources: app.Spec.HasMultipleSources(), - RefSources: refSources, - ProjectName: proj.Name, - ProjectSourceRepos: proj.Spec.SourceRepos, - AnnotationManifestGeneratePaths: app.GetAnnotation(v1alpha1.AnnotationKeyManifestGeneratePaths), + Repo: repo, + Repos: permittedHelmRepos, + Revision: revisions[i], + NoCache: noCache, + NoRevisionCache: noRevisionCache, + AppLabelKey: appLabelKey, + AppName: app.InstanceName(m.namespace), + Namespace: app.Spec.Destination.Namespace, + ApplicationSource: &source, + KustomizeOptions: kustomizeOptions, + KubeVersion: serverVersion, + ApiVersions: argo.APIResourcesToStrings(apiResources, true), + VerifySignature: verifySignature, + HelmRepoCreds: permittedHelmCredentials, + TrackingMethod: string(argo.GetTrackingMethod(m.settingsMgr)), + EnabledSourceTypes: enabledSourceTypes, + HelmOptions: helmOptions, + HasMultipleSources: app.Spec.HasMultipleSources(), + RefSources: refSources, + ProjectName: proj.Name, + ProjectSourceRepos: proj.Spec.SourceRepos, }) if err != nil { - return nil, nil, false, fmt.Errorf("failed to generate manifest for source %d of %d: %w", i+1, len(sources), err) + 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, false, fmt.Errorf("failed to unmarshal manifests for source %d of %d: %w", i+1, len(sources), 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...) + manifestInfos = append(manifestInfos, manifestInfo) } - ts.AddCheckpoint("manifests_ms") + ts.AddCheckpoint("unmarshal_ms") logCtx := log.WithField("application", app.QualifiedName()) for k, v := range ts.Timings() { logCtx = logCtx.WithField(k, v.Milliseconds()) } logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds()) logCtx.Info("GetRepoObjs stats") - - // in case if annotation not exists, we should always execute selfheal if manifests changed - if atLeastOneRevisionIsNotPossibleToBeUpdated { - revisionUpdated = true - } - - return targetObjs, manifestInfos, revisionUpdated, nil + return targetObjs, manifestInfos, nil } func unmarshalManifests(manifests []string) ([]*unstructured.Unstructured, error) { @@ -317,6 +262,7 @@ func DeduplicateTargetObjects( objs []*unstructured.Unstructured, infoProvider kubeutil.ResourceInfoProvider, ) ([]*unstructured.Unstructured, []v1alpha1.ApplicationCondition, error) { + targetByKey := make(map[kubeutil.ResourceKey][]*unstructured.Unstructured) for i := range objs { obj := objs[i] @@ -416,7 +362,7 @@ func isManagedNamespace(ns *unstructured.Unstructured, app *v1alpha1.Application // 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 *v1alpha1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localManifests []string, hasMultipleSources bool, rollback bool) (*comparisonResult, error) { +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() @@ -447,7 +393,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 // When signature keys are defined in the project spec, we need to verify the signature on the Git revision verifySignature := false - if len(project.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled() { + if project.Spec.SignatureKeys != nil && len(project.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled() { verifySignature = true } @@ -464,8 +410,6 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 var manifestInfos []*apiclient.ManifestResponse targetNsExists := false - var revisionUpdated bool - if len(localManifests) == 0 { // If the length of revisions is not same as the length of sources, // we take the revisions from the sources directly for all the sources. @@ -476,7 +420,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 } } - targetObjs, manifestInfos, revisionUpdated, err = m.GetRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project, rollback) + targetObjs, manifestInfos, err = m.GetRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project) if err != nil { targetObjs = make([]*unstructured.Unstructured, 0) msg := fmt.Sprintf("Failed to load target state: %s", err.Error()) @@ -566,10 +510,11 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 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: %w", project, err) + return nil, fmt.Errorf("failed to get clusters for project %q: %v", project, err) } return clusters, nil }) + if err != nil { 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}) @@ -611,6 +556,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 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 @@ -618,7 +564,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 } // No need to care about the return value here, we just want the modified managedNs - _, err = syncNamespace(app.Spec.SyncPolicy)(managedNs, liveObj) + _, 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 @@ -867,7 +813,6 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 diffConfig: diffConfig, diffResultList: diffResults, hasPostDeleteHooks: hasPostDeleteHooks, - revisionUpdated: revisionUpdated, } if hasMultipleSources { @@ -895,6 +840,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 // 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 @@ -936,16 +882,7 @@ func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sou return true } -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 { +func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, revisions []string, sources []v1alpha1.ApplicationSource, hasMultipleSources bool, startedAt metav1.Time) error { var nextID int64 if len(app.Status.History) > 0 { nextID = app.Status.History.LastRevisionHistory().ID + 1 @@ -958,7 +895,6 @@ func (m *appStateManager) persistRevisionHistory( ID: nextID, Sources: sources, Revisions: revisions, - InitiatedBy: initiatedBy, }) } else { app.Status.History = append(app.Status.History, v1alpha1.RevisionHistory{ @@ -967,7 +903,6 @@ func (m *appStateManager) persistRevisionHistory( DeployStartedAt: &startedAt, ID: nextID, Source: source, - InitiatedBy: initiatedBy, }) } diff --git a/controller/state_test.go b/controller/state_test.go index a3b7cb195a94e..5d2342b601a77 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -15,7 +15,6 @@ import ( "github.com/sirupsen/logrus" logrustest "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" @@ -24,11 +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" - mockrepoclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks" "github.com/argoproj/argo-cd/v2/test" "github.com/argoproj/argo-cd/v2/util/argo" ) @@ -50,14 +46,14 @@ func TestCompareAppStateEmpty(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) - assert.Empty(t, app.Status.Conditions) + 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 @@ -68,21 +64,21 @@ func TestCompareAppStateRepoError(t *testing.T) { 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, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) assert.Nil(t, compRes) - require.EqualError(t, err, CompareStateRepoError.Error()) + 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, false) + compRes, err = ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) assert.Nil(t, compRes) - require.EqualError(t, err, CompareStateRepoError.Error()) + 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, false) + compRes, err = ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) assert.NotNil(t, compRes) - require.NoError(t, err) - assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status) + assert.Nil(t, err) + assert.Equal(t, compRes.syncStatus.Status, argoappv1.SyncStatusCodeUnknown) } // TestCompareAppStateNamespaceMetadataDiffers tests comparison when managed namespace metadata differs @@ -114,14 +110,14 @@ func TestCompareAppStateNamespaceMetadataDiffers(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) - assert.Empty(t, app.Status.Conditions) + 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 @@ -163,8 +159,8 @@ func TestCompareAppStateNamespaceMetadataDiffersToManifest(t *testing.T) { 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, false) - require.NoError(t, err) + 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) @@ -174,7 +170,7 @@ func TestCompareAppStateNamespaceMetadataDiffersToManifest(t *testing.T) { assert.Len(t, compRes.diffResultList.Diffs, 1) result := NewNamespace() - require.NoError(t, json.Unmarshal(compRes.diffResultList.Diffs[0].PredictedLive, result)) + assert.NoError(t, json.Unmarshal(compRes.diffResultList.Diffs[0].PredictedLive, result)) labels := result.GetLabels() delete(labels, "kubernetes.io/metadata.name") @@ -182,7 +178,7 @@ func TestCompareAppStateNamespaceMetadataDiffersToManifest(t *testing.T) { assert.Equal(t, map[string]string{}, labels) // Manifests override definitions in managedNamespaceMetadata assert.Equal(t, map[string]string{"bar": "bat"}, result.GetAnnotations()) - assert.Empty(t, app.Status.Conditions) + assert.Len(t, app.Status.Conditions, 0) } // TestCompareAppStateNamespaceMetadata tests comparison when managed namespace metadata differs to live @@ -221,8 +217,8 @@ func TestCompareAppStateNamespaceMetadata(t *testing.T) { 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, false) - require.NoError(t, err) + 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) @@ -232,14 +228,14 @@ func TestCompareAppStateNamespaceMetadata(t *testing.T) { assert.Len(t, compRes.diffResultList.Diffs, 1) result := NewNamespace() - require.NoError(t, json.Unmarshal(compRes.diffResultList.Diffs[0].PredictedLive, result)) + 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.Empty(t, app.Status.Conditions) + assert.Len(t, app.Status.Conditions, 0) } // TestCompareAppStateNamespaceMetadataIsTheSame tests comparison when managed namespace metadata is the same @@ -280,14 +276,14 @@ func TestCompareAppStateNamespaceMetadataIsTheSame(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) - assert.Empty(t, app.Status.Conditions) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) + assert.Len(t, app.Status.Conditions, 0) } // TestCompareAppStateMissing tests when there is a manifest defined in the repo which doesn't exist in live @@ -308,14 +304,14 @@ func TestCompareAppStateMissing(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Empty(t, app.Status.Conditions) + assert.Len(t, app.Status.Conditions, 0) } // TestCompareAppStateExtra tests when there is an extra object in live but not defined in git @@ -340,13 +336,13 @@ func TestCompareAppStateExtra(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Len(t, compRes.resources, 1) - assert.Len(t, compRes.managedResources, 1) - assert.Empty(t, app.Status.Conditions) + assert.Equal(t, 1, len(compRes.resources)) + assert.Equal(t, 1, len(compRes.managedResources)) + assert.Equal(t, 0, len(app.Status.Conditions)) } // TestCompareAppStateHook checks that hooks are detected during manifest generation, and not @@ -371,14 +367,14 @@ func TestCompareAppStateHook(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) - assert.Len(t, compRes.reconciliationResult.Hooks, 1) - assert.Empty(t, app.Status.Conditions) + assert.Equal(t, 0, len(compRes.resources)) + assert.Equal(t, 0, len(compRes.managedResources)) + assert.Equal(t, 1, len(compRes.reconciliationResult.Hooks)) + assert.Equal(t, 0, len(app.Status.Conditions)) } // TestCompareAppStateSkipHook checks that skipped resources are detected during manifest generation, and not @@ -403,14 +399,14 @@ func TestCompareAppStateSkipHook(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Len(t, compRes.resources, 1) - assert.Len(t, compRes.managedResources, 1) - assert.Empty(t, compRes.reconciliationResult.Hooks) - assert.Empty(t, app.Status.Conditions) + assert.Equal(t, 1, len(compRes.resources)) + assert.Equal(t, 1, len(compRes.managedResources)) + assert.Equal(t, 0, len(compRes.reconciliationResult.Hooks)) + assert.Equal(t, 0, len(app.Status.Conditions)) } // checks that ignore resources are detected, but excluded from status @@ -434,14 +430,14 @@ func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) - assert.Empty(t, app.Status.Conditions) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) + assert.Len(t, app.Status.Conditions, 0) } // TestCompareAppStateExtraHook tests when there is an extra _hook_ object in live but not defined in git @@ -467,15 +463,15 @@ func TestCompareAppStateExtraHook(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Len(t, compRes.resources, 1) - assert.Len(t, compRes.managedResources, 1) - assert.Empty(t, compRes.reconciliationResult.Hooks) - assert.Empty(t, app.Status.Conditions) + assert.Equal(t, 1, len(compRes.resources)) + assert.Equal(t, 1, len(compRes.managedResources)) + assert.Equal(t, 0, len(compRes.reconciliationResult.Hooks)) + assert.Equal(t, 0, len(app.Status.Conditions)) } // TestAppRevisions tests that revisions are properly propagated for a single source app @@ -496,12 +492,12 @@ func TestAppRevisionsSingleSource(t *testing.T) { 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(), false) - require.NoError(t, err) + 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.Empty(t, compRes.syncStatus.Revisions) + assert.Len(t, compRes.syncStatus.Revisions, 0) } // TestAppRevisions tests that revisions are properly propagated for a multi source app @@ -536,8 +532,8 @@ func TestAppRevisionsMultiSource(t *testing.T) { 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(), false) - require.NoError(t, err) + 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) @@ -549,7 +545,7 @@ func TestAppRevisionsMultiSource(t *testing.T) { func toJSON(t *testing.T, obj *unstructured.Unstructured) string { data, err := json.Marshal(obj) - require.NoError(t, err) + assert.NoError(t, err) return string(data) } @@ -584,15 +580,15 @@ func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) { 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, false) - require.NoError(t, err) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) - assert.Len(t, app.Status.Conditions, 1) + assert.Equal(t, 1, len(app.Status.Conditions)) assert.NotNil(t, app.Status.Conditions[0].LastTransitionTime) assert.Equal(t, argoappv1.ApplicationConditionRepeatedResourceWarning, app.Status.Conditions[0].Type) assert.Equal(t, "Resource /Pod/fake-dest-ns/my-pod appeared 2 times among application resources.", app.Status.Conditions[0].Message) - assert.Len(t, compRes.resources, 4) + assert.Equal(t, 4, len(compRes.resources)) } func TestCompareAppStateManagedNamespaceMetadataWithLiveNsDoesNotGetPruned(t *testing.T) { @@ -621,11 +617,11 @@ func TestCompareAppStateManagedNamespaceMetadataWithLiveNsDoesNotGetPruned(t *te }, } ctrl := newFakeController(&data, nil) - compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, []string{}, app.Spec.Sources, false, false, nil, false, false) - require.NoError(t, err) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, []string{}, app.Spec.Sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) - assert.Empty(t, app.Status.Conditions) + assert.Equal(t, 0, len(app.Status.Conditions)) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) // Ensure that ns does not get pruned @@ -653,36 +649,6 @@ var defaultProj = argoappv1.AppProject{ }, } -// TestCompareAppStateWithManifestGeneratePath tests that it compares revisions when the manifest-generate-path annotation is set. -func TestCompareAppStateWithManifestGeneratePath(t *testing.T) { - app := newFakeApp() - app.SetAnnotations(map[string]string{argoappv1.AnnotationKeyManifestGeneratePaths: "."}) - app.Status.Sync = argoappv1.SyncStatus{ - Revision: "abc123", - Status: argoappv1.SyncStatusCodeSynced, - } - - data := fakeData{ - manifestResponse: &apiclient.ManifestResponse{ - Manifests: []string{}, - Namespace: test.FakeDestNamespace, - Server: test.FakeClusterURL, - Revision: "abc123", - }, - updateRevisionForPathsResponse: &apiclient.UpdateRevisionForPathsResponse{}, - } - - ctrl := newFakeController(&data, nil) - revisions := make([]string, 0) - revisions = append(revisions, "abc123") - compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, false, false) - require.NoError(t, err) - assert.NotNil(t, compRes) - assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) - assert.Equal(t, "abc123", compRes.syncStatus.Revision) - ctrl.repoClientset.(*mockrepoclient.Clientset).RepoServerServiceClient.(*mockrepoclient.RepoServerServiceClient).AssertNumberOfCalls(t, "UpdateRevisionForPaths", 1) -} - func TestSetHealth(t *testing.T) { app := newFakeApp() deployment := kube.MustToUnstructured(&v1.Deployment{ @@ -712,8 +678,8 @@ func TestSetHealth(t *testing.T) { 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, false) - require.NoError(t, err) + 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) } @@ -749,8 +715,8 @@ func TestSetHealthSelfReferencedApp(t *testing.T) { 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, false) - require.NoError(t, err) + 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) } @@ -774,8 +740,8 @@ func TestSetManagedResourcesWithOrphanedResources(t *testing.T) { tree, err := ctrl.setAppManagedResources(app, &comparisonResult{managedResources: make([]managedResource, 0)}) - require.NoError(t, err) - assert.Len(t, tree.OrphanedNodes, 1) + assert.NoError(t, err) + assert.Equal(t, len(tree.OrphanedNodes), 1) assert.Equal(t, "guestbook", tree.OrphanedNodes[0].Name) assert.Equal(t, app.Namespace, tree.OrphanedNodes[0].Namespace) } @@ -803,8 +769,8 @@ func TestSetManagedResourcesWithResourcesOfAnotherApp(t *testing.T) { tree, err := ctrl.setAppManagedResources(app1, &comparisonResult{managedResources: make([]managedResource, 0)}) - require.NoError(t, err) - assert.Empty(t, tree.OrphanedNodes) + assert.NoError(t, err) + assert.Equal(t, 0, len(tree.OrphanedNodes)) } func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) { @@ -824,8 +790,8 @@ func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) { 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, false) - require.NoError(t, err) + 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) @@ -856,7 +822,7 @@ func TestSetManagedResourcesKnownOrphanedResourceExceptions(t *testing.T) { tree, err := ctrl.setAppManagedResources(app, &comparisonResult{managedResources: make([]managedResource, 0)}) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, tree.OrphanedNodes, 1) assert.Equal(t, "guestbook", tree.OrphanedNodes[0].Name) } @@ -872,8 +838,8 @@ func Test_appStateManager_persistRevisionHistory(t *testing.T) { app.Spec.RevisionHistoryLimit = &i } addHistory := func() { - err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1.Time{}, v1alpha1.OperationInitiator{}) - require.NoError(t, err) + err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1.Time{}) + assert.NoError(t, err) } addHistory() assert.Len(t, app.Status.History, 1) @@ -908,8 +874,8 @@ 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, v1alpha1.OperationInitiator{}) - require.NoError(t, err) + err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1NowTime) + assert.NoError(t, err) assert.Equal(t, app.Status.History.LastRevisionHistory().DeployStartedAt, &metav1NowTime) } @@ -965,14 +931,14 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) - assert.Empty(t, app.Status.Conditions) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) + assert.Len(t, app.Status.Conditions, 0) } // We have a bad signature response, but project does not require signed commits { @@ -992,14 +958,14 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) { 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, false) - require.NoError(t, err) + 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.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) - assert.Empty(t, app.Status.Conditions) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) + assert.Len(t, app.Status.Conditions, 0) } } @@ -1024,14 +990,14 @@ func TestSignedResponseSignatureRequired(t *testing.T) { sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false, false) - require.NoError(t, err) + 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) - assert.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) - assert.Empty(t, app.Status.Conditions) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) + assert.Len(t, app.Status.Conditions, 0) } // We have a bad signature response and signing is required - do not sync { @@ -1051,13 +1017,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false, false) - require.NoError(t, err) + 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) - assert.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) assert.Len(t, app.Status.Conditions, 1) } // We have a malformed signature response and signing is required - do not sync @@ -1078,13 +1044,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false, false) - require.NoError(t, err) + 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) - assert.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) assert.Len(t, app.Status.Conditions, 1) } // We have no signature response (no signature made) and signing is required - do not sync @@ -1105,13 +1071,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false, false) - require.NoError(t, err) + 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) - assert.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) assert.Len(t, app.Status.Conditions, 1) } @@ -1135,13 +1101,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes, err := ctrl.appStateManager.CompareAppState(app, &testProj, revisions, sources, false, false, nil, false, false) - require.NoError(t, err) + 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) - assert.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) assert.Len(t, app.Status.Conditions, 1) assert.Contains(t, app.Status.Conditions[0].Message, "key is not allowed") } @@ -1165,13 +1131,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false, false) - require.NoError(t, err) + 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) - assert.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) assert.Len(t, app.Status.Conditions, 1) assert.Contains(t, app.Status.Conditions[0].Message, "Cannot use local manifests") } @@ -1195,14 +1161,14 @@ func TestSignedResponseSignatureRequired(t *testing.T) { sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false, false) - require.NoError(t, err) + 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) - assert.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) - assert.Empty(t, app.Status.Conditions) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) + assert.Len(t, app.Status.Conditions, 0) } // Signature required and local manifests supplied and GPG subsystem is disabled - sync @@ -1225,14 +1191,14 @@ func TestSignedResponseSignatureRequired(t *testing.T) { sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false, false) - require.NoError(t, err) + 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) - assert.Empty(t, compRes.resources) - assert.Empty(t, compRes.managedResources) - assert.Empty(t, app.Status.Conditions) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) + assert.Len(t, app.Status.Conditions, 0) } } @@ -1542,17 +1508,6 @@ func TestUseDiffCache(t *testing.T) { 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, @@ -1706,53 +1661,7 @@ func TestUseDiffCache(t *testing.T) { useDiffCache := useDiffCache(tc.noCache, tc.manifestInfos, tc.sources, tc.app, tc.manifestRevisions, tc.statusRefreshTimeout, tc.serverSideDiff, log) // Then - assert.Equal(t, tc.expectedUseCache, useDiffCache) + assert.Equal(t, useDiffCache, tc.expectedUseCache) }) } } - -func TestCompareAppStateDefaultRevisionUpdated(t *testing.T) { - app := newFakeApp() - 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, false) - require.NoError(t, err) - assert.NotNil(t, compRes) - assert.NotNil(t, compRes.syncStatus) - assert.True(t, compRes.revisionUpdated) -} - -func TestCompareAppStateRevisionUpdatedWithHelmSource(t *testing.T) { - app := newFakeMultiSourceApp() - 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, false) - require.NoError(t, err) - assert.NotNil(t, compRes) - assert.NotNil(t, compRes.syncStatus) - assert.True(t, compRes.revisionUpdated) -} diff --git a/controller/sync.go b/controller/sync.go index 5f896a522f942..40285dec887f8 100644 --- a/controller/sync.go +++ b/controller/sync.go @@ -6,13 +6,11 @@ import ( "fmt" "os" "strconv" - "strings" "sync/atomic" "time" - "k8s.io/apimachinery/pkg/util/strategicpatch" - cdcommon "github.com/argoproj/argo-cd/v2/common" + "k8s.io/apimachinery/pkg/util/strategicpatch" "github.com/argoproj/gitops-engine/pkg/sync" "github.com/argoproj/gitops-engine/pkg/sync/common" @@ -24,7 +22,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/managedfields" "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" "k8s.io/kubectl/pkg/util/openapi" "github.com/argoproj/argo-cd/v2/controller/metrics" @@ -32,7 +29,6 @@ import ( listersv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/argo/diff" - "github.com/argoproj/argo-cd/v2/util/glob" logutils "github.com/argoproj/argo-cd/v2/util/log" "github.com/argoproj/argo-cd/v2/util/lua" "github.com/argoproj/argo-cd/v2/util/rand" @@ -108,23 +104,11 @@ 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 resource found: %s", sharedResourceMessage) + state.Message = fmt.Sprintf("Shared resouce found: %s", sharedResourceMessage) return } - isMultiSourceRevision := app.Spec.HasMultipleSources() - rollback := len(syncOp.Sources) > 0 || syncOp.Source != nil - if rollback { - // rollback case - if len(state.Operation.Sync.Sources) > 0 { - sources = state.Operation.Sync.Sources - isMultiSourceRevision = true - } else { - source = *state.Operation.Sync.Source - sources = make([]v1alpha1.ApplicationSource, 0) - isMultiSourceRevision = false - } - } else { + if syncOp.Source == nil || (syncOp.Sources != nil && len(syncOp.Sources) > 0) { // normal sync case (where source is taken from app.spec.sources) if app.Spec.HasMultipleSources() { sources = app.Spec.Sources @@ -133,6 +117,14 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha source = app.Spec.GetSource() sources = make([]v1alpha1.ApplicationSource, 0) } + } else { + // rollback case + if app.Spec.HasMultipleSources() { + sources = state.Operation.Sync.Sources + } else { + source = *state.Operation.Sync.Source + sources = make([]v1alpha1.ApplicationSource, 0) + } } if state.SyncResult != nil { @@ -144,7 +136,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha // status.operationState.syncResult.source. must be set properly since auto-sync relies // on this information to decide if it should sync (if source is different than the last // sync attempt) - if isMultiSourceRevision { + if app.Spec.HasMultipleSources() { syncRes.Sources = sources } else { syncRes.Source = source @@ -155,7 +147,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha // if we get here, it means we did not remember a commit SHA which we should be syncing to. // This typically indicates we are just about to begin a brand new sync/rollback operation. // Take the value in the requested operation. We will resolve this to a SHA later. - if isMultiSourceRevision { + if app.Spec.HasMultipleSources() { if len(revisions) != len(sources) { revisions = syncOp.Revisions } @@ -170,21 +162,21 @@ 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 !isMultiSourceRevision { + if app.Spec.HasMultipleSources() { + revisions = syncRes.Revisions + } else { + revisions = append(revisions, revision) + } + + if !app.Spec.HasMultipleSources() { sources = []v1alpha1.ApplicationSource{source} revisions = []string{revision} } // ignore error if CompareStateRepoError, this shouldn't happen as noRevisionCache is true - compareResult, err := m.CompareAppState(app, proj, revisions, sources, false, true, syncOp.Manifests, isMultiSourceRevision, rollback) + 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() @@ -287,23 +279,6 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha } trackingMethod := argo.GetTrackingMethod(m.settingsMgr) - if m.settingsMgr.IsImpersonationEnabled() { - serviceAccountToImpersonate, err := deriveServiceAccountName(proj, app) - if err != nil { - state.Phase = common.OperationError - state.Message = fmt.Sprintf("failed to find a matching service account to impersonate: %v", err) - return - } - logEntry = logEntry.WithFields(log.Fields{"impersonationEnabled": "true", "serviceAccount": serviceAccountToImpersonate}) - // set the impersonation headers. - rawConfig.Impersonate = rest.ImpersonationConfig{ - UserName: serviceAccountToImpersonate, - } - restConfig.Impersonate = rest.ImpersonationConfig{ - UserName: serviceAccountToImpersonate, - } - } - opts := []sync.SyncOpt{ sync.WithLogr(logutils.NewLogrusLogger(logEntry)), sync.WithHealthOverride(lua.ResourceHealthOverrides(resourceOverrides)), @@ -315,6 +290,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha permitted, err := proj.IsDestinationPermitted(v1alpha1.ApplicationDestination{Namespace: un.GetNamespace(), Server: app.Spec.Destination.Server, Name: app.Spec.Destination.Name}, func(project string) ([]*v1alpha1.Cluster, error) { return m.db.GetProjectClusters(context.TODO(), project) }) + if err != nil { return err } @@ -344,7 +320,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha } if syncOp.SyncOptions.HasOption("CreateNamespace=true") { - opts = append(opts, sync.WithNamespaceModifier(syncNamespace(app.Spec.SyncPolicy))) + opts = append(opts, sync.WithNamespaceModifier(syncNamespace(m.resourceTracking, appLabelKey, trackingMethod, app.Name, app.Spec.SyncPolicy))) } syncCtx, cleanup, err := sync.NewSyncContext( @@ -357,6 +333,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha openAPISchema, opts..., ) + if err != nil { state.Phase = common.OperationError state.Message = fmt.Sprintf("failed to initialize sync context: %v", err) @@ -415,7 +392,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, isMultiSourceRevision, state.StartedAt, state.Operation.InitiatedBy) + err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source, compareResult.syncStatus.Revisions, compareResult.syncStatus.ComparedTo.Sources, app.Spec.HasMultipleSources(), state.StartedAt) if err != nil { state.Phase = common.OperationError state.Message = fmt.Sprintf("failed to record sync to history: %v", err) @@ -547,40 +524,3 @@ 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) -} - -// deriveServiceAccountName determines the service account to be used for impersonation for the sync operation. -// The returned service account will be fully qualified including namespace and the service account name in the format system:serviceaccount:: -func deriveServiceAccountName(project *v1alpha1.AppProject, application *v1alpha1.Application) (string, error) { - // spec.Destination.Namespace is optional. If not specified, use the Application's - // namespace - serviceAccountNamespace := application.Spec.Destination.Namespace - if serviceAccountNamespace == "" { - serviceAccountNamespace = application.Namespace - } - // Loop through the destinationServiceAccounts and see if there is any destination that is a candidate. - // if so, return the service account specified for that destination. - for _, item := range project.Spec.DestinationServiceAccounts { - dstServerMatched := glob.Match(item.Server, application.Spec.Destination.Server) - dstNamespaceMatched := glob.Match(item.Namespace, application.Spec.Destination.Namespace) - if dstServerMatched && dstNamespaceMatched { - if strings.Contains(item.DefaultServiceAccount, ":") { - // service account is specified along with its namespace. - return fmt.Sprintf("system:serviceaccount:%s", item.DefaultServiceAccount), nil - } else { - // service account needs to be prefixed with a namespace - return fmt.Sprintf("system:serviceaccount:%s:%s", serviceAccountNamespace, item.DefaultServiceAccount), nil - } - } - } - // if there is no match found in the AppProject.Spec.DestinationServiceAccounts, use the default service account of the destination namespace. - return "", fmt.Errorf("no matching service account found for destination server %s and namespace %s", application.Spec.Destination.Server, serviceAccountNamespace) -} diff --git a/controller/sync_namespace.go b/controller/sync_namespace.go index 43e0dc6170f48..9203e27f502e7 100644 --- a/controller/sync_namespace.go +++ b/controller/sync_namespace.go @@ -1,15 +1,15 @@ package controller import ( + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/util/argo" gitopscommon "github.com/argoproj/gitops-engine/pkg/sync/common" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) // syncNamespace determine if Argo CD should create and/or manage the namespace // where the application will be deployed. -func syncNamespace(syncPolicy *v1alpha1.SyncPolicy) func(m *unstructured.Unstructured, l *unstructured.Unstructured) (bool, error) { +func syncNamespace(resourceTracking argo.ResourceTracking, appLabelKey string, trackingMethod v1alpha1.TrackingMethod, appName string, syncPolicy *v1alpha1.SyncPolicy) func(m, l *unstructured.Unstructured) (bool, error) { // This function must return true for the managed namespace to be synced. return func(managedNs, liveNs *unstructured.Unstructured) (bool, error) { if managedNs == nil { diff --git a/controller/sync_namespace_test.go b/controller/sync_namespace_test.go index 0124d99532b91..e18f52800bf03 100644 --- a/controller/sync_namespace_test.go +++ b/controller/sync_namespace_test.go @@ -1,14 +1,13 @@ package controller 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/util/argo" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" - - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "testing" ) func createFakeNamespace(uid string, resourceVersion string, labels map[string]string, annotations map[string]string) *unstructured.Unstructured { @@ -248,8 +247,8 @@ func Test_shouldNamespaceSync(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - actual, err := syncNamespace(tt.syncPolicy)(tt.managedNs, tt.liveNs) - require.NoError(t, err) + actual, err := syncNamespace(argo.NewResourceTracking(), common.LabelKeyAppInstance, argo.TrackingMethodAnnotation, "some-app", tt.syncPolicy)(tt.managedNs, tt.liveNs) + assert.NoError(t, err) if tt.managedNs != nil { assert.Equal(t, tt.expectedLabels, tt.managedNs.GetLabels()) diff --git a/controller/sync_test.go b/controller/sync_test.go index 1dbfa2ff9e1a5..7882b54340bfd 100644 --- a/controller/sync_test.go +++ b/controller/sync_test.go @@ -53,8 +53,8 @@ func TestPersistRevisionHistory(t *testing.T) { assert.Equal(t, app.Spec.GetSource(), opState.SyncResult.Source) updatedApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(context.Background(), app.Name, v1.GetOptions{}) - require.NoError(t, err) - assert.Len(t, updatedApp.Status.History, 1) + assert.Nil(t, err) + assert.Equal(t, 1, len(updatedApp.Status.History)) assert.Equal(t, app.Spec.GetSource(), updatedApp.Status.History[0].Source) assert.Equal(t, "abc123", updatedApp.Status.History[0].Revision) } @@ -142,8 +142,8 @@ func TestPersistRevisionHistoryRollback(t *testing.T) { assert.Equal(t, source, opState.SyncResult.Source) updatedApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(context.Background(), app.Name, v1.GetOptions{}) - require.NoError(t, err) - assert.Len(t, updatedApp.Status.History, 1) + assert.Nil(t, err) + assert.Equal(t, 1, len(updatedApp.Status.History)) assert.Equal(t, source, updatedApp.Status.History[0].Source) assert.Equal(t, "abc123", updatedApp.Status.History[0].Revision) } @@ -255,76 +255,6 @@ 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 @@ -363,7 +293,7 @@ func TestNormalizeTargetResources(t *testing.T) { // then require.NoError(t, err) - require.Len(t, targets, 1) + require.Equal(t, 1, len(targets)) iksmVersion := targets[0].GetAnnotations()["iksm-version"] assert.Equal(t, "2.0", iksmVersion) }) @@ -376,7 +306,7 @@ func TestNormalizeTargetResources(t *testing.T) { // then require.NoError(t, err) - require.Len(t, targets, 1) + require.Equal(t, 1, len(targets)) iksmVersion := targets[0].GetAnnotations()["iksm-version"] assert.Equal(t, "1.0", iksmVersion) }) @@ -396,7 +326,7 @@ func TestNormalizeTargetResources(t *testing.T) { // then require.NoError(t, err) - require.Len(t, targets, 1) + require.Equal(t, 1, len(targets)) _, ok := targets[0].GetAnnotations()["iksm-version"] assert.False(t, ok) }) @@ -421,7 +351,7 @@ func TestNormalizeTargetResources(t *testing.T) { // then require.NoError(t, err) - require.Len(t, targets, 1) + require.Equal(t, 1, len(targets)) normalized := targets[0] iksmVersion, ok := normalized.GetAnnotations()["iksm-version"] require.True(t, ok) @@ -450,11 +380,11 @@ func TestNormalizeTargetResources(t *testing.T) { // then require.NoError(t, err) - require.Len(t, targets, 1) + require.Equal(t, 1, len(targets)) containers, ok, err := unstructured.NestedSlice(targets[0].Object, "spec", "template", "spec", "containers") require.NoError(t, err) require.True(t, ok) - assert.Len(t, containers, 2) + assert.Equal(t, 2, len(containers)) }) } @@ -489,7 +419,7 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) { Group: "projectcontour.io", Kind: "HTTPProxy", JQPathExpressions: []string{".spec.routes[]"}, - // JSONPointers: []string{"/spec/routes"}, + //JSONPointers: []string{"/spec/routes"}, }, } f := setupHttpProxy(t, ignores) @@ -501,24 +431,25 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) { // then require.NoError(t, err) - require.Len(t, f.comparisonResult.reconciliationResult.Live, 1) - require.Len(t, f.comparisonResult.reconciliationResult.Target, 1) - require.Len(t, patchedTargets, 1) + require.Equal(t, 1, len(f.comparisonResult.reconciliationResult.Live)) + require.Equal(t, 1, len(f.comparisonResult.reconciliationResult.Target)) + require.Equal(t, 1, len(patchedTargets)) // live should have 1 entry - require.Len(t, dig[[]any](f.comparisonResult.reconciliationResult.Live[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors"}), 1) + require.Equal(t, 1, len(dig[[]any](f.comparisonResult.reconciliationResult.Live[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors"}))) // assert some arbitrary field to show `entries[0]` is not an empty object require.Equal(t, "sample-header", dig[string](f.comparisonResult.reconciliationResult.Live[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries", 0, "requestHeader", "headerName"})) // target has 2 entries - require.Len(t, dig[[]any](f.comparisonResult.reconciliationResult.Target[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries"}), 2) + require.Equal(t, 2, len(dig[[]any](f.comparisonResult.reconciliationResult.Target[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries"}))) // assert some arbitrary field to show `entries[0]` is not an empty object require.Equal(t, "sample-header", dig[string](f.comparisonResult.reconciliationResult.Target[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries", 0, "requestHeaderValueMatch", "headers", 0, "name"})) // It should be *1* entries in the array - require.Len(t, dig[[]any](patchedTargets[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors"}), 1) + require.Equal(t, 1, len(dig[[]any](patchedTargets[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors"}))) // and it should NOT equal an empty object require.Len(t, dig[any](patchedTargets[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries", 0}), 1) + }) t.Run("will correctly set array entries if new entries have been added", func(t *testing.T) { // given @@ -540,17 +471,17 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) { // then require.NoError(t, err) - require.Len(t, targets, 1) + require.Equal(t, 1, len(targets)) containers, ok, err := unstructured.NestedSlice(targets[0].Object, "spec", "template", "spec", "containers") require.NoError(t, err) require.True(t, ok) - assert.Len(t, containers, 1) + assert.Equal(t, 1, len(containers)) ports := containers[0].(map[string]interface{})["ports"].([]interface{}) - assert.Len(t, ports, 1) + assert.Equal(t, 1, len(ports)) env := containers[0].(map[string]interface{})["env"].([]interface{}) - assert.Len(t, env, 3) + assert.Equal(t, 3, len(env)) first := env[0] second := env[1] @@ -592,13 +523,13 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) { // then require.NoError(t, err) - require.Len(t, targets, 1) + require.Equal(t, 1, len(targets)) metadata, ok, err := unstructured.NestedMap(targets[0].Object, "metadata") require.NoError(t, err) require.True(t, ok) labels, ok := metadata["labels"].(map[string]interface{}) require.True(t, ok) - assert.Len(t, labels, 2) + assert.Equal(t, 2, len(labels)) assert.Equal(t, "web", labels["appProcess"]) spec, ok, err := unstructured.NestedMap(targets[0].Object, "spec") @@ -614,7 +545,7 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) { require.True(t, ok) tLabels, ok := tMetadata["labels"].(map[string]interface{}) require.True(t, ok) - assert.Len(t, tLabels, 2) + assert.Equal(t, 2, len(tLabels)) assert.Equal(t, "web", tLabels["appProcess"]) tSpec, ok := template["spec"].(map[string]interface{}) @@ -622,7 +553,7 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) { containers, ok, err := unstructured.NestedSlice(tSpec, "containers") require.NoError(t, err) require.True(t, ok) - assert.Len(t, containers, 1) + assert.Equal(t, 1, len(containers)) first := containers[0].(map[string]interface{}) assert.Equal(t, "alpine:3", first["image"]) @@ -636,7 +567,7 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) { env, ok, err := unstructured.NestedSlice(first, "env") require.NoError(t, err) require.True(t, ok) - assert.Len(t, env, 1) + assert.Equal(t, 1, len(env)) env0 := env[0].(map[string]interface{}) assert.Equal(t, "EV", env0["name"]) diff --git a/controller/testdata/data.go b/controller/testdata/data.go index 6bb0d5ed320b4..3dbea3216ea4d 100644 --- a/controller/testdata/data.go +++ b/controller/testdata/data.go @@ -12,9 +12,6 @@ var ( //go:embed target-deployment-new-entries.yaml TargetDeploymentNewEntries string - //go:embed diff-cache.yaml - DiffCacheYaml string - //go:embed live-httpproxy.yaml LiveHTTPProxy string diff --git a/controller/testdata/diff-cache.yaml b/controller/testdata/diff-cache.yaml deleted file mode 100644 index 41a1e8a4bbeb1..0000000000000 --- a/controller/testdata/diff-cache.yaml +++ /dev/null @@ -1,498 +0,0 @@ -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/assets/api-management.png b/docs/assets/api-management.png new file mode 100644 index 0000000000000000000000000000000000000000..ae066f0a6a87d38e9812264ed0d5775eb75312fc GIT binary patch literal 14376 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 literal 0 HcmV?d00001 diff --git a/docs/assets/argocd_architecture.png b/docs/assets/argocd_architecture.png index 84fe437a9ace9e03aaa960e4936ffb09fa4dad34..3de4dd9f93d4e630f775890066c475338bed4de9 100644 GIT binary patch literal 121649 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 z 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 Q!2myUQc9BLV55Nl19DOD4FCWD literal 152028 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

D?r6jIzT`!BIq$EgLsRMQRi}@)Z#9_%; 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 W|NWxepllKWpM73zR-l;;%y diff --git a/docs/assets/groups-claim.png b/docs/assets/groups-claim.png new file mode 100644 index 0000000000000000000000000000000000000000..d27e03b661f825436a937ff75d90ae1f546f1fad GIT binary patch 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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 literal 0 HcmV?d00001 diff --git a/docs/assets/groups-scope.png b/docs/assets/groups-scope.png new file mode 100644 index 0000000000000000000000000000000000000000..45557b51ead7f69512a3908424bc419ef2691201 GIT binary patch literal 59680 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 E0mHsFDgXcg literal 0 HcmV?d00001 diff --git a/docs/assets/okta-app.png b/docs/assets/okta-app.png deleted file mode 100644 index bfc4570826b0a25f30128f6b84e54a10c9d74f2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260259 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%0Dw+!%A| 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

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 diff --git a/docs/assets/okta-auth-policy-edit.png b/docs/assets/okta-auth-policy-edit.png deleted file mode 100644 index 91395dcf5b07477818eab1a575e532b8ebbefa6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63571 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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 z?uCg-3SvGok?%e5s4nZUcIUCOkc~=O@G2Qr{9ILyNlTxqthI%mfb!8|G^;g*R?W@ev8n$0%BAXaxY>vmPn&*f;<_5U zh>g@cR+J9JIeKQ&xI!hp!i$Na@pMR39_ma==7&H@e%3AKA6Cg>aO0gB4oN>g}bXR8TbXm^L@?!bq^1?-zxGtYnC%owjmvcO0RDxX^Oe?k4 z_B-2LnkE>Z#7dM8&qU=)xodN+x;k6TB~qv?mBb(J^nVg+sO#_aab3*S`b)Qwt*C+T z30LW-Wovb4B;5?;#Y$c8Wh3NQ^yQ+Qzj87AmX>nbW_?@sR+Ej5iiXQmCs^z)C(2Gw ztq7_UrA>z$w@>To7Kqu1z^iky!bR=qvFQi_u(O$g1K@NU`$l%EZJINQFyQ z!~W=(`SFsk%eSkOx0of5r1yVD*w5hn!s`}Jj-xyO+U961d8Ua6{l!4+%&#bIi55;> z?MN;+(c9X$1y!%WOJ_zhA=dJ(!Y7&}&`@0c3Ny`wqul5jJr&GcO_lA&Qh76Za^(1j zGENV(siEpp^7qQnxR-Ud2R2JzB{Ap5pFrGOV)fS26~o#EElHp%k`~!#p@?ehC!Qt` zWSwT=h)a|!BA-n%f8;VfwP)k5<%gzs+h*(&EU7< zHIrn&wD&+wTr?mma~EDbgV$|VDVqFoWjz@7gWTbniakkRXXz%#&AjQb4(PeW7uadv2A?NiRCj`v_>#cnfJR9aCVueV%Sw%LioZ74-F6k|rJ|muzc~GC~(ex&8DG{lkn5 z`Z|c3A9_upN#iG0G;JbLUr4%v$UNs`X0HR3mGxY~15CaUroXd4Zsxh}mt$VxzTUyS`4s?={EO`AZ4m8Pp( z1+UR|W>%nzQ}>z&6&as6aWn4!^6Otq<0;+m!Uz&UF>GO{EZZyjN9?mrIRRYly<1Bq zGQA$uGUw2xolHr3E{u!&N?J^R@f8IY*1Zg=-G>qBNc6ed*LF zU!dK_FBgbp+sKb>MdRz0pabS7Dk~OwN+^A=1#FEVFZpgQk6(@U^nQ`;5b@x{IcRT0 zYvzlxV)=4o8?lJ$WPw_;r3o$?v-aWA(olvWE~X>+8`bs|wQ(0Dkk}h>**6uVuxC#7 z3ixeNm(7ev*)I;#s);)upP}99&G3o~=$`)g>38kS;p{Q+S5i0bcwO^wG0)?C_3@*s zfu8ykzn#P)nI=*4iK6}e;+)YSa~$sguPm%&XjABp>UFS&RLe$w?F{TNcD^%KjIN2AeGN^e8Oc z#?LYEv5X(AQ+;MX4`=YnD6doGF&6S786@Ho7ch)CnXHtxxA%<&3 zY_911W!1p6;4axC4-2vVKFo}ng!{~^*gH8h@b}S5Fi%>GETtsZAEO%iRz~IW7VONv zdWX|yQ-#dp+7&v32rm7xJnjRh;RtJ|g-7fs85(sqJgcuQX&MkmrKN4Gs*-x^fEzW| zYp>~@l}~-4ynVBNa!58>hh|CeX)~REN0uZ4ktNxyc9An}+Jc?zO-LAXgmZir_qn~N zPOcwu+t^`A7pf~)D0Pcowl!*+%&umJ3ydWnW|iRHDY>}EN`_x8k8p`**`1BlYSlCa^s|HAAh;v_U zXE?QGe71CNd3f?IA()W8IZ_n|`K8<2r=Q9xwCR^nt>x~J%Jb%Vt#=dJZiOmTv4ngS@e<2MG6;V0P(9j#+|k`1Cw$QKXt>f6 zsN^r4J8wQz(MXyYk5(l+x|MxNp*A3nw&dx@w8Ey#g%cxQn1SZC;fPMjPOf1wuh}*s z)a>HTB~m@XTQ#fbpF7-b&9xq$x>f6KJ2TT^z;1W`0y8^*lBrcG$5I`SY2XnrSG_vF zMc}ym18>SQU9DE1ubSC$s?nj8gjp8x=-HH8Gx^&k@0u#dtc4o?x^T!U~uDn`t8 z;ip(EAZ60!%}HRgspdc<97ON5#JZxqy&l7@F-67FHkK=ws-HEj3(b{AcOPvzJOIL9 zjW9d2VnfEq2F5L)xY3G>G0~4@5U(sL)U$Qgg;aEA+MVnn>vr~g+8RCX3Ypz!B6eu zN|hlWvz`QcXP6L=9>s5NPGe*Q$5eLIu|=jaBWWhjnX5cKXJ*n4HYB?FY&0+U=;eE7 zTV~cGsvTCcOyoFhwSWwE9h6sWQasYCzGJtLbO&ZRF2T9QIKEEa;)vR?y4V}f^;$sM)e#TUU?WUGnL*UIRg{gOmsD@Go^fzXHO6fui7kZoX&fn#|Go@W(D5 zo7~OhXw$>HY5^hw*=n8MVX7k?!b?;07MScVr#?TD?y!hWos-m-6eZjnL7v8IuVb+t zO&ZnS4qHs!HoO)qOuY6Q*!35ABlLD8DrP)L3akmMG0Cg2)~w~%CM#59)FacwF0PZU zb4F`fp}hHua3Qwrp$?w+6}k3buRd96kG`xMTm6C8dYaGQhwcZTa%`oj%_cc?$z)X0 zgBJBjvw!yRviqF_z223>8n>k>!EkHFq(Mu$%|#iA7LUcjUk>4a@D*QBNGT#n3Gjza z`{JiKH4ISyla~31RE8D+&u`<$@GZJfNU`_@@e?Hp2v)HIZ#tQ<^$24z#IaHgl*u~L z5_L5F?cCJ;ADo-uo}sw7-2zhl-PX9>1Rpu?T|M!Z6z2Nio_p>zKP_;(**aeGp7Q?7 zR5&2+@@%DB;Y>k@6!Mvhnusr4G^S>4!yxA2~yD2^T(d=`YW?FFCljb zr{101(f|v5sLnOzG~h#cGxQ%O=&!ybud_g`FF(Z_y9TjNX}$gQq$LPoH(LO4QkrK* znFK`3H%>adlc%(f8$2FOwMCA43PFCVVp2HSo3CUH#`L|J*<8~#$e6HT?kQar@dKXk z^Kyo04kD-n3;D@vAigv6@5u*-+o_)I|G!PXqojwL6>^lgCMd7_N_VbhLlZqVJLEwO z*RWbllB3kggM7TmRk-8m!#R=NO_gJ6j(&f|-`a1TNpX5MKz#CCLb-6wCMs^(?Dn^5 zeJq35;hI&OLPuTKC7dpP`k!NWljA_hfzYC*#nd=1X-Ac-6@zeri|zvu4I?d5IAyY* zQ!Aa(%GTgr$urwDqXIhw<6`jilwA~8w>HQflq_Bxeczvi!3COJ=CBxB5vB*xQnyK@ zJXuck-Ath>Rx2E3g{EwlTVPCQ5YD$*+3)ZtJ>6UZuVbzOR_b?!M86Z2hU0gB2LNHS_Zd!Y)143G z*y}_Qyi)>FEKn967D#7^(6y6=9M}}kj-{-q6TvCl7U~PtgF-&i+RL4?>U^Q!L0FC6 zdGTz*$)XAfmMY)V4O-Ayzt=$c2rTzq6gqjOf@ipa(bQ|;J+%!P2atF}nS7_jEy4Bc zU^J_Oj7}DZpv@&U5VXrzF`GPPUE&k{0;B0h*?el7J2^nKJxlMZJ4L7wQM7<)>Z$hc zzYFU3vG)JMf*Kp-cnN{vBtDY9`}E(%_5Vd_HF<=0RhHGDo+k}*{O{3y1! z-SC}U*X4Elem6Y7-aqfQJ#5>ShXs;`-ND;+%am{VigL_EBfN!%{sirP zSKBRKfB#c-Cg<6yxd~ge$B}}MJ{q4M@1Ngy($&9&gk)6U`kKyZYrvd3LIunDEbd(84nQ&=FMuMHovg!2+)nJn6_|b<*19z-TUGW6*OU#ln+{7u7{r!WC!~&%_l@J(Tld| zmIbAn*m?7M%f-D0RHv__c{7n5OtbCTJ(3%MZA9E3ID(}$3^}fT)BqZ33z!8UWHVhuih6j z&=PS$qi5zE=ylQTp&c_;2PETosX+w|<{`Tg5w9Yf2&jY7 zOR1#Q8?{oZh4VC{H#N!_zvE%!4oKjA#4(RK&oobNhPh1?_6=2dq$&EDSm1hMs9wlK z_=LZ0LrT-0aZpjT5Unz*H7Pjn>l~oB9WuHE8wp z27&fpQyYk<%tY$n8*-R%KAwy3PDf^j^g6yS9u2QQcANQIoJX6Y*j; z;kYE(Nhv#!0Z8($AM7I}QLzMGiL!>XW$9#L!wuR=Ok7M8c$G}IOxjH%+H~VR8I@oI z(XR2P9rUY~Gd$@YG% zcqSA>7(?AI+m1t~G>~1KliP2!Y-q*hD)msmL7P#PQ9fOD2H6NAlYRIM-MHz+t;P|GZ=yFFT>p|9I3Y=f3GuYL3rma&G0)s#O-?!}yb?&wbQ4%x2Ok>}Cl zBVxkV>zvm+2tN_pQ5z9b1zHAr6C8)XV5Z5um8qDy(p*@C;FbUc^plsXQ09Wa2u zkpO#?aChYHpisI8A=BkdhlZ`G;?*sP7MQ+D-Nss(N(q9wi+NER@#S+kKX>nfXd6LG_JQ*(2t zd@jdr%r{#s-aOgm`}XwSaU@Y0krt6W-I>z^$4AaKF0?xE`rz^$v);n!IMOl8`o1*2 z3E_rA^M!{Lk15KON;^n3Y&G(&5i6{Hu=nj^Utcbnt>1MLo!Oh5Xz+FqB`*?;nuFitS zjgb1N`qp}8G@@UG!#93$KM{OFGE9#7TJxdkYkI5|Y%bz6eoUI1w5n8F5BixNNFqhU zZ;~?%Qtok%bG)=Z@W=7Tag;HKKQhrZk^Xti8kzGhr@^9RQLT5#D8~4JXRWAWxOI4R zxb0g?m^&wZNJ$7=cy+CepF;OLHl^73k6hw(T%5Zj*p*IHxzo5Mz8G6u z;~ZO2`l-0380e0~; zrb@9^10O%_dsld0->X`;oy;yP@R-U@J(Qf9*r=^9)h?)7McV`KL9_Xz_$RQbuxYGf zt;4MA`*D}AuSl;{Q?F7}Q>)RG3p70b@o`hyG3bcMo7T==z;bd*%KDxJMnrI=*zuj2 z_%Zp@2se6TT5E}X@0w-IIhm(y{U63edgD+6!}NWVeyDvD^C07v5QE2l2+=IhPg}^w zC4_mfIbQcm(L{H*t)yS$VcTsBL87yw47$N}4kc{xy#0)+ZyoV9oO+99ZmG4aE{FYO zL2N7qy!mB%CAEX2#(mB|1QVx}cSiTh^vkO!C(WmZdh2t}J70BtIi#T%sBvTa zrGCB^Jm$Xfb%LsvS^jcw|4D{j zigxX<>lkQgL6&Hk|CCV%J}>@Y!0V#U-=Ejs1)^aC|J?*$9%&fAOJl*)uKoUw`5pKT z?cpSL*XxGrl-ql5%iRq%F zfB*g-r>TeKe|oZW{^z!U8|1q9g^P!io9o}UfvRE`S4Ey!dYIa1$ynL~a|ZMw!NbeP zEB04~|N7}aUH(y3^N*?z9z6K7>K{M-|EsDwn>tC`+X9`sNc?BP{;B-ukN;E@*;)C0 zXlP(G1(}D>JP>-O2Cy%&Yg4Ryr#SJjHywJKgK}wv!i5x!hO|;G zHfb|SPT6L9g@%Cx`tLW~8?C`S7H?_y=f&|OO6jQdp)Qk678^)m@g)4#RIx2)HGZU-Vvp!`bMUqhNzf1qs0#O(k=^*kT zL0OqKxwpnMPb^8ej=uNJ_n^7zX4xB;^a0d@rh(xXbq1T97#OPyVmrVhdms@tsm14( z^1pIRUSB>5Sn6Ns`sq1?9o8Tw+_v>{oEVnolyr6$5hnz%wzsu|R>B%wFYhqz1@5r_ zxXF}fg>}I7zRDF2+hoZI@FD8|3;6goGcRZz#&A_H&;% zSx7C)yfhWUzM{@;^a2uLuKf+yx|%@LQ!j`-Q6?!FEz7c)XG19iO{mpWhsfnMK_vI5 z`HN*N5GQP#crnh-uKS)++kL~{it;#c=SB;z)|$XpPjLzLiYG})%(d%m=3LfS-SZ@t zDr2TgQ2XX&?9gWSaVSR&5h)~yICGo&_QI}*V9fT%Yu>mYCKPR$|9b~q%-JXWpYnrC zWb7@oKO`nc<`>hE@}T4-Zjl=gk*D+T=tNjDYB$9o%a?DWSAJOI4{b9xZugE@=#Q>Q z3{DC}wWcH4VBNTxWS#U!^8#sIYmS50O6-~^$*m2p4gjTuUITPZ1vHU*)|z#^`V7Ax0(;HKjyInxVXF&oqN=-$&bYsya1*`qyFJ*ACw z=B140_3o@btT1I8%H%|cVJioAMi;y8)N2jYR}5lW5~}%n*277?9L&BD=@Ra`mLviQ z(+$WJJ-cgALnW^7ta!5({CHIOt$-X|(ftv8ALRP}-f{4UkC#poNeS2vV>m%2g_w-F zbSm^Y^IX3E<8T+{f+OvDiRLP|G+%CC0YQH#vf6MnFlT0YsK7uazjB^B_epxqElypb z@7K>{uh`QhLhKlT4uKKU2(-uCd-k(WMRMQ9U({spvk9Ba5HW>fBGje0aWO^QAU2dt zrIP(5X30ObUPj`WU=<>5m)@mXK9Cm|hcr+u} z^gEx%6JFU)|IGvMW1`A)D7HAw9MOAmwh_Fw-DD_Iy*)0RoWrRZMnu6m*1e!fsF9k| zOLSM@GVOLTo)~h7x{9Sat1UjfDWGE}rg5Oa;QW5&`I&bUot*F$OZN(lhetrQ^^xb} zj(G}4$^Of|&(rsjL*+rJ271&ChS`v3XBWhEiEizV9Mu&~z(z5mycDcE))<|UqK-qQ zQkHzMh+Ev%Dzu38Uo#t!nZ|&LprW^zYd#zhtnse}QAcRY+oX$HL_8nS7o>y;+E3mW zMLpc}>20(SU&?vfv=&qtaa9x3q)@XiZ7T6}X2C)F2lDw#t7?ctN4d{X7j{FWfXhz^ z#*zgw^zalVSc>wjTybZw{4gKD2EWnsR+qdes4g1XAGJ^B(*U*_x;iC-1$r`n?(k8YS-6Po)Wo0gl5z~gk z0nOAi$~8>`16#eB@)ZRQ0#``Kt5?LB9|UY?771k=5OZiwpyp;t!JG&B0b7xQg(k>vT1-BR>oFz)pk92 z2GRy0+~>$1cuycSYJ;Ij0P(?tuNflsW9U)(J28xxnq4%ETrL}W=cPbv38_NhFr6PgB^x61OmoHGit9q=*)r7hz&(nB=*1F?>4g*k(;_wM< zPi)lQvpIsH^ekY!G*^!I9Ot5T7y>N9)8r;Y0IzMbAWTEZ2{l>R-iEq#XBrV+q3wP} zgDn7s)6?dUNa^15E^Z%BPOe>kgNfQx+{8wW$brOBzIa!5i}{l2JJQlQGfN%KmVb1# zo1E=s+1_Hd#(SUo+6ZRg=7%St$kXTR^eABTbXPVbi7P_{=YL65``6tktd*AP>q1>X zX=V92mLAh{tAcWV8(d!3yTige#tgv`ogdnxgm1f|yL zBgw9~`_>ycn5e2Xf@J41X5z6M8pKOU9acT&`wq0}wdrfSZ)=-*n2ejb1Cf0J99K(L z`NZJK_d?nkjHSgUw>Q<#T3~eRKS${xExfweP!}Pq_AAyL+E4Kd4^GN&BcFm{$)cAg z$PYz6ySgJ43}Haois*7JeK!;}qL;5~JQQT0PGsg*FeGW=F$&_L4XT`daxMI)*0ar| zZ=aGFSL&!zpLJg4alU$Gk7-wp22`mq5o5TBHNirR)ZT ziwV{Zy;^!A!8TBrpN??X+l1yp^GU}&7K5L&n0*KJ);BY+nGQZ=9N5KimXdzl+7eD> zsP>ceigf^gk;jO-W?35rZZR$RG9qCkU!ilMBE-^X(&_u>FJ{r=%2|9);cy)_I+}#0 z?Jc1*1~nA7$+TLeFNo15F5;-fgWI;yo&{Yg0bCcVTqiLpgs|&E(0va@zAIlLEg(;fLwZQH zxBLnMtND779ZEbG*ybEm5?6=_x+MNrdWep+m(a!_wfyJ=?&v2nl(dLFur}dTd?*pY z)GK@tGz^KG6>DYla@MXM^A^%>Kb1gq7Bb3_OJMt@FK%g3m?()Ox8CPh4D-uf5^Bgw z_m>l>NSmQDc~TH~)J=T$O*f8)4$LfQ8*v1K-)&J{y{Rgbv}2)eE)tByF_m7`FT5gCzVh+r6<9W9F} zlpwtY(V;LUu*8p3xoY(Gf9({pQK5^~@UBBrTp@N4Ds+sc7|&Y|Fs~|L4~1i+9w(M{ zmJzQ+RrA29ikWNc1IOtuH&M!fN{=WSK zB@hY3IKD@yk!UL$@G0Tz)DE$t^0^7xfzGjSATGhXp@uwV)_R%RP*^et3j z36+)gJolaI$46SiX9qWPy{{n&E}I|dZQQREkf4Ygh^}^_2M#X%jG{mA;n!wdK!uzY z&>aBF%90Mse*0pQZcw!TMzV-F2TO`aMhQ1VIXSx~>qgDCek)m|*cN1|cb4p)ZiPc@ zoy;u)1_`P8u4CSx&j<2SIc-P9ZmOnyel2-^qICCB0&Aw++o-t?zi_Yj&fU3;Z)prT zc9#cYf%ucd%Eue4Rz05ceD6egQGBJAVLi)vVZn5w%9XK;^=ZbNouxiDcoN1X0t0=` zI86sxskOdt!y&w~u7F>99g3WbdDC<6*~j+GND2DgPC5~>d%!@S^n0~^N^5Y4=j8H8 z@m)gM1E^d^%JG)=!LYGPD_r53l(O^L;X>PRQKnQosa~N;o2utV?Y`;G;%D8|Ds_a? zT;)PaEMIpHeV?%Jq3K&)*MWf)S8T#%mbcWN8{1+ag#dnQeY2gLQ_G%vYIzyhdnwqe z!IvnkX>aH)Jotq8t0hLnRKE9mrGZ&z0@ZGqdM>JM>u@0z^IU{v_Z6lFs|9oidJP+H zKXryGnELWYLBMwOE5S!sk4OmMi`=H$yUgc;7XvCCIr%Ux-sPq@#5~boYa#s6Lr|^b z?A^-MVi6uczvgf%Mhbn8hn>5mT16l%)L7Z+Jr#)&eY5VQWQf$Zu6e3YT+QJ;O|wv6 zR#QqMScIKW^s<0kRrkV)MR)1u?iLY~UnIO^(Hij`JnCy`{krM%=csfJNZ&ibNy_^L zH7Mr!YU|#N_cc2xKcoEA)=;cFF#XCcbIXY%dgsPQnWK>&wYry{#_Y>8PFrncYu_4N zF_u7BY?o~h3DOH|aRBDD*|=fb6MzffUP52F9k{#9A5&`W_jV<;jkZ8oRT@tURBJtC zS9x}nXtKL(!1NSz-_m{|l#H!?vn3w3yE4?FB8dnWZXGmsC0J#W4aMv+N02~tZoa$> zjX8My3M?XTwCYs(ymPxVFJHYPdBr+nktwRaFh zb|tI%)!H4~JwOb_0&{aHgkH8`n9&U|Q8Z^q^6KYxJtiDD(49B94DHb>Zof{bOKp!6 zkhWvZ2{pgSJh!{b#a6_2pJRJ2rXP#%e|we#UD}$HY`3pJy=y)AIo2P$_FKk#q1RR7Tfs+>l4mdmKV3t8jTU{0<($mUS9&amqWj~{aEF=3h82muVNza8 zRLpc)sS}_h-Wzl4jCmf?O8RCXt%rD4@6r36JoWq$($cgZiIm>hjnnxcN6U6{yip$q zXk;vv=vJ`T@!s0jg!@!S9EXnV*w&E2*jsBl>qj{r#Yrxxkogi$wNK9<)zS5Oz_x-!@{-<9~7=F~39bN+pXUttJxb)s6M?(B%h`OyXd;Jq(F0;rK1FJIn8 znt^U=39?|waEWmPuVcQv0r4wwi(DOX|6bGdngmjA<(l1@xSzE^nk=%*oU4xbK*coq za@#h=^+C+>$+n6U9g`KSu+dit75DURVeRWk@jcB%Grk z$2=1Rw;ZpC+H6T`d3zq2v@8_qn{D9dQ&_V<8C*DJDGm;5@|=bq|7>NBbfN9Je%ag^ zVX8{NCQG?=>h!9ID#mQKBl^T)`qg_HxjN<0g}wop?Q%c(MSOBHV9xjn#!7(f&r>-r zvkgN@F%{n&hb5FLs^)O*hP3GMCbc>dy%^Z{z_9Y?9oV1)5-_l9*%1}_4QBPewQo~? zaIusIxxjyE&FO;Y&HI^A3gVa&I?S`=@DVfkp6Z`;jj1RA|Gl65iMio z8)zjhIiJ>nnsq$ zao#tms}cVEhNxj$|0cgl%NJ;YQcp@jr3%Bie$4?*@8l0pJ-~k6D>3hh^E+J;an^hn z9v)tp-DwLFx9X?#+#6YFOc5NUBV~!;YZj7ZGuj1wytmZ+4+6u4SyNwbS$?&ya##NF zph0Z?XvN-bBeV*!zu$muY_xuZe)rAu%W_=YSgQA7ZW7Tp%#UuqueXY%l%}9jI>JU# zSLr#*))tBjn|Gf)y00#FuN7(ZO)*n&)wRL)I5ZHBPu>}oA?w`iS+?O`u!0%lx*`x> zX^uQTfQ&@}CZWi?h0a}GL`>uOCK<$i^&p&wd>1>#_IuiMZ`Az$yF{?pC~`@TYet?F z)!*59f2&u1mf5%bCKY6%tasGgAl@O`PfpZ+D4*+tbdyY8i|=LL(lAyA7E0J0&t-UL zx!lrq{=xMq?gg9<;pSnxQKV^^D~yQG&;VmhUFFGw1YuhFx)7*Ca8=R;Qby}+!l=bF zB}$7-9V%Gap6`kW6py!nK~N4QpP5Xn1RkoY*mymPh>Bs4`%q}Y`*FqVLjzy6mL1>hCzX?}?Q zF0u*u;=WG)*WWT&ck;y))Gl zT$5nlZT4BpI){XjcS)^#{+dp2hP#VPwU>fhj_3qzf)^ybXWrdWnhT_{E20SayuZ}f zvG{g^TWBOPunDlfBeao1+T3)UFy9q~d$^&jK3-* zPx7&Jl`MPu+`SL0TPcA0T*4`HRbK#9G7u0=VVM%h3YCS8p9{M0{`({<>If+u*-g2J!-0o1V zzcpR>$8SU_Bog5zlL#NU6Y8`8tt3q=x*@LKi% zdZFU!?yX%~^$du~PmQd28>)Fx>i-Mhkx}(tpe=wpDS~Yq6f#15T z2rla4G3pyST}6bQ+0^L45kds_-d$!CoN)0U%IYj;l%9-r^{JMJ>gTnTV^*0<(8*u}(4A{bbl$}6?)88aEE>@iOA8Khd`M%k zEIm2dzHd4F>qFr_(qmIFqgJemgGSJfw^4TE#gj-XK|5rU`($NGkl*Gv_R8b+W3hel zgJ~bRg0+a09^3>} z>$EK~f;>(Ag1GexT%}PU(d^N7tx2M4eWE&G!h8)!a6D+;e&Wb26Wf&{@6)XF$w3Gz zKiPfpNdy?q}@@i~jrGq@jb71M3N=w|XuJvMK?=bn)^W)0^tYJ98_!4R_@fv zBike(AH-aFx>qf>-?D`r@kvc(Un~wh8Xa(n6oZB^?omJ(9>^^v)LWI)b_%GwC~O1K zj9kSIYShqs<~qsNUcp|zeuYCmt@6M^!Pl8@q zj3TqDhsS|@4S)_jJUrQ^O4%(%%rqanGH^Rgub@g6tk-|Md1RHtLO9f?vCnTe4!KUW znwNK21^9g30%v}^4cMgNZ3Nh!d+d=5a$9^b{nF)@;KzNNnXa^!1*U-aIKSUuovJ1W zvQ_vM^55RpZ4J;I#uA6+=}Z!XG+Rx~qx%SS+?w7Wc6>x$@`6M&svn1ab)DpbayerCz{ zRZaM%sHnRJ$Zm!I@ixA#?6vPrV85**g>nF_HmcT}*xj+w3*xuk#bkUkGtanCjt7J)dihnKQcB;i^e87+wYEk< zCCieWl?jaxLgLJXd^&kr-X!q}LXmr4>(9@EOq#>rGcGt&^yfMU@O<17Rz-~7Q~j*T z(>uAUujGq`djCF(Wxl^m77uRTbEc~-wkd-kcVmPRDK!!Ow<-mtW85QyY)Ha5@YNYU zbpCn*^4U|6SK7?PrxLW?iBv8DdtG=2jrd#&i`U+|6nk!)DO5U{(y1Xe)}N_}udtQ~ z=zA?mqA~*9TWto4BqWblohvu$f<8ZP$<_c(6wUJSx~*bIcnvo9=SGKf?>x8W`?0`Q zu*2QVOHZxJ@>Cp7OfSZ&q5D)tz;2wqWyM& zhc0&bbJnR5mjzQL7C?b?Rh(Af)UQ`GXQ}fU{9fYbY5E@9CimygiaRnwU0@+(|9!h? zP|6EW`OJ3ASF2?AyGfuNnvYC6L8VaNhgSAejHQ_-c?f{UeLcw8(nmNin1@g1yNfEr zXN7`R(2oyhBil(;JXD@93WXGHj#~S`4J0>0sA>T}QQENPcwNWoa4x=*Yo?OUZZT2@ z3+2^Y=YtK|n4D<|?OypA70wMTB9E+;o^QCv==O~dB6-;yhoQW9lwJQx38EX*D5x# zoZiztAN=H+o8)arsA0L&HBzz=C~j_Mw)h=8s@ZpNvYzyAcq|Z<%Nol{uChMqauB(zm`*P)`3>}2otRPg+wb1ynFPW z+Q^ClNaDqNAJgGNM%8jVI`5~gUdX@JL@iHU3GmdVyG_a1b%it(WAB^Vdin1CW!R`! zE4iYeNPVz(v32Agqyxt25BK%inBl02@IF6{k{dp;FVJf!=BiKPFztxLwOdiFf$r3J zZfB|G4MsR?(s0#$|3>?%Y1GYc(oF~d5Qx&ozs(E$4~vEaXP&<8tWj<^VY>L;FeN@m z|J3EZm}ka9okc*k`$k+zw+NR_Y4y4lM~~#xNYVM>fLb zcY0|BLo}Jap&^oD15B5;ez~20Vaw8Ro5(G>bSV*EkHx2jR0 z{}x)X6&?wMA#kDC@7%X?`n8hDLU(eC#?Z`tm*LQHPCDjClpd=^E$tcxUD25lBKJLx zgq`Qx>D+}{;)vECsNYu;(Yj8sXpK2xJ{l$~J>#|}(!M-UFVsFk1EfbKe2x@SB~B*> z{KrQ5s?B@WBXCcNXoW!3g%W26(G6buY*DXO3XY0mWbqr;7QQoTbc=52=4x)FpZ=XGzxrXlZ_c8+pi=<0n+s)I4kVmML@y0@0+<`XyH#v`jfJ1Nr#d+M4hUO3 zg9tpI>H8k4={+9JFx&xnTUk~Ek9w@X(m)Dy%Jr>=@)tZgIr>+Yz6J*1Q@ra+^o!Rj zF_S-K_8)4iJ9SIH#ic(!E(=<_BXcQp2D)Q-Kx9FQNJkE-mdwkVQ7)Bo4HjF77rG`H z(`^jb$rPxzWbP@^@2~9B9dFeWsyC;{vc=^IZrOsr+d;_ZbD1xJbwL zutmVA_h??W8!y0l>O*C9{5(5mhII%Lb5AcTFmB;mm|%H*%ps zL}(T6DxqmtnPoW~!iAW{g>&FuntW}<1z>?_1=Yhs!*dqB>N?uJRRcfqDN4bEgTiCu z2_E~&mzD#cT!)u?#E05w&xiD8W+SuPnIk<^UM=KX+q$m~cT;j5))Wqf-GZzL7nhV| zdGDueHPc8Vp2?l|^F$u+u5g4=a77L2uaK%rjsWTn2Q!NetLDW4tL8Tqr(O0;y zXy}P(P$VIa2Lyk0`%2m*IK!^_+lHmO7Y!J`x7(gg|iaGm#9NDj3_{8nU8NR)&lr{)ZsS8 z2Wc6bQKSL@6PY|>*VMiL8~`gb;uUt6vsu2$dv7hnD7{YUMBUnf5U1-`B>>-}(jNNU z(x0(lHy7z!W$49(b2dswBcox7j@@vwH0 zf$ajPe=q8ua>EWgkhb@WR;o3iFyMww*KwD!Qm6L6?OvKzGXPBYZt4w=%L391r~)Mg z6yTs|7Q8G~a={ye*P%gOmL-|BFH1iDRUiN;pe-h6hjCe67`O)U0TS!|esV~Xs|w%) z1&|)$<9y(kDiBHFkV%F94KJ9Nm+?|iQjn9D`*#3xM$~7y_W=Ye}(EG z4on<)-wz%q(*L%Ce+5n8WqcAy_8DKsrM?|d39O9+6!>QR^X_E}aX~2VrvSl}Pt9fU zFH6r|6hM*y1;ScdA77e41139{3k*s=>Io*lEU|b|$&wK$Kpq*Dco__JaZUCAfEUL zS=_y1Ij7zKVOkLE0Wq%E!?E};WhK}y-M*RFrCQ?V^>*f_Z##1v|0gmN(7|?&1?yEE z*rQ`u`bkP^NB%XS|9(4=dj%d9j0_FDYNF4`L9o63?abXP#0B6}{qGPiSY`h^3;#O{ zfA9MLm4yr4_y6Q9eB~(gIVKuGF1a6W_pBA0_vp8EcSju4(*CW`|IwdqH(Fa;VE|1^ zF+#GgB8E*vE|h{RE(xFRW64bj!(9)~u-}T7mIzA5rrr@qm`L1`Rr!CIfuB-oQUS%P z2yHXYh(}%0fYsiv{C@wpSqIHv0Df}3?bsP_k4}*h7T`+7(0Z@(WQcQX{%LkW{Fplw z;iB&AKuA;$5H@VNY1E2MaC4t#dRaZ-jzoZ9-OQk%f6t@&k`#K$t*D%Liz z;lpE^Ka4c%cMMUt)fm$b^#tj)EF~37j3tcI*!lT?ill*X7881>e5?Cw2JktB61l5D zVlL}9{`Gc1wwMoM&gK*E%%1`vkzbq+qw_qIKR!)G{=kGh(SYw z8yWY3zo%=XgJs7H6NNkX)8x0o0x#osekcXI7{!d1a+zT?^?QpUasC}M8Z-yvNTOh4 zSA?;$Zs^#P5w^G8m$~+DWdaN!z7@0{$aPjAD9!ypj$!7q!ZrW}I zU;kYc=LRI6VhBc)`Iis(_Zt-B_TT+uEh=>&{;}WShFcdUUmxyNSpS|qpg6|gjrt_i z^@frAupxA&NNWIiZy;8}#|x{x330O7!r(L)OV~MaksJd!AOsq*{~nq7a?zv)JC zKaU4+11udrw^i2ON!0qC!M_+y#ZGfQTeFdpZT9tuXs5Y$MKd$9%Eb))3u@(h--oda zPAZU*rd*+NTF%Xe0I||%fGuR!lfqW*w)U=rGtacF1ML!f8L}t`|&pczrblQK!%83!i$(3JO z;Y#%IKXL_ZV$@u13IGK8+?jyxJX0yXfYII&OC-D~%TdjWHoZte0brPjHYEp4!eRPV z&Fctgs>UTBZRam={idF@`tbXD0VfF%)5S~~<4Wh^j=dA!nfXgJ(6 z`hA!JM0_DbAFwfq{y4AsW&hum3YDGTn-OjQc^Nk zcTCTFS1xHrNKSeZ031cy0Vs}H7&yot!RrmOm6Vfqv=+J{4sO~qYzl+`Y^>J{u0y?E z+Szs!B2~+p`W`d&TL4Oj)$vJ9R1IJelhg#JSf3oQ3(v)TY7qJSk9GpD|D_7{F=+ot zN4+`+F8)2e6tw`j6iQ^6{;;6*f+f8;J#-NCy@NeAiloC`_Mnh4^$-b$ZakRYH)PJoJg`x^EhzqhP) z!4fX1L<|vKiokv>mx2DrM~6=oV_5C%wu%4I13&dIc7Ze3m#E*$60mv)2EP@9I}bot z=(-?V0;tJ%oA-Obk6JNaU9;o(!btv^|INCwT2A#f)>p*|GqlRaK`{$*6iET{|Dj$I{kO=3IZb?z3t7NH+Fuu@9eaY>@GtHFU{w?AlLHT`*Dkp z5|~ouwC}t2aInbuLC>hSmN(A|FMw@U{%XY6H79enh%{`P6zBku@OyI|@q9y)r#pPc z8BIZ5yV|9kWNUZU^Z`~LE0E2t(|Wm-MbQCdHCOT*f1f}6dhbHFI8F)-{Gq9T-8xuE z^^*}*UM4B8O6i;nW{%*N$H{q;{(8|%KkhH}+1*hNB5skxhwt_?%Z0z|PEIA(mt|pM z2k7kmpVY?K`*5%E3%PNP`fGXfR#P0y1XOXUf)LSo%aq^OzeyyuA}yom`k$#dYVh1O zT4l7a;pt~j6|`4(sEGPI(d8q(9K`m04>;^CIBxg*bPY%j(A|!2H{Ejoc-RD36P+*( zU|-y+KiLYinX1!sfxkB4lnuQV_2XNZQtW*-xYfWXPrR(Yi|s7(eyRdzAp%HMcXtZ} zmU_q_&5JkzHox7DGW`pJmK&5BBWy5VfN1+OD_$R0UyFsV=IqFNE+dSSx5a;uzJ2fz zIEI?)C-L~cMn0`rWT_D`y#BO;gA))5N*2{+YGZ8e2}H_3<@8UrNkfTe=1? zs(Vr<<~=y&8GMkBl7u(~d)Ve8Lik~WfqrL)1eN=f*wX6_bda6|o@eWoEZl8rLvLOI zJblaX!l6vCXM=Ih2lL;i7&=)$ZY$8bc_qy-SM^x@0=l`twUSlxJM-bqC;AfRs zNOFGNWVu*KKjnAs193pGSfbzxzjTW}o0ytn92V^eMOySWI=(~_?(hB(pY%PuShZa65kP&5N%cD$IbaJ& z{s10DX;!(s=Xp5OHE0F_A;e+!HJj=ex$MFS?>Q*;dNgoyW0{p+)Ga2-@67WM*i-=q zKBeXmp>+#bQST=;e7?_vsY->v{e#KB1k4_f_76Vct6))3{?p9B3+nnjjLwaLuGn~_ z*tFPV)5yE%fyMVPMRc~tM>|Uq_c2kU9W|9aR{q7BmG))UV@xF*<%8FbodPv@x_^w{ zq5A6p7v98dK4#f9`NboEer~!kDc%4+YGn?CWGTk}OfZh%QPyoe3Huszk3pMbYj*)4 zOmhUj^&}pBcG$);*6boleYbu~k+{ST@aNiq1T<~Sl7T(F_bRS&+4qr%EyY-60q;i! z03Xoomzd)Z`C`EImUJI%YBl%Vv}(03*p2c%z5c<}dA^g|TSZja$Yv47AhHw0d680D z)bgpRv^#I17wSosY@sC{tur_$o+vTr>UmQ2@^B0Fv8xGHY`X4O_oANsx6K1JdpEp{ zPFGG&s=V#+hs&Aq1Hk1}P@L-zw&{cg9(jslrqys)D-fkl9FrBJ9yuLCY z5#I-hd%E^%R%nlM{eI~ePO}{@Wv7O3pK=0bI2y>p_nnX>)Ta38oeLHCbq?)!0X)$$ zcdQ}T#S*}k{qX;>_vZ0XuYdS(b}CdzbS$OFmI?`BO0s1s+1Ij@ecwsZNw)0!Hd)6$ zc7sx6XE4@5_Av&<5W;g$N_~INbDr1h`Tse8^g6F|rq6ulzTfwKzu(vUx~_W+kP2Mt za~5||fYtiO=Vu9Vj^)aYH=8y$M@xIpJ-26^I)*2K#ELym(T1C`1-OG%>eFu3FDkc3SmGoxUs5^*9-wvC%`Vu z#EX|1pK#Ib0P>-q?#gE;bNPBWlJk7JeUa@StHCPJ^3 zMYHqtIqYmFvyQG}HDu@ciEfb(=H|upAZtft2sc#*T`cc5KLBzCXC{dqA8rj;JVFbw z)XmK_93DvGs=@xz=*L#GIoc974NPXD0!R#LV=Y&|f=(j2mkE}JzrPY|BF^{MyfK1# z`xEPQqG*+DQ86V9gt>c$kZSgSO{569dHZ+#c<9RfX%X4~uoq=Ux zC0fiqN#E|@nqjg#>{5n$1j2bvny+gF9yBxoj#7NBYN$w`w<~G#3uXAh?j+;vbcnSA z&}BV0-U6c%$aP(#sCwEFP$|}DU(hK>fQ;$3S!dZB*Aq;+>n+Kf6|j#Z|t+=$Cl<4S0b z!UO?l=pP|6!Pn|QzWSz;T4>d9K^?3VhHq1W;h#q<8WUUdB9*^){()>F+=$$O^lm*y z)4{npj*MY*e1V(>9WL+>ZumgbFCKv#)NdN>5Qcq_Tmv>5uO3;b{S8~1k#uFp@- zPeFqMa$*FPo`NI)&DZa~_P6i-1z=n}F>B6llU7|L9n3Wc?8O{~Y$xis?^lnG@^y5= zE~fd44VD^;fV0q_$Y(Cmn)&6x`P=Tf*Dall$`-Iib@z8ReY*Z>9m&r9_Sy>cQ!~8CRT{B!AfCsbe3>cDUk3ZB9x%q-tkNYb~mtUHQZ+H|e5j?Xkbzy}~o^ znstAOESL!BYCOzVE!{o=t*cN_d-Z!hlc zO@sftWFDPvF@e#nY$N^o_P|@P;H<8KW}!z zYt?mmh#G)GZCq-z(Q5fY1AP-cIVrBO+C_*c#;eKaAIY9rmVQ-@{d7jo4U5tbqG2&h z6-PpYIAj~lX(XlxU!e+DBLP1y{!5v#$27p%LZ8SjiME8GN{a+)7w@j$re@arRNZF~ z|HlHwgWjIvqj{=FcCzPEp5wqnS$RNK>?*hB%v-hQ)hq%SFR{~vcAV*F8pVrSX@KOb zTlF@@r}rGAebse?4w@0*$yd|hwFM02PP{sjoD+7nlhDQk-%_S4*8R@UW&zv5tefnq zF#Z!`UHh0_qqVJa%1-16-KSA=pHULbTnA$Z!D|n9+}C!zTK;MsEUd<6)#D$Sk#6Z0 z)S&dK_L`K?Q!1#i-cW>ooE(A8|fOqt1c z6589-ltMe@!p*d5un*E5Ze1R3V25UIHh09 zdou9@zZebdm)ygeW`j@>EF<%?0&Lz9FCx6E^ka#K>2U4T z&BZ2lw&j_3Ctr@@@_W{v=Ci4N!{I4A)J5tzZ-ncXMdi@$-WIbC0 zl`yY(&*tFph^zVrz3M$O&5Ko&telq>SyyW&fk{Mb`*@9^JF0K0PNzb!%ZGVXNBT)M z@9IdoZ1?STz4BonNsv;l{McTY3!^$+xUOZ>hXcH&FzP~y3PkLft@RV~jTLMic4>DF z?9A=;9)|5x@1;9k3bW{LZeoYV^izIUhJYQQ2W(y7G<^;;p(5md5{xU4cP2!id?0nA z47Ycg=H`k}j)ZuELa@c1uOsxBPU{<#160Op;MQ($kD!yM9GzV2IzIcz_U_avFr7O< zEg!YJ>Ni|(4>H+=sP`71^Fx$*a_PQfuF-!AS=cyp(`|IW^zM`eku89-KgStfy%z2q z{0W6|)_S9BXJcjyG@hZ~8V?XwxVM1e$)T3x`_$G;>2)sMh>+Drv60V~u;<3&K@hCY{aCk7OacV0w*e%(iP)X>9)X5vBT{E)k*Axr$&rO}7|*-9s9$~B7gYAgm>c13JPXNNfnW1+)! zL$7j~Vx-(WZrfkT9hG`DCq+Z)IK%OioVL_o?tr|+E&d1+V9$s6k|Q zO~0Fj{)TyEDIZESldM$iYI#@>EQB209NcL01G(~Q>ax%VVlFoO{*?wKpVuxjT2>3tZ3u zB{3>gKHF)6nj^(>b9v+&wm3lfEH$&xtH;77lRzoXVoHkS1_hVeNF`3i_``>oc@nXW zNxu2zSkQor285)9W_Jt5ZHJkbTdWt)|6c6`qSH5^ECRXr<6>4ylZw_hn=16vhSnP0 zoG5xQ*4o`-*poficb|f7<5dV(+A=%PTea2SYYxJPTHm~GCNK4Ad3kNZt2t027eHaQ znDK2USvBX|Sl~K9-XR2OTrN`#bx3vt{s?TO%~l0>;h@^r_L4VrZ2OdX)-&v2x6!S$ z*<}(`7gu)jI)J^&T&mZK2&is>290T`$W+;=8i3|4T4QRR87j_`(zFsrq^mfW7_vX( z6Am^5{k%t)YkqudO1{*m41|>b7~iU>FslG3Io1;;3<_E?WvO*6IN7>PU^$)8piw6? zP-L{+Li({Vi;vdod#NNocPvCAsHzwML=;h^&+@Zx>KDx{Jcnm*twIk z$D!witvLZ#&3pnERO;etc8h2DxZwEf9(Qi#b>DNJAsG!fO9%iHFld@weyV##(JXTRZ0x zqp&>ObaE6yv!8GJ#pW8%&+(BWMbE;H@PV%<2xmB8(L!ZH#HX!*7_c%15D+i9zX{uh z8B1yAJD2rE4R6!68eJZH9o9cyVJ%WFTUI!%hgk0xpCu@)`()X06Q;IUuLU|4xCj>u zT(kFBwMyDS8?c19fUeuR=GG<5sWtHA9_*835WLc9W08+Z*y@43_npF=j7pj|hc4kz zSo1$=1P1~G@6v*qa|L$`b>bAlSp~c{FIFx6C1yF5*7W`Bj48mr&4OQjcv+b7oFh$I z{KjJOx-<79sN8IO5n^|1l{OY+@{et6G1&y5W_#O!&2DTuFYQqPd~gkL^b+51fO^*C zV87{JaDdN7MCF_BM-cK;hR?-}n$$r6*El<~@TRf7L%=P@tjl)&Stcq;(A?I(`Xsd< zP^2$vi==*-^qX{CWON&QXVUhz#rslD8!(A@<#~LM_9X6Wnbn}xx|EQRv*gU1r7(jr z1FljEq@cO4*+!XY+wFZIYK))JAn+&WJvO9nU*sQ^mgTD3-d<2I1O5{n{zdGBjum|7 zVoH?HpS3zc3&<5;xHlH~yhVAVhT;~)Y zHzw`h4YkF+G}+nsEM@)XYr`LOm;PM94^M{*l^fNLbF>dhfMd$}_%9WbuYj4s88^GA z=DlM;G0}|XEc(NnI;Cv{^t&xuoq}Dk>yBL?R|ZoD3CAxuT<3Q_ZsQHou;eZ7MnKSg z5u_FY{$jk=CU2_l3;zLQ31d5yleS{=#_cxh-VhaWaczE4vi%3D`O3XCLLt380_@sA z7mP3AmP0b)wE&6=>hm?;;?nPPf9ezgK7V7_Qux-;cl%R(hpiVf(2_b6Fa-!k2-khd zG}XgyO;gHxO^9|NLoiAclSv%XrYq)gAnr6uIr+dHGL?{T#fL0b9M% zTMbHR6lsU$?~Va*O8vYF!VwyGRo`s$W~muRl`cKcV>trfURi3-+4v;l#-@IQ&cWu` zs>#>s%VtS-2>e+<8~z-{xU)2G!S{RM{2}dBvs+$1Wyio5T<-8V`U3i&eRcXTP^Ldo zM)}X#U!7b&L$<50e^WVT(+CZh`LuJIcmHaZg7*JU_#fY(`-fB@mrM94C;iK)qiu$g zeM<1~shcB=;qymqyRXNg9U<2BTS`8^XNb^=l0WQ3^<#9~5Wm2}W5le4rqz;zHT_=? zRRxv-gzv9y@;0q``0Ei@atWW*QB7mp`WsjT-vlTKLX+mdzlpu;XMeJbjjk&A_pb!> z&U=UN8TLJX-wDk=1_6{ypll=bM;5{7$4haY~2Q>D7f)u#ycsSt6ZiXK1Du9+M zf7qH$feKLO9JOp8$q=5Z_9@Es=tokr+uu6;Njvu5Ue!m8nTYV8ExT|Ny}xJ`0nq^O zCG;?&cdh!^f4>-e>Cca9F?9dc%mMaVKL={ma8g|7h@SJ2|7VkG@)pHkFA#D6*Sp>~ zRIoidX4lRTND#$dS%0}g1w6m_i-9qBzQgA?10#STUf4Ro#M~TYb&P=Md_TZts^xdY zfm3^}z`vmf>xKCFM;arsrIeJ!=#l5;NA?o?FK%UCTdV*)DSxL`YHWP#InQ6w$sVEb zlcZzUpnXv$gq2^|IyFf_DWr~{6zLG7C@@1A}-{;0z0vY&7&>L~8O1O0*L zx&~0A1p@aOjePZvjS9*DcrhDW=+RovJOSEX(m-C3ryXg4>_g=K#hr%mP-!8uDwIHD zF&2m~_+ki*61Z$1j!``Lg`QZV8}g z=%qU8wlaP>NyJV#7}IMO%cp(UY9Kc?lRvCjn-uxgbjI&R<*S` zU(q#qQa21H!bTY*NC{;KJX&M_x8I5haPH6Mr^QF0QhVMP^<;Y+jVV;7>^gcrURWM# z24dH51OMmCA3ekSO2ozmpvv+2UYOqYAA!Zo!cIkq2*_OYe||*<_&^3{^j~}2{{#%>r8^)fsdAIg{8v!2KL#HdcyZ?M=>L11 z0xkhb02fh=+TZ)j{|v+npsx!~RQ&_S{_h*GN00((#HDmch5!5tRqz2-I)%UId+K1M zj?Vpj`$53WWC8t>ED!W?k;MhY*PqYiUGa;NyYf0Zx&jJ)dpA<9J@ygRWow-)8P7}w z6kilv%Dlt_-|5*B?TRl)d#y+i?iKWz$2<1$_1s?X^y5l$TRE^h!|1s#|1Z<*edVbH>z=VgWkM*AiAUjtf+I!i&+Q=3G zD)!HtIPord(A<@4kCU40r<*>YRT0hmnfS5Cow$VEU)H=&jPyk~!jF#W&oBJZc3A|p8?4{5xKig$`X@W zqcRY&vzObCf=%*lZ*p;gX0f2TYR$Koq**@R{FY+GOjL`(6EVC)iBDsAVnW1ogbw^n zE_J&euJs#d*LL=kzn*q-r6NX(?(X!8{oVuEZHemKva&H()R$n@*eoD!|2#r$X*u>_ zhep6te*HkUy8#8KA*@W`&TcS~w&#S3)K!3pm7TCY8ah3Wl4m1~j%!mFGTfYtYM{xK z#F*p5h>~v$JzN9_zN%;U&u<@aa9pT7?1Z06@{ikE7!HefQal~eT{9FEdsMTeQu1k) zJa#sU(W%J4;h({k`S>}c`)uz}UBik?(<2{rtKioI+1sAWhdFxFX(EBu9$72=g5C!b zg8!H#jHF6;^a?H{axgn z*JS`tbo|#JB=E&cZI@xmzbxwe10N-)&mQ84PV?pbAxlWN&bToWpOhU`?_C}(oq)`YQZgW2%b--0WVA%0E z5$2fCk==U0@|menJJ&WRJ<>9j!^fJWk;)!2vDp_W!DIDKfOuIDl~??IZUwxcTx$`z z0DJrz5_WcN$z@etLXz~NKTKjBS82s;gh2r zN-Awu3917*D=T@Ag_OLuTbEi7_ks(=^*|q!+f{UZt~%*TjpwtA82P*Jj#|=LmLz7)cEdeKoD|Ijl&43XFk0_y743H;&7#0&7m+9Sm223-_bi4bkiv-AZU1a2qoLis47~?he510sgqiI}!^1 z@j9EBn^*abd5SRw`cX+q^{gGFxSN#WaA- zVkNvbz znC(jkpo^!Bl|Y?6E(amCk$ zY_?X|y8OY_xCZQ_D;E7*ldBOV6uxnQKczdA`(p7rs`x-*KqZZ9bpG3%K`EA6+l_9$fi`> z4o`No*d2tV(JIDWEgwB7AKWjW1&$7%0k>ERExZ)XP?sB1N(A=a&rq-RRrAFw|rsRR;r=hgX6b9z-2 zvi8OeF`5L8iRxar#?!n0W zz_iHow$)eFnoTJMQm6s?JCed zh@qW&(6QDFuMCY|#Nv>1k_n>gh~B{`9Z5F}<3OCX=c^ZVRbDr~uk(JU_Q{;{OB$=Z zK;0#wCm(M04FxM_;Os`qT!yjo#d{@BdgOr!q)Rfxugr>3hn4`eKs3nQXOh%KH>H~z ze=YXLxA0#AkyX;_VsbtzIj^nc_Qy&IRB~xx*UOZMazQp}TChJ31i>cB;OdQQ%YR?J z!CAP9cFQ5d!<0-w`>KStv^^bbo;V3nP&PtaZ?Q6*LD-R z&1i6rSV+EaAK6?80UNs9imJJVB&%&GRPEx4UccV(@mGQ2}c|A@C%_RBCe5!>Mo&&sBT zbUw_zZqrRQFsEN)$Bn4*I5C)VvKI=PUF}%jyU)Nv67fFNx=H_j{e#<|d{2B!{ zv(cls7RPp)X&2;*4Z=sdu7zh10nJ5YL{pT;1G{n1J2oae(~($~NPO3;c$8=tkf-F| zDKG>sg;bsbLq2ivC73N*qtG@I$dL>PE_e849|#~0+TN_Pa46rvx?RErA;Nbb7H6bf zyf-vd=v6TS%M&1{_8jHT=f1oA_*5ciro&89@_PRijzMz(+_Bq2Zf zcF54L`B~jkz9(>Fv=_8HC=K^A*%kwCcm^89`%$pRtQ3U;YGKQ4qc^?MdS!8@#TavE zPi$dT%0jg7>4!z*WM)1J3aotw6NzWCJ@KR6asRB(`<&Y#dvkXG`%I0->MD&{h{#oV8 z%#|fJu~1ftmRZgryxXKt9Nx29D);l=)>a8|n_y>c)nw|qXu2`I0`4HBe_@IBab>r9VO&N@iS(=2YF%Fp+J{}|2K9qwQvgH}+~ zq{b2Cadu&yecI|or+AyO_e5}+>5z6Z)^*ao*vGW`6gnt|6BHqcnx zygL8TN!a@J$&~B|9*S@ZmqqA%E$a3qA=l+|*7j{W%BOd$Yco~SS|9W{n$P$N0-tth ze}^Y)=P${Z|9?I1=^|LKKaCeYX2AX~aMxmGq6y(8 zbrIYm)cyZ%UQL>lLUD;YNX*OpRtFW=9ZLNw-#td zmRF{kg`HQxHB&4s{N~jtT=)4RZXKbfXDY5n>Lj2utz1FeY?B;txfmq+O!mDjuPU~j z3{`M3EO~F{KUS_k2fREPF&D8Lr@;A9Xoo?{5Tu!->f+&vEi!6ui%^evoDiRKfV0_C zDmm;5bMBBoLK&FMzQq>XnasOP^|<0zZmxqSZ#g-UPgCcZ-#Biv;Hg{gwnS9OoVdhI&ZTMScPG+AJ}yuJH|uDY$83q%?7I7NsxV>w;IV%Zhn^IigW({Z8Y~Qit+&@scH2iJ6ekO9@KoW;UCIr@K~D zH7Im7m_y!~iQwc@`k)@MpnRC8FAq;L;E6IJg;v}FXC-E~7S-g2K`vL)W#!uj>k)#(5&0<>&$2keI05MxGC@2gXT z*ogv9?{4|^LnvqM9snr(BmLoADmk5+Zxj5T7pI&z`!dO(Xsqz9?>pFl4W>=s&8&HA zN%u9?-eaz{{IUM<*s(HGX{|Nop)sP{DQ*8x z{#Zc|myf|qW!GJoJw`*m@ZpdjV2`sW9<$wY$K4(Ka03(^5$XXZjb8^1t@!n-hlMoz zh3A0MBo|X$|1Cd8lH1$+BtKMiKZH?A3CLQX>}9^o#5YWPazDCs8?C1L^1_K7>(Ui8 z+ez)DnoorCV4PR5N;7=}fE%!{QpDW-^PLHo0f5=1AiNw1(fb7j6>vF$n;5LU-}1_| z?v-%usVBMe$=M;h`CN4>?*yiX#S>pg?2R0@r6Ctd%XS+QUpciaq>w_(Gw0t+{(DmH zU4(i-CJ@p8?icQC;x+HHF#m1A>~VjhJ)mhz zjQ7}^p@B85eNeSN)c~gGd_`O zsMJ2%wIZL>Qn&)hw!AV&>A92eeGlo&}4%yUUx{8mrpaC5_S8 zUdz7BPn=v;MQ*g?=Zf_g9kd9*RT)pjvPi>9w2Yx|&0XLe`!PM+`nZ7790L|=cyY-H zv9hb^xm$a1pj52qIYy9Fw`j^0KY-g{5OJ@;eO9N}bbbM0_#fFSeyvMFr*5~%=A1*G z$==$REU1c8m;l(^Pa5FkAnzRc{&jQG=V?-vCO$5lvqAO%wUmj-^=e`IU z)TdC!x!{T-phuH+6bdGX0_89dAP@%R8B$me2+^%z0Gql3#lm##2J-s&9BzAKqNFzP z{mMk~X2WI{Be*0H!@BH=YXu5>;8v~HH7&67T10NB3sq-3Oc22kQ>`j zvkwz;9*{TAacfMFkSIwY%&i03D!Z0M}sZ6K`}+*E+O@rIgu>xtUc=&?&~==7qn~MhR%IBC4c6+AtM9 zSVcU(zYAd9V$ym~w3oVyMh`Yt*Vc)=rxJT>E?Nz?WpLudqpp!L{B^|E){0~2FOAlN zfaQi_&UL+h@v}sj4d*k}{Cz|~#ndQ5!n!h2-kK~k(=w!sFgj4_7B7Bdp<=q1PPmPt zyzsTKQ6_ocGB<+u;plF)aVv)UMr(I3R_k0#_1_3)>n?S) znzRlBiPt0BhHcJu?_LHm`)r%M4l$P7?yA7l<%{?AOl++y?in>RQ?7IzItuOUg$7CW zSsz<%Ja4b1EWChMUOr3`+!x({8w=Phq6@;+3_to;;&7PDtp#kKh)k&DA{FGT|bN zgPMj|Qkj&L{iHrf_Wb2auZ|${=!*Ed9J|H+S{UZm*{gacH)TfsR>nP)C9CCjv0{5o z`E2$lYCI`rc?+=pUd@Sb{5L`b!Xf}y^y*W(#(mEe;PO*yrn|^U9qDSH^Tyt;ilm^A z80l%VT8m{(5V30*!2z@=L4aptp)avq#YsRww{l$HblBUXmX<%F&!OVMZ6ZtRfuVeL zk?Hk%-KwjZQkat|K_{84h`(>{jFeB76EDBReM*h0b=APhK5C~Qz7N+bHo(@ObB;K#8sRjM$%jstkL_mi@x;wz7`3D z$fwG!8+}#c!_Mq<+)hF6&Sl!FtnMLf%@4_P?Bb+l>kWx43+Hk1q|mIkOYVE$=6GQu z5Ba+u3K?Yw2yV^N#Pfq3XSzoX2+Oz#a-I=>ol}n}n!Tp~^;K3O-ntIwvdvN6A}7mo z@w(r-zrV|(5sutVVMV#7?5K@qNTlVHDW^xKUQov6!`~iW^yf2wDmW zD}$Lty{QsxLN-cverU`2PPfAKrB&!Z4w?fEKH}JVu^?*H;#E4a)o!BO&WlC`A|BE? zp8?cTSV@M|%uS#nKWFJk+Y%pJdHqpjE*4(Z3e#q52a3D-J*ECkjE=J>s2}g8DJCQ!LGif|HGdB77gV@wog{>i zUj#RJ-Ha-`KV;3W1JYSeaJmyMfGHNnNN&UtugH&yn|x-+GEy$oYKpIGS2*z)5gJOb z)CjvU;-nll&)SMpsSg_Edk4^97v7548I&iFNF`9qVT<)+uj<>rE7=+Sabq6d|2n~N zauFdg8a)xWyW+F4kMEK4OHv0$ys>I3)U5Rm9kD`AjYEcSEm>C{y?azRky!z*2m~^v zIiMLVTT*l&_wEUQnQBUUl)r?|PfSF*3Aogh2hyv_aDpsjm2HM;%0a}-u z>X=wL!_`Pm+~OMAEg)Jta=)i{s~U^)xeTtQ5VGm3-57#p%W`(}~BQ)>}UM2EM~DQ3z9`^nq;+p)(PmLIBq$ z(lIm)JbLjM5rxqpaKVYuyWQ{ay>odU%|52n<5NwCblR*ry|-)FMtN6*d(iwkNEpNR6||-tM&wVg2C~>(+y{ALNyU%{t|H zZPd|x3FZK&tbqxuYeSP!VnxrhKO>L1EYd4Nc3v_DbpX*$iavVAV{v;)pdFa%e0f%7 zzHwslVV1$0Tc1mIs{xvCAx7D`Z+Feg%g+av={3hDO(ILkF(7vY%@b*5X}va7na z$DuI@FZh<`qeTv*!)n$#p#E1b`3-NIrtBN0A8;)~Ew@=cuehI*Eq}iwXo)nj)-{5i zb!!);PPYzb@TzC3tKSBdL#+6tPt#lU`07NfzV+pSsTl5S@1a2qH z+wvz|FS6vY(P7kO$ugSM$8s(#T6&bl8_60|jM#Kk^&dfV=+PD-5?!v{SK`QyS`qZw zF#*>bJu(~FpOGwbJzFwXGzVH0Wn%YVhLL@U_WHd*-g)6YJKHWud4FHm&}rJqehqN5 zN#crX+!CbP;MQFPJc1x;GtNJAN69<4B$kU>`&?~!aTpc%y7h2P$!$F^a+IV zsOLX5(A{Cyt+2|&*z9dDD87>k(YRxZ54nUxFZ;4Aos;U36z`4h4-*r%CT}q5uLV27 zrNo;8&Q|E!!l+Su6wGaI+^OT$<|VM})ofpe#t!YdAogz{B}~n{S+;x+6D@b%2>mT} zyPk{tqm**3c5;NE3nbf6s`WkAiy@FgX2KT|4a{qkCxXwRi|B|j{v@Dl-8A_#6?o+j z7e}Zy2MuW%6Wxw`7jgzfUsC`HxDT$7bMPhWJukjWi@wes^-k^VDmcyj?j{h&e8n5w z@Z5Nnp0-y<(!2r!J1W)FDxFFEjvK0}Z_G~Rq=NdpJb3^`N739a47f#YX1Qh!E~(HE z%ctnNNqs9gOn*wU=q%FNf_V+;U%3LnUuScOEPBN@>8jw|)nBb=CVkvj-6B19eq{@P z4Z}2WA84c+`t9WtP_5F;?lK7wz=g#xf$||?z)z=Y(-#%+5oZa!1T@5Ic{VdW>Q`#- ze;aoSe2$Vi{>&OZWZ;#uUhsMcUtDI;6E2GDcYZB)HH0M|U(EGfDpLJ zi#p=GUHyeccd(+%^&I70%9RcKUE4)jj#WO)YBAfhsj4XRG}sba$l@k)(PQ1!4A5cc z&HMV`(~qD0r?m&{;wuHI!V}G}DdoIg$*Y5zSSKCdelM*n;xK)yW&^pHRc`G;jLfj& zR7ke+nW?ql` z`jd=sU1XdmH$a=ZcWEH=Q&%Plb8XJ<>gpFLyl;RJASd$^&*rnppf1K~l0JoooNol@ zHV)3XDZ9JTKHCzYzWGdx16&uEJR*AlT&bG29W8%+AL`WN0fn>vXjNnD$*aYNm%38K zb%8n6f#~kyn_b3JJANI&oV>!UZqou)s_Bf3hV1ZCZq4pDVu9mFd8$Cbh7;%*RPL*| zqv!`W97oNz01S&oox8OMD-CAWCizh5Mm)80P81Q6N)q3HOuWJAl&|Xl31J zQM>Q-*{Wx`7tm7-JWSbL{K=t0*GyGbW~@9(aQZA_K5Orv@m?7mfvt>Hg~OkX0IS3f zR;tBgPIO$#Z}KWZUoD>X`*nt}(uki_ntw}2vg5^tKPA~UTu?6{-_mlWNJ1lCqFe3b zsP>R@jSs9wZbWEsY`B=@P#~LZS0@m#COa;*LUCF6{^%XUg2c02j|8xx=NoU%EH9oD z--JpOi`Y#(Bg01_6g?pe&Y5d5k#!7hz z_F%;zD3Z8rbaxwUhY9%=!82v1Asnpt{b>9qJOYd!T8O$CX|?jW?{71vOeBk)TZ3EL z$^N6N+OL4|w-Hk>Ht2$mQc6^l$4vm(Ut1qsNSbi%LT`tfW_7su(~sBk3=fui(K2e} z77f}do?E^jcaS%44cQFSz+NdDwMTPvQYIK&2Hs(no-H@!i{)Y~J$@2Qv{XK%sAwDN zruj8odv;CIC&j?|p*k1-lhgPL!$fVKp+25X15(e0G zlyb_73o@|U=~`XCN%c@&c=5Ve?w5hQei?B&aA&sCq8VnSJQ$zY*sKk1Gbrzbb|X3L%sk`c zcrpO9OkJV>Ouc2^Xm}H_=^CHI*}xHdNc(~O3jSbPHg2#1UN_H29K*+5)lLAU0W4!e zoW91`})`EVh=hneD0)`P{r#pJb>kc+KNit}uX7kdB@LQ~t=o>W>({MSthWTPoB--Nc{88;mgLT&aa0GI zCXDv{O}(I#RNmZY^C-3^5RF=m_ZDZNcn6UOhE*Wnzg}>c1j%esY=EdN_ZiGLMLA`q zgW7hGiR3a|vqA&(?~_mm|02-A4)elOMR(S-mF(!1s1KQF>~GJn^!PT0kwW);$tOU{5Lwp2_Fo$y=1M*A;!wc!>IKL!rwK9sgX!I&I$>v<7BK^eY?SctP<3I^rS36KJ+TkyMo=CKO+I$40!dysiOoEI#jPbX%n?3o&oT zJzR0j_Q%UM4%E)YqPh>LnNOi6x-|{7RUWA+w(sB#f#(H<=SKjaRfNSEc#fR(&0hyN za4r>Rm5o=T{#!n28M)fy%9{&xL;HdrBc8EdNvp7 z%Wn}O+1p zMvEgn(`@g<%a9ZSPXjNUDX+|@$SL3Z;LskWRSL|=a|WCcQ%|l*PisZ%Q%Feo48g+6 zO4h~cc2|%09zGOscZf0eqm>BsQo^`kZm3vOI*m|n)ZS=MoIsk|{r>l8Cl&MZf4C2u zkEtU8cdFFX6m<|;lg>z&kBL%-YR4^Eu^#J^k$}~A>%NNQb~OW^-52;vCc>(=U|d_@ z8(AF+0|ee;cHEJk=Ef;GGl+cBv?)Y~1m#b>S*LBfd7fY@c?SiLgYIJvP=>C|@98ud8Uw>GVO5)hwC zh^<_@90Bc5SK{WXV`YuGP=##d#hi9va(iOQ6eoF=$g)anv*&0~2aX2Sfa2gEDamW6 zO!!DttzVz_kywNp`z%hq+>8cmrlTfY1i%5-kfzwvQPiciT8c+R9#U@*c?1M_?y#2?+*9xA<)ewrd(#jNB;D zLi%?INc8)y%Zg{^N}tN4%q(?+mYx8BDh07%ZRrH$j+hx7K;87}PDwy*{+*SorBGEE zfCR0SfBTt*tyIb+O1ZD4KQ7eaKri?$`hq%Jo>}O-lsO*sV?;VU4FhJJAif}-{ST-)}9?Cq#%K(d_6&1deRx3|X2?~vqK*1RD`>X;4W zUI+ItvLpOAKM%Y|D#R!ZDxqpvsjek3oZeqlSr^ty;Zk8Wx8`eo7YuKX!I~c;NcX2! z0(OP)LD^HUOqm7e88DN6{@nS20m|{$x;tCXfCHOWVnC5oDe>86pA;xUt^jJ(;=^Sa z+-y1EB+ez#sTRm2H#667nRn~eV2fzF9T(sDzQ?28#!}d8ZNusP4dE80aI3+V5rknQ z7cdJ*N-_1i@1!hZiR@&$*Jr@Cqm})@GC|U(Y!?I{isHx0LL4y)0;%g@!Ts#R+W+)#c&Rz?lBZ8n23k z@VRPmw3Ry|CoI~nA$95nx*wLF$gG??WY#d{#qzkMs@D* zH@9gKo=@uwugKdlIhExW0;PK({tT@&Qo^Q zIpThorqVUB$3dpxv|-;SZmTWkJm|dPc8?gDu%0ROI~$r3Cr~Kd+I)X}PQ`%IqmuZMl&(aE8tgqo&neuTJGjd7V~L1NnCoHD?*!Km z2P|+|BoYZ+{VEgg$O)rW0aMu6Y`D&?IHo8kk((`#!Ug(zBOW;8ckOHyjwB~0@|dS; zKR*z>_@tB|t@zAIpv}7zBS$t!XG?z0KllSuMhdu>e3oH>knYpJnD(+QZ2U}8zL~do z7H6_c6U%Zf`bq@k!M^a(7+uph*Fc@xNz9>ztOjm zRkjG&rw@48lt#**G~Wj|zPg%~$SCQ5+g>>F4iZ2t`|bo6K_5PQlfa_q1(J^{iE>f4 zUnw|*{T|>ETwv$BXixvEsgmV?yCcVaZR!rUX3?XMdvz|@iz#AgI*0STKi}jB7ob|% z6LP238pV*KmwP}3-q>e$qn8i#yqU8)?=e{J_DW6dq%IF7{;XGjb zngbbZOmsB8{->X^jR-{ojrHm!B8=X*7xO}g6$aq*SpaE4C}*=Lsblf4=OK{Eziy8! zB`}q>PDh08iAu}=9+9Rv@Hg;trdfTIA1@yc^RyU~>n}A$+)p)y95J$Hma5D0NXyd) z=J>o)5T0_@;1`JG^-fgq@R3g$0YS9nB0I~jQ_$PrQQmXRd z{O1*BlH*U$lGG!Qpa|;$BLYCr#KZ*4FrJAy9DdNO zNiXF=1m>sDfLY4(_;OP(Sa>u4u)eQh_zb+1D8{hgX~~7C$>zfeePRHX`TnV|(_9;+ z!XJ8J4ZvdyxVWf;We`U>G_szCw>*biv;u=h8I=O!0h6kQ%8B2>-xFq7XbbLKS;X(0 z@gRFk+~L7_rV0anB0rAqP2q|BV;Y`d8b(Hj{8YhzkQn(9caC%p9D*=32>%O+B*oHG%S4=u)_fYLyH+Vv}`>TD)8&Y0qf6&B<+OTWosS^Aq&81mcQ;n0T|N}b;bH0uTtCHBqb zsR&Rh{pw7*vrj@nFMNb!!+YT7g>i*S#l6CF7$@PFS$uQKcLS0{q#6nT@WjXy#4qK zK#uy(4I}H)(Cx3uf0GeUEbj+qCTbj*P*@Yr3um7-cE;nDwT6QIf9B6b^8Erj*59BH znDw5B(d)z07MTtE^cZVD@IEBAu!5?^GkwMrE24azTB|vW|A=bH#rjS@9x9oYg*L&v zlbO5yj~)SXV}~{D#R~>V?7+>b0O7)qR?4JsEDZnf^AZ2>^A6UPI7sEhk%g@dkiSO$ zHq9e%=l5TIRe>G6z4Ec+-tQd(hu7~D8S|(2c0m5!b@^h!^?RazJ4{E97*z*uuNcyv z_Yd{*&mnmDoSzBDU^)!0^#4t+``1ToX!!aTt8&q_93I^N_yG9p4zq+rzn%8;wLiz$ z@6SIPjTkusG2eAcw&>4k`Y&TRcDxq<%zvlhzteDJTK~0%gYEI(Y54Cn{C!UTFKduU z#DXS9%vCPC7i)J4jfiNsq>1RoXv~A>4z-aa7RdgMctEokO9LgwH|)cxYf!ff{&8`D zpAB;1QIOFx@A)ox#L%uGzi3$fCXzK`W?-$$9=-;V35po4tlAI%mY~8vcdN+k(cDl~ z8x2O4^=9!jtHVcpoNFGwSB|20iMPIc52x2hi0c=IJWRINlFW>Ou$bn#x%}u<;bF|d z3b$0;k5`v}-(sLIP-R}cw^DwukK5r#5X;8$HH#yjZXe<&U_G0_hfvC9cULg~gfkij zx%?au+L+32YH0WqN_@wP=K)n{XEW)EMhepBL@alYWI8AbEY#d58cQCFkR=xd0iLd7apT|PE>qhoBor=lot0af3@q!~Py0j&2F*U}ia6*s{0zzi^ z_zJX@1LyC1PXgP|w5cWx1fcPeuNg<*9=Z#Fw(CWvEgxzCG=EM$dmz>D7H;@~-wqHg zOohhn4-eB{o#4R`IFFxpMDtA+AKcV07*p5Pkp(lp@7;||4`5>T)u6i3obV3!&#bW@ zaQKVMT;>(Mx^1ZruXG8Ov!gZfWCa8^@LS@TtG28ARoea;W}+cna?S z#ml*v%l__Jxd#%JP&5zBGb+3#PUc)T{nom#|DvBM2Suz@*=+y&%Jt!dJtIoGzg?2M zB%_ZUFDXPGJq;5pzZ1NHnSdwE?Qg#mYRxbXS>Zl9;b*)q6wE}gLo{N$R_a3hIZ=A% zJc2ejdG*hCIbgnv#ysRe1IOM8!bMS`d>(U%FM`N0-=$L&oe@}^{E&IyRn>1|6mKnj z-h~UzIN-eA5HTWh^ReKc&Et0(2Of2v1hdMR57B`*s#)VKCLUrS;I-)=?B3RSMnj}i z>MBf1f%;%F6oK?iV_6N)W5rGjy*)h$BwzOZb)VoC=qa`60|YKvhr#=GI22Fcegi!& zXu^`z#|=0Wltm36W8_mW&4E(aM))5WfYt$_^|xNvwBNgIL&cZ4wWiACu&Esa7sBS2 z4-|nJ=Y4&-FhVh#+?EADvo)>jq1jM#cwI0xJD-()v@JV;q0U1Y%DE9V$dsU|^zO`60OYsD2Ysh3{k<;Ee`kvI;U(bU5!-B5;idsp2p z#vpSf5GA8&^O7{o=h7ktChu+?i`<5T2dPeY96U^*nd0q^TXyFAA(~CPUPszW9yp4SF&LJ zLlj__^x5};HX$=mRINfh8(A~!r&72gMcjT}vyq=g*_Z-V-E8~4pQ?OUhJXwQ_3TLq zDHU!Uh;1A@z7J3C+wTeEIatJcK?v;}TT45gGuV<|V4nA+C@?C_ro<1y%IN#jb8o>2@46SM*Edf=GVK=%A~$ zpdKg`@f)N-(mEoDY=m2OvA?9qX~EO(b98g!X1){Jp|(#8>CQ(fzE|?r0f>gtEeQfm zUxSUB+Qb*5pz(ydLX21)fxGte{$(Mguaj=S*5*`7ZTjsGQa5vNb-W!zKLsFl44e}6 zP)@a@UCe!A>NF9}VTl?IYBGgBQka8JA?s$=%_^O6#nmI%XWEa>e1{1n;% zg}IiUxxPiA>m>_7(2w8gth|Z)RAhUuC~5vBPkhY4p44cfW9$7ye!F3YK;nINC`}gP z@9cx}J?i!gYg2URgcf-qCjY}XFFqM-ER}Ow7p4Clxbk6uwnJ38?px3_MI$9RYswwe z1kW9AAGv^I^obEKo39-FHRK%ZaM`A)Nf8nW#0`v85mSOBa(T*!&1T@_$q9B)BWO|^P5-5UU& zVR=AfS(Oj1z_OqJwe(8g=>~bGT=DRDe<{F>1Wzaej!~E3Y`tB`%eqcnv$VgI3JQ+hdCg(drG(9R6m*bTyKmBdV)?%kp1WwVR(-_ zb#zj~emVvASmgT^a|ie;Ld0~6F|H_(i1q~>1VVv{;`^`PCV#~xri%ogw4f<$*KY*J zD0rHnupKTFY#RwMxDS2aAMsh_YCJa+C30-rN71xU%plxskNjO}a8#ZUPRdNM+XO?d|->89mcb>cD6@ zu<`(GBxk4FY?wBTpFLOHYfFTgkm_3u!*w7x&IpfVs3@x91{VAyPU>Woms4vEH1>Aa zjnKXCa+(43k_)Dh=QNAz!_MPV69t7>7CE18&DC7PAD@!E4l8RCh;S@VdzCa!=YzwB z#s+L8w3NMdlfl=w2lfi`T+21D_@~n9P=;btW4`FWV4xgNTxF}g{1PoTsLpVr({7{^5|=fp>7v?x7v2d zDl^8>LZT8-@SRy*qOCPTuO(N(gK7e8kGNHgY(%T-vZ;}CH=mgooY3*NofQY&8k z=b&SSu#H}gXHbLY{MiH4H2724_&+}gO1{-N@Ivf0K$J(6jY}AlL#HDEC*%xIk>)Oj zzLoq#F>wfB%u3LK^jhI*4I2i0C5@h=d=2DrS54ow=}MD^{>X08A>3pgK06vv;1x+hzWs&Ll=rZ z!b;B2vF$t;lJ{elJ4THMkmhC;_s3=@KxRN_0HDBxuY8uiIGDec&J5$@u^oJX9@@q) zQt-&LUUW_}YxQiSg7O^a!m8Prj}N9emQIQ`js6-!%NS#1*o2I8JM=k7F-ODWw>Mca z8|VEE;8d?(ME*>KzGx3Hxe}hnTurB`B-#T|XA&;4_g8|mM&^^+xwlxMr_aY`Kp(hi zg-*G?qLWZ}ntmVPIMwRd=~RBon)uE<>s&G^c^JyE!?82VebtHtSL0jqM`+vkAu{M2 z&)~f$zgU2Qh$;|`!hq$!{X%Ctf>X}ZyaUV>#@Di;SC*;LdTt{{D^7HxOPZ1%Rr>6+ zqyr`?gTR&T%zPAGJlnNXmI~oaiefwyjH~bYsW%Q~<$UYFkk|f-oH;!7tuTlF>crGI zU{FuMxn!U-!H{7FPSSEB+W8^9RpVe75#!h zTkc1c{gzCmBw;mkg-tEKPvbM)mVT%vj$wM#0OqqzxQVi5dejeAzx7RQktmGm9Q3`B zXC5hPweBY*+^g(zwjj1pj=K*?Bx@2L7!HORJ%h9R1j#ym9~#1HQHA9+3$<+LZPq#; zvT0Ff*H8KYF$SYq=yWo@_oza!0|N^g38OkZ)Zaq(xBt)^*C7M-Kt6%Cwf-XWP`;ld zrYc@8D#pU2In-ut-dwqvOR!9=&^yMrfjrNY_r3dai$KuH63lh^Yodt-l!-DEl!#0V zJmdzP0dNyYNX;o7fc9)Uy5hEe-@;MvLz%zs?a?{FRJZdLfOKmF&GC{QM17Av`1^`Q7<8b2X_Q1`DU&cr5OB# zqn`V?jzOyxF6c&&324=FMsED z@-aYtqXTU#s|ojfmP~+Fr!kQ9fSdwGZ0XhsF+s6e;NR~97AN1 zth2{WbN&QgeHf-q$(!e#77C;`wJPxgdXAp6ym0%m^U2*tG(dzC*%);~(xYgpR-CH2 zM+60N0&|IV4=?i5;6U6Vga|=`)wlWq*on0zogvOIK$zU@fv&p`9o%G_CE5Z0tcQ7C z34Q{T9xpy>)i`O)EtQNpnThZu<^%LJ<}L2pt)gYm ziwY+5@vchfY%S%j|CbIi1 zd%U=A)6Dn+{_bi|LYm~GeV*Bpz|}}rJO5qmP2h0GERC_mb3?NvT{@54n7FAU8PlM% z^&!Nq01MUVFR2=+FrV2>az@3WyjC2RC$^JC#W9Zlvt92mVm`YLHGo{XpqebQ#zuEk zc)Eerci*Vx#xdSQ2GM(MVDIj2YGCnmDO!QnBFi2uo(Ku=J9=V~_OJA|oIU&Tv z9|p!j{|YB+r#;2Hvi)9{_{-y#22%@5d5_M`1U3S4E;k`}h0zh(Y=vMyGJ?jC6_fE> zW_p)=pox|O_yL;g8jhE(iDyA`DSiSJr7k1$hgLn8zQlBvFDWl*a$}T{c2FOBPV93s z0)eYYi3Qptgz^onw0-nn&gG(4agF}%Cf&el*c+{eRt6PtcdK019U(sbS9=@wYv~g$ zTTBlYhgGo&PtU0YMO`GDIY^pFAaX$TvcRk)X$~XBKCW@x2s;f}i`pHD^&abcmn?nWY;*K4Rz%*H75$aO&V<6w6AF&1(A9>6Bqs^F4MYhDi?l;im=>+4 zA>C&5yAnHn=BGZobI!`Rf*wsn|0#WsYqIe(J+%q}#PsLZOg~_Rtx5+L@V3-wEq%l+ zWN+S~#T?TTudrY2e$c7YZNEa{)VY4a4#{_ZjzWFGvHJtjK(Xc2p!lk{C?p&v6`frL zCy$H^A2asJ$Br4m67lto!4ecg?1C6qlfcNm2qW*l^$|h(){^6BOi%gUEy73nc^RJV z?=6bu*E>cthm3nTGm+tegdQp(;(HIknf{PE-033GPt-XR_!$b1y;VY?TRa0Hv{eB( zCVatP-uvz5*%&CPqAj+q?Y>JsIw^Zv!TL;wYfFt^S-JAlDWzNC#M=9ngu!(3>*=>ZwNx8B_~Rl7kbBfi|+MY61D zE$LMQeQKKIIerZ56lAVx0nj(zgKrr|2n&2c0})4`*rQv!(Fp@oFJ3WTbJ^|?U3-*2 z3kd6|hjT<8Mcr5K6zr^YsiGX7COWFHMs!M#*xlmK zTPRxUXd>ze!ct7r6>^yT3CNvhT-xRa%8XnVhfTH8Gcp2#1z%EQ`ZW)f*$J4g^r*x; zv?LD>V5ASeKHGpsDkhu1qF&VwvH{;hBU|JA(E0=2m<;)@b$5sgO8RStb_jAYe%IBr zrxWj4FF%1Xn1o7jFXr?)2GD>Q>lwLRi4YKNp)*kq$6TP5*ba6Q{mw7q;=2=D`t@|x zoyS`|a-(W1s&^KB_M0?lI|yd^dMj6{XHWF3YlZ$0xtXnzEpI@Q=b5%jHQv1w7SF=1 zntT?5SDxIn=h7M1)Iy4DDP~Weejo!BbmQ!&{Jp8RrwQet3xs%4sP|=q3WxMJ&LEf( zOI&CsebQ%Tsu8pev?@b(Z>CBd?~+zh_c-f7imT?9E!VFn5&>8khO@p)-$X`cnYZuu&a(d<(abpo z5cZg#KdMEKYGcNY+Ropd&0DF#Yt2m;Mhul$n}hDm)TeB?ric6{-ag#VdlD(tgfyx? zZt1m1!m%5}5B;h1CLo_fXqDV=;M`rJl4{_iqXi^rGGA8;PvDkGsr7Sw6oJhjCgNu@ ztR3O1MS~&x%Oy*MoR(YceWzY*E_a&%JM9@c``y)ij`Ld|0`LKRv$c#8-#@*Qy+09S zWU?|HGXdz(xF5u#E53uy+Eo@+GV|wYopJdOv)`j+ekNY<@%F${r;S>jtqlW>L&2t{ z=5mM_MYnR;m22-~K;9eY=KD*oAhXROvN_G0JHeZpbAruEm&<141yc&{YAzk5wo)~8 z4Ns=6Zp62aZBF4P+~?#?>Ib1I=({8&EV&JEV~dkFP=!Lsy)A03FroGw&*}CLoo{X+ z(i1|mUOiIDd89%$2a+WD<~12N_YOYq^M$b7Z7ZeVmuFxWMy35|fFGdm8*ij4ei=LC zEGS1ttJ6Gg^Sr!6SDqM+fxKYSxj(BU3dOE2 z4pwwE-q)|Qw?w$K#E;(p>&JaC_u0`lP#)yFR98fFS-te#k!?qJLvR*eZ*9MuJQ zhX03y<}4EVq;e=+bK0I)T6vXUIf26HfmdedV;4{(lzIo49Z3#AgHRx7J!e?$ruDyjUqT0&DAl-EtRugX7Mp7_ba+%bmc%9Wj4vPk57)!DuZ1uAd=hY+GYM$N#xO#lr#=hg#A5RLAy|Gk1Eme0gUF19x`5*JS%gSUiCowJ zg|ks`iD)DS6tu$}HU|1C-c`x~CzP$bu`H9~u~32fbqAol1Uwzs#jZ#qhqQ zc>@kbqTOmC*I641bL3iM%GD(fo(FfpbkjiexRoEHCWl$)BFs9|gKz>QbH~*xPUNjM z0-r~Id9tkk*$w(CC$vTS(De8{SKPD|_v`}n`MG;eEz^jAEu0b+5ME?bP8golFR2MuZ7wOtGV4234G&KU7QT1I0neos;sYo_9usO zJocWCYT0dO0p{77_y)5Z7^%pg!4e^-F%cl5t;h0sH8o>E+_-P#r;l5b{p zhOf{rJo4W7P7|>w)idPdqYZ7|C42>8jLmV2y7N^d8Zm|v?wEYqnLaOl5AT_8=uYJJ zBeM=siI?7z!=O%rD!|I@zy630u<_SyktOMr?P?whJeX3RY|eX-;4=a6>#JItAYRMi zJ?PqYD=V0LA@Qu`{tAi-Bnu3Mdofs>4NXKHP8vpQVAN^HJ&*94rFdv<- z@Vqz@xk=n9h)+Iku`Ww>RYQMg#i>@x%&E6+Uyye0$I*h0W;$cf()z1#MAy(=J(TUY za6jeiZ@z-i@FQeLOCPo;wcHJ*8_FA9Njg}^^7~`yu8S;6n;`x32v?j@V9-9vt3I`^ zY}kDOJK1?6_6p+!a5=J6=A^cAxNOE`>083{^lIN}3y0?EjaU(SZ@ejLJ`G#co|oW~ zetA0;?(G|&)eE4%__tggo*$(Tw3(M{sORq z$1QOmV`v>1la)Y3U^VQFiU7Ad6L*C*Gx08thWHa!XB3oo(iEba5_uH45W5FC8vI=x zgN<>bi{!_G2KxpQ}M7iK=;%W5r)nAkgjvh`{rUgcO4Ux_`SY~=sXq}^jCd*a61^W2`K6-EOQG>l`nc+-Q3 znjbVUQOMGS^(DRdOe-fKyo`zAuM*%e5ZcE+@jE?K+)!Om8QOGkt?FT8h!wLMH*?oN z;xtQ4K;RV!oi|m>-Ckpz>S7}h_yONF+LQu)7#K+xmbuZWyPrC{r1$Sz`PfJ zI@Vsd6)aJ?6L&l&?r?mM$7))m^Rqc^HH~%mPj&bwaPH_6ha?SbypitO^`Q2&x81Gx z6CV^6R2_gb8Z}oManb>K(BiB;v3=|tn5=vV zA1Xsz4kn8l8}NcXLcwR3t*;UC+Ktp0KrYu*MR}|qaw$>Z<5hMF(Fd_kwWSzwmXCU3 zItnRow)?JN2%$>2Y0z5Fn)Y&wX~Nx;uc48c$Sd4-eP8GgfN|O}|9N-n!uF%(+WkFi z9OnUH(Xjbweh~e2>(Y6nCaF`q$Bq%#j(GZUco@`bJHWtA`4D;A`8v?Hv$iIvHGY^J ze7`Hm;F2EylU{ri0mymK%x&m1PqB$!ZG$dr=^!TA>Hw+Ube$q*t%7GFS{~w#zr+S` zfcI!E*pd$V&B`4qVY5r~J6)AFooV!P?=D|Q|bWoc4mc=13+DG3z8^Dlx)p z5*UFNv4;^YoELY`U{VgC@>RAnza3c(4%L-nwefIS_eOt;=!nUd6Ud-3;IkaZWCd2M zZBrKBWh#5l2_`P9=XX~pIS2D0PN@40o4GR?2oyiHAe#3ZwYsj zs&=5d<{9Cff>LFDxz+g5kmp7di5jd22~20*vMSH`W= zXCZgz3{g|hy`Eyat>NAU$-Dt4HpA-jL^m^h7R2syTP-B$6PioGDx3=zYlJ(M2SOfG zuxnjwz4{tkG>wqm=jc#81{mAIs!d>{G zhDa}^9!D!gvFS)o88?SB;G{v)nk7D&@iJ|xgZw@lsEf@PE3J38k@idL*3s!Bw-l|b zDO#j$u1D}BNb9<8!5BbU9i`_cwb#z(6ctx23`VFpe{gJL?vO*nz^6}KpC8~7cA2|e zk+i?F%!Q$x=BEC#bl5^?*4dxb;d&*$+U18epV%pMt8{m{=D8I_D`im%w$CW#(&fU{ zmGdhZ?Vd6&(o%)hd=Hpak`!IvH(wMP&1Gv96G0$uMuwb?x{khG*K5pMdSUI{6)TOo z-69!MKAGSvUvJ&DduOdNhvR`=B@>Bb`4CTzB%MR^PVHu!X=`gT(q?hUVewb5 zinQ$&w6$)D73CKXNnIJ{U%B5OomPgBIv5nDhT}&|Iv0WE~*X7ZQV**{4M zNwpIE8Df3NG1{rQtKHYwV1TJ|lxKX8VG|@AYwP}KOK*jH9Pg}{pEPl|=f!Mu2T4rj zLI9FQ!9S1)gFE<1SL>Ad`+8d!JNqi86mO~YgdyhHGfp&(j25+tMO|gs$1K*tG# zm*S1S1bXj|_rEU5dYhP!BdL3?3t$1U;>>?f*)ixp(|*4d>n0&nxKS0<2R=oWHgO6k z(24SAy?nuX5~dJF1kh=l>wAhRIte5>;1;DALKJneKf?pyN|S9CM^0n1Zh08FJ7nX zR61qp0De6E#{}v%Xjg?J475r=Kc^OGO_vPIe+<$@N9%x4TZf1Ch&qPg9jpyu3`|VG zKxrZ+HbPARGXf6X;>*xPyCh29wtg^APoOJLPg`5Z?tES_5YgiC$(X6IEdv95{!B^g z9!!6}_!b_u{K&Q3>5;mBtlM@d*{*7R3F1ymGcd86#Y2We(4Pl6d3>CZ;Fh=8R(_2$Z( z!@YA>6vlad?C{gn z@YB|h*Z`V$Fk@gkSg<=Pe5oE~AAF%i2WH2wDA(h|UssimNmeFP`+hoj5N2-t`kCRU zOYzmk4&Iil3}%t9oYztT&nuBcaLUC*Ma4}tL3qID5NJJ8lVmygAjx7N)?v&`W_xSH z##7FN;nJV2DRG=6bxdC(SW@%A?oy}mf9i8=F^^+jSAG&i>6s7=SHKt zU!t1==rJ-^5=xy-1GNT~rF93J9fS#RbJ1|$d z%cSaYj%LM|m0j;HXx!W^8g_4K8U3Iqfqn3s4hUaF(h1(e-`nt*-(7~wmYo_r)+qfh zqz6S;(bU&38qPBWhtIiq5lcP8(-?GFOyP<$abIO)eN%aZ67#8Fq+kYa={|M;+k9y7 zFX?KS$%N9)Iv6u0Fm^7|2>LeKs-B!47hbQ&(C69~20hb!j; zPqUWQ?Daf$C@Xe6hCL8GcR{%SQdtDn1($7`va&&Q6%#74op;w5_WoGzaXgJ@?_;lw zwLn*J1G2Fy`%f3P!GFq)?3_%AbdE4fED*kEcZ)`Q$x;tEfZg(rQsq&-beY4pA z(03VQ4{Gd~?SsSa|M&PmUKr&_KUL)Njo@&V)XK4cQX7=pNVk9y|6uS%80gPmxB1)T zhq1@i-CGkEn;+sl8*De$@g?~^7jKkO!)d>AXlvW_=h_LmE<9F#D9Wlz+YkW2=9jaPY@eXfc+S zBmJ!S-#_8tU;jV(Iio$bV(q+EX*3Hd2GLb>-)kJIa_&}HRX5obu^U7=5DA*dxiJ(m zkj0VV@4CF^qipJ-`Ajk1rIp|N^Lr>OBgPy0-?Mc%yHB6PffLU6S2r9!|D4aG`B%%u zwukHmhb;0x&;428X`&gdFCW?ezk6TmEqsI=g>};nyTgOy-@Wg;3>ZQ^)$^G@Hu|qC z3}19n3r?0V{SM!#|Kq46!bv2;I15j{uKai3aWw8I827tUn$^Eg&wu`~nhBWGv)m7F zz+3%?r5()-97asAN&Y)EzvttBtk(aJr)EYuESbl=G)~k*>v4{9f{R&%GQZ2f#Ds3S zPeyfhM?7!P{P$1vdPOZ*|IvhxVLuI$K;m`glv7%p4n=)Fj(P&94xjS>R*}Zrk1S4@7ZWRyAOFYMa_}&p*!@Rw zLZ(lRzf~*;WayPGn0pEN{`Ld@&3T95!F&@apIU$j^0*Z$ z(@wmr@Va7=3Hm0_ot>*Jn8Nws%Irw(-Rw`l2Sc|A9b-tYO8R!=bd*!JOeawqtloKZU zA5Q*1ze;Tm`vra{_ia@5BZt{s#7mvZcT#}iRBB6R%FyAda8tqNk}J6Nu2U(Vw=bw; zcua;#5*l*ofb=#cr?&QNw(+Odq7H!jVu@u^jLyAt zbKz1pqh#fv8I7KlnD6Axd9L+_Xh^4E2G6tp>mI?xNCrPywy6*wr(k>K9<6;0h* zZ#$*04`hPojD)r4t$R5I8L^n$aa7TF;Cn0#RmFb|rq(W>yH#lN4umhNo7%ITt-GqU z>8m&gW*;N+R)fgu^p+b)xGCSC7!`s?sfQ~9->8w{u-qE~p zmF3!m?FW2*2PtPu^k1_xn=Ix`$j!+|N)gP;xhd$0jBixpO)n~?14B^hvY{7^OwRAN z2y`{Ojm~2R>uWFT|Gu~dU2mU&t(8O3({8j?C)iO zZa>Lc7P*+10{#@QHOu0nUr)v5DiTO;+{dRs|MfH#Z#~<{uujRciA?~pPp*SY>F>4q zH#;%)BHjZ@_l6hgsMlYy^`;yc$HrUu2Y9vi)dbdNgD!Mswq1r!oi0KSnJP&=c-{EB zz4)D{&h6aJP}*eEN*!78*CbC9=(jMomLIyWECtc#^S`6bf4IQ1>O0Yv<?Nv={)PRJtM`ov`SXJWs8#&?An};3{Dt1uIGVy+yt=X zm!5d42*A!=p}%~YyWQ;OCkABa8~*>~jlD&Y^;%}82&Z(ACQeAZe|WeVA`g8rmE`5+ z-)DyFAl;)VdCZ<~F4d(B?umK1Q|qW>ldiP!0&yu0*fRNXz3tJuH#>9m^RHgG zL`=h5PxrNIT8&cEz{O%QyE{i29m|!gP}6A3SIBc|%|WX4$Chw`P zLx~TBz>w^+GVMcEt~?yNx!ULDKv1;B3C++$trej1V5B{McNxpMBH>$moodP+BuW>B zDMp~3UQWEL`q_uYuiuORXW!t9?ECGoXwF;^%DDMjp1?Hm(kC@!iMc^#jaAjj1r zy7d1x8J}@1(eEKicAt!#)1dNmlG;T%*O_l0sDxaDKU*+`A@UH~bVfd+Ev>Q_%jogh zE&erQ_P=f(M3_>T%5+mOCd^KxCRToMk)pluxoxLOlAQ0_N;QJxyRMsd{!YiFff=Tu z>!mc&3(^01&!Swg(Aen4du$@+503ZOZQF2x=%Vi3^R%6}Y5$|6_FfnAOzs@Eo+|(O zXCw?rCBpI*`)P7g@h<5Ue!HYw+`AR4W2swaYlYteze66YT_`*{=W# zJ9~mrt6&3?vy|OSNv<;S=q~eXaX?+*b?lUvem$?R)wxU8`DMZ(x3bZ3wHtcYiEWh& z4?>FG)P3zzR*tk|A}=;z0lXZ4HDp$u?J-yv@K_w;Q4qcRIcE~eM=k?Cd&}vQpdI-9 zM|RQ8H;dQ11wna_1p;Mvt1@D-jr!A+4o)hvKI=neNfVvVDS2A*s}>d-H@XYfuIz91 z1mAFjopR;TNt^jnV(}d{#}+>A>-W7U(qjy-!13_TmMuPBT}7~C(bm36VE93u$o>v{ zhJKf=7LU_>}wTXG$|c)c$TKKx3@loQDa9Fuo&g6N_{I&UDI``gWdk$irK z`HqrHmO=U}8spDu}dd1jaVwCv>GT*#s&Rb#Hx7nIbNUP_<6 z8K$znH{OO6i|(tg>UKdwwlcx~PIOA}6CFGTJ?X`&Z=buvmG#VoRHV1)U*lhIk@DhtnsRD@Fcfk^q9vBfc%A6%WaUiegaq|U3UqU{=f2x&UG3EvuE zrej<96-5;klg@6E@2zz0U(a0Yx8`D*U%7_wa4SnuQYdhyTRs|N162%ooh8cUmz&yT zK3~EP&pW?vU^UZNpTWUHn{yto6NKHl>*zcl3U~YLjOjYIOX8Xc&0BrR+!jrHmV=U% zJAGUck%UMhL@XrqsWB)61t~0_vVzCpSF1u|KdE51qGrr8itWhuV?judsjN6P#R)g6>{h4G5t zHE5`;^z0mme&UX12qh1S*RCdiSsqpCJbO`9Id9N)lk zYKCjI^0pdu^K8O+YdbA_^M*T62CfE0uxYk)qoUo#J;s8|n!jZkB@pu88mX{C4^_oD zet8j$$P*Makzb#P84J#x<_iZM^W9%`SOp4JuUgu~>5WIe%TOcf8-L_z7qqaI?9TN$ z&aIZz*MvGd(`!bym6=23Ul~Zy#ZBN;)JL!Sdn}T>rV~H93XJoM%d0(I;2Je4mDw6V zNoAmNjj+pqcC%D+r=L`@d-rvyn9Em)CJW#7d94o(Rs_}s7c{Zi&h>KTStsF#NP><) zbBM65-O6?ob8aDhCU59-VvogL>M}NgliEfGSLEHH7JQH1_yk3al1YLw%Po2V+r(`z z3(}v4NcX;3%eS;Q%9FV&8*kQW?ce_Kxna%j6*5D6pg~FCU2U+pym*NJy5G#2K{n0c z!jm$;86KCptFu*mUbCd{^5*I1#`FCzFXrkLJzq%SF>ftg_n%xSs|9butQe7JJHjer zR_f+x+4S?Ox54^7@yz!dOJps5=KCqnI(PS;FcPgc40>H(r|>UtBH9gbGV0iM_%+_J z<~`CeJ^X6qKC`x}_|DHq+oCgqUFG)~;o7d>*_o=6=`qsq<7Kb(0TNbM`o{EY)j7PB zrlBopPinK)$8v>4|FukTgrPZf9^l^J9x<1(;b^+8JY5D=^MQ(Jwyme%Uq=|Mim&UTBc!8 z@>~~wr+bJSlF5^K(qSY~M6e=%yiK!d%j_)5e>Fhz;6{AMjE#1pjnTLtKdp8j{SlfD zo71_Ce^8L;)%yuo+n8-w6vJGAr^4Q9fwtDnrHjfNmr;+Oxt|x@T`6K{x|Htr^TT$t zitzlKZmLtz>Dq4ZSIq3qXxE<~YTt2P6|UI$@~mmlB(U&>R@h&=Wwn}0L}-_j`zMKq z8+R65193fW9Tg+a;`%wZ3XW86xesfu0U!MxPf<#O^B1Pr-LN3^L9AUo;*!dD~s zE*$6?JY&2HD`hm{CA~TRNT@H(vH=?{O(giybFI(9^*P)HLj=zFKB=~{KUK-LEbh|d zAZw>ZnGNt}JvP2;vUsmKL+BM+vGjE|sn*6$lP;P)U@F-|D{7rSWfB;ceEU1*zg(Y= z?C<^M*l~SI_D7jjaHZUINyZg5sQ1)Ch?3_7e`Pj_(DHO z^+Y@p@fvJ@z?C+6g*~Y=E%NG8E{layewM;b0|9{%&qv!2xSgiuWft2Byze5z2CmfY zv{v2{9wjBnADm`#;!dA|)3~w2gLS(xg;sY|hk{2Amem zXsjv`AAvo>mtAWVzG2^IsvXOcLbKqishfQNt7R!+Tk%7~F@^0h>ia#SOAl%}qS@5X zdlosq0uUt6;|hJj2>qRkv-gC;B6LAdx^TSzOcE|c$J(nN)j{kl)D;OUv~MUf(2 zb9!L3k$pdLWgT&4`eoqW{BtMhi_sz1d%CI9ZW0*h&aX_%M+&AoT^pI~IBn3OWSkfj zHP5}?^zZp;v)==o)xx|%W5cFu#1@)oX}# z^(dZkoQU$_TOZ!Gi@DIpBcx+kTTt0%kWx@N|9!a|n*x7@!f*pu`0l)p(Y@ktRUctj z+)k+qDh*Y&ik;ETS@7cV49U1_nDU$fk;{7(YFn${(%j?dxW_GAS2T#Dlr$Ax$K=Sh zhY})YQe)~)5M90CZrSt90n%7(q7vQ5%XfZ>Yi2t-FLT+ z%5DVy5*tL&i@Qz8dg$J422mHZZXyjb2X{KHX!OS3&m?Jv;9L*Sb{_4eI}d;pn7FD_ z-uCPo=`je0YB^{8DGUp}#oL@`Nv9T&WiuX+u`7?CAVrx7p{6_NGtWNRUHZ7iJo{j? z@}k$RefyypraLNFW+H>mJ)&#IyCq9QQKd{mBAesJ;XmWD3^$*?gk)d|<7XnJKCwFk z{_~^k94J{jHz%emHyE6Ur_w^P4Cj`%yEshmJ42-Q3>z;!Dm_Ui-@m7mNcf(e zm#Oy|@`>xIVF>{fp>(?yMV-${`=ivH@Wt~VTYhvlpx`cjqPI0MaPoTz`L7Toz5eL; z6d_WPd?iA*dyK)I^}V0$wzpV3Sk~`*v)(zs!nC+v7Xz?1lT}UOjnoR86=C8$MW*(d zE&NA+T{5bv?i7`>xkzzJ?D0BBEmw__nG%`mSlK6jyf#LC?S7+ucO&6f<8P*=6W6|U zS&ZP)KhNe~a?AH(iP9dbt|(3qAtT--=scfbKSa;cht#^iB=6$9c;hFkU$+Oh+=QN+ zoFZU`pwm4+$*I$FGOuiy=vh{AzN6brR^5;t$8)^xiao>ev92$yErS&!lm>y{1-Gib zvxtTEH`^E&UO!p$Zxhdi_PYim)m)qBqaO>8(Q;wuBZ+KUWdl@Mp;Q`6IPI=U^e`EbsE52faAifRfL z*lY6Msp+H8F{O;wGkWOc#gmQYPfHkpajN-DFUrk+xxv+l{^&ki^5&NVeT{FO9V+Vw z@7Pc1k1`Q&8Ennr;|n6SH0<<_e3 zRZnmSt@^3?ejP1Y6q(rbT@~bmg`ze?7YoCnoI3CA-R#CQ3Mu?O6E5WExawRfCF~#VQf2JXvM^)8TOc|a*I?}j}ldO%| z`s*E=SYiklGBga^LuaPUJ#MLzd6hP-InK#=y3lGlTDtH;;??sUX@vK`b)<>eJTN%FETbHF>2zw<+48x;fWj%86ed>nw%s zG~DB5oYooc6N~{*(?3frp3;B#fqu{aWzpS;rl-)XuYGsZ@kvU|HRIsOWmKqiAl+i_ zr-^t;sfLj~m!u`7wLwQ>$2!`wtykQfObB{U`@1V~A?zd>9_zN8MuYJxKkAeP3>Nbr zaT@SD<)4?b{r+KX#7EpZePBT6m*UkJ4=O_W=xxgXhqsr^f@(yBL22Qq`h28wX=Z6<=7IZ%uiiG(s(k}MRq+w_^Z^rG1Q~KJ1P_w`s zilSsP-8CqO#CETV`Wogu(QB&pBKRh2dI!&|XW<;Ymta)D(dO)ANV=aI`t}%;suig_ zJ9!eKv$c}3T((pTFW&D=!g{c)&T^$)QVCf(W3Jm;K0_;O6hJNyf14{fyLY}F|3y%_ zJsQzSm~uSpCU4eZYqIQ0V_CzZ>SKGI{&uEOS_%1xai4R*&xtn0xxOyW-lM~(j{USF z;mdKGYlt>MVIf&xfBbPa8BjL!lS-Z{9J75+PKwJatZ|RLS<|t{Zu9#a9kY zIv{!(bDMh$*EYqZ9%`L&wS>UpYfLn+!j{qN&w}vRv>fi9-mC!Z;7czN{5+Z%o#%rJVNwZG$Y4YVH}A zHH*PO5-lg(8T{tCpefz!wi1hx^A;R&W!6k7TJyl|1Ubm)J}cCL7q+A5adz)W8QP!d zmNAHCOLj0dM@kYrEp-$R)7;qsLu(cCCKe?4hN`w6JFCN_+@2jWQzI)G5foRmMQ)oQ z6Y0;eY|m@TdYmq_ulisyhwtPu2xth^)}Hk4y(aQv$+r^Ywi0f+Axy|y5Z@h|P+F*h z@l9is-5buXv&jrOG|*p~2W0-#Akx*&!cPRx-y4Gn)p7dvtGhkuVQ^q(_(T3&Xal^M zr!Fz~qI|@#`k>3JnlRWr{JS?I`IY_p;Bi}KUEThiP5WhKIV-irs9yN%<}=B{%_&9; znrDCamk|xv@X6QUH7JD8<)+i?#{ z4MZ8S;4e1JIlK5`;kGm{d`XEzguzPRp3lEA3BcNkfEz~38#K}gF>!)sG?gPhE zTdMT{6NRGl!y#zC8K+thY>XC82=GR%zDw_eRM*W;6&cA>iWGj2fQCjnyJy93%DCp9 zzH5^|UWvb^KFhTkmc6bRy@2H_gqfc)E3>va`dPei-m6>%Rd#XA$7a7^!#R(WkmK!y z<=fJE_=P8P0xwyGnePIMY9*ZqzekSy!P#B?;W&Pad;@3CmH;htwDP4Wny?TX+m#3E z2*%-3-FPB39mZBI2|vrsDA_pTSw_SH4Zo8&BYPVxl5$wyG&d5>7Aq^wXwj}8yJRt5 zjX8c9MVR8M5W@>+hQstYv68k#g-wEJJ|QSN{{sEJpJQ6YVLSv3*tM7gSnBSW&Eh>U&r$3h^j zW=V(E7yf0xh07CxijyV1>0*FoAFHuH3ze%&>9^xbJ${>Yu=4EXq{ezTuE34}?*;a{ z^TgdDDYyv0rNF*{jlvTn1#sf9qkm&lW)5f@r(r@Pa^O-(xF-J^AXKL-&KIXIBViIc z0deWOKT8z}_;X`-8ct?yR65j4dac31nt;!!V#w=9dgXOCj65!I;-L)jbK7z0kV)ld z(`&qqa}m3(xfAu01oI#8N|^l7BRl-e6_o{GgyE1w#id2GkoBO^&X_0GVHIGw+ix`0 z{tA0?C_o+NLm2!k@|a-g^RGjfL<`T=A^N#?G8mz(duBR7N30eWsceCTUkAftN=WD> z7Cdq5vTICqLU@7iInASfVQHxK0Yxx&8M!P0)pGy<&!{cAynLqEP(P3vX*zibLAHFQw>M8RV>lhuoHKZk9!UBsZH+un+Oqmsj_=KFggW!jm7q87KRnli>fi#QR|K&)q<*JLh8D-7^H5ERQLC~E znEv`ycv_V?BBG~|OFhtHjLJDrqpF0hv-t4>*JJqDn2}9%ya`G@DD$Q&wS@0Q*qsEI zZ`7Sud=FYHjaYW=@C^gQ;CQMtgK9x#vHa<nxqfbN*)B=^PMm#rt6gW|W*Y3?-+@hvqU@57P2PKPEqBm(+z zScG0=m;!R=hX>uIy_+JCVk@x}0JPt*k5eW&9%UjF>WAfm373^d2FF+hwt90gb$%?7 zd;AAA8OiiQB(#ZvJmR0CyR~5T?qo{FspL@PV&Pk1m^?!f#ep#$K=vS3T!v z#I9&Y^@IEiTy$z&2EXL*rFaa|FV#W~@S4wl&ZWl8jfuiRty&B$VSQB?(P~uo8o5Qt zlG3T)aZ}_d=$dh|j&x(mZg9h|8kqWCJ{k}>-&&H2{)w#Z5Wmr3T564VzS`6CEBw5q z(!e0{EaYT=i#F%LAV(vGjNe*7BD8&$>E(!U3^`a#>}leOVH+$zo%S*%A8jL`zs=A44UWUJ~0ZdKWucc+LF#t_qkvWJcuJ!ch+{CAls&j!W38}GN?n0PSs=r zIN{j|wyt)Oo2sC??ns~ity^T(l5d2R$hdY6*L^c9hy7)IDpqY3yfi5ic<6K-Pn(`s z+`{p~0V%M|6KENKVpKn2@c1B0AbdiwZ~qnJUE>%0qkWYVQ#9E}bcEuzPbkcGDZ^iN z)pRn@=Q+~^qPSJh89Gsz_uKo}J4~tj2l98dtT``s`~+&2OtvI3J6{hsS`3Q&XTBOo zK-3&+dJ@>iJfF%vu1VI{T3WxYU-rN=G_+jWiR7%Tti~#!e{ioMxA6(m?!$yNV6ToT zP6hbbmr>+SC^3$NiMR~6AUb(Z>3w6xhu;NO`x?FWW)y6{L;2$RQyeol$PX_K6psQb6?&%Jgm zjjJm2lp<(O&jaTZt^8G2O~>1WGeH(CEq|;+t2Q8bHCZHZd4#88JvUusbVfMJwdIU- zPyY}r6&jUQ{YKsYEu`_FtDvYZs1>Q|w74pRIM|MtgUs5rr`B;sNl<)m4s5y4!+JnQ zVzfJKyfNH%=w?GHlW578Fcqh?vBHMPFTiEimV34EiuMz-q7384{%5+?qwK8=r-?y_ zg*7hWy~aX9?c6U^Y_aos6*q9IU0nv^6pk^+~8S>9vhRC?ibL~2-t`& zieDv{=tcy4dhI``#kz~OF(4|!E1$n@-)zGxPa`y`VZ^ADe_p>REWNq+w8hi1(#L*X zg)#w7MbeLc-6S%ld)XZP7YCv5#Xulg5=;FaFQtXhA<6}^U$u7e;!X2HYeuL0a^RvV zw-ER8JiNdlzfvWMUwES`humx)owRxkY2n1PB`~7wpn@SzQbY3OYUOOYDEJs7K{>t~ zppa`RR@{6w-y$`>>I;#%B6Q!R7o%;Q8ODU7pA&)EXV4t(|M(!YCB4*@;vCt}R+sl` zr&^ElIt=8%#ydDXTM+K_PpCFC}Rh z(~K?9Ys$a3u&g*q%HwGNE8!kmlYYxltTu(Jkh)6)B7T(mmR03Mw&S>$@TWNEvzapz*QV4q&ig@Rej zlSDuf;-%l~6(tLa{c@2AzreT0-Y)Czv$Vuf6PNCID`9{}W*efxUKeV1<$tBxV2t@1PePA@T*!$+K&Od2w=YX@`+LX2OSknNL(oe+4tFJ>cYjvk$|y)!!zN6 zV|IC#1=_)HbkdnoH>y`moJp^o(p(!No8v85g}75275QCSYTd3jCRt@t%^TR!g}7sb zzDQ->Ya0Ytwb-WJ{@7TNFi5J$R20Wxs z-8xhtqx!)D=M!=}4{F7wVr21~&-{Jt)zlJ*J2Tv`t{#2-wz#o_Ojp~PArDR2P12$t?BKaX;C5Wla%{KECs%9fJQ|mYeNh!)VZ862S z?-9pMs=7HdjX}w^=0aJaeEV(kzmM3`$EYUh`z#tnxThVI!6k7M&!kV%`YJ;;hZ-)A z>3Et3L(~jnck9AQrlt~p!g?7Rw0BBsP2cy4JS*qM_>+~uG?XB@y-oTeN1lITI|hg$loI*)2yzm~4*LtBB**kM;VP6(uKZ9fX&WcnCLynJA}9dh zW7aSld!7-wt+?la_%zfY&2BP8^|?=}L`GO~U5B07Ie_FCl*t1X7E?6b#X6W;qM6;P zT_b)hwDBtd?$*Kx=^n6U^W+1^Bg34IhB_G4 zm1jVH8s$fm9as^lY>h~jc(<=3a=sFeEULOPWB)7nJsnkI>Gx-x91a;!uK{BqVBnP0>*X5kdtqYMl zag)F&oYfol*t;hnM^z`+647njWDl_U@ipY(PK(^>Uu`<_WfE}+V4z!BPV5iati9hh z;_Q|ka#*MUg+_RtE{Jbm$i|=dGDtT>OaoGwWU9O}E;9N1^huuSByXK>fA-Qk@s+!M<{@zX#JSRk2E*T=vN3j@!Sx~=D)tOQ~xf75tKa3L&$ zY8hR&QbGhi2c5(x6T~W#E^^!(N^6NjyEs}(#yXWgANYML+=qdlQ)8sV3IK?WoS@OSzGQ~J6~91Sk_WZY{meoGCP%1?vCJ5z|ZOG7{rpzYxM)ly3@6F z)xVTqYCDlgN55s926+A`S{KWV>#00{8i$M~MT!mqX#Kb~hF}$;&V+MZQw8bR_0G-I z)0IsF$`7J>JqCWz#*{jJw~Tl6{M)3&SkCJE8#t|ydPayo>@QYmj)3(Sxn~)E#MgbB z!^ZTws%z;F$QI)NoMk4Ly6{Ouboe3-X|ow!l9D39PpkUGq?|Wmllp{&@n39^LSgkJ=114RJHA8j&iDJ2UHD!6ELA)|f4}sY|=!3#~O9 zo;Vwe&4Ub`?yK-|Tf%P<&+c}LdHBH)0GymW8;7-K@v(7w>pF)!P1_WZ7}$Q5c3SXX z)2Je)twrqVaNBTbC9zOyX~1V47K1Ul1a7)74kE1du7rb7?(tf;C#=gs3@2zhjor$hj4V_n4UhWDAHY_0CaE}^Y2dP@yz{MmF1{yb)#^!#s7OAU>%jrZ~2dZjCzn2<~FQr5(!0 zD*HI}!0P%th4zWHFfVqj=hpYY>9;1SIx00~s7E_57m>7bN&470o=Z{P)P5Jv?QNRD zTdr105*!=JA@{DBG^;xfh_;UX4k7FAi!1083`K^E1T>?|LAJLutS3!!z8v-oaD_UK z{sdU6P6Z49U-|$-6~2UKIRr2E^YhdXp+ev7#BSk{|3r^0?szZ=5BuhG^f^y-a( z#ubBfQ^=Q2O+PAyWh;v9%Z~-Tva8uR_xU@pJO4s|N`!=rqGoM6wTggyzH`cHrIqg~ z2WfV9yYQd$&*QS(L^_po#CB`k#t?RS-*)i8j4>XkQ@HV?<}21`2_fICGk8d@FHgjY zJnFk?z-;o3&H#x?c|4<30MHse%K)0L(pecT=c4XZaDzP=!S8n+-r`5)T&v+6{78{?ayyD+58xPm5wYL3f?Nxg$L%7y00j?Nx$?ntV zmmA=Z?t{Ho<4PCzf z=KaWV)>;z7}w5ydAltyT$rzQ2^=RxcZei z36^gk2&%*eyf4zyFnc!N9BCfsz0pzbvr>D>pudN)AMtt*M2)Jd#N~lB1SR44HEL|j zq}FYE6wOV~zdkd6Jaw%sN+&ZGx$gqyUGR%8GV`vQDL&Qz*jWBmj!K~cFt&4~9J&mK zmSqsV+01#UCdtzPMPG8)uET>~_wk?KgTw6z0m+M&Ya=>9^}KVc`OGMPEGzzMg7;{E zvUvOkZPIHp7)4(d7v6;Ql8&j{(O!qYou1r(nOge!ZQrSjFgZ?m=rW_(&0bAcS18drXa>}okuaVkaX zHIlD@{-}=R59MZ=>E>qkH)o({95HJq4t}>CsqO~?lp)O%s*^p4&}%~ zFDSl@D}F4R%r_hJd(SBJek}WDJJ5Z|=k*90H%jG8j@?>Ijd&Ta<0*O+DGQ74X1R`z zGQbB)01Q+Yx6(Yff@xDer21+k04;+Wx8$9WyPY%((%?2gnB%rl<5&?~n1p}Cb3=Gd z7Fe?xjW`p*;d^=+jvyv8^uVIUN*^g$M0ksTOW;5m4?NXxoh+G)n|7ea+L&Ym$(zx< zznl5|8xRZnrWJE<{Op)yc~rR{RCMF(DWo{81XKFavRjuqIx77UFT6mx>pn$E#+>hE zvUM9;Y!h}b%Aqj@&9(|ZKofPtd?#@unE4YJ)T~iGqsR|-?(F%`Qh&0!wkP}|AlSCw-UoP z#6Q(yDybCf|K)o9=}&{hzVsv7WqOH%dzdA-*dBX$uaMj3aS5A=VIN{ahmu4%Btyv| zYAVv}68kO2k{Z-f((5#bHfN^H&(_gW`F5Id2`Ixf<5qHO^3p*89HR9>J+`5^n z5&%stp!AEe$KqGaxiXKElSOa8NO&e;$%g<)=5p|lB-W>R)zSg{vUPc}&)^w0d_!g4cuW)te04-97u3!dg`(?bty=^# z)oZ2f}4RfCnE0UC(RVP5cR!Y z(>I&=^|Hja0W;J|y(L0PENJ|{*Ht(&CNf1E6GMd^{l zU`gQ-L=r_0OW4P3zIb_YkZ9HTJucO)Ru)wsB=?;Mz^@-WBqB0 z!F2i5*#*lg=6CbqJUf-b%C=Z9M>MYD3n;}3K41rWuEeTtOSH!qRBUHTE+x^$ZM!uP z;zVJhQ;OA_2BxN}t<>xLdyFWnKCTIfEU^n4cnhK4;X9drNAFST5igbC6K3u?7f9lm z>5z3Oa8ftA%cC~#b?vJK%gv;!nM%_C_T<*m=?+Ox#^fly~PAMgSaj~tAA}MCHQHo1|-S^yW^A^z8vhr$& zIdv{Q(|#XNC;)Eujwf|@&zq$vDfWh>14Z$k1R#I&#F4BwGoC3F@_dMjIeaDqwZeyV z3z@0!mcQb!oa}^GFxJxsn21om+Qy+Zyf+&2UBVvg*Fv9yU@>aod@Y zqwS1g4lUWS8(Zsz@*S)!MtGfXJ53m!I(QB}R*m(Lj3pxb9o!OS8a@_kYjoK4Sa}S#(2GrInKQd+#)}>X^~O zd|cuN75y}6NM40oR-q!n@lw%ZES-BLq)7AQjfsl7D@WOtJdcJ~1K;5y!#f3)p0ZIb zqvW5J^|)L;HQb0Hzh8h~SKnY0SN{7Sp1xX9ut@93JT3mYHYbu54Bw%2UV8(_$ zN$V*)ebuWjTkdFiWi2oV?4fs^UBfCRC6#0xXO1fJGdgbxHUIn(kL1jlj@Q!k?Jm9` zrzh{*mGh!oRzTmK)+#wk6PuK(UUl;<9xmZw|LZb~*+&erE;0gUA?;3#W^#sQPc;-B zhW2UXQ_Qu<2UllfjLAGH`uK|6n1yGh`QWto zD7)~~ab{9JsnJN?PCtyFiUU);Q#er#wyxQ(cc3+e@?uaI?HDbAo0=w&N4!YyMqI?A z=!Z@j%MKWDDSIC0xgU~T#L|+gTBhB_!1fP*@*o!fd$8pn@W#SL%oC_K13;WDhIm{>f1;!(a8=VDn_WUFsZ<{5WFN5FO-xE?T$*QUOhv3HMmh1zLw)srj zQF%0tg$3;|!T4y zkZW1X3j2f41@k=8UnLcXNzMS#`6Xyko7Pgbf-yx=Ie>z75JNWq)lTsakod6N@osfo zPd?Yy6dcCXtEWp5MP;cZZQ_EB(Si5*s5kTt^ZHgRr>+c+XL(+JH^^uAuLa}eZ#4p3 z!AC+x1sfwlBkIJ`r`tRtJ=m+i60e8ASLedKKiD3;F=fEnQ%)$od3%4)-KM2rcF4AX zQhdjBu-Zm%7-~ky)2Yk?R^Do7g+4*JaWj6y7_ohA3>9i_i#?Tq~tfpwB^7#h(UDobxE)m zP)}Q;Nhpf?L;{}37o?k+`L8i}{}B=T_u2lQDSLZUr(>0C<>LRJrYUcE-vRnUwM`uK zw5b0z_WAEGeQ~4dVOaN;@qf&c{f|LC>~K(n`I;%nVf<%>{>LAHC%%EIn0p-4RsP@4 z`Jd5BqNm>KgqbH(yJ-hvVWIRaI1{nxQ=CIom9fkZ7xyO<$4>xXu~4$*|E{S39_1yJ zz~TQQcG8M!ss#ZjH<)o$5A9-1lq{JzHXMwV$&Gal!*>v^F9SCjJ<-rapjq>O`aSR6 zz^arqSoA0S^Eah&xbG`+dwLQMlhsN;M5;Bqi{gLthCaf|`CClu_kw!+kPiTY+Hk%y z*2!49h}&?jL$^<^s;n}KP;It^!u@|=6&(r+m=otHH|%Bd`KloLKh|ClRZ5DJmDu7J zwCRT+#PWq~XYjv+^WI0OKs3Q>wO?l)*I{?I&3^OOY=!wmGkg9QHExf;T5sr|9Z^4c zcHP$%jwe1^$Gw*LN44q`?cP#XtYKSOmZ9W?aQO%A<}aCAb)1ItKlu42-?2Q*45>xt z{zt~EBm7fUXxwT2h|2A)Jy-z877NeaOc0KnpTQf@id=p5`$Z27V8$>dS*?2V%v@C4 ziu!(!5rbH+t1_+qqd5kom<0KOsS+vtm3ad~uyq}J_4yXq2C3jvw%JxOet$~o@dF4Q z-yytn$FFMA^uype_!MooVy(V^w5PnGF`uf!;}XMD__(yC>8&S0#QigpiS_#>j^07B zGnBL2;Q^3s2sDOy$H^6S1G?~e<>^1N6z>Z#-7ogn3@6G~?!ecfXRPzf6mN5G4u(fu z=)bFdZ|En%i3~Ku)nAfeehj-OO~@nUEcCw?Mh0Y~2pJcbnhAsW?JqxGurVuu_roCw z_SzkC1=K(9n4CN<_qTsA@~DJ1Y_ul;6o7SzaIbPH3wAnUS>Y} zJEOe77{Vv~3yAk&{V(EuO%L~)ANbfeabV;72>Mdb@A6;_VOIvfe)&7hbJ0P%#UB8Q zYVZFSMYX$N+&%y#v|^Lo2dG5Pe}9iQ{^SE6Fi+3#gFI#NQSRRFrQ7rdF@|Q3x^b7F@b{Xd0HB-r^e>is6cU7*!*3}t*x#dEHTsgzd`SF) z_a|&zfx?;AUnD}cK4@nDdAIWz&bmPIasF$-U7@rl+`SVL)Qvk+tx1o62M8#a!Go=X zm3aRCKe~Tt;=flLZH!$B0|h${1Ou2G9;aDt{nKnF1p~gSg5r?k4^973>ZE&xqWxGH z!d#u5F8si_6!pRb9VC6um$ZL2KH?@WQCzu&&aVHub?f>%wO8lkKYIRKGnn-XZc?I>#Cb}eAI0)TRlJRYL_vZGV#|~!={g%})S`>RAo`MYXLHww zZa=|2b2zTi*5q{({`bO^^TRF=LQFa6(~z z{0tIar_q8*0Vze<*}d;=-#q(~`QFXHn5V;dAb~!JndwUqO7ZD$5oa6Pc^?Xtjp{m% zqz5RNjPqAJ4feBCbMPGqjkih&~|j}jdZg9BXY?7SqGGjG&H^6%F5tBg43AQALvxPk1kKD zqJY|i%YAU8j~@IshsefZ2flbM`aeDi&6)xjGQP&nr~?ql*Lryf#J+OWse(h1oSxp_ z;NsM#y_p`!d_Lv;>%=Kg@T@eFbeQMtd^cI4;UoRT-V`^`1%AqEHE8h{lt!RZBTkMH41L=-MptaHssF7r>yKiaPPp`oL1M3z;vZT` zST}|nnEcAM$xlqkH#-gZQ8D$*K0zWMRRxE6Y~!V#81ncB-9JJ9>4!I}<_mlF+ZzGc zR#CGJseA)@T`}~-Z!`!3am1fNm=!X=@bwtPk3@}W6XSYzh@{X!`1OSyt?yw~!+21K z+WTZLY@2gz*NVm_@JLTLHHOmOEs5|W8&*YN0@u^eXj5%J7ZJ~_Uey_Bj&m8VKG&o- zTI*)1bJ~u&v5dA|Rja{4R&U*GN(rUEP&Ve1ssDTV!wt!c=&%TSA~k_I4}3VI?KYSo z*U3@~6y(OnWP1=~_WO-<7h5TIbdm%My7stPNf!{v3WrC}`o$f-Z?M#GEhxR&#S*4B zU$IU5zxfI=axLIktSIlA&Ec~3ojDtEIP=bf({h?lxj@@AJ{8C#*#3M{qYtbx9lr{4 zGZ14v2$KHuwaixXtu5GbOU|QOO(D6i$jB+!*%czcd zuBLFX+PkEJ>4MEN@!e@x8@Uh|Tjq{DDf7H^rB}*@#4u|~eI{TcD$~jg2A0~BwbQ^_ z;{}ONvgxLFjs!4Kqyo+fkt!#vCQLh7((&D82Hjh#3s{fn$bs))++iQ%Vo~(q!-gwB zxNIp*X|pUzmf0MGj}@v>f+1LF(Hx_4{sUVt=$kpNx#y5wm<|Q(x^Y51_PLtyFJ4Mt zQY+wb{nNbW5wqrd2Bqrvz>9d-BLDmvfJ*wivw}?4Udj8uuO0U$TeaH(Q)`8!CCq%T zu|>0mhUdxYkL7z+L1P$yQZ{PRt7Hc%lnn&jGtz3cH*!h-m5?0&hJL~+(%D7N**G}U z>8CplP?(AJ0v^NW*?3b0n|H1_+v)Lg{K1BQstjU;TmT#oJ}<%h{BvCF{N5xe8=@hN z53vosF7|2R2csI4H#RILq0KyONiUbofU>7@t#`#};?A#)NOltP+GMA#aqS%#%zx}% zTxDgkBJDtWoCfWXS>Sz(Ead{hpq)6J$1fzbozr27_goszcW8!c?8G~Pq)msr7y0FC zV#`Vs`jj(M<0a$f_sL2N{O`kJ4lbt)%m+>AWJs>>OGKaVKVf~#T$}6u)qs1cmrGo# z%2HljQ>Dt1#N~K`^hW<&RFl-iFqQvOe{Wt8!tQo#Kc%Y|ct3f07j}KDqEorWbC~`^ z($RG|dk(buHC#^yG&(~yoptG-y!@EE&hdr_xPCENOxi4UVNh* z(g7XQ)@w&TRQqu`6YM}#;%NZezlC$N9qP!D8L8p^N z;MzDIpEn1RWZRYJ=-y%l*B^A6lRDm_?G(jl=rqak_k4>und4chvw}sU0hcy|*nFkv zk8RTa7qEKW7T1aI9*K|WoQsh!=*2ts5!E_g2>**}!`Ss|((@>W5$dcCBn4bF@qPIw zdIFbIobkGwYoDeO*KTbLMiUwaR6)Q}>H?{{RLG@qbsm$?Ws6-uA|}zw@`I59 zCxn#yhy!o-_;{DIx4M#hw69A3MbWL9c@&dm{xPEqx zE~4jk>BfVHD^+uxep~Psoc2tU{pqS*ELP(IMoTv0TUO(xFI&Y$7Qpexdgmwsq$K^OpdpWz%SUscw1#?sfJ*eTfP@`{h06`-$602ngz3()IdkCf~k3i zcSc3vmvw+^iQO8%G;lF)Dq2JvO5k%WP_Y2>nl$5=P;-|OBzfAms$H8U{urQGGqu53 zQfR@C@pzd@kE#q@+3>mrT@T{%GJ9{!4x^^*+^gfA6`8bmZas%|^``QgWUyO4KK-P- z4&RmB$IV(!sUzy_!tb78IJgSbk?2*2Hf*e=x~ZS>PW2`_QW9~)xIe}y6|0Es7%S-* z_I@!XY~Wp%HF56Vw&Xw_E#YKtU&;b=d04%R<#OClC&{@hkqpng?G_@L{gtoGW=ehun$ePo&3KnYvsbRW z4ZY?eD%hH{ZdMh%$K%nCH32Z`I8+oIEpUc#Th(qV zfeb<1@wRW%ayQEfS9`H(w*uhminyNaz@Y=%gWu}dE!_*M$h^pl*f^|#)m3pe)f>6t zS|{K}?BA>2r{5JLIa|rSXtkjE{UaLQ3z)vfBe=u5OU)-GCSY}}R{}i*j!L0U)Wox! z(1x>3a*Y7t)COopuzHn&2X^C=y zBXH+@Q>T8o+8e4%?B>P(+;gXZghWbS5i0b zy0n&L7tW_`7}4NzNM?&%QFTc_X{UMdTsc4UbPc{ctg0g`qj>+P%O24qaBTy(LLAq; z5?;icd6C;r1V#x5JPSoOl>X2w!CK|Iz?BukVb5DDC?-sb!2>{_yv#-m&~ufOrbJ#agTZO$AxNAFGgc4w&0@p2og-H|GgdUB@~ZRo7lc<^Jn z?{&?ke|?Izu*&QGRy!@Gwu$mOPotPyt&KnAP%f}_q+UhSQq*yjMkv%C)ae~ zhM8QyRi=J0X(na_PVB!6cFNeDy_L}3-p~GL!d7f)&o7@{)oGh-sZfDAl-MB!`jpsG_qJLq1 z?u|oW|1+KR95mcHRus9%JV_SYV;A*@yEY`FWP1}Opsa~R$L8u>_Vr~EALcjJJ z)@wMJDcZq_{zdm+Xx!h~e)T;YD##(!wwqWaz9n~i*IBP42OZlVg^O(0M`iPhN33bb+{I_?juW_sV5YiLX>p@T^{?MmG-P2d zYGA2=%Y8jfn6{Dj;3;0?*y54l; zx*=2{m5|XA3KMY}?&a=y`MU(qKeS(Z9{~%H`mo!bm0E2Vdt}c??BA3RG0optjQ+`& zcan2_1Ev6rzx*EARe-A3sV|P0G$;X47*9P(=^!b*NHYy?VXi#YUDGEo$j6@z zRS{3&R)1*nspKefdoj8K(gT>JxnY9mc??hP}RpXFprbTK{ci_G5KBh zbRNfnV_-z3XQAbMHzHD;6cg1#9p`LF+H738VyXRO8_Hkm&OdgK7g)p(5=^Mo>z(I) z(X>42+8xhNsz%2KJo}Pd9N$&o2uwTfAD?xzz`o#k`;b90ojj{%91}O}kEV}QUauSM z?8-eyG4aX^uN~8*ErA^heLXBvN%za6Cvj$}**n+iIUoArrl9qb7t&aGtX%02v`126 z4FxV!y;M>>&R+6dQ{su~DAc%ia9=5qs^9r0UNQh9uHvYk91R-{OM!Dq?3j)|(|0R+ zt@Z6AJxl3veA)Zz4=LE?4!5ymIjl!(hp(FE6zZ2KnRg^MbX=Zk9kUY8JOQ$`mJY->sl@aF1#xogPgmW zxXuaPmiXNGX!C^F?rbn#}V(k%H05+lZp}@rn&6;-fm(5$@ghOd~HHx84so&6zg%<+cC=ITmap z)XhS1EqXkx>AB?hs6(!<>Rlgqww@>)`7B1!?oP};xvsoYRjV_E)|*P1yT4q(9HD1@5{J|8%LvNx3QF>?StW>q$*{1wtfN1EbkD$)(Av}*c|)<6OHQVY1XkT9@MXXtvx*|xGqa(}8ow?@77f>X9K;x1*JPk(3U4ORufjDgn^H4;?U&qVi_Ea;ELIFaAuac(^5FnX~Ddmz+ zdf4O>R0f+-YBSvIvwn! zk9g?V+t3l|*J+nIE!uR3yqX-Gv`LSH2eF^RndaQ#B&_qXTwA7BqazFF;T2?DR!cwU zGBx)F!w^S4OAkfRU8n^xGLuP5Zg53+OaR*Mip!vw5WXGG(|wTgpOT*PCi_o?+016OCkS0zm)u%EW4{PsYQcbY~JJLaKv zw-c}s`6>llDU{#u-&Faz2wGRC)y}>cz7#;<=63ngN_A z_u1D2@v3GgJ2WiLj-J>8!Aj?0CUtf$1K!WUj)t#4wkB$K@SBe_X5%$?*CXtg-$raZ zW)}@ZV{3_@`OjIZ)x-L}yc99)3!TQ1hnkMan4Jwo;RV87Z+7aPA!!1Z7@Dftd5500 z;x>=ls?w615N)8AokO;HQ9Z@p3unBo1F_WNnM5v-pkG~{$eGxSkf^=z3TMjOZ|T<% zZlu_YL{^H07*l$y!pzj*v^X=qYmZBsDJ zlq*8@_g$cc1r*in|2ve>k-X?dgDc`?k#?Zkw@E0=i7?ErpHG}U?Rbnzjs@bVTe3r^ z;IRtE6FZ2lP^I4MgLfF%b~QPAyzC)x#X*v?;gV|IjM-SzgiAdrCBNQvrFW_!VfJ`a z$}yCPV<>JCV*)88XY3MjgQ~@KK4#U=B^lFtDl@Q&lbIu%xP`#N^JKUp_iFo0U1@i) z=P;Bwio! zH8c2YO8zvclCxMlJyUfF$vrG&T-03>0uI@0s!U$q-)V}wu1y8SZ1k!u zN|t3U|AeA?mHfF|Hr!&eTBwVFCHGqfqHvDnyoLl;ukNDOX#Sn#dLc(Z*!1O8!aL7X z4MQil>=I+hO(r46#L1*A>gWCaR>rZwM(h3WjzPLJM&>2SJ~ExuqoRC6@Wmd76PJKG zc>|ztE8=nQahw99$SIXZOX3sPu#Iu^w$Ef+`i8eR64;Cq!ZB_)DHW-6$RfkjEl(&) zA_Qi8lUke=15d=JD83bVZwnvxpbo*ut_{`Q(ABR&?_Xr7%wSUeyDP5xB&E|%`5T^T zJC1y)Qs=L{VFSQ)&we9y4k7qj8|F2)k(^izCu1d&@B&WILyaD3XnCG>`xzl!=T3U< zP(-EYt8|06rlWaHpJYi2nCVZ>aKXjqNVWSz}RhZP2CP7rsFeXy?B-&? za?i7}|CWiXRVi~#it1RO#R`;yWlB+o1u|+&qFzbau+W%Y)#W^_W?GRBO2W+kkdjQK zSO1*Nye!uojc-7BXmv=!aE^rU8FZUxOo95Oo+JA$dk8r#z7QqCVjA0K3 z6Ga*#0(!s9D~LeiT*P9#aJRhw=Q$VK3Z{=x8S`Va%?7|wV%8@~woWDzn+#+gQ?CHl z3I|eJyqES|8g6RwHM$!^1{rG`0ccpOQpK9Dx0lmVhbF%}C9o2@wI4ejuZJUmZmVP> zr*~X9=;_Fs9R@oN_R_2m391Hy$ytS+>TlmkPOfv+5emeO*_o(Shb$57DT9KpD_zP8 zRXb2eLL#~XFX#3EqD9!{ah+pw-LB;`dEUX1aQOsQSVCIVC(GXWeZg$8J$S;)Cww*yLV0O zHKHL_Zq;^!hzdI)lDqiK&&ml3gHGLwNC-{Q1GrB$%ujZZnMd%Mmc9s;0}+8=d0I=) zkVgHa!@?JqQ+G9B4euKDDKxs?@F*s6yV3VsXqnLE}+!RUVJHPu5t$Yy);;#;O z)&Fe*UA3HFo-l4j0iSypo!#L;{_@Z3a8Md;{hT}?SI7zY1*vrZgALr=gYT>VKoi-= zmGIhcqZ(d35!&Up1f%O4VE1rDJ#&Aw|3y#31 zqplA0xqoFVlWK9HOW*TVCSX94Qm77{lry9C%94t$SR!&aM|y$-(w zhMT_bXC`SqpFAHA0I;22y&@Nwc58`+yQ^xuS^Pijy?H#;Z5uXzM^On$kz}jnmWCo^ ztR-8vqLRI`XV1QqqEhzkOSVzj8T&FyB|BpoW*B50V{C)5&3LcTUC;e&|NVY{yzl4v zr_aqj=6ijw<-E@2IF9p~$9E2~hPa(gBE}}bDjYpmYd0i{^`@|_b^GY&4<3k?^>KIX z$vExPo=}2a1ekce*0E1P18{@ulaF;d_0-d5yK6nPiQ7Gm($giPKcH9ph_X7soI`#( z#kTjzy!{vGD7z5-kFPb-s=EU&{3y!EXOJU5ff*T{Q0FE22GfQc!g;55{^&4GG8;lF z>C}3UIR+N)PW5f-)Yv&0%rN>*@q6t=HPYWjKQ%Da!FSJx)4}LgL)~`@Zcl)iWNp4` zJEP5&OY-zh-h!OlmK{e5n-ifEZl2CMPFZW-Kp+>{xfXJ!HgTs{OQ~ zv_`&(?5#6O#_+W2xa-bMbzVfx<}i;cHMOFUib|d62vF55Ihef`{DPrVwkZ#vU!GjP zw(@eQv?aEvA<~^0%lbqTzED~I$9gT;3CXcq;ea{TU98?c-%zVgnjzE zbCR|CB+{Za=L0c+N^H(pUvnVe#D+vvNDhDU_E4xxsxz!TS*)!h5DO~PwRT@H6O(h^ zD2y{{qS`7gl6_0YOrZSkx+1f@@Y}rpOFZo<=hFRlf@H{(XC3Zxi1|lb=e~emq&Nf_ z-OY=x%(q&Mw+jP)jeD58*}lnj}rzn6s3D<;4gibKB-7lxkC~_RQn~?+ZIqZm`G!-ef!k8s7W!|OJuRi$$ZDnpMP zy8o=G*;RpB#R9oHl|BWxr8aWriY@(9S`$xU^J+R2dh>y@q3Uk+6tke02{zL2wDqGw z&c(KJ6ulugehWcaiJ|7)c1ku$?~_$N1{Vn7ch7Pej0!D=AMtPFMQWXKGkFgwD}U|| zAHSgccqZ&n`rz07$e!4Q2x^Ja7?ddAA$)cpP;9czRU)7NBdU=Dh05c33eY&cf~n&` zusO#LyA#S-8FpOlkd2`4)h;DOT#?1Bgx#QZc+^){hj>(9hVUaAl0v9&DDnRIHZuJ1 z8nn`GvzNP8A&3TccWIN4&u>ElS}@)Zc=YBSN#&)sC$;Qt=ytU7yp^K(h#02VCDBe7b_f7kVS zh9bQ>0UZ>VM`2`|9;6UX%DaM!y1>04xW8vF@k61BpOwdf+_WS`sQh~DFsAg)Yi@C! z8hLCB!&75xya;Z&)pqea<8)KDbui-j6S&*w8QYduANxfZ6YG749^sHBZ`xC3Wz8EZ zycY&H4^`K0lKF^a)~$zVF<9Ih2y2$QDkcn)4T9H~jt&>RUn?qSstEWVk9Y@nAQi_# z0!r-b3)+|_$42FRcNV@$wmu`5llueKYV61JF1?{vZX(sFmEzO??li@|2yz{M=yE-g zt$V3XC+Bg663HyW?Q$w7Hg%z4Cly-ZIC{^Z(w}!^P5-3fU$0zSL#=j_+Ty-1A{?VZ zHT}ZO35ffkT($v7ec2D?H*Y;bOTx~nczFy)qDBXHF97}5S9N_w@u>j;c}y$j5pcH^ zw{f(1o;hLeZs{VzZ(6ai%>Gc6T|><*pEOhQ2EK@^^wFjcakri4$>(K(@72!_t49$< zUffsyg7`4-*`&rissLwFcSBH3@JU0!WvW~F2aPkef>|f^?`1M6b*L{j)jC{II@GV$!0>S5-)tX`;lT>swdHxW=A64A-Z^Lkj-GEq5?`Vqw7P`a_y5 z<$4hSD)$^5o{cex@Sg3~v+Kak+LlaO9|SkY%j2q&bqj|RFB9;=(>t?7!6#~OPtn(N zaqdBhc#jH$**yMIjkh2*erFA(wYk#P^;E_3_q!>K1@1x6iqG#^37hGXSU17_6FFwt~UkRmEfW{Pq4LA}d9LVyre-FB?i!g9ed;CM(xlq9)*~ zd6$^?^6Nnw)WpjWlMk;wba0(f-*;j^QtpWIZ+zYnfcJW#097=~fA$AW=r!us!xgqJ zbr&=I%+8BKz)WRJKi~z#25F1Ul|=&Efy0yWRL^YrPO^*@i=T1vgCO&vblFgPB(Nzd z=3?ohR&8-3`H}*j=D%+@F!Vq75PGyaB{aZpp5H_4Y+VW}1wDzW?k+X0w}cuC4063> z&1AD>7i`UfqJg?5#?2hu-}`9_Dr?#Pek_VZk-GEt(!^Kaf%NaeY-ZK3hY#{PC4jBQ zMT2YF-})C?@e&n@WlH<+;5P8G3l59|&lEl%-os5D@(HWHME%a7EMD-cH~s@8>90L^ z@Lc{+@M3Xg0>j=wt{w(Yx#s%6hwN=`@XsDb@BfkMYFxz;&xwoQecbo{+`=cwf>t!^ z^*@519pjDEdXH)O`V*5F)QAn(eOo>%5)G90Z@#_2{`C`w1?BiDiMsweksbnpnV57+ zg0N07-K&vppCU?F?N&H&ijBO#J<->=uf5QXar9_{yEKC_OyJlrbnX;}HtQb-fc{v5 zd_dGZwq8ZJX7)P3PU0$Ery?8g9t3dDrwmkh(b!4An3J{h&gm8Q=D6a1@U?}P zAELpT5yG88Z=#={YP@zh-(W4vZg8pg_{9Y6SI4DDyp@)2-~fz`0o%lkeGU0}zErgz z^lqhFV0aL1QZth=v9V^`iD?SZdg%_)zfPb6@4o%+^Mit;ewFP&U^AO_Eab_dgQsc1 znP_g*{BeBG#)NYOrD7_R272-cn{grm8I#|g?E>&o3R?-A{S!BSo1u1ws(VOa1!JQ5 z!*puS5@YRN{z1cWlsB^Tk7Id1haS0aA7fU)Eu*S z#p*>0nef-{7ykCWAP=Cn9}Zgu_pSw|f~(Z3OFlX&De0VEr^Vil`qw?QO9RO5;{5Ac z6{D8zsoU*l4VJD|SA$%u4)*?9v0rEFuLb;rM&(AJ!q$u8Qr%yp`fU^U3?rBTCvbZ@ zvA}-K?%#*HH%Z|y{s>xSd1#UQU!O4!=4kwk#@U~n=HG^J@H7i}#M#fSTL1MKpTTZw zQc6(%*TMZ7%+)jC5h~B#rv7a@{`qOoKqfOZ?5BtK&;RG=R5;xLQB=f1A!()m_Kbt< z0O8F3@YJto{xv_pe;j)OvTZyp_uOCo`y2kXKvYLT`16(DDBXYgU?U3)ID=>M=a2vX z(tjBYuMu#ajGo#2*Tp(}MuFGw#{D6||Mm=pDsVUc|Ggm^S(vl&D8yxPb+)L!JG+~? zTd0X#hR+Oh z-QbOmVzSy|x_d{aV$v-2IDP#|hA4`8nu$Dw~zZgY>R` zk}8~|&Mu$rSA6;J#^qdElz0+?Myk|O7S3m`09ZzqlkehuxPOMADW!IMmM?PBj+c^n6_f9+u zvF)J$T~14O?Kq$_1X`<4PlQVgn-6``+F12EKP{Dgzl4FtM`J0Pi-FxAw5qv+2`I2J1wE`FpkJi~x6)Jy zXJI^p@SjQU{G%x^SrL)pTs$Q%H68)i9cplNqsYMsCT^JBoH_XGgq%P?_-6W>&BpCZU4QnP}*M@MPK3$nyt3+ok5^3nH< z>lJ)j1u~D!hjPq@Zf;Sw0k)r`?6Y>87HIuAB4cM2~tf!$q3a^ zw@{NjNlnr*p6`qVtrM$GK85L*rBUf3QA%NP-vOZ5e$KeY z)rJZ)?O6HZz$a_fDPuFd(RpOo;clUk+SaO9XWh$I9_M6c`O_j?P}kD7H2<1ppd)C{ zf(q+zm-E|t83Km_+_65u{5jnUO@q4CfR^FI4!uRDJglvz9=9;f-cyS8SnC;1N0K<& zOO%ItxA8U{0JO1lwH}MhTBUbBFlc;FYD<<&ISY_1Z!OyHdDqI|vgdV59_c%7sfL5b zl7#b4wf|+ zWUNVjn$+xBtdpXmex2vb6u1^r)^Cv-N#hv7pM#0J$e=P%fJLp9)01(TMw|dLk)hR{Sf`qXVh{u zNFWOXB7HSuc9}(+FuTe9){EL{3N;pO+mqv=JU5A)xsWGUBIif8y&MxmkYETO4iSi6 zMAu26*cAo(z@xoRTrj#gld5;l+$&!I#EykZp73qfe_mdgCWFk`s96t|==(P-~p!Wi`fs%s`wS)oM zr-n#!MV#G_3>AnBZ}w@E+GCoY_Ms_ZdO>{$6{h2=wQB%{V~WxH3Odh@T;vdcoc*E4 zs3$qRk_9B9*j1Y`^;5pTm%yC$g$>od(;22IMwQ=b2AFGcvmgb7c=9^N8=o$q%`G*P z@^nA)-r1!I(0(%pTt7Z8a+D2P*4g+~SouV}8-8F2V4H~;gHY^e@>w7k5(hSeNu03p zd7uVTzD66yCTj{PNOfeW{l=C9Xf4Q9>b-V~$}O8diMOYJ4vI%ghL2S6NB-0SX-~b* zWXaKa+j9GH6(>0RynhsghKv!2e>K|Y5A-@-h7aN8g;mFRy7WkBC<*C7dJf{e{DSPh1u%5~{-K_;C zM}5m0WyA_2M*~Lf(zjO)-x$zL-JaFRz_*6``zHN)G{SXJ!X7}R} zbD6$aHp-ZpYBeW$0xxz$DrMb+QUC#Pe)`zCxU0bYkDmxvZ9U(wt zavw#oG@gQn744MBE5$}UrR5bXAZML&RFd$(0Brh&;mzBi8FqeZ9jNpIIj>Jeaom44 z0_E5=`$O5xr19X8s4iJlZ-d~HNH6^o%UNt>nru3;YyDgKx?7QnY_>b0t=`Mw3s1Ke zz)i~#HAFa1*G?^zD~47eKlal)Abn)J*JfV<9V|g@d9Ld6c3o{*L66~{LCokUP=w7nf$emQ}`h>9S zeiv{4`I!jd8N+u2s93HmcAhchqt{10i|#KgCgG7&z#($DIh&GV$sx9P{=)r&?$duM zkxQ#pq-RydOPqQ=@X_r#W_W)|nc~yQRT?AmgAD+85go;H&e$*V^voo?6AW?>DXlfx z5MCIhhn;0)JT?4hgzWnJ6n}5)1P<=*Iig+|n@xJgY7+%)L@`b4kIgq1y_Dc)k@HmPS z`!Y)+!E+0jCh9&<53rf2m8rI&aGvsIrH#n6U_%%qi}&iR10v@nWhZ~p3_vAD;+}Z{ zIjyPX=Gx~xpP_4b6sAwRDDBsKgO?97XMds3OGcSW+vAN6bVZ#FwYUEWxn#s(!EYbYoOmdE6!4H-aVrV+?tZG2`LgA-VEL4J zRFSr7sv@IeG63-yN&=^Y3pIdn=tlSP_+1&O80`1*c1)6V6a-D^zZN_)UDH(cye0kc zvjFUXb5G>j&c1?%%ScJzo{)Ic1rd~yuB5rMH!4f$F#+^`MSEkc=@X&WBleOv&_#x9 z086`H{Bf**WWhb4ei?N9!kzD!H@n^d9IR)`Wr;nywmi@ur5e7K(byp=c~D2Gyn%}DDYfRo6sdU z*Xa}~u}ptf;o=kWznz*%R_a#h)s{~#B9k4S>*^eC1N>bJo@sIrAZI0OI&FqQ`lE#O zv{=L0F5~HGad-0uzI%+~^o~W4`NV?Y4SZF=C!PO_r(KNZAN4*;aS{c*f?dizDx^SS zzaOz<+(dmA;PYC*IX5#H(1LIG+`6x5#3i@un<=yLfK$3qo{pLOK|`zebNi_*$D?qi zRzQv@X@MxcO4`CV)vfRwgw+8pFKlv+^8~iibZ9sr!POKVs|=tFo*22+Thcv}&%B7! zVVM18$u5Hoc0C%Xvx11=tSOtpammv{JLz2p98e@5L&-oGo!pf4*N3mWP zVEWvsQ>89a{{RA?{0>n6NpZ5WZc&o6^ct6%La3djOI;%XP*T~`gx zn=NANE6#nDCBc{0GZ+u9j+&A^c9U!iWA5hN!&tb@8QFZE6R2}E_S+_WjvT1kVl2y4 z3g+3^rZuP}E{vo{g5J@p3&U24NDo8{pam|-=FNr~Da z1I6}4xZ~(IxgzUZ`hZzV%>^=1%iLp5583h>nJ*Jf8S6%eA??1hJerWBf>M z5y~ke)Fx`UYPrCjoq}g%&aQRyK})B{#(qX30_2N!65@utt?skuZe&MK|Q{q8}ef>;R+H6e;GCji}R7|@zK^JTl-kk=nz3u zpIesxAOR|!__aCWdU2UXn~m`TkCA=}zA(@6a}S%ueaafjt>Q}v-PMfWl{aqyJ=W{KQUdQ;&3VmjZqYN=uT_8c6Vv1rnH3A;%rx#w61Uat_ANKoZ1c2< zED%o{c^Bjc;^R6;gXi%Q&iLnuxiZ`5hws)gZ=3qqzc6ywFNo~l>9RM-1s9nI;mO`9 zTyk7i@^)Vih!I8-9k0;K78}AQK)bDCNQc`nqd}fQdg{ng+@=<-E$YP$^}Hio(7}RE zpNOI>2%P);h+^j76)*-vyXO^oHpq_mhWEi)zkmW%GWu1#ck0Bfl=TN@!tgD_@v37S zz@r_LC@Z1_N*KsmF}8^`RF%qB8k9e7T^d)c_uFW?7tmNJWL?m#(9zs&U-za%_3Nv< z_D=bbQu&=r-O(6?jIVDV&RnHSiN3r$?sF7$KeFh@!f>_j1-*tFrIv0yanC9alIW2& z^5V&W+GZG{Y8JWhp6^{Ttt=-1eSiL5rzN z?YfCdxUWi@XJ&0Gw~S{X(4u%-`I?1#rh>j4ec`5X8b4888=DAlXj*5of>r^O#2&AL zcLrB@8~lYm*D+z+z*gzoE9Y^#D`th}g$nZ*mYE;}G+~qagK)t-z2&H&Ory zRa_>bybDUj|3L8$%inP6b{iJuLRsnhDKqli+-|M2PGlkQ%J@3rv$Ky&ba}Bhv5W0N zfvAO|bL9#eG~o8S*8;@zFRt3;PF=cY`cA?~!9;iVjv-8u76;Ndt)?(6oN24rxUS0Q zzP31Xf~}@~gO~eI3B?R>@1})b2Az5eF~nLuCds)rsw0;G4o#il{Wzv4nrpnkTs&dG zNymo=CCUX&#V!Hm%|y1&bWB_YTFg6}uU^5s?|l?3cllM61dy3a8Y-%=T=fQHap2w( z8uH7@J|2#~{W>nwscB^@%ue%~Bp2>(uq6a2)@4fyM^!9$5$Tu4gW!hR!MG2)xh+8a z-F)s)C&LHl*?~hMmwzy)HU)d_mRche(bqBk%!_>=f(ci9xZ3I;OR+=nnTnpa7b}^l zq?+oCjAt2e;JDRorBqJsK*9ma2;Fy;DORf2Ez3B< z{ei6Api`~1{q55@MK6fbWuNuawOZK&L+;lGBlr|U5(fr?{IAsfGELAk2Vq&IsS8w zZW5STbv5nl7)4#C`<_*4gByg*wO~_kQBXTi7jTt>+lT^)bWcbl9rg59m2Z6di(;@| zR%X4|H_k}5`K-nFFIgtBFqV*0zh$$lcBM-94oIvzE`H!GiD3a!@X zt6>#p)|*Ewi>!-)7=w~3{j;b6VrXeW2g;Oeih2Y0->3r=L<3tL+dDOEQw~0iKis+{ zjiG=H6TW_4dvp@(rZ?7Mw~?&lqd;sL^;W|}=R%Jk*<06_{73PDR|f!ZNk}Ycl~6f4 zb)X=a9%0uY=$eBuuK6@2z4&@%D`-h#P(G6{+ui#8l*s38%fTAAWWk9ZwEtDEhSc57 z3-t;H_6_z23LVzjJaP3$5FGbRWGy|w9ivclB1bITeH2KMw4=0y&;_my4KELh)Q&H{76C77K$acZ?2sI_ z>^i%XSKL$BY+9NHk`6GH4f@Z87hPgN$Avgf$GRbhe8*C(uh>#~|580}aUS5Zk$ukq zsmI-s;%YilEC0~vA!)JFhs3?|jWeid#8M)X$??SE`XY07lc8U=+s;E7e(99Md;G;m zhrU30!`GIFN1%fWTcGh}MGo9`Th-#yFk*bXOK`!Hf&%ihuLeRXTlf!Dp|m|MYC#6XW-@<^a9wdM;Vi$YoPFc5 zlM;@UCK&;}GFGr~y|pvKkxaYaGNl_Dj;U^opWKUmsk`txwFaLWt#jpWlR0BtTO5S- z$*Oe_ykfdP@D}2gK(vx@)fi3h?RG&mPF35)(18Ms55y60tlHR3ec5!1gbf1oLeLo| z?@ri>6`c#IWqDjs|FJd>s+;T2Ka--{y`H)7n0YiHfMNUmXZx(8Ew^6K^ZQHkTfk%F zBE#)D-`}3Mf1q5E%;eh|&-@Gva+*=sf=yP~RBE}6Gd_OEDmafj(2TC#LYLcWhK$^@}3{ae7 z$NYLOv7B~l=?OQ*u_=dL6~EqN5Qy0Lp$^l25mfWa=9&z4wfUH}V1Dny#`wd$iWP^^ zhOELzoB=oHvK>){#y}y6$IxAI`^?ML6myTU?aLs4N4(~%D=naNY5rb66l-u^OFd}T z#R^;Jbsg%w$~h+i2CqXO20Vqh?F9y3A_VE2B{kd7YY1y|1NfDzORT?O0N?W!+G#)% zm|RqmJOfrCycGO zXbn;+OHG&8ZS$ZDbi38EAez?IzP$A9T0C{l25Q`-F)f+lwrzwZP}SQZc3EQY)(kRH z(0vXj*?NRQZCf?`Erh?U*YejH#X>d1IcrU2y@fBP3_St$6fH(nA-TtwQSw%d{^-#; zPQ~4^fGilX$)`fQi?7~d;UqlL{a76pAJhW*b>EQ`g5b7acjn9L>m6UD+S=K=%0LGrMl2dmeQmxbuUh=qDFeP7zXvnBE zY74dDxGcG0cmyv)z7#OlNa5?cu=P5#>q1Q-(|AGZS>uSp$|qA7$`H10eR4%OqAK9C zQ960ZD09hID`uk&ez4_h{ENOj1z$(bt82~#qSfAUvRzCN0wM$&l7N(K$0KSUX#*^^ zy)YFW4QZN&{uo#SaAQ+q9A5QGC@sWAn(x;e?rqpaR$x+Hvy|W1GuX)1EBwJF)3Zur zQ@~Nc54{q}bR-CO@}ki!w+cbKx&6qk5OqT}+e8+!0c<^F0h-Zc!Ft~J^a>2znzPG0Q{t`$qs9FN zJ~{RlaO<99xB-G-(b?psPG48DbrVQL*bgzWU&ZU$094VvS0{uX=MN0oK3U@{XIcfx z5CY)>;3q-4%csg~7AZYddA{j7gfhZ>B}@W0-vZM zU*zzw`iloS$d!POPj;NOC=YWdY#I7*;j9)n8w-kx&>x7!4;AbDFM?RxNTXz#Jw1Scw}tTq(B`G#Hs5U1Ak5dY(hPDPj9DoET%&zr#twzACi(PX# zp#$FnV~$YpTd-!T@%L_Xt!CPf+(+;K8(5bJKuw+b!+Z`iMSQ7I#<*k_`;mM{tuu_c z=P?EX2BYszThHS0C$;4-c5AuCCEXM13E9?Akuo^jBVWbV0K$LT`;ZJ4`;D>S9upix zr54E8;y0v!xL}?k=8^^dSUqcSftRy_*Fy^33XB6!+ep{5gVI!?*V(ri!+DVHV6ZX% zWE$UtX_Q2kPWhS0s!3wXU)`i0aR>QdkV)R^Uy0d;Em|8%k$OG9xSnF9Y3(4wAMT{H z4Y%%o7p1q-j(8JbWP1c(`NhbKAwclZuD8R^&0eIQ{C?C7i{#JI;*CZ0OE1n5Ur8K2 zdBs{m6>l?Q3di?Y_yCc?+J%~C*GbYFEoH7u@$eTlLHd_0U&pB&B3Br1YL)3 zw~HiYB25q!Nld0RnwTV)XEVeCq5Z*Q^s;*OG}3T-U5S@)&ht0ukV?o)m8w+4&Lumt zB_ut4leM5?$cS%(S47t@)YvK+PCm90>7fh2g@RT$L(!}hxA>$7n>{K#5hpLb*RJBlX}}5hU_9n8>y)?{?^5qTy2rwsErQ}aik>d z+U(6!a1R`^U+Ceqy|wacus`GC8mCJl2mEx(fWLbe)PZM9COh>Go*0P`Q`%jdBgV&V zFEP($?Wp6UdjakoQ%ZU zH-Va=y?gAZ?|c#<{LXEQcT1i^4~A012Ka#h@h_y?f~HR(a}pi4nPwWUJ&StPzph z)RH{e4L|lBfZe$#?6~C=ZU=-kfeI#$u#z29n#?>vo6}O4c<4%I*O@K5EWi7+kS?wbd!6lw|B2he3r+ z@8bFn+{MxlhE!;{m2sT|Em8j#-vN#k&-hMEe63BChiwPirC&tb@0Sd_8<_m0$I)1-=PK7v5Sj6&C0Z6^3*IJq=pWgd# zfKIDzKvhs7*W0@hP_Pr2!*uJ+#GX>$|L5OQ-3)M)he_#qfBDNn=Z+4RnB^XNrMUgP z`&P}Z3>ui&k6vb}gaKY}`~U&=c4-S7A)B_hLKaYNEkn68hX$)}{7Pk0%S|t+l#+eV z^?yA=A@k6)%INT~YdugXTkB~cLjD^dV5SaG-GeozDUe?SinIyhSHCwH*Pe*|bj|KP zqzK@*{Er$IU-afoR18}hDutg!uukRWZIT{v+guk9fG5-1$b+$4A4@f2Eq1>-O zj(kUBpbPXyJ4)>_OaRTnqo%1Tn=RDw&nP#|j#)*dx!Lbwf;iR?09;EJMfCP|&aavK z^@3nTU*3Qb4G$mI+W+^*|9VgGJ5@Wd|GzXKjJSS{k%j1oT?q<0H4n+AzAl#X5wEC>i>ZDz~cOG68<*{Kd;{Z?S%ih z6P_KUHhuuu(0fA__MrguD@^rdh5&ZYc;*?aR^%y~d4`spZ{+Prf?Y->XC6%o56DyL&BP)^?khBm4xf zM?r?vRQ01F{5|LScUXp6(AO_qc*~}K*g|c^R{Kx2uzCH~d+yiYN6s6fI(N4>O=j8? z0RSCay^!i7QbhFJSxA}kiStV9ah#SN-SqvNEv~u({9=(e+wEmHe)%ms`{8=4i=36; zd(Q4|7HH3Yo*EL4(cuq68bUJy=l|x*HS$yWXfKDY)6V~T$FYN_p{_STF8I$}EhSW_ z`vBf7Eb{?RS7TQwCvpZ)EC;$ zT6X(bA<)6kuG?PlVUgWk#mV0+GSSAkG;I7Ju8ajU#w>9>vHt6vXe%gA0o+pa!K|_* zUf{7mjTY#7yIfNRVw)Dz0Q>m&fe#E6aVt|r>MXK3S(%xW1BQ#2g2lEz6M?e+pP<=E z7|JD^<=30Xt4ul| z|EiiYAlr;DVs@v+Z4ze^K1Hw8Vxk{gw#I%~`NCRH7&rB=Mu2?T#SdCvoqsNw>b)S- z07KR+4V4C8FAY`0`^Z_<=MObz+7G@+mUN5>pbQZz zPKA9yG8QRK4v#uU2es?@!LkWDJnR(WTZM{ZP5l!^fKCh1unUm&e{b}IH1pIt_V3C{Dcs$%Uz0j5H{BsMOAV^fg)IWwb8rj@l}5ceOVn`*Ht;Lw+Cs zzBB)zVF%%q-#$_A`nvKk9_Pe- z^itN8G)Tuy>FGkLN4V5{#kWQJ$)q)h=%l94+oHOa=oDF>djK$uFm^yr@_CWh^k~xd zh5hZVd5<0f0gLLN^inEDL90Qu^q>t!y=B*xg+}Rsof0L$5D)?g?6)AMOyt)5It5F1 zcufojIXwZ3Pfwp)BJ4rf%0m29oDJG~wC>1|41lbf{p&o2yNceRT(EwUL)09;-8?vy zKZpoG%wsCM94;7uY*+f1xzNvmtI`4t(c~(wmzu1t9uHZG=2N62?z`+Sp1md`Plh+{ z+}Lm=${@dsqm0bxgg^32JuzaLFTGwU?LMCX6e~t5KoJ#z>u^gn-R?-8%Psx}Fk6V~kWhvKM7 z#*FK`CPq$)+BfiXZsm{PK5bxk;^hDII%J}9E?L`MnllMr?8db#$2!Fls+SO@sa9O`Q12L=^q@w9tL3awg z-K*s)ml>^1kV>52di3?x0Y7avGl8-*7v>VYcH!6tfj&|gK=r*L`CPXzT0FE?A2Yy! zSg1kj0O*z{oyEYvP?lc4otSkeJdrHBMr&t#HDQ=R6#Z`7IR zQ-sRUp$B?;;IbIppa0fvKno-+N=?nB+d8x8P{1g=j;239+??vS>b(4eA80+!oLs!- zYB*Gi!jSk*iP9%4zdp&aDrJosC@@SUh1H+ag=t)NfAi{9G(erZj?^(;If5c-eriqk zWb8Ku5LVLe%*aURsJo#(hwPR;Mv(8Da@m=ZQQ`qSBl)F5aVZ+#tyN>L&?1L#UjtKwE7avlJ$*J6#T+`mzX{8%nL%T-SdR00Iuem6 z7EoLdbCqWOX+Q<9@fI7YJUU{25<^6@U21&zB$hCXA$e`immOL^22|dJ0eZWP2>Sa9 z09jjy8Cz7L1r4&~T6ze{@0!u>sOkG|aZ$5xB!_n&Ew0IJPKx;q=cX?}xTKW>#tE`f z>sJOO$Dt!GUB;r1z8GRVuFcGJlBv+yF$W=??BZ0EX`@t3gUIF$qSn_;S0*8Q=+2^0ri zg7RV}Wldkl6w~~U`gnhUfRIHXLhj+!AoxN?y$ROSERw7S^S`<=mnXQ=r`uJd$1@nC zA8>Ez{cT;zI%=Q8Nzj&6vOc3I4v-|0(yp7?Yg@@GjL~OgAISzSp&xmFx)HF^%Pa>J zy5an4R{|-^&54;jy!E;dc3qdYe}$dVU2F`#NW?6@{FyRwx?p%_2Q;Ko zeSUZuPo5%oAHNXkUPu_S+ExXnZaeKW5mI?T{DeAiqm>`IEP_FrN)-tjPY*qG2Y7RN&tG{%5jiU zJ^rHpY+Qdgw!xweJ8SZ}OTH^&S^e_Jte@$-Ht*AnZ$Pgmhqegocf%ia^+*h^WO8{*!|vu% z6KTX`2sDZi5~*K(wX@XZT3hSSS7h2Q-3tI{W&uM}7x5K99eL;&pTU;KXtK>A=2W^P zo;`K(ub5ihhBymt19u0_WaqaFWqsC@iZ^_EtiZO5$j7m%!fPs z3XKISB>{VjOT%-dZOEKwO(4f(`1YuOB$8PlfXv}*QEp~B2au<-Jq_Y+5vs z183?lk)2>X$P~d#k^b`~CZcY0lI~^e$`|gn2fRDy7?D+BZ>Wy&@46a#cyku4sveLL z=%M{K>xKZRm}+$SVln=Tb-JiU^r$=A&y%mJ6_g%eBAlz0>MoM;xIg)v*|#GLPabuq z$auBx1k@Gf>xCS_O~n~rI6p0Tof{8qwK)Izv|{_Gerh|DG1`eD>xc@>_L|SYD%Ahn zx8=G;2G=agG0lLe>C}cz%Or?J1|En>TVriyaSj7%?dUB1+(wc$H2hnKB+992zT8%? zDyZ5gFpz_bIolQY#N9rOtwUp|l;RIr^&2tv7_kbE(O;FSbQ~RUkazy^?9NBmux$%6 z=yj}NPeA)`dyh)r&G(GaNI5xDyA8bL!A?f>eV-tpnVM6uJwKw~mS8P-G?j+_)sZV; zxXkVNcON}GIG;7IQv^R?iJQiMF#N-RX#xIx>c#nE_ZV%+i61D#SDXL?6^AUhWv)eI zvZew#ieZnw&Pwi_+9|wWaz(l8^oW|g_3W7F-8}3(rTq>{;4W`H2(UPx;-$RT=N%|0 zO)K(wu4nJP*U%1*iH!;d^6(p>h?3p*d-NBP+^C;+(D?vz)K9XX=`hY?+%iKe)!Thc zRXn1g!F^2b;?L{#=pg_uV&7)8zF%hf_A0%S)~p+DH@+(!@nyuAUIOq1Ej^?@Si!F( zFb(JS0@9Klz1DhKFK9ysRajlcje1LByc{_RO)r!rYpYW~LrhEd`rC;CDj{0jq+%M}?X0J}vAtm4pR;cz zGRr{-Wy}8EJtmjXoaa+;*J*7QyKdD7l~%43eHP!uf#yJvSqm@u1W%H@=ThXbtL}+f zZE(|egkolygM#RRNr)J^3#9L3+Y`lV+e$h-<*z(HoE>&t(2j2HB}L6{tR!V0j~h7- zf}nS>@AC*Uk_Kb7Nl0 zr9^W9WWpKX&b@NZp;8aa*H2;AunOKlr9IP)&TS4`ANGNBmh+G9!q*dB&*_j@oikX4 z&4S91wx#jK!7_ZHG~l5#dh<7h)I*v=-V|f%wgF<}kxftL-EO}}h8%OWu^v2&p|D#) za7y2ufG4NyR^rq^H?i|ypwu-Enmkr@=cfDY)e{D+QYnJ>ZNwJ0X}ChOY*8w~+@;n% zI_`cCcngF8otOc%gPRT7N+FqHpKzhGJcmlt%~tD}yw8+90fNWK8aE#x2V~!U9`Mbq zzExT~`15S>D1%#t90Vujr~T#!@?DdflvEv+D~8!-)cVPeqRY5lgdT=zwt zc0~{=XMps2NnUwFc?e!@GfiY^=dTk-4Q6kcfCz47di^!KY+~eUjTT}_g$iY-LX~2>BRd=*{HDs z*EEn+xupk7 z>Cm8McNEYhZ4w>J9uEGi^9tlMF+~=3B zlP9j4IM@ZjJ^bFB>L;kqW63FAa8VO>w&Giw^e02h81tDGSxa||?4 zNs`zz+AVy=&J%26a!uT%zUPOhB!l@)C#c>!HD15vnd|et&dWu;C7Kd^w3d_A`I}*> zhT`A-9AKQsL{IC^)C245U?5$ab1r)!ypj} zcX**y2!&9F?hJS#Dg8)j4sPiDrOWOn%(& zP)^Goau{9S@B>t@+pURfIrmF8{Qh8C`xK*qu9p587iHfI0!g9P5XOM2_;h|-%MV64 zIuEJKR&m0Hg3X!#hrO>1i?Zw5R#1@}2}MaM5fBhiQepr_0Z9dwF6nL%hHykdrCS;S zL6q(WMM}D3NQq$>hEZY=hWPe)1K#mG$MOFBemwtR>e|;{d+%$lbDis4A=Lp_^{U>? zwK+#uCq57WWw29GgWDa9bq4k3?`+|6x01i=DCB@hj9r3pxr}_HuEEyC?Asq1_`VWT z)r*qaKFU)hNHK3;EZkmkhrkfyNQiL#-T<84IQ?ERc_W?5?Ij5xWdiF*;-9{Mei5id z8@gE4TWZ}R$y_p~9LN8VPRO&tf5>y^EUWC-1bf;2B6O!nKr4_nK)9>u-SF9|@X#N$ zDPS7uAGdyQdYfG(s7?e%WI0|DM><%0v#RXwhfOaS!u-!O{}=wV3Vif%k#h4hStbGf zI+wqz)k5CZ3T2cgiQK5e)IsNR%O}61I&)a8K|#{Q^stV`wFk^-hL;>8s+y&b(uR~= z*zHr78Cdko!O^%w!}PwUl&mz^-B)jAa`*Zc7LIWpA{%<|C+wL8&bj^_-AxtuhXu|R z^bi3M7Qv*r*}|~W*8}m+b{N-5!5-Zbi?}yTa+T`Hz9!{+7oC|U%5M^wfjHIt8K!e3 z*|N`~K(j6XqJQ=Ev*v&W%>MAh?bw!{#%9F+zKGrU4$ogE3bzi?@5vI^$~mL5H+`M8?C90ZE`5K&AnN>(X|5Cnil?LTXlNHZj)vw6N>u^q9^9!`hW1&!*URj1UG0|Ie?u-N zFb;E1^gFSFA)P4=9%{)msLuleHWLE_`nHnGHFHBD1LTnH84k(FO&Lm{9$y>FbmEv<5oQ*#ix#o6~Ay8Y0RaSUiOSU>f zSY0hA(ZgV~+{2(!6CadLQy055amOmjfzYp9_cF-?H&_H&sa5lW=`)5Tv zq8BZUD5HzjRko!qmDuE=?Ax!tVX0lUS+%FiEs2h2Q9i>y1-#YGU7v7-PzljB-}RrK zzU$rz{=J@d=G)%_Rh8qSUw5TVX>wNa7fu4V!tmd2MK)9()!#?1xh)`%o}q_701^0V z6Lm@)Ia}ofJ2<7#My`k?_jZ7bk7FlkN#g*~v46MC#xOOkMb%mL3_|J?pmh!(;0-Wn!jZ7Drioux6jVEr?)(DNLIyz z!9AK^v}#gYloQJ0Q=Q7Eu5&%QYW`K-WJ4$RJPBQe)%OqT)BB+TVkza#MW?>dokyKt z@`i0b`2lJoz0+ltm0yKia2syuqnez35x?n^&GaEjMALoDlF!V0|AN&Ajr=^c2by90 zKAfCx-9H<;YnM|^#i=&39+7MaeF&xZAVm#SPB1Wifj7sI@4QPXmgJmV6Kz@e4LCDz z;T^CxpFiOfXDAksj9O z5Lt`t(HOsZs+N9(BLOHbZ;+(BLUj?7fNz7GS=iAvQk1(_5g=RFcLL5Q99; zhQX7v=gz- z=xXfNg+VEwEY+anWaTH%n7rq>qIoSPyHK|kqkFf|VX?=k@<;R6mA4xCdxJw3ZW_PKqpv-nN^53y2g9ip*qj#l)-zNO#{2ll1n)F$xr@%NS;pW3IbM;=wV- z6w}`)QY(;Hl$(^$>Z$+{p3aBapFfN}Duz@!F-jJKd^gz(1 z4&bY9&#^xl<)lv7jnn9vgSOR0Mh2n!jR2)qvd_`OBAT_i@B6BN!qC3WFRwAq83!aE zPUUGJmi;UHq!)vY;?=Kl7?~F@QEFM}gg;&ns*`}5I`s#e?shqH@-0%M=@yqjuMTctY=z|GYL zFoG>(Z#o#PgzRE=WZk|_;p6C9eI9tG!Lctw9{J+IoJw8Lx{5C&N99NvcuG=~w{OKS zfBx6w^8U#ZmzEC?QS+c@jZe#9d>;@~4+8S+Vozs49%=I!WL^P(7(X3dkEng-KZJy{nJrcG4xEtRaKzfnnEPjJD&J*u@6M`=4> zm`p$jK51zDyl;#vkT}!XAi5oEIJn(ScMX+syv%=8`;u|@~MOhI1c%6S_Kg7gs~hC}DBRi;;uLi$X5}q@*IyUZif0WACr*ukb0LDwtmMq(Bs*m!zld>Pw-M|2|0YBp@}86_8n;B&9=W+E!T15 z#gF)o5ht^IH6SS~J*ilyQ9^A4de3CaZdz4y0U60mUUwGp@6I6?HJ&8st*-&KoCsP0 zvrz;HuSmsHv3K?}eqZbu!4Dwn8$wMPd^8(@2GS@d4BTBBh=AFk?l4LD+)_yiVcY?V zP3sY&&g}G(h}Lkuyc+(j48_=$Z*nuVM`UX-V(Bz|ev^yizp6-5yi^8Zm@w_o?@Yt* zB-^jQE`I=thcqXr|E2cbEuXT;b&6Az#w^rQ$af_NPr1Gm(sD^y(va^KtIW`aDgA)IdUUw0@N34Q z4eVTlRleiL4wnD48r7nZV|mSw**%g=Pgc8h`7<1oNyJWx|8=!Cykh~mSj%zqWVXW< zIL(n-n+6Zsmy6l=7`OMnMa`w3pw(R7CTEX)PEf>j_%UFaSiJr)n^U?+{NX^JvR?C5 zj}r7U4_MyktCB1!-n+%*tQVt_>B7S-q-avxBU5QzL`tEsxHdPbW&u zUC^I=**s~`iHb7F%36{;IavTJ6v)uXm+V4Pu8$NL5qfOR?>Q}xT+dc}R|Sy;T{h=O z&H0uqh^yY*aEq7ng$2=z=nLD8Uj-Q%M;&}F)1>1~ubxV)5)g4VLH5l9>a`pS}U!mAPqu|6NiClRq zm;zxLn|55tNb=JdjOI|jgG~aZE70rlWd+%N)UyzeDeuizP|q@tmq~M)w<~I~I>=J< zE>^s%T?3Uh^Ph+kPC!8BoBOh2%+;IaP41?R{^V^R9)4uApR57+lSKUl&|Tr8Q$T^H zD#h;%KN0xA)xUklqQx^F<-*bLCcNFffsT+7=MVl$_Cx1jIs%~lb#r#2sx$|H8}CEd z7n+7^b#CEwi{Akv8x91ah&{E$-S=31cQ?u>GFhx=KEHTY_EEn!-UYpF@tQmXhxOK% z#MwOaCIjGHgpPeKCwOcN>?^VY!If|qWZy||pC5X6nHdZ?$ByiHS{J`MUMv>!9j=%# zg%c$YkYghTsYFhmO6aDqTr^WlS$CK2LPV#Un zi~SiXe!IQ5Pf~qag>s_+igi5Cr#`wwfnUs;oMf35gQxSOd#@% z`ePub4bs}6sdwCdE_D*${j`d4m;PW!vL(>-QdFzVrZUH%{w9d8jp4DJ&Pfk)X!O#> z>MjDq#U|cx{49(auS@_+qRPD0%lx1l#$q2s)y#*Fc@ZE8-ey?MsT>7MOTdrGt_0DY zuxVAfx6SVaLYbX059)KQVUxw?`B!x-T4NOM#`5Okg_;H&^Efqh!zZ9z;`?B!d zHW~k*%*3yy*UM0^cX6BTpsW0H+45-q%R$hNc&~6%j-HA~dw|EF2;cF2Wn2s3h*@}2 z0wiRuWi*CcgO`34#T<_3EVz~ok9e=&@XRONbjEHas!6^-!ich6r5 zo3c&I4**bvBIpi=boaLN(*&AxqO5>>Aq@F8Ow*6XYM1zw$Hcv|rcf*eJuKdOcP=_} zYZd*XX=rB+`X;huI;^BK%0koa0gZyO5Z^XBxpEJxRkp05Rp}HBLTNp&2UnO@HxRlZ z`kNpuF>Q~@U8uMp!Dk>@xwXJmSSOpfo0b+5=WCA&9@G|fn0}i3Vr2)a9LXNs-{Czl znmdC)aS8(3{W7p;y}*9~%3IcVRQUYI>x%!3i{SoU$}3z`alU2|x|z@~W3SG^*Et z>jWU)MH|2C?(+pV98iWt@H`Yi3GuD^`X_RZo0Z!^Y{QUw z5>o1QUZNn&&OkY&$54TM29+mg5bHQ|@dkghqiYih-+H7Rqb0H&y;4oz>Q7n^cdg$o z6ST0HsKptt0(AnouqPTM^H;_Fi|^%*S+gFFMyQOpb z&8L-(23Y=Uvez^=eavNYp&TYdIiuHwsX<&ij@rD;#iao9ZcCFb9&2W8Lc`~SdLpNVI6AtkXpfqaroDA&$ph#UZcQT|(##u~ZWcP72of;Ag=9Wd#PI)rloPK5UZ znu!|F2)YF5B~sPPyUuXi(oPh{c8%@!DU6l7{wApJC`=vR?U5n>YMoE&P;cYl&M#19 zfWsykk4xF6?=P$~>bvV%Vt5wUp#%QO!rcE)Mh25E9}HAs4KFB&>bg1ktf| zKd*ME-aP+Q6lnS+SZkwE%}Dn$_j;{x(p|qnK-IpYF9aw#8Koj+SQod?T8P9+IW!v% z02NQTzsS#Aosz{Y8lx)zF*-ZcgK|5DUg-xM_KV{gZ8EFdsQi%)7+i4N)zb)7X1M9O z(7~kiRO`!xfqBVrp?@mid^iuIYKQcfyR79xb0IlN(|YuHlpLo~zA~}m=yI=1!ovwy zo42lMUk|w|dnFgLmLO=EmtdT@G>+aDj1EAH=gpUf+R zq+#mo!*PilbZdxi#AcI1f%hTFfiYh`{Ii=r#!moQShwd{GzGaF=H~L`Pxd48pU5X7 z=|&jz7hlAD=}E5G-}EJ~o^c1! zn(~dwFF>O~zkjwlIQA7K2Ydo_u4Tj2i7WJFsbVg&e}b)xOnGrbppr(R>7S5bw~Rzw z(6;-ANTE)cgx5}l{Q&x*4n))g)*m$Z#Gcn7lB-R=yj*k`t7NusMdm;^24g#&ZN6O{ zYZ-Zl{&}je>(i|Xlv!sy;QiIi36_Q$w}c2l+=xcE@bspc#(*(sZZ|H;UAA4ve#>** zDm%(&H#uCl(kXtjcUWGRO<>&>UowsznJmzk`5r2@_W<}BR^^sI#eMf|+7d;J^(HGE zBL(?Oo0Ft>+BldZi5M%U*8S_oq8zlWK5rMU_N$WEz@^uoPs%|wr)&F$-+NkgJMWgK zzq?oEf}HWeuTsBd*HMz|zGN3~Si~?nLBmnbL;6Sn4pH(jrPbeXsGIXxsjSNsapN}E z@L2D)~aN)A;>O(v~2P?nLJTNR)pt=xU90-vfy`ML>%MI=e9=!Z1h-rJ9Ub0reWN zx~cU;Xi7H_08pw7adV44HAF()<(mHyJ3ON4Vn~jBIMFj;A%TvODZI={Ex3(T2vBx@+HZtmFl5zUlpa zR;8alS?fF=rn;UJQ_jbkZ;xhb zlgaJxKFISJHU%x-o;RGSj_f&lqR(}V0`b%zZM_gyn7iul8q6R$&W7of zqH$tk5Jz{=?2qNLxXXVDrUosDHzyv5pVpmb6J_~u%JZYf{*oOQdo4-A<5th{@w%Un zBS8CHgX+*B>bi}g_?11!&Mz0_t?iA`t~u&9M&;!-FNW3_MC^i~W6w|Y37B<6s3cX5 z7?;?>$FKx$zK*ReNk>z1$Pyvknzp25{A(un&eXh8jcaV_a1E9bi**==?QlKHu_8~;?*zo#Q@Q0i23Ctt{qPKrA*Z?? z+#+cPA;xdh!;;T3t7SD)88wbt^VupeYFtVs6t+^uajIvIHHsQYSllqE^SXmGeSxK7 z5?ElsVY+a4!s^Hd{D=?S6}1omTYRmg)+Ijj-7ka3;H5(sUEaX{sTPQ!1n%j%AcGr{ zw9D-fF_4j>d3X0UpurinISHNOwYFC#JwwPr8kD48il;cQ+Xa(36T(mGkb&vOAR^fE z%d0J~mAfF*3**)`6rb<5iJEoMe z9#60oEg9ZSyR+sJJ+*$6^#M{f&7{7&bWKI6rlQFp_2T~V)CZu7B!sxiEq2V{$G4_& z+peHfo!fN1oZs6|WhvW)QhPGh0z9%Ho6z=Df^4YIaE;1jK?2Yo6yNlC090i9tFmw8wb2vyXFcBie50IS>7sU~ti zVe35Q4}|6~NuKuPt1&$bK;ZJ*GCd50k4|w@p6&T!fBgaIC+`Hv8K!U|ZtEFHSUE>& zd8`$fJp1k;zWSZd{!b^JMc1VEZuG~*^_b>bkC&p-yV!LI?zD?qIQ(qqTeA(zXDRp3 z5Cdh(eb^IqKbpCOj}67QYWrzsEp+NsvB`s|YewXri5DUA`M5QteA6{`qXJVg>!xR$ z1=zi>1*8oObIr1p-^-y{^4P8>N{biR0`3J698airjd86Wx<8aWD2AkF*nT1Bl|@n; z3RoP#tr8X_cUw86HY6k-r!B49-Wb+6JtQ&YI>{%6#Z2*m5xHAu5v>fQe)7&F^K$No z?qoJ7A5tHr`SE}!r9{?I+5RQAhE)FF_hI^>~>EU&9j5b51ju#)2LSIBvh>| z(*i`bzFq8-Y!(SqX{{Zid}3{ka>`OcE7OLmxqWX3DgY((vM##^j38z6#^gtNx@=Y= z#?4CQ?gg97V+j=?`MCT6fSU=Q_S5`)YGVNM4^8k-tp9UwTSr1wmvb+mE1sY*y%{6X@jR1%k?JCpr?KqBY<{2{{?B)nLfKqyliyo;hQe1~&nf6#` z7nokJc}>{0bIK$vgr7n7J-2}_b%=UA{%q#~?Sh4&U5u;aHCmy$t4*W4oOHVKq{xPI z*b&l*-ux<<4amp@FrbGHh}S{oXw36`}Aj#=dm9P$4#& zG{5O#eR@Np(Y566xXq#0WM{4p=T9*MvGRJ`4>vdb$R(M9KvuaP*8e5g%`qVi>4X~S z+B(qEUch3ixNHItm^)M5drLEtoDb&K*`j+=%yp?fPp>*rz4^&Hb}qQ^ddCIrO^?A3 zgZNOT9SF`lZ0^kjSZ3-f68C=Jh7kcS(c8MHo~m{rslJI)_IbP6OD_+i>fZ5KjtSIS z*CI9f_Q~feQoDjWp!tCz0{C|z(;2QR6}crOOlbfijF>&d8p2Nz$w=`)TLbfHXa9z;qQf7ASVLD>i96H*$Gs z9;A|2=Fo2;zIzur?=kxZ8GE44Kot!v8o~1l)ai$tb!v~T0Y?~maslgH1br-j@dE$V&8;Bu@9?D2{g-Da8FcY3ESAS{1qi>(K3nHqMPS z6yT9vsdP3lO<1hw*nNC63s9Kje8qbW*heqy92DQfE&~Z06*Yab-x<3zu%9t#hrGW4 zbLo1e9-DWy%GaJROP+J5Kwo4PZFgN1x-AwqH2DS}E-7*?ZV7pz`?F#>-`)Q_ATDY0 zmB@=7gjvJNZa_LKWUia3n7X(a^8I?Aw03tsq@6=NfIl+3k0y1eL%6?pL7n@`pfZvnw>N(1zOEGAyC8l9AY%A z2W1h5C1r^xj{vVnut9MZ`Cn)+DL{L}olf%KSx=mdyaVj$Y#qD+kR}cg8m#Fn1bJk2 z+zKLPc?A4$R33*);C|+4%y-R7u&RUL=R;mbc z$tT@DqGy2&sI=n8605;S>!Q!&;{`ohqn>&+<-fvS8tS8YiyiE{0!R*Vp!N%HKLr9> zduF|?F0|RxPoq998@x5&Z%M7$U#{v1bgzxE>DB)BHctn$`Q=s0XxP-K2I@8dD+9Y8 z=Y#_HUGS5SjJr6jMDf#^KOYHa@#oMDQI4Z?C_*lE_{+pj)?lbMvu4Lv`|PW4hgR8Q?h(AGdD=@m8Ba_qwYh$CGl) ze58IYqX^{XM^B9w>&fi;k24Fv<$XMX08t#jVZ+Bv!Dfx?V-^%6S*rP|wXR7GI*nf| zoz1t=xP^p904{{PO?-agY}@^?{*;-~_BxZT{R2{}+DI$~P#LScY0UzKlLCfG5aVv1 z-owfwUyPo8HQ*6ez+kjnau)=rD~@Nk2Oa{^^X0mV&7N7&n1iGb|b6 zkSHyp*a60c$u{sig>pep+Ni*i32>nM*)&lKC zJ})BIFJ{k71GE)RR^$HjyO9o|reC4qJXge0rG=H+M!XBUHNVaf zOM>JLT^eGn0*UIP_LC%3f(V`&jg}jC`Iv7JxqHrAZM`I>lq6mHKtI)-(nnKiljXwel#P0yinu^hL^{6<4> zeo9BO)h^B86w)wudu&*{*J?gWfOshRP=6&%0z`Rjvt3%h!OpWieGH{U`)s(`>sO3! z@m`CiJ@06Txz(oqft6*rQ}Xl|4QZ&HMz-3-ki!aKaSBuzp~9*G{+G1A_@lx$eWMgq zxSlKWwi>6=VNRk;AsG%jNO0ff0}%OobK>!%6XQO?n#i5IjqYxjJ+I^%tw-gZ%rJYE z!@0EObp7<@%%|A#T;Q}oTVG$W=ZB;kL|nS_dW8SNF)Id zH&nr;Fmwl(l3vBf3@H%vqaLOa%Fv}jBZKf^41F5TRvXyU%gMb6_4H&&}q zUpW?C0B&GK^jN8ZZ*4>SHCBY(E&VKu~jO8ZI?oEL;kSFG4&AV!qvQSqw{*^cGv}2jx%+QCttz zUQ_e2g96mkV+)Tpv;8!?-dt~&j0WVLkx!I-{)i^$gGzZ=2A;Y|rx9}0C^bV2*4Wbh zb>M=W=}PmyNs|GG393%r>Dp3|H=}4#X5=Lev#>v2@ZUv8#NPG$BUTaH!5(dP>#!RA z*L-}Y0P2>6ftiPU=^q4!V+6$D&{L^kfF5P!{`tx0AP3EGic0x#E)G}3!Ki^)5xz9v zK^%K@Y#s*8ho3wx145iLSL6TdB`)LJx6ZgNgLcIKy!xM%4GMo?RT@pfqrmc?mypM| zZ~crOIa2XF8r{Jsna_Yj_0I(%ZAEp%bvto+#QT;TMx2c(BrziWzWMwB zve;=w*UIOS?h&E_4k+qa>3G3Qw0&98?ZuhViKG1T$$(-7lZfjOE1tRZ1}LUHOK4R; zI-PG3UzBjt7Pb>-*KxbZ>P2?4N%$|Pr`r3c{sVVNOu{2x6w8O$Ak`NiHe-r_8X@8~ z_uL>S7;^>cmTnU8rfOI8q&Cz6gU5skEuKlee|0 zn>UUIm;Lp?4tsrq-%OsnluMtb(%DS@jt0*y=A;jH)YQKUGw`6tbE=-?-eM}I%RywF zUmhLa2`~chAIC}Gp~e$gA8tgkht*xt_mm($e4AO@a64@`3OU_$h?{_mHn3ezofltv z*dA?_d>!WUq4J^bmBR-Zh4$0qXJl{%M?@$vBY_W@g-BiGnY|0^{_xO07`4M)Vjv=d zpKzAaorPTTBBM%hIgSM~HAOLHR7pjLlr`(9VgJtZdUlr@iIBV%5XV(@s*^x2* zN?PpX)J5JS4ab+E4P{9!^N8pG8DUpXS?kleMuIn8&}ann7OQUh@*?M@2B${xo}nH>mEt|fP3Rg zeY^hbq#{pq^6Zw(P?QR(DVRnNy(1D}wl3c2Mf*KS&6F4Y;@VJI#MfxnAvc?&9fV)! zQJe+7`#0h0VtM*~m3i`f8z1Hs;#9xwJUlC(wFqx)I--3!k|-rtYN#ZO(OWluEVheX zw^SjnO*dwHOnA6xz?e9-8<1??+q$Q=L_Qei498!W(N>oeTRBc)43Rz|3<$`_;?$44 zr@Nfrx5%qy$SgFci*m8+rB)M(QL|7Hu~lRn#)g^=x#{nuYSsTfAS?=K-NPPi8KA&*H{F|sG=TX4&RqvQaXM<&mMN&n~qb1 zIZ`k;QO|R@J^FKANc)k%rdlQ#9i|Tax5(; z)xkIMVohkmf6g-VJD`b;S?nI84y$h0XLKY|c_VhRPRIE{d~La(eqZ())^YH_#uPV? z6Ie0{!%DiJi5^-H950+~VO{Hs7`J1MdVY!uF+rA117Hf~b!g?)i~M-|j9kuw*1bP{g>SGNtn>^3 z-dIj4v;OP$PWBP`tI5R2vn{0Dq=C6~aa1=$|(gMFnoEvF3#QT%*Dq{+jzXzeo21Ke~i}jZT3iW3&DysU?BsS~qhLU0-E# zhQG^~{l-s+2LkgTey7;c{%iU0h7H{$jiUguaRkF(qDo#Z{QTwqd%d3Q!<7UGQi2Fj z_bxf|TFFr)8w;u!N-$lN|J=~$2$6!B{;Q(uMX;6uw8P3ctuuK)+m`p`|FglM0Z|I8 z$0Ae3k$x*c%|t#6%k%Cb3fuP`7VvoYnGrgEun*U^!oQvyuC@&-{K`l+PBuNPeb2)1 zf3Yi=Is6>QN~fhbU?w4bg?frxsBT;A$ZoQ)7M_ z(yDT?nk_e;Lg@3qb)j@m^6w3Gdrg*}6s-!`O%${n`>YkT$qLDsRlXy0aEett$8T(=`0{;2R~g4(&=WG2Jz zHTl5iG#1OZ)+Fs;^6R1zB(my2kYpDoEVtV{OSi z(2>1KoRIs_8J*XS&}H-O+AHM3R$Qo|xNF;9-4^E1;T2VRl-t52+jJeJy!h(_&KmGY z3h_@}n|~dY;A~0$lo{ai=4MG*%Em1Ds!~deZoPyCBD$TodYtcC-lN(W3k$RoDcv>! za)=y9fOMMYZY`fua*Q8sDFXB-8U}gHM7dl^E!Hi>mP;wR4tq9%6p9a-hKOe&e@h=$Anm6 zHvT;!NCWvB#{RC1(!`d!{cMx3HlEQO%*<{pQ{$onbDZi-W8y-#Luz$#+wXY#XL2xO zv&0@5rNrr!4pST3PMewjCB|*6N8U;TI8s#o|Ftkcqn=cMS(e0$>+9L-YPkxc!**X< z9i7&A2GNdtlY6l)c2i&ZBv#gzNu1Q1zD95lM`zl|slZpl*I*@33IS*5K`XVqo!|%o zbKXh`9pOq`@~PDnPE>E@Ze!WNi!$3Sd2x>hK(69v8nfQ+SKZ@K&r%vvPCNdSLO|2N ze;ql2=&$_|tbB3Arbp^1im7@`&!(Dcj8uK!0_%xVLv3W3H?fQRIZ}uml!~;?%viIE!?%Q zPN9PgRK!6i4Tot{aDwJ;9rw0(-i;EGrt6d=pWH;Wb*B33t1TS)LaE@cSneMulLTP-=@iz0v5NekSg~!?tJo6*Tf{ia$1DfD(aL6H3t%&YSVKUaS&D{ zC&kamT5L6{R^Ym)e1h~?oNOH$#gf#2VkyN-@(#$ql^2gn`s`0U@OV3=?xxQ(a69WE zw{IlYHhy&Lcz!L5`%rZYP=FZ9(_*vL77BkI{h_sy2d=OYUz(&$y-t&pRl-=H6UqS& ze==$pn~6BaK5C`b;|Iv<^f}-wV<$}hH325f3@Lf#$-LFGQ%tPduWx0A8}D&Me|WHg zdhEUY^Ll0JSg;#3scPenqSgtwHdoN_ot20ChY6Rar<31(5G~R)})8*acJ??chpWwXFb1a-D$BwnwUHYBV)Qo$l%$P z5;jXIPxQ6uFcEV4%Xx5jao{ZYySv;+fSmCAoX-N8|HwC2dc0ewnwscL%(nX!g8IFk zh^aW;gm2cwxgj+$|L<-jWkcw%m$Td=WXxYl#xBx9G;bs|{)`a$WUinqM#YMrS-i!`akj z)kU@m2p?niP(wK}d7)+4kF>UYdpjxS+i~}{Z&E7Ii(q_at|B~M)Ai}A|6byT_$7Yf zUl!J7P6FNwYACM^R`ku%c5a4sDz-z6d1?z8e3Ni4uv@B3F-2KWC_3L(6^ zQxKnoN4-KBwxJ8OBz|r^6ZQ7_Yl;qhk`r%2OVvRdf`lpTGyg+5c%v zJdaxL$=GOrmD1iYyxp>tWwieCKBoP`sPhRP(it#tXIK5L1zaWA;E51-S-WhPT1Y_o zdH;mCI1Ig-347KXqKDTgdlChPqH5=%>Mi%6cm&E`I@?jfvvUoJj{wT_O>o zBIZM9JgUlluk3zS%*S0>j6c)@XrI`8LpOY!(UC{JNCrc5NSj~=2%7X5LgqiXmHP}d zKBUN#Kzo?~e5iIcU8!AUuCI-o-{6vv6`Zb(l*xu`REf`sgM!q{=A%$X=QPw~buK!) z@I)V7kQ;JD!AGy|OR^%A4?#+E{$6OcS*L>KR8_XE^YFxGI|(1TOE<0rNxK}C^V4YI zQrp>USH4j>qdODEQFUaQUb6-=o>5r=E;yBm4DSHD>jdMha%74V@DC%#<8HX5)v@Ur z?9Tm`y~q^mu2@p~p|9zBjFBbUvR^1~V8%+-=_aAo6MVcx7#|p{aADpGag4tzQ_1Q(DtuI@C|T; z2d;Hwqqm&sH^(=hN(}D{Rc}^0$Ct$02n1&)II4DCFOmB(fLUm+YAm1fV$)91;$S#^ zPi6qf?^dHrH5{tt;=ck<5nW}335rbk_{hDy9%57AwKH?0dK@uIZCh*Ro*vh`-D{9= zlP;;ve=?b_Z&F$tUb8=Q8@R;l$6j|y8q0--IT{0RHA(sVc$J>H_*9{-b9v5POjb&) zY;b~jbww!YN*+)3{sh~LJRYE#L94g)WrC);16!XigUsyIzH6GP;=ZkWGs|?f%U%sH_Zntw z`tuljQ*L^}T|EFc<*oc1bD;0(4#0mXQ^ifNI)0A8Q`? zQ7Z?&)+ZzOh+g;zLIia764n`1$t`BIfYEQj=Z&<&nT%4IXKM@BBtt>uU{5&sGp*Ds zqt->($fnB0a$?Pb)Drl75e#p;pi2Y99yvNhcwQ2qo?;?m4HzDK>c07O2GWfH#mSkM zTOcb>17J9-Cth&0>wNnf1E*kESFBV$6e9NiEu}pEQJ}NO`u#@L&R&2)tE=f(Tv9!z zKNLPKstGku@^3Y6_U2+!h;izs|JsP#iAZOFl{g~~YM%g#_84T*v7B72Hg(BEpB^_0c7n3 zA_mczOyyUOlg-LZF}xGdyLfgQBf>!az4W zZn@>@+J1d`WB4tbxa5w33pw)cf>?bVKhTJUwB-)xh+Dp&KxHH5vEoaWzd|Npv9=zT z{GaGqU70)mIyljOYw_;Etv~%gbu}R|EF*hblj+~1?)Er_z@p$aV zJ@B8Q4KS27c&4-GR_}Zb?Dadtt69U_CB6C9-&L7nrySB$bLS&Ike^B99fgMsNpE|R zBircY5!_+R!%^eZ(%>3(go#WC@SGZG7gd7cJ^N3e@e-jWL#%d7PDsn_N4||{OLY#N z^194{+Z#*e3L(0ckC%N1*2DB4d&+N$OQjlS_nDgPv=}s7P*1I~YgG3pM=;niGmvM^ zRZYq##sZXhfAlw`^~bMJKKMV}7RIY{J&cUiaCdsQcXj1``DZuO&qd=^d3aYgUzji? zL(0htf9o|MrlrvjKmC%`8?u%1*574h|JcYO`-fK)4@&X;3zTX)*#~yd{g>uMmZCPE zlAFt8+CEbSypwLZa0nM#%q`NzvcCx@@uN05!!AoGBS~5tDvuCi6aV}}VF{%eGK(<3 z*Z8C!Yk3Hc#^&KCZt`DH7I>(jS(2V2H%YBcXECTbAjFCFg$49{A|N?`^>Wke=qT>| zuM8EUgIDu)iq(el4UF?Qlc7ve9iY-U-dgno$-4j-wKgZ+L*V1huSEc4>i&k%!vOGM zu%zeXvG)UW$-c^jQeJLHiv9?fR(p#dT;{*53j9Hae18oYvBAM{`H*-jYL5oJX)P=K zmoFw#!1nDiU9%_R1$vA5DzTy_Apw_Wwsznp`Rr$k+`KkciXZQkls&S@9ee!l z^&>|a9qMsy9-b>D8^hWkA3gXGtzFKQdiB=GXBCr9##QmDxI}1-_~v9~o5@OI8?Huw zQnYc;X!ts;KXSPi^QkWRW$rC7bBz4W4~Tu?s12~NHRp6+d}FZD`y%` zU;Uxh2%{xoV3B@`|HLT`X=c5kVCkxY0=Zu=oTgZ;Zb&`ZS8ghh5#V(zAJJYnUz>QP zE|#{>dw-u)w1Pg1eS_!CI-h}?PQ#@R&-n6-4`AY z`*R=`sraD@{$pr2i8IXW7sg85cAMANoXHfN%}ig}@MZP<@g!i#d1EXX+2EX~SZt=G zXe}9^D`j_hDT>7_;PRR$`d++%;P0DR`l$Zyn`nmUfJUsjAoclJrC-dXT-wyxz&0{dh)2A~?O#VHzskP_}NL9XCafUJz zm2E`-=3QsA<`Oe{SmDV>`S3UT>#ubBqT4mH*7S>R)Xr2SweT+g@#m~d8`4u#$Wt2t zcGBNW0q%G9)0$S_S%vz&H+p@&p>Zi@=8D&q0xw7uu}OG2y~KQqx3P?E&-cBYv>%Mq zHYyqI_1>>Pnsz$;eVKOsZk|hq2RkFVbiwk zva0S@pcIgQ;^5v=6+x!QhGGDt|Eht{YA-@~b__#gcL>({|ux1RjFwNLbUbGXYKSpV03 zfC--?Kz{zrcsNcB0RgfuvYO@K|EI@b_}?aR`nyrU%}ljT94ym6SJH{Tla>G25={nm z;Au(q)s218X(2JWjeREKf@9^>gVCDJ0W^%6_YB&VHkFrSdvnz^X+yGq#`9Jp~! z@vdrvx@1gwK5>RKYs%rBa#H-v%AZIW%7eOeh+~8?WrD_IRW5Vks(0bGS2a`i(3mPw$Xi)k5P=8S*SQ0DXx6rwbt&Y@KVMlSJg|--$qBnE zZ`Z1CE#x8aTat>NGoKl>1>W&YT<47q{5{X7ZZUsed_(KKze}!iLtISE>kA(`3=4Pe zl3w@DQ8}H_65*J9Fn}Hkm@7&o(|3CeGFvxtjnQ;i)ID>wA1;t>ycKdf>tsJEt;xi} zd3Ks&B=f|jmmw5lpBG1lY~CcS=+)vXPW?!0px)n36Lgl{IQew;QT($&0wkxjJLu8& z=b@8-l3lxuwcbEdT}%pl_h6IZ7r!BiQi+|*B#!;+@LBH*@(9+2$iqm2t=_P8X|h0-!%fJ}_dVU3QDyk5j_B5FtD`MO!Q5j3lfkCMl1Z81WF`EhtIjrN zL>{iel@oQVNf#`e2vODCIp>dd%5j2V>Jg6?T4Y5Qdi)K7LSDq|y-U=Q^`);)IF7nw zRIP_0Vva^^hf5Cps6|ObP8|H5aY|LeSw+{m9s;E9XO2S?%b!@;YN}c{ka{A01#)>7 zdHwenUCzC{w*8(1{e}uHSlmAkgK&f~j=b%?elQ&k3nX8ZkQn_fKzk8Rxd&3khhHV+ zrf7GO*eGD26A}<>IWLbmCwX|h$XuDe6CiWrJ>#pxC25#IWzk1+Mw~QCx+?Ou^Wh}0 zoO?OiH#||oHBsU95EKf(Wb8=)J%1yzCo+{4Es+tG$kg2hVW&qf9&b+cnUdm;zD}iC z?qjU&vl;KQ>5b~+W7Lji)ZenuF5kP)vOy7ADR=bTvPhPG-YsBm6I+&2t~X}+siWjc zY@B7=x6#qc<L-CRF?^vYlVuy_WZRmABTp?`hx zXbdz2$6@OfXVT=3Uis@E(v)CW$~0`=N89_a?>12UzxKX79_sb|`y`}Paug+7Ii<2E zyD`&ZDO*yu!Bj%Bg|WNULc%^7@n2>p@TnttHKNL^8btYA6fi=5&y%AZy)~Ovf}19JT}9B z9Sa+~zlEq-YM1KW802M-yYgA*-1fre82VAgkpOF*u>=IYN;c5cNJ1q@OZvT)@LmXD z*T3z&uk<6T{V=npJ;AIyK?Q<{=L=``A`3@^VOV3u!lbA5g? zI3N;>3QB~pmC)sFz!fcw>z7~f#f7Y{GO7JDpkX08U10m4fb;u}|D@QDb^d-HT_Stg zmD%Sj*=ODPNUAT9oS%ZI zySEDD6$+5OxV!GmGf)m zo88`(#A=$743prsxCG&IA{{1;L3UHz; zkM%MZfyhI9fqee*TL@~^8t3D3o88WUON>8R`*@6(IMjVP$V%h@X(Yf2}sWzIaapHqi^?wx(8(ZoY0(Js^6g4l!L;oaU zW1+qmSm5Q}ElodJG4QuY1(}3t)p3pY84%=tanJa{Anqb#V@u;vz)qywUn7Lj%$spV zh2q-*$y|NEDF4UikhS<}yt?cGMU#8h{69zIbyd1I*LFPK2nR8lxact+rmP z9g%=`tF)d}=GJa0pb!e#UcW?+)eUn8BoUP z#4~elV&&Kfv?mMoq|Xw4i*km~Yf zU5o%0kc#o`7UWj^Hh{NM5#FbP3C-HaFBhKvpQ|AnsJ?5{lZm4X8JSrx7N_9fOevyB z+6)gA&q_FNm-@v|J3y!#ovc%rpZMSyy*&%hz0pZEd9c`Hd{>57$NIN3zBkS3u4?!0@k{&is6=1EevgI^H4 zZASmYr{`8yhg7D}%y=KihvdaFR`w`-UJ8FiGls=2b z=_d_DBOGE~;I%qU$!v>4nzo;(TPUD0OZuhUY~Haar5(a%$SIr)UmJELR&{AJkMxAE zzk5x;BW;&;we`lsS_1ZJJoA0NKQ#U?)ezuT$`vzr>Y&m7MdR1v#y)Q&KX(vDlGfOh zxbYjN2UFIfNQW#w2gHnp#?Etw;-$@T%bK;9bZXa2)kqc`WL`dA6TX?p_j9dxWvgIg(ccsM)r)>%Re=Ww$oLpCRzSOdBwIqapz z)nY$YT`hJFf}Za4q0o=rPW2#)blLUk1bspKlSCF%NrUk>S4P(Z!aXwSa#QNMoCRUs z8emLs_H86{&VuaZF8gXarG)c{GnO|}DKop~shu!6ZJ$nFXjvgz_`s}=Lct$=FsXFS zO0hu@rx`*2>rf?$sZi#S+?m0-QuF?`E)EVu2Ac(#jnkxR!tCqmcf4mpKRv0sY%Y8= zi*qN0xVdPM>H`?#{mWHYgM{^D+RTHUWH0MM-)SbiKVlM!KbJA@vskbqm6b--kMw#M zCzl;!@Xf|7j1(R=2hW-_4i#NU5%DFDK`nUB5C7`Or0 zq)6ZIW}13#aP%jeQq&)kKzbw6?l?DbhGePNoOWTaFK6W5NzC}F9Biv>a9%h*K!AnZ(Y)zD5g!??D=dv5mdF;)_ORB^NL z6`HgY+=l&C(@X@cQCg!cH|mW$GrGt2mM;3p2u z=O9ucmIY}GKfCMGvJGKaPB0~Uedm+d&=3ZAI7xr5vm7Vuqld!4rv-x;T9qp%Hu@P( zUp4XaYA@#lN9Pp@6!j(sU+poE4^KcBIcqx6@b?x6<4ff0!DHno$cXum}s83P&ekVpG@F7yCH!to0RI5ixNFtF3%| zj1~o}fwDPn(--TM5=w8HvVrjKetgf*Z1@H~0DM68S_+c*aMdAqT-G&5y3-8owYA?x zAZ13LA*GDlV*1On>VjNcnK)OIYoV$ul*e{5i)n63rot%N(4;a}KI|q5KWVNNdXkNy zlto6CZ-0fU6dUeLt#d?@uguFo{-QWQ=bKfyds&gQIYaf_Bi_Dxx9JxdNlx${q+$OQo80p4ksu zeceQy&pNNrCYzvyH%Gx_KnOK;Zr<~M=l%NGXn8e^Y)m?$wHjJ{!dgZWud*rwC28NP zPO%d=x$dwYmrrkQY1vL=cYJUIqfqz1YEis9c^bkr?zFEb^LZ1L`Mib4Ijt};<(JVp z+ODT)P~NwdP9XMjJ>uDw;F}A{jviro!TQ&nJ)X0tS*>8&xi+y2O+~X z(!rFgM^i}ImI{2NqX{UZnzf~DP%TH+pm=ii11 z*Et6h;!?4~Wbb#PCeO}XU+MMCWVSK1<4jyF-=XI!D<@Yy+d@>`ukYG|skQz6 zk7WLLwJDO4m3hwcWmlxyV0b5Du&0=^drXB51$$VM-RDxS`;0LbBn?bxAY6;$Ajk{w z+--t+Mu}S71>p7w2zWDHRwyGK%W4gi_jE%|Ps7*x@7rZ0MOR0p+*z&8-eV&01p~;S zv02gSi#6qH*m!(nvB2s?f16HRwI9XG9;P*Tx2AqTLo*>RpfnrEbVr7tTqq!Mj`1Ob zRZM{gZPlFqvLj2e(<6url|yP&JZ*ACs04L_bCnvIC{q&(dPU16G-$WX?Gy15;UoHV zxl(Xy(2Cr~mS~Y|c2~NgdD3Qj+qFt+cvfc`p?0Z%(`*@U{lHc0MPC|a(5&ZNs;x69 zS@#~$9ekY1L_?6oYsY?yW`JzXc8$BqG&wFD#Mz3~trDAk94af+ANn6G(kqUps$X@# zQ@Kvf+O51hrbNm(dv+0UZ|gQJ18>XeJx0?h`T_O$~dD}uVyj07swnADiaPH0oH>!L+N+X}RVaS|~ z>hj@y*yZA6v+d>lOfWexEE2waCjyZ=%R^83B%zi$?={e{(|CnJ%xcQW-alDM3dhV| zXuVfM$S(h*1$X8ykW?l1E%gH#*2;@>TnNC4i#ge7_^tqZ4H?&h*6qFJH3HMq$aQp5 zj%Hs;ELHz15aC(*+vWl%)vChb_z5N+n8L^D&F%9zrQG5Apw%8nRphA)0iRbG-F09# zeZ8(e(g(qzKW(T(!xct$ZF0dj0Y5&vGBud?!B^4glo)y`P3ab^vBW8CCy*W;p5}h56lSBOiRemh!uo!G7VlMhQ}rkhVZCjog(4 zqM$Hr;k_jB4zAX1(tg=OFL2fPz58LH<*A^)4Ae4TI^T068sakDGY}ou#5F=T1Dbg6 z@?t+F+=PwPfYxITv_h%cCM4&@luLEYMxl{pES&nf#D9dJ2#7MO(q-=)E6X{MX&w&e zJjIBkK2?WQJ57roaO9;*FOP9b2HpuDH~f&|v+h6o9R3D2k|AAlt5qNm4+XlZL`_OU z@<>CegJR~=7rvve>0?8LY1?$EMX}y}jjE%o3yV@IqKYYr*mM?E5>~IwS+N*AjA$xZ z*7ig*-O7S<)Qf68MJEryI5iwy{?gfq77z*vl!MRrp3!#;i) zxlsHVv|imG%gdN`?qCcEo|sftj}eP%6+Dyuu#=5qsOsjA3XSNugjbet%^TX8zG5TO zROdIr4Q_5${Kj6DwS}NXW88d|q`Z>wxi)sUbi?WzymwCB9p~s)Fe(iOLyHiFiFWCT zJM6h_0D~>p>(B^N{l7V*>CurtY?+VPi?_H}5k^=@)spIV+{fGfpDTBc~# z>3*Fl9LY-a{%iR3N1K~%+jROW;}l&DVa(@;N@P@gNU!Y9Mp+Xk> z{Qj7ATrHrR}5f|ILQ07fZI{g{@rm;zxO3&hcVc5bx}hbaWNOzhm-JM@Qzmte1!zNQ&L z56}R>>1hW%wx0Eynvai1*iL7eFdvP{<*$RqG;a=S@~$MwtwI_)w{J+Xcz6Ph`0}* zLpZl_4%T+{hfxBEIg~$CZEm!|mK@i0Yuv{b4acS)$TaaJ7ebKp!|xArLoN>AAeW3k zVB)gita1Mq+5HtR^2zxPQVOrepQ@!oblTf04p0#D*b=*^zIuHN-4jM609{D-t}hdq zwhsNOnp9mp8K8`JMM$rFSZ0w*QuEel)(7$KdYtAu`jF3X(8bi%GeCtgyFZ?CsA;k# zdd|cdwf3b(eE|6ZPmH`_sO7rsBQ_TfTh7dO@IK)6#hvN1L?aatPAT>6{3cgWMqu;B7j0vqC;B(1%3=%`ApPy54pW zbsRfR#jz48>56te?$PScWUqDAx;3thF&UnzF|#$J6;y~4vNuceHPtrmBT#sl%7up_ z?z;%JLwUW+r`n_jwo~Usm8)02)Jhqb)qU;idmvB3wECaUj8PIe#0~Y_CmTTbwpXO&si{z%H%-a~Gm zuihR4GOE%(`ySalnv63lDBqe@la7w*s+M?3WzHK0ePXCTre0$G$wzg9LkfBJm4W-q z46|q>pJS|c!f>KKhjCRq!J{I4kMGy%-a}r7>f>+j0=V|=R~u7r{}F|X={MY!mOM+W zf;sAFN~S436E<;sGuQyG^9o?2YFhm{cft{wPNA)yR5v|Wr$PTuG0dA9vh3#y@Rf<) z_&nSCD~*cDYg(V~4b?hb6`S1JrqcuUk4`%Ct!@<^CV1Z&4|1)1<6Oc}c3d`|yb}$b zCp%YzI?(Xt)X|1rlSs;=rr_hQoyzSz!<#D(+3&Q~s~pAe#0NNolW#BgD0+OK7i$Vi zOYOX$l|JU?p>EN#kgL!f=T;X^&2^_Zy1L?hI-YghkSepVRL1(x%61Ohk?isB-z=*3 zW!A8q0F*w&N|764-7KSW1n8QbAAj`+UriDO`9R^m$oM^=MIaX={`a8YKMsxG3@Guh z%MvrzfO!HnYviCF(3_ZVHI3))Q7Hl45=WV%L`z`J#Fn!>AYlNxzilbYjgjO4J+3WY zXVOU0Xt;mtIg4~)G|-!zLRaO&AG_}W1PT6OptDUNPq#wrY>pg&@Mo#qxXoR2oB(j- zoLFOjwrqlTmo43W9w4N1zZ!@e>#YY`2|?&QVMGRxPlirBK07zdY z>~0Z$O5)s)}*NE!z_ z>G6YxV?-cKu`nZLS8Pg0omw>yhxyHIK4I3@E_rBd_bQB2of5+ zd!i5wyeB3pOTz*nq*0aR%(asoL4Y7JQc| zg2Jk4=cq?r9}YosFaXs~H*Wk_5xf^2LBOgy2K5i3>G5$VPJhf9an1K2I1a{iO&mh= z1NyvQ>v}ymFH9A_P6*DJ0@&308@?o&^S^720(GpjGNlRLsNFdl}Fvwwj3Z@!^Nd zi13*3b{H8l(75@M-0lefn^5b!N^3-6BD;Pe@J51r~&!FA;$7cA9=*7r**l#^>;;tJmjkv z%!_vKS-wV_QxnX?i!X46oxfMf$!-2^3$cf?a9px4VOlHDoIB)gu-%d;b2I&_A$;Y^ z{ZYlV1Cp`5{fXYvG|eN*13kwa?VAV1iHw(DF~4L-=k62-8A)YON9E|kd~&>8=(8&q z2OdClCSZQ=T4t~OGqd-f^a2njI&y19@aLoHNQsmFyj{MwJ2Io4(aggN(!byhuwxZF zG(R74_WJ(Lgsdq6d5d2u0D6<~^)1BZnDG*o)9fz~VfJ==k@J`-0aai~Bh&{T@k`<# zijKR;jL^qOyu?h~j9=2)TmuAR@fjJ_yW&-1ta zG2#643$Ld^v=fvn#sbG3uDwPGG^L{9yzPil7Dk-kR{KvS@4YDYv*A`Roek38dciTJ|qRITQ(j@z8asV|>J(_7f(7 z?^Pv#DX^RaJPb&E)4A3qlfW48X=HJmS0`}mb;gl|xVSgP6AOK0cWJ>q|2_XB^QDZ% zCQsL5{FIJ=y6R4IH+bCV+!wD43b8@MFBWg$` z81H*(WjXj6q`ol@4xn%+ta*tgxl2}qyu?G>8-8gB*Ylc}&~V}N{fCWev($z1@OJ)^ zSFT0Eo6dtCedDKuU75302*8D6?i6LiVosRlG(eE6tlUXE|A8DgH2j3PX8o53(Rjv? z9G9>CPAp)j0^jU(fVdywW)!HA`8K`~I71*>-lf~0bx1Lx3l#cm$=UH3$Ye$l$~EBF zJb#q;JP+NL=nN2=nUB3@-w-(wQ4PQAhr&2F@zF%4a z2Che8L-md&D3vjKv=e~&a@_1VphxSgG{pf+!+CM5TdN7>Up^rLGPv|&c#=WsT=Mjudgi@0Cy4`_w(e=Z2pcT{IHKT zL&=4eUF?S!jOJD9HSqywUPFQWJL^!`R5>oWOKX&Qp^))Q_58CrN6_KsOF5zoCamdw zr(VKzr=Mic%{GXR(j&LCL1_mvG4}`yqM0}P)l76WXa98|;Xj@S{Zj0$n-OiFkEmok zT&F8wa4uh~-EJ_E1iPnppQ|NMn~o+k?#^Yi-?x!&@d;k-H~d+#Hf(GY&*o!a zfjb=YZLo8fs>yrme5CX+f{zHXV#XM}0Y`N+-EXvv*qX z%d^}uKHfXl5tlXYZpcS0%$TcC@F?x_?%MxMZYZ<8nm~0&yf3Z^=sp>L)<`<+q7JjW zt*|9ptPuDudRphoFXz00lU>_9Km2uI1h}bz4qUiuj)ASg==GDOWg|^#tiH|9u>-Qd z{U#7}_RPtP)FTf1HiQIxY!aSID+IPcyE8`sOxd9*lGqE;DcgTCATfj{)T@-9t=xC; z=-}J3>Y-L4Nw;)10;~f1Qc+)(QfH=aNb$k0pESAo z(Y7!9sL4T3{8OM_xHPY^{`)Z%B$l3)V(F#HY66WmK}gJ=Sv~mr$jH&_rM1Vc>$YyD z>zpe*UoURlRz0TC~W5Inn_Vw>$cq7S^xp3`n6cOvgClg z^CtZ1xTl1e5X`%nWJ-T(qew1=X5gi(uB+j!v2HS7pePWy3u!Zz+)kA7WE%`E zr#gk1ht4>SoYnm%GL`K>7Jp^00pVXE1@e&h`MzsqAx>S2Mztf$WU5Z@aD_eg2*xcT zv#fULNa-HdXNLxFUnRc}s;|F1NwiG$<3o8+tPwY_wLb=v7?^Pg`r;?1YXGi4QoK=qkDqH62db;1XZ~e- z=%c%ixTQSAg{hAMw>8!3>I4<{DYj$Ox}KQ&f(RFMVUxpAPdt~ArnXA8&?alJc-hq3 z?SS8R0!|RyU}?54x}6V7gPw*tG}nA;7rKb+E49Ri$L+Ie|H^oq2EF=^N{dn3 z5BLw`1_LdX7TR5kY$`mc`-r~2#W{6hCCJ?J_CV=#wf3>#{PT6I%?81G8h(}qz31~w zR!x^H0Zj=-gu~ORMH{}Nx$pN~#y*O6Ke=L+5Y59vyj{6=7%yRfKsJI$<4y8;|#z z);NSuspufj?@YNW}HLQ8^#8(Tdm zfhcnQ+G;|bxUq$Ueu5zw{yrKT2ij$@%R$k~N8bK^$&qjDEa_8((HkjuV6^*P-ye+M(*#OLyLEYAT;hDto_JCBgUGpXbKg#LIuwH{Ca z^gW(2bIFXS<~`o5D?^e>{4OVMG@PN~44TaQbqNL27b`+bflIaf8Q;Snn*0q>9eujz z-x|O)y1c4TVI{2-)kS?U;*yxFMlxsd%g4kOPU$4Xy%OQ;$UH^&p4j7}^SHXSHM>tk zTnBf&hxP?0frw5QyGpAL+mQG)fOw5SkN!L9K|d2s(Q=R~i*j~(?A*UEGd8Zbb&Rja zmsWn9>{r&VmW8M3GBEg>L4DSHjGMOq;uXdjm-&K;2vy6b#!7~t^GU;JH(*9Wr^v+Q zQ0tlULyFHfPFft6{mYL!VLgkOA2TgT1Fnqx7b>K(i)WOY_HgDhZOXqf1TJ>Q$fEtE tuS9&(WNY=Db(sy;nxc&999mx&jk#4gE3#$NZ@|B^2Igl5P=^J*Mk3vw`(QCxkxSCs$2dx z#l+S<^_-GvvraSOfmYsz;5^tppsUDSD~CZNqAf|V)w<79|E2cpugOiuPkt<9y_W5I z$uG`rAHp-xbTaQX&gdNeKvhLtX?m4EWmvv=FQ{`J0XKUxZ<_V`_%Wh88J5Ci*xlM0 z1RZQay18~AOFce$qh%YR+q7f&P8>9$W7N}$7unhM2ygCW+_0VcW3iY20;wyC(sZbZ zBf(8JU#fIsTC`Ve`rSKZ=xgxTRdn7Ca>PmR#<{7J7^h=7GIip0v{5;|%*;Z+Ce-k_ z+)AHWu<=ttv6j7M++EB5ko;AoXxpZ8N(Gtbby`A{FW>Yii|%_M_`Q-)ZV>q1OufdK zidX83%K6Z;1U=*n(WmbuXukFY*P++&9;1>;psBy|EX2FxXH!R5C!e=ZG2*$Bf-ohFyrXL;%ioCmapZN}M);sQpT+h~WXjR!*uoxfZ z%Mhh?JR8a}RmJ#B;dGrZ(B1bauqB<@Txb++HE{2tY^vZ-T|coMMXQM>-qib2 zKT=9j^JYDd=tz4lzkp;o^GDl6m1*t!?*szBTeBveEu5=@AckI z1WowcG$uFi8NV%xd_bv)WflZ!LZA6w$)ZI| z!-gWAV!-eMXnvF^vxls+N(i0VT@r?vyA@gm%4_e(V?`A8l_sAjvl`I)NSyhdaH321 zeXM-`APP5%yhFMJlSqCjyEy05pwZF`OOVr}hx(1$cUA7nrm4)7Hqqir4`dXXFP7$) zW|WSVD&Y(gbCPTjTWj~$8X7iwNE1nGNGBVG8-4h}{!K~Fkx?m&H)~bSMlLj}uS2He zZU<}BK8t~>ciJPhm#Q;4cRo?7Rl%@x$v*{rE>IOs9nBE>^s!(*|DNhtzVpM?H$n;% zYNBd&s;5Cml{ciDwfteqpI@ralIi(&XJ!w;7xi|q6x0-G6pH4(yY#!5*}d{H^LMq` zwL?qajow^kSXEh7T*acoO+rcXOuCnJT#cp|VEcZJW5RY~-~Gs4*_~o{bf;y9XjkjV z?MUGWAGhrW#|=2{XIxuyBV4ioivW+C$3cel6d89i+eeN@``_bkjsLQd_sH;4|DZz`cf=fvN#^qyp*KWqDXfnO| zM_E=lV{38$)<>d|4!C8oTFnrvm9bUwuKscj|D6Psgkb(8ego@OYw7Z?@>ypgXDnwm zXBSdc8BSxK*<#U_$u95UQ|9APymCA(JXxwU2i})YA=ZwRI++cD6*;DTg^^IgF^h(Q zRGtaJ#zV7(ha?Il~4LJb>@0;~Mx`vYNd9U@;n7fsjhIS9|}O-?j=MIQ^|N4>3( z4VQ&w*P6XHd$V8+VuzW)?!mC^DAuDV7d$z|yv4@6)(QtTDb=rAH4S&WMBByCw1YZf zP3!L(!W-He=uz-CaKGQ$;Cv?VjNm&lnxE!FVLxj0WegDh86O(OZAulgE#5&oUhz`l zpxeYW2+}={aduCu1Ak0^%$HJTnNLl0O(f@z?}z3@{8Z~3Hi|Mn;94!J{NDC` z^n3fat9H6(oAm&u;-#{mRj-62 zmn(k^rH-DA@a=ir_JBEzBonI|`UwQrLV$RQiLo(agxRL5JRi+5eeQbA9b-~%rW`(> zBWq22_E02hHBm3_^o8`?dt*TdLBGUg-HdUCsth-MHw=w*4F`4Sd8>ICoJRYbZkZ2X zsnAQO1@cusbSoKzJ1pNoRyU@6tzvh6Ssu|aK~R>KA(3xr)3|{@Db7nINAezf-P&=y za$R;O^n}JkK+9&dZqe#i#ZSv3x0hO*3tfp_8a~AbpH8yvbRx=kY|AF9T_~Ir498Yi zImVXd=j68J0$k8m%-OSIh@`CnTX7h#fO<<5=P*_G= zeXwd6#9F$sEV*1mzCun;u0~P8->C58QGf?o!7tV6~yj9w? zm~R$nhTZKcoZ#ZTmAEnfVY_|dt?;Zcjc#DQeFwf>16Kl$bL3;o>!oo=Fa0icl&gE>pJ(OLLKNRbtmnNe%5$DJ4Y$wIjh*LSd0(X zN&n{1`f{@!*;GB&2;JUO)>n`2D9T-%cA`R=BSpFIg+f5mvp2|dt?LY)lYp2+`zJbtOY?qA!fXIp*~Sz*Z}JPZlS8_sHyN&$k@(? zj@#?`r{nhMWo zCG8x*wEP^L9GrAw*tE2?A`T{|LMl=+zl#I^iPD)nI@$|?KrSvW94-fTz-4;UsPbGiqkpe@E9W3k}E$nP*FY0|^Was24N=J9m(7%8G z^b_oA@joruLVgbm7$E548xR)$QAe@=S+L*j{{7|ef+C=cvHvv}|4j6+ zyTD9~VT*wNT{SW6AIRr{z(P`5NGYiTpMaQM{9PLb-tPbN30$KF$?Z3t2%?}!pgfU! zsOpNkGJ#b~w0+*X*~6~PAXk%A4`-y3RGZ;2|D~NR!OI z8O~dzWYH}Rc`g&s$A)v4qVNA?~GN=(h$3y*lNYDaE5I zcxk=6WLDffWkLhO3ZcA)ibf!T^53_%;cNOY?45;HoN8U0!Lo%bc+rtWC``)_MjjTg zQ@k^t>JYiKNlaP-=IL%FdY!p7a+u)*R#AHzCT?-3AO(J<7dPPgv?I9wB{e>^DB&b= zRoTN%U|TTWe;fbRp|*Mn@3#9BGxq5k$g+m*>cklb?beWP719yh*CjO#$9_pk_%@83 z&a6d2myiBXk5-^q5%%iAEt37SZZWd}c!;5(aGJgIUKvN{WP2iBXk?#3?v&@{vnOGR zQ+a*Z#K5Mwq;F+sxDHllN0(+VNXcf#XWq5sOGv&ErRl;ECKd@dAwrfq0 zQ1onOtQsfjj4@EMF_9l>GkqYw&*c7e{~L1Gq4zjt*gKKtvL*3NBxsWhizhs7TjI*t z6;B=?TCVL2Ssc{~)c$ltA0^%-ZG6|Ru5ET09b?>#dix}u9=J?QDd+K2+C+Z?iJ@V-Z2 z(Sw@xkg%pVILmB=SCn}rA%TWR`G7V_NROnfV)K{atLG$!-{p4SsZmzyl(~8~neB(v zl7hn9`;Li~Dk<@xvslr)uQOzBvB{d5v0l+!m^iwS<4*>mR}X4tipkhoDOgz18y(J0 z9`&+b(GV^4)eX21SU^{?X6bWou;-|cL&__|k*W9Wm#Hz}o`g4Xp&xiPL-=@islq@j zD5tS_*cS_0=c@Jg^39Jq=Y#4|m{+&k@oH5g+`e$dNrF9&%YDaVZKrT)q|`8>&;8`hkU+x3B7+JsPhjqMe$!ro<4ITT~&izyG;t zMJ$YnWf>0*uAi58m0WB!ydq+=Qd)gcBuwaR@UAYu3MQ=SO!I~MS3z49=vAu45c9h~ zc(q8B)7h+fjkUO9m;KF)RjESjCmZjs7#*4pt#%=0PqfxdF}5ZvkR1*HPxW{W*<#n9P6BG`gVb;#Ez8T2xhum+niF&;SevskR%)z2mjm0dScdt-#jf8Fr-)Eimm(QE}$W(7@H|2tk(8#Z4 z^u66iS(G1`|4w5zT}1K!+O*4n<43s(-y`H;nj_iGc2~84WyGnUI zymeu6?VOj`mc(wKpTK{sV=^%JDGVB5&qzs$YZ3J zI`d5#wz24|b=y4xpF->C4txC5&g-JJT90v(gFZdc3|itj5-U~L+I ze!`;*QNyNBGZb_z>>WE-rMtszR*xHIsHGNrmH5(POWlCG6Qx-a36To&;Dp>_qrl+q zpY07%ms2RD{0c+!lDR_Bq~kE$W_!D3#43zOC7_GlOvNUwWHqFLD04@VVH5VcTJq=( z@u6kIX%X$+8{Y*9Myl8w4M#@f3}oi z2m>;1cU}IfO>K{c3H67-ellONf?*)wf$eWLsf1V8utf{Zzf(&P84)GpmEEHyAij`= z9AqdYtA)GKK{JKyinXGBbyAx$f`YpF-w*C)PPxV=Czzk zrTvU20#bb`+>uzrW*}yxXfGstBzzoen)lRdxErL2_ofqFRP*e3y&+2jcHEiT-@c%` zG;u8UNwX*&0_Nag*RPe5336rn5%iYEyK-m!+%e?M=k6sUny9btjm6yEW-DLb9Z?-^5v|IY@Slfdn8<=%^zQkja5Cbvwx-|1_Z(v-FZdE zy9{G}dZ`55x#LTT38jY{-Zy;+tU?upB#zHYv2%Us9aAzbkD0gSP28S@)M8pRxb|ea z&-udKkjC4VTAEGX2MdWM$gCLsu-lAsUk4KTFRdL-pSIpm3&axzasQT7ph8M!S{L*z zEKgVk+ZYbbcR*+<5YZP{bjF4|94+GhuuNEyLBFf%Js)QlNz9^ird>Q6ED-Rd$^o3O zcDY9%R(!3<4~!eZgF(@%W-Q39SxzJ2Z>mQv?5x_6UJY6)8xCk6P}+Ba?dh$3-(+#y zYH#(dSeOe?#fQoUyBoOguSL?oF0&fuhCAP3R$-70>3Mjmb!Y=!>o(N#AnJItN*}da zRk$9AEVqGmJeFs@BPL;<-HW=4ao@bL7^?SRy%t*ha#rd6t^KIT-JlF7x@do7oyWdh zbATCqZK5XQ)?;^fqEFAWLlQk5__o0xDEO^;mpNhDWkO5d)yf9EuETnwM&%i(m+t3> z#E-QLJ-6EVcuFb1+>)idrHq|Hivc(ADpS29*k>{Q1=V3OD~6$3r+h5ip^$QQ^p2uI z%YF-mBr@u`Vu<}94W`(!^~asnF(m}-oXe=j>)Uu05AtA^byVYW8!jP-#lWtqs3a~p zo0Ac88#e}r?0Sski(>6Jat4}`srg7h73>T^G^MU9z@EsKT7+Fug|Z_RzW6{FtZEm~45^)-MkuX3%ro7-;Evqy9pk#CH6%%QcV*|Uo1 z@H*aWFB_huHSj)+E@SzzT|QDej+1TH%$9I4Ur z?dW*j=Wzo6s+TMW6VJ`iTuI(b(FJV`dIUAwa^ReHnWbEj(KkFjxkjoYkrz!2RomVA z$rYutp=l>5;v zZ|X_*){Tuc=B@KX#Lp@pdY>JVZ0o+k&LW#Sd_RBwS@n~3lo<2ExG&=+R>Zdw-GM<9 z5tj2bj{B{Uvb{f2G3ks6hnQ`rmTcgo7sF7lMhvXsOh&uHc40(DmaixA@RM?hP#44_ zFWTp%NM0s{k`dH!@_8L+xzw|rZ`d)4!mhUQCRZevky~>BF1Ph}ih4$g(kXWvf$-`G_;N1mS3))0*`@l*5hY@2Xpr8_}>mMI_W~X zhWZO8qD?U=CJDeY{u}3~uoB7#_O$6kI(E1MlDfFv25#WMr-=`k(}T+%HH217dFMK= ze18}s`0;g_(3+Cyd7>Z$x5ovH$P-lFx0*chtU5cg3T_}bH;G#BBMIfc4Dx+t4Wn-? z4k;X*=Hw;nG<2J7NdasUaq@ne`>rLo&8P_^q5jTV?Y8#&^8NUhUI{R%y;)kV*aYk* zJ1RV-;m|JRkcNTc{jbA?+OlJ&;C`Wte4&sH z99rY(CQ`8z1-BpXvPDA3z0?l3kipgCM4|?PF`e$;yff1CCl|~1*SrkIGaU0zZ=a5r zE}AVO@@1P)b52E$B05p8A0>d`r7)Kst9Be*onhE{Umf!rm}=yB9CDf|g>`=GYo8wy zkq?T&7f&SEKgQJQ!f_xeDCL_4R{-4tyXnsU(!H>Pt}9XV35K~5($99*KBl{cM9k@ zaaTJYy8XpsagoXFHMtq7Z*eS=0-LBmri7^0(5=x=y8qLFQ=D_TAns29-CF1n#t#{4 zT{ndRHuA>%kdMJ;)$8?>Et{?1thH;L*|)*&M=A{yH7>2zFx(SAF#B@q3k#79qurzZ z^(mTt^9d6myy?F0hr`BzMnFR zzb*({2r}ETX${dJ14~hVco2Xfub&Cv0F2&Apv@Z5X0rUU@YKuJ)wPK(q!fT+?3F~z z0yg`<=p|wK+ME8x$G-;SbdSmhY*EN^+TWmG;3j5=bh9HXlG%Fi(Wl0I#>k|WEcbhW}8qU^+ z<{e5*6~G4@X~|#Z~Nq_X<;4{DZh0kfIND( zb>HQwHk)>J^VObH?3-`(Wwf?k#}}FNAjh5X*ju6Yj!O*qh(p4`?Khh(?SrzBP`hN` zTFoTHqSKRAe+1fSI6q@e@#LPk@3!fA0ccL_gUIq~j&Ca!=J`4_cq@@-9QNRyrv&QH zWnPfiqWK!j8j^c}r3Da$hBR)>DX_*Re1m)OrJ1ioDmnCPD~C;aR9E_DIeKiWxknlu zMCf(BglCH$>K*TSjaRxTh?bic^!IgWLz_UI)?<5e+YjDSIcv@ps_oqnuZ-x#KIQL#wVgX$kEdqP zKD;TzE*z_MNm5J%9TO_5Ra~zEe`YR-4O^_;jwb_QQv4`u*LC?uWaD*RbRXxu7Oy`~ z3x|hudvvdp$1BTbj9)*YX*qpYkHQTduFZOoSJI73(=3|7XO*k2-~w}IoATPPs|UHr zI|HJF*f>92Fo+VEqMZ3D>rCv3woM-@vOiUxKX!BKT z7q^?DcEFsIr|llj82g+ZUaOjqUv7r)k>R?EpE_C<tVlKI|4=zjkaJatMY)rE1$i z>V+qJV|Juqscr|#);a>4!Fmm>IP;H^Z}EuPeQ(*2i?{}IrMpQ;5#nZ%a3n74CZajGC_|VnXvH zK{|V7J8Kh6mT_3QHXy%|q zr_Kp+wZ=*hUl&uDBP!$Bm11;en=qjYiHhB_IOF__um7Lmgta#Fn9HJYSo9ezoi_gOC>7?B6K z2QzP-`iqg$^|HG4C#-$FaJ$iBaPFR)y^t*oOy8zxJ;0#aF^iA6Fy4T*#p?pX&Q(-BVOb|E#zueq&#mHV$2PDQ~YU}M@e#Rt5j9Hx`^>$i?~ zmzQh0MyEAoBr6PDCiq$c=f%{Yw`Sik@dinF?DV}24l9hzsGDjU{4_APH@3bpzCy${ z<#X=ea(|-psD6)%Nhz5&J6XHAzQbkGZpT6q1HOiL3XEn80HkqzK796P0ANdJB)J{8 zBmH459`3ZnyFvMmVm)U~s@tG!i8;B8uxPNiOg-N{xwUVL!XKky?mIENCN+;~reMct zk$=ylQlXi(@Cl)6E%~CkubJX_KSR+j)sB;ZcAVtEf(7q3mA=ORS>zvLt5-Y+Vb(4+ z|B86g96$l`Stcpc*D%M-d0{9rKw{v@ygFvEIx@(rS-;t$V=~{VmAqO8(+;3dUX0ez zk2Xv35=*zBdtv^y`-yfM{hKoDrlvO^E`at2GSlkjALe&$=rY=8t0-|>mJy;8C5wzP zx(omeov!M#$}%9;2MoEfYEj9c3?RYDLSu--Deq&w!-jK@PC$zANP7(mDt~Z8lFp9R z>)aJu_e?m(J+~Foox0*Kb1rKE39bW>5k>wT=_ZU1#m#U~!+jFHL6&ss(mue!YhX1? zc9Z!krq8OLugy+kK#9FRK33<4H0R42E;v=hCSsWr3tA{PNR53C1P8u+uXaj^0vU9d zhqAC|7emFJ zpPU|<36(QJ?v5H@Fl>uZ;`}!Vh4~q!(IH|@_qU7wL@56JMX6~Kg2Pz}RxhMl8uV&7 z>T$lY*Jf(KH~mPBLeJA(fsRDM?b>(>5SK1pE9LVhNP-9QUl<|ds;RUiZP_004S1}^ zrK_^@A9na{PoC5t82LuAsIkm6iyzJnw0OBTyENF3s&{iA6emB z;r=jD=)0x?yG`8Jy{w^V%7|T#8ue>zGxZy_g&G zJAp`(DUxF+&P$R&nSQ_^Z?FC6PHLdwGdYiZP5;dW6Lu@rxYgSbEehY2db*v7Cel^k z^rNuP-pJFc#e3_wO7$DJMEA$-&>CpmlU+tj+hwZ!ER)B^S%0}Rd!wBDOPOeJp$^xh zogS0U*q8J2M=MP2mh5^|tK*d~36Ik#x7SG6kD{Z^CypH4)~d`Xk4;_fhD&^3*X_Hu zG~d+|lO~BF>>LcJ8vN@=*B^>RLM8i(@9Rg_IPb=pC4*}_?Pa9~WP-^%0F_8tBoknzOWpgj4XPi-*&PJZQ?*sb4k8C>)jz_BBY!9>{O`J>j4CyRj3zxpN8HVE0w7T$yAgtInxU=zpZ~OpW(em zT%dWQLy8!F2d;oFiI{U~T+MpD27vGU%m^zM!7+NwKu|NtJi(lOG4OifWgI8kj}js1 z@Wn;7ER%NQU;=m`^tp)U9!{ld0}vi{7`1RRn#6SRciR@*zu8r)2gKn3f=YaMK(_hA z)Ou?L$tu@t(hVr8N^?YP$3rrRJ%NcqE%*I;C9@30hSR--#FsM0J^byM&?)z28@HW4 zCcsN&9V@TvzfZpvweAeV?W--TJO1`YTR?Le!$aYSUf%!4ZXLfM!VB0k{nbvwkfeaG ztD|M%@}N4Yr6J+2mv#W&1xFpn&V3)&CgopI6FfiJ&Xb<%F&8HlIr`b`uv(Txc`Sni z?GaqCCgU=y%%~go3t_VxEv;I(Z(`G6-sj2u{G+4muz|w=t71HyWZ-Knx;`$3-rOF@5dup zbKLScYOY4}*(pZg4Nv{&+<+q$lL{tGSJ2P+aypv3OXV`YNM@;he6rEZBKuv~xW%kL z3vPojZCzN*79j9yG%6*bT7uuOtFOx4^lvf?@p5|TugadC*4k$QL_Yd~05xRtG}P7G zB#?M#d9zloND2q)MB`#Xptq2m9(*$Ftps3sg}%qmjnKm4%%s}3c;$kPt{AtMz(v$Rs{^#7E!M27$*n(&VUKJF=3>MtnB6y~+o0j{WNFc=2WTz34r=`###gFDzhHyzP>~6DVNhpv9kku z#LR)zCAl+u0PoaD+|~*rBDN&MWXA{}T1>tVY8g<$H1I6?qDcG44k8(rWaSy8rp z>2Ws@MHLz~ugs+vuUpz0r8q)|1BsOg-|Gg&0#2O*RtVK(ukw_P&Q}2`Kzf8n_DqPp zhByM?VBipgDyNO6ru)6M^Y3~ted6nksfcENF2nm2F%20hh6P99tsew5Be+iV)ddW{ zNVqLBt{)9muLHWo(si6eE%&3&+lbY%iiC2Z_+h&(avLhvK*Viwe9=K+UF}D*QZT(5 z^|ktAv8mf^?q|aWG;|9|!))oSK*nL>VKbOrFkQUzdp0@_aePM6=_41LWr3{*kNxEO zyRL2--sK^4w&ULqjk<6t_#XpiN~X50Z!-JSbEbCY>_lsWZ6`B zH40xWVa^Zgb(uZ{-;@_e5Ux$l>~% z^(3#6uCCd}cdjSjSS24^{vdlPK0HB>AZ8OV@8vWeF3?M7#Ov>W#Pk#yvwwQD>^uv0 zGdu1O+Z`lq3G8#OADf-%&F-OZ!PGqG#{59sg&)IH7!sGXq3W;e^(k%`fN}_u(cQHP z*-0Otyx{__;YD*(SolCY9<+=e2zTh&*6r@addX$_-iSDoY{FtveP5LTWvkjn*mB8Y z$_j_wD7b!OCkD!%AZukhDetwIg6QpDib~d(4#4+OV9_X8@c^uAaR{+~kx|xqeINfp z^rZdz0L;LLjN2sE!|Bn5*_^A}pX8SvVk)H%{%;n{_uBoiQo|~}nh>(P4U#l3I(y+q zQg~Ti%v)RaGtE;`Ov$b+p3!S@pa$KjDCJNfS|6d9Q~;q7tIczA5Oz5;a2<(1Jeaj; zfp@PJ63-shE+p4v>eso~FMI$X5LT(8U4wT|hZXD_&4z`9ka;govF9PS+u5P-t<%nT zhFVI+U^wwMqvvdk7wOAxDUEUGF?NUJ)e5o@SLsA1f1ctgscG1AzPc+`78(`oKbihWdK?==^dZ&7bC0SpsoeT9GI)_UG)W#=Wy>`yEbg^AyUDe`Xy-0+bM zIB3XZ4}ZRdxgirAe-QFuh#anMHDU#5xw0agK(b0Vn+k-*NQi#j+MUxulEX6FRnh(< z0PDQ0W$PWCIu(S>v&}L(`FqKV>8$R8oybjTd_1MeeuvLS@0^JMnb%;Fd0c!DlZT9K zzh&h~pX_tb;JWTlS$8?=4<1#&ZH;gHJjGf8xP3WV(+)Gs(H%{=e>~@16oaSY-wH`7(jTd zuypb@*RwgDX|$87bt=T88l@si{P7MvZqSkN=! zDmQ38X>F@+PaaayPwu)W2|dy^@T{;HxVIhq28V=y4ZaCv8Ko`0Z`|DGnVhw#arFZ) zyOPg?Gz)tx5nc1U)ZPa(5y{CM`{yMa9j#+?w%rm=_PowL8p@b zwQs&mEl=~IqS)bF=2UCmUd-u#y-(j%z=~q5hV&VrZKX-jH9Cl`i0NIHjeL9&aC*oY zPyb3H6L51fv8*nsOqG~cQxRDMFS{Ei4|wcVXk8YUJmpCT$gz?w+Jppc8}QV_dXF8d z|6-)ax%hqYSmv&`c7j5OT9nY2l?+5{eoAtN*64i*GZ9=czwS*)ks9E ziMLjlv2axvlP@M3OPERe61a9TNIzDILdtOu*`BMqC~^y62@yIgUPWI*(*T5`az7;b z67~42Q*IZs#k7)m6%QA)01yECbt=(I1mKsbZb<a`E&2H@Ag_2{?xO z{C1PjFVwt$8xY^xJ2}tN@>F5T$uWA5m#QL(*)(!|q|F0^xKvs&XKe=3!gxc0kww zcZ7bdd^JoVfj=ReS!;_F;Lyn^CUI+o$`>Z)jwjyyW4zmvU-7AEoo5!9b}gyY9w?6p z(|Y`ElO#fLPIiZF5BJ9SOtETI$*L0n@oEsgCd0vPoJ*&RELLa==58zBl?$BzIw~eb z*c;ygkQR9pc+H(3+#vV+(XY0{$r3|rM%+P z)`A-~B4E<>lI0hFh>{6s`#3mWfYti=MXltx1P5jTd zz*>C0o7%H|qzeO0A{LGSi1B~nJ?yA2iGm+Gmu*IO#L<_kt2F--9+fAwYRSu3(C3Rf z?Pb5kL_!WHv*WtC+!O=HUz`nJ(rcf#bb*67voa-Q|E>@SxQP;PAhL+@7hrO`8^+SJ z3*e;9)tSUUy_6tEQ$eEg;>2`w{J8~mJTpk*v&b+ZTXv^L1kUfXK)$LZkE1bqRU4Fa zY5uCU4=7)iY$!OY7h*gH1RCe6mxbQyB>Q;tkUg-Ma2>Im2Y6PIkRF>CwzdGJ%5CP2 z*=c<7zh|I0(Shcp#{N2fB>`|SqZizPz=Zb)5)NH94Fj+I+54wQyV1_8qYn^OB;~df z_W-&fm&QflDgd8m$+n66Plu)nfIdEJl`i`0Xpsae;hzd$)&a4Zx!*YsuTaIp75U~r ztd5Oa7e!Ox#(aM&1L{D3^v^<+96IgdK$nMMOzREj$pAI*`}R#Vcrshw@93QG{fikV zN8TR!izy`00jwBAw9$dT;q#v1A;1%i8Yx-m07$A`8GArdU>u;PB%9G{?wPz`{N1M1 zMJIai$F$x1TN?ELbV98R0}kYCOA_5JaToh$3pSKn0ZN&W`-tc_3byspGI>P7=|dn- ztIuz9q}374%6buV2;xzU0fe{;--=^>yy|&5;KX%iKPj_hdIIPtYamZQ-*v3Jw=zO+ zmNfKz#Vqa7o2KLCqaLvYM)A{~$d##cm>b@zOO{;g+p&W#AgC;)RXm3?QtMW72c)n2 zSzRIKqe8;ta60J|=NF>Chhg%NR*hPs#C~zb?_ZVlN}>bv&vZnr86Hl0tV93gAKc~$ zl|&xi19GmiR8CQ|PmBDJ7tC*fRLBU#h9N6v?36$zWou_Z>GAgbf=+Hz%IPtF7!ZSx zg#!c%6SN5z_-D1;<}oLkeV^l1fy|4H)?z={VZte?nLd9Nm&SEkf3Y7a1ElS1_ckXz zXfO?|T>ZH+T4@04okeeIy%*n~b4p>jzN&60T{lo`{&EEfT!EVfOq$*E8hxyq2;~+M zeb-L_lk=O1J3s{qCOCM~8kdd^CnFz8C(Lk>Ij#xoWaQOg#S3KX4R_jh1eJDKe*81yVHJHAF_aPW^ zK?vL4V(|q&!aH&rs zN-c-_wWEOa-tTlbW(AdqYgnoE$fHunH#eF?#V3bdxu!KC$G>yE1h%<6dxlyuI_1_J zN+O%F04G)3dF^B(cAbK&ps}Q z$Bp9CL@WZ_-!Kh*w?4|Oyo^|;r?CtG<=)Je)u6uiX_C+rfOi>uM33|5w*X#Ob&CJi z_q5Bu^p_Lr-e19Q+Jk>IU1HaIx4%sXbrgkdWw%JcZYaA>Ik{7WBz>UM;AWoIK)%9!wy>stwc|Vb?-LbvDZCaA z^~Oxab9&XEwo9>QB1mF+LoUYC4Mk{8mK~Zk+w~Co7XBi z%!Spnk?8g|jYj`jhqiP;bG|a&+#31oJc4f&pqBnoX;c~L@ZZN4w(jpv~op8l}p?mTIT&qjo@< zAarU-ox(igj>pbl4(sicW`r@t=Q`GuLzE_}cf**Ui}7n%ZRl1zT0ry-`d#)$x;lZN zaf7UF{oy^x{uayV*g7LYwHLJt`X{nQhe01zzhWh3&OLL;}RQ_)*r1VD%O#}h) z#I#k^T#oapb5Vx#B!^<$0hp50IZz=#lUko&|tS-RYV(WitG4L<)J!S>A zgE?un`;%j9_t<`;3<#_&cDWOs^N%|2@M%KOiaWRUg6vZ?Xjxn&>y(W(A=dC5{in_@9%^Ir#&76 zkS)`4=<^76%=2HyHDRjK)ejjIbTdO&N~15lUT7%i<$Ese18_#Cbk16#M&ZfPvRzxG zk{H!mQoZgvK!<|zbU$d$i^-!8$e)$Qftq!nNC7cwO0f_=V|*w;uVMm7Sw;MB!~v4w zLx277g%ld|@`1j#qoxM)=W*QN&hXK`(ZW9>Z1hVWR#>yp|1ynz=;c6!6Efe~z9}&> zfrA^Hz;9z|Vga5=_@S@(OWG^#s$80Z5Z(m9ms@q1bR&tC+;%#Qkmul**Zo;5tcftt z0z)E41Bx-d96vc0ZQ4q`DAUhmYr4%uu~xOp7$=YbgRT?r5JFbcuiTNn(TV7 zoJ$SYXm+@A7SpJ@4TZi~17O)P_vrz47)on40?zu};#KIu;g?eWtK zTS!=Asr?s7CSd0DK5-qXae*02UU&&Cs*iV`Gv(8zuA>5{X)@_U3FB6>_}>$;-qX;1 zDNR=+Jo>Q%;#GhFpRzmDDPI?(75Cw5L?s`51JGLQT4br+bOAPK>&vP&LpOja;V|V@ z*TtVCZ1C%BQpV{QUw@P7FELHa-Ulz#4h%^3MjFmh$N;`wl_--8+j2vY({eI3wU-U7 z!P;plBlKe%kV3ZL_?WH1)#3cKgn#o7I@7vyflk=;#cKY_bQ2Jh@>!}%8Rq%g0s6lg z%u=e`A~5AicuQjW;b^%Hg_ybs4{*2)S72be8sPfPAf?Oz#3-iWkMzMeY5TF8W^mhE zUz^h(e9DMyIIpAAZF$t-3XG#GzO4WA5aYbd7H}{o!u#}y9CT#GaKRG*__e;uDI&IB6UHJlbG3mkHlglfyo0x4pC8D#;^n6kZXN&M3kLD;z~ zz?wm(f6#st^XHJeZyweKutpRl*Zu8p+#wZoFq!w`(kL}o76fR$$e$UZcnz9L1{?c8 zqOD_Pqy#)w?-jBK7C z5{geNIU~FF=XWbi07VRdtL!h6e%k_+!$0KQC71+idv+-4Cqx(2rWM+xBH`)l9ycqyI!8H~{BaR?lGmr&*gw&hlJMXJ( z&rY=F%}rY$2BEWOr7`k39&N#;TOKbMcex#luQYC^OK*3%x9_bUf#yqk@HTG7z=2bD z6V(&K#KK$8h1hZ9!;q3tbV7`Xf{;%IKNTtQD^he@=YI{%Qy+I$6gk3sX zX?#|`s}|@AwmRg!$=9h6iU;DiMuoKtDL1vsH|=I`y!KmA#ez~zK;&Mttx=qfFE&5th-(!@LO9xK-*%` z6tkaxbttkmq#+OPNfu2Pw+9lM@Dqq+uK&e>8bFm^&>^@_j7#e`TzZlx9KPO?{y*$} zcRbZ^|9^w5A|weRR6?@%D60~R?0Kwg+1V+wva-pDjAYARA@ewd?3KOA&i=hlpH%mK zfA8;qzyH4f^r+K0=lwp{b-iEn`Fy^_-6(oy!KbnwI!aNsFe+u43XLc?e39KBbzB)^ z0ftc(cfi&Mz`MzSuP{1L2(O5B(R9=6p_=qN$E%KJ0F9iB>v^riZ?|wW;)7!5+*6tM z=}c(HlMi&DJKHVxN~S}lsF?;Ll`^}`ZQ6a6$DH_DqCXO{$OI33Nk%UA6m^v2yIkp@ z+Lz^fpZ_gk`f6i0==~*1svV_g!>1-Lws|Ygk0{UDv+V-Q(jagiMcj zW01#xJy!`LxD(CdQ}+}8jVzDt|+O7x9qA5Q@TXmz+>p+8zvH@eu}qG7*rbEl^Z zaEo)us116QW~V1<;p@~m$J=O5P>;c1fF8YDpD3BG^6Bv6rP?gq&UqtMoyYoJ?0n|H zRV>u~DDA!#X1(J_>9@B$b+}uo(AOCYL7bTCEZQT>3(9eD_f*l;2 zBnAmVfJ`@a|MeAp%C-ZQq2vhhKGqB zgjci-!h2@Ej7ER=^V^`WqvS8-z9;*}@MSO1%U-@BV{a9Bb*eABf{SM_#X=(rR6GJx z-M2&u1hvC+zi|7wtv1;}o(mFyGMy>XTu9g1=jPd3Rc{TAa)2DAy}n4YwzHw%GU8Sx zVP2B_!F2FPNJ;#~SwH{3zyx_L-|)jZuL*$(_jf-l=@k#EqS8_+s5n8j!RjY~;USv) z49O$KB3bSmDcW$QgVn&jO8)f@A!g5D%~J1{U7L(?gPcG0gtH)!1u7SKzD^sZ@}+ha zUF{`+P$8q4wVf(mU%$?#ThGy8rvb|k9rHwWn};8~?L=RmX4jOVaD2al&ac3Zma9zn z8mArJdNO3o?SYg(ec{W<>35lnO{c>H`ImU21RS1+GON||m2dO9?unJmXF0Vv6g(h~ z{kZfo;Omzo7HZ^Lw{Ydb4!O}#X)K5F*9X8xeAvpL9aZ3Sl#GD1gei9M3w^=Ra0&j! zkDagVLp?QHQkOS=?96AQZXH{ar@wf*tSw^ZU~_GH%DQ5|yeY12g%G(*aDhczrK+tl zq!WKbP_*?v%k^>L#*Ea61;4SUM>as@I_|{O$+U30FGK`<6}wAn9mSTkShI10PX4R- z8!fh~7jH|66<>8i16q5UY6*mA%yn)+llyLQc|EBX(@jbCGV(0dg8pl_ETc+PYx@BH zLuCHCMSDB9;XpR4WNPkvZYG(_>^Hy$upK6eNlFt?34I-W$D&d>@fwNRW^WS^-7g~M(uE7DM2Yv{q1ecaTa8{SNzot%vO``4A$mQvj85;#Oj<~XP+gD zWf5~BcqU>NwjpqE8!pju9Ylmy^PFRfxE6k;;QA#Rh+6J#7CaF+iX^F%F|F2)+e|dJ zuYP+$5EBQ~%?x$d?PUA`fFNB+vS7K{BM2Fy6`XouC9;hQ`+5)cd%QNKWm-zdUyAaMcFwKs3`OIg{GXZmyTU2MB z9-Y?|JschvkQCI<#B`}AOO0s&B%t%$btgpL?^0l6$}|3%OqKkUN5Jn0;2FbaP%qAL zScfu-w@`K_;H)b5H`N-*7(7S6HFn$%z(Pv74l*9@nUv=&KgdxpeM-ZiQJ&Kh$#+GH z(%|aRFHT6noiNDRLvbTf+RB*)cHg}TYm=38br3s{^xqx^n5MNu|Hr_VV46{XFN1~Z z)pP~qn0kLkZ?C=keOvp z6CW8{5o)IsP^!>k8_Ge5j#LqKR#B^!Kg}Jdq$w|FaXKiq4>*N%+jn_TdC+w2#oLg~ zb1!ckl-oRa{Y!o5Geb}eSmLEGq;oFYlS&55fF^{pg(nc!#im-K80Ax(O|t;17JmQD z1zfeE2m~f_iLidtuIivv1Avod`O~mV#XihbuLuZ5-rSO@^hCr%$ zUSodevj#^N`8dWHXn9S3{K#R{|MC5!3}x22%BsCpzZD98$#r^UQ{*+z;M(fdezU{I zZjHm2OC;4+Y?b*GCx0yyZjOlgDP;5BQW>NxP{h**zGo(B+rjb8oG-bTCl0k?pUGbt))56%9oP9fxaR^Ejsq{3|1$d`Tc3M5+9#t2aJJt9Q-%fq@j* z>oIlp>X>fDA-)os%(~Z4mj|U}tQV6nBz{yG4r%fl?&CsJyU{+R@VD9VK>reU1LrFm zrGo~WZ$U;56%l8soE`#sy4IuANy>YnjT8kY<{$TKajkVRbJ6uydLD?x+im{>OS}2uuA~oYa72+zl}J)In%D; zxIS~<+yu3^(s1Lw*5Q5Yl^Q&?CdTY?!`AtDh1NJhCB+P78v7BK$DiH4?Cq_wfU|Tk z7*NLVF8K?;>CLQ-iQMV2Bvn7_xFwlU0pOI7C);^I5vwy^>aDb$S8M>?5ByWefx{go zMGIEV{;O|CCq3z;=i;7tltdyjV%Sz(8c^E50}T=CQuJadUK=c23|nnsmyM##KQMi8 zICwLziBBR!L}C8C$-Yi3M~>^(P|-wAEk0iS?b-pxY*P!50TYz1Ma4!bVRszyFxaX6 zNlEodv zKJ4w6p^J4HWl4kw~bvhbI6QEzO!OAG|7emX;mGzzZ*|Gykk299#|D$dn6+U56o z*ZOYuw@;yBt_ts;yT}BXB0ocE+Cu4gp`lz5orGgBDSXIWwt0!;lW4V7UlKuxvHpf9 zvqXkz#zlU+(0}*JMb9T8GnX{}ijDb!@5DQeGDbAXo-npEVnrcKUB$SIuPZ-Aqa1i5 z0mUF9z-H8;w?+Z!I(KVd_tOtsck2hrszU6C9k>M&x9iC*g0QH{N7_jAT} zscGZ;5d3iZX&|JZ2p*fli=Tny7j64>)$O43!Pnb?D^oGFEkm{g4OFs3`(J-UsWlHR z+XU^&rp-+&(=58uyCj(B-W%K{9I~6wF6XE@q%xTA6UTYm8NzLJ+Ly?zEnpMjW++d9s$Nld4 zBznHG#X#~}((+#Um4Ljf1XuSAU872@HZ1Q%Gr&C3?WNw{-YF-IhSEpD22h`vxd)%f z^N0x9U^jSmUvnMxoW9&FZCFu`ccYkRPe-^xrvCZMPf+*1#q$j)6-CBfZ0d@&E)*IE z-)mXX(0|I9Qu}2F06*JiShE=J&#>xBTy)=OSdu;}+d(Hl@0Rx0w1-oEKdvjJ(?RIv zJC6-S)KF+SMwtyjt)NFpF)3HKK{bDhUy#q{$w8R<{rJIP`oC6F@+S5g`0PT*7gc;md;XFA)HT~%^di!N=FH={{YS7mqmU{_aveTH>($EjbY)`;2GvRyng zV~8T2%7sJxlFK?ygOK722%GO79>_Wb%oXeb1qZI=t@daYA^QtV^dUQilFD6PFj14ex`93p*!u*Sl z<{&kKgsq{Tfix_E)^Z9L!r|>{?hq&^imS&3T#MsovHolYwUQNpRm9JfCvTb6lsUb_ zQx-C5fPqv=(UtuD7511`^T*f!X>e2oeXkpGv6n5`-y9$sX34e(^w>QAr|qL(3RNw` z;&O*IgT4x(W@2>MI!@tRcf)n9*t;yMrmHFsq5+ zcC8N5G{2<$qb~x*N*U^+_<3twfTyME@!mMv{~$edt4^8?&+9LtQ0fQ&w23IvvMFPK zVJV;jjBb}H7`9Xc)~4+1pMk+1~?1yT>E`#=bx9Z zJT3Wmhl=OdrQa-52p(Pf#LrG0<{p>T_Mabwss2wd1@8xG6!I4w&;1)*=%nG3&$EBy z@0y_Lj_w>yxN-Ab{(W^?^o>T-@=*MR^#cL7f7+`el}~K`JpItqUZOktAiATi^6S#8 zMUf^)mlpY10ZsTa`?uup-CuopIyY7xilc7`lK**Wc_9kY-x2sRcj{l4K3i$FPI&bC zO*FC~H0JNMJZkMf-(#NY|8-J-ZrHJl*KGLyz*nA$yarqV8dlJxbsGP-SnWh8y16zL zO3?5ZNQN5w*Q$G1kh}cejnnA;nKbyThx;JtpRX*TcJnK-`?crTp*){70wepWne6KfO_+iT*>Vk+|9_X!(-M(W;%G&(Uk@$+KS;f);Y&ITqJP?xl7& z&L91L$i5$){V|_C1Vul_cc6V{1u96GJFl_BPrB^R9MMzG(YbS)bR(xw1MlRLapel; zd@yG_N)(UIBp^rSZfg&f`sW#S8od7p3v_A5i&-y_ShTYrprD7IBUQGb!MdzGkSzzP^CD zi1%sZ&$?mAiHJ^GRB6!eZtlktC53HV0VM6}hdZgiE|5Tt$D~zx54yv(bYJo==p!#( zg&sZ~%Q|DD7H-xba5MD?&WvBtFObnqq?RF;RJee|+}HQP%RA9w_)sVm_jftUcH9Nd z=SbX`w&dnHLKOV8!;q-q+J-Gg+{-cT(f$n~!47)Xtv>tkJ$Qf?1hh*1uab^IvKYWwDFU6#i_r_EA5Y`#D;P|9>!T&pLO)RcRYLujuRV6+ z)>%Lp*JvaL{@d$E{}d4-0uwLE{UYnPPdfUqUl@!3`-Xq4)PJnQGwE;J(^$xQ{}@w~ zu^5o1a0lI|E0y&h0fY5pMP6@nVcQ^ncfCaNUKk(OgM9+I$D}L=_dw+8v0aJnywp3R z&itC8!_9#%Hk%o2Yn4NdPPggbYMAbi02(gS#gDZ8)f5wH#ueC}u+B3Z*!0NGHXl-j zf=~}#ITineoBP8gp&(4W@JLFR@6~0X1=g`$)p>kzbg%?ZZ=u)6U>QPCym$J@wHl>7 z(>`2>aesRh=-{ycSLUKu5$kUa#&^*Gv`!y-n1%Jr*vM&qy2KQ5!{rS?ldff%WGQ6_ zs5@ICi~BpSDWlm4_Ep&7)@{-3N4IzYY=zd5EwACvXV33VWVsY6+DAwf+-Gnr$BUcPkG#{naCpIZs7+7( z4Adab<-47JKDj{t8ykro0RR1DiR8#Z9>=)v4!S=(M4!M6HU&^tH9eH1WFTP-C(W!= zFMfiMT?9QEN!NYOMdbfvRi*;@05iFxjAsoatpT%~=|5(TAO9&`MQM-4_#!0U;t}Yph%3yi_QU>bl#e z5is;u*VX^k1%sLTylEVFebL_FeIGV?@J;a4dR!*+dcVjA46Io%i)QaYC*ELTs2Vcn#l!N zF~7z5xW}yEq{>~mE}Q4ff8NJ8WCB{q8DAPEtZO{aj$u$AJB}@)O+fuDryT1(o2bXH zSEx&X!;-@^y0+2~UvR=CG617{z}!SnyyQt7;~P+n`{L^?E5zHex4%`NV3==E6bZYE z+uF$wpw0@1AET))I~(tB=cvh?u#Vxfm-KD`rI$A7kz7!_@3JsZ^ue-D8z&eFp+$+= zxI<5ByQW&$P5M`fBmt3R3aSn|ZMAN`=lTW=1)z_O_n1uSquI!T5WCK2JoitFq>B5y zOJ%jVEhDaFgWf(YjXV15DycGN*3d>Si&)5Y3cR7wA2}W8)aTnV`nt4u^pqJUaaB{KAe#`VNkjBjEs>)#t}E|d^% z^ZAG?D}J4RcD%NHreouli%(NlBh$ki>*-y=6&}v(s+d#`dLE=yj+HCQdq{I^S%-spC~2iOUe5&V+A+uew>^stmi$%=oRc zQMNf*Xo$Bkq&{j#1PwY}vr-3=Grhlxu6>A_aCFXI4PKHNMuC=O@TXF*5x0$ejWQ?N zz2^#CS8MVvhf+CzZ})aGG+mOywmeI~T~cGk;cBkpvmV23!A0dJFjPTdnK7OAxDB81 z#LABqs>r4gVV`T$t(o!d3AY3pZ|+X-*i@awM&2L`ph2HlShU_wY-*-uBlgy(N3Yy@ zdLj$XY=Ls_EAC}HHHlXg+rE#ba~iF#Da;vKZ*)b(god&yehqsdU^&*nC3{;f;)WUR z0)hHN6xMqy62>@fW(+hGvz2e2gIs@Z+Dn*z{T_9=t-Bm5x(fyQmr+t6Jx}1JeF^I4 zr6+Qm?>$`_a(Y6ocyI59TA|HnTc2PeBKk*+M)!?7Wx0oJGVR;sGrn9STmvDqH7?!V zegi6dbzgshP2^lBiJ5Jd5T}`Ah?&;q_lcVNo!?T%16eH0OC}vn>S$u{hD3u~?h)}h zZOqM_m$eQQ#b=lmSLfycVAe(khg~GlC8K(@+!)i68jVn~@qbUJojlIpPETBVHD%% zX{SEsF8JzL&FUqt(bJ?yuYA7x4HudFD#dBb&ixCcrTqB|_k42}hbt;J6zv9rpKZ^k z#`j6Brd5mw3_LegdekZIfR8k$UeKp_BF+u-li2GGaaI4|-08hdMPwu%|u zxsj(SdgZ-tS&6G@ER0mCc~k+!_+TFAy9;e%XxB@VPMYd+lb#{{ORmLkpCgBy?xZbu zXKGNt+NF#VatSBn-=uW9zM`p<89%s#KtQ;XrFc^zG!HQ0+Ffqbv0ErfD(QUbA?JLt ztvT0xiQra8l?1xuhlzfzc&luD4=`2+EM#-U^@eEh2)OT}&sdOtiYo{nb2i%1hTA@@ zv8a8igt5&Rl-yLd?0P}nk1IQ`EhsI^W~Gia5_#i;Zs4Q~vlNN=2QMx4-V?)As8lM> ztcY`1RO%gVx0UhlJo)wfJo^+Ua}41i>vrOT@IP+g*a;j->JK$}j2zdk?3M<%?OG6o z?O1QqP=#h?YOHdEF)y&THn}FHh6b6Ln7qWftNdRHX&{u_^GU9UO36Dnht5uA7Rh-w zU6D3t>q=Mfy#|JZ*sfMs&7FetVAhVY+5T z`ZT%g3&CzG)P6_uNIxm9t6o`_A5{#yd)^0-Gz|Rr*y7O1B&n64&SwiZVI8K!Df2|mZu_?W_$Y_z*vURyF^P<3 ziMO)xSeYV*cn)rKH%rf7II!K4cP-C1Ky7^9V9Df}o!V|7!$C?%56<`uhV3U-r)F?D zSIw3!85_$8j3y>CU0E3h6P+?yQ%0St+n0~ zYpsE=fc`S#vooFKCU-vA=sCp_Ex1AN9>?An;SsV|zi}T#ZKGTs5^fGEZtpIcmk~=%(498rQ&*Q&N46Ae-cePMXU$7+ zA?Ffx>c-6ol^*#SJUnZDwZCNG&~SUyq_k+~tjy~3xSI&MCo1j5WGbA+ibS{Qmqprp zvl1eMa|ZJ!DdUz`mSz;HD0_t7iF$<_aaGIn_OXB;7f?%n+!jDM+?926u*^P{gX_Y9 z3n2Zf(LsY?m~6g>5`9r6Ba;ccsG0Ev6CynE3)6HAt7^erDtA}6g_g@7m7P{n7TWC9 zT}XL8T6oTV_^l|5lZtEi)?g=-`e)O@;->tAnaA0TV;aHB<~yTaVG<8JUEh_)PrF?` z*OlRs1ILE=m&H7eEk-1w957K$cC->_y`4^E-HcH{^lb!C@_mf&Gs&(<3{V;_T}@PY zZFqG>rimn+oFnaVtMDO(lOUtpVA?F;(F{~KCPEl5_DOkk0?xzSx$MNG_`uV)>@|Sp zM}sKqSbYCVWB2XgI>0woSq?PcQI=t89v}(tBz~z&60U*Znfab|a$9#d=9tiTks|G+ zigk}k5H7S3+NM)bR^-SlW7R4)i;cMUu!h5Y@S3V(OV0%FP^C+H4pT4}eX>moK%Xn9 zy;s3GiJLP<^S)6>s1UZd@4N}lVY1Y`ToI4I#lGA}X|RhEEfi_Php@Y0TRT8vip;1!!@Dzp(n;-5M}_vzr}f3-gWi+y%&{A^ zaOu{Un=La28aQxzb#`YSJ|&;?=k!$!zjlqLOBMibt?TpLpEqUqjRDME5cQz4)oI<3 zN?B~Ce{UNPwboXZF3QN~#hq%6c_)m)5K*F-$?o6#GKAb+ElZuOGhM+i;f`ZXUYWxP zCh1o;Xw9oUXb=p#;#6=x1wN~EDlA&cvjpDoUD;$QMlsblp!aDwYJ?JjDzrjZKpR@S( z$!pP@euClR_7_nCAL?&AKjOfXPnFJJ#MmyOVUl_fy5aHKLlCFBGHqtWaH3JE1(K?~ zR1nbSEE=+VAvj!OojQGdFB0Mjt%Z`E6roZs;?+qg4P25>dqAUR&>TtzZfrQe0f2?5 zK`SXeoB7_{?uap&?+RNY2*Gq*erEBa`Sa&IGEaV=% z>ycbB?o3h3X+rc4sMrE%S$BS3IYwY}c|^mDYh#wZcW+B8y(h<1ly0oyHDo)+!!GuT z%#J&=oGt#`n#2#Kt0o!B%8!MV|C=+A*Jwur{eW}y4s`1M7xb9h!~*~3fCO-aivn# z#-eyI2s&m1kRK02BRw9SPH=TRiXwWWA2BL{H~sR-mE5)5hB#Je2w3bsX{4dAz#d}s z$3TdOfJ9DLOVnC2sxy9RXaI9;>+5^=mSW5Imo5A9lBMUn?fb0ypJhI7J6>JyL8L)n zdHt@3>-TEO#as+ipLPJ#e@ska;^Z*kzMCdA5%j`rJ55nSKn}uX{_%qJizsf3N-|*~g=322L;;h6i>Zp4 ze7jy(DCjBm#CRW^`vDozg7bB?@bh^wXL~@>otp;CG67P-{buzZ468H8pMPQKt9 zJ=5nbtcT;X`cbdgLvfIMANdafeLc=oOzk+Mk|zp&+u!)u7@?Q+jmR z-%?MXtFU>tGX;ffO0>rZ3gxQ{m$UG=7S^)oI}32Wn_U~+%b;LPR|r4ZF7`;ARm)|A zge$MtVl7xwB61SxmXnIkHF&{%?`3@J9I}zt(T9@2Ksm^EIX&!XJM}Qn)f1Yjj^tHC z+O$vUE1O+K+uQC{QhG98ENky{CEu}G-zAbU= zkK=;%J>t)qWi*;|C-$}>GXkS6hG?0>(21iLSaxejV=q3b3j&Y&kh=JRIP@%H%JDWIJ-h72G(w;HIF zRGfU+^zX&Mkdte&OCenn8i+pskKKcfY`4!7`Rdu3-OY0(2vg$7>E8nnZKjvEAftC{ zY~X=jDoS~-vF9n^_7&eTYdX6v^YUM>X{HV5T1|X~F{kiBF8&ZrL8fq74<;lWaIlo^ zKMGxsooKp!48f~n@Y?eCFFKA*$K-BUV^xFyGC=g7ss;L|Ef8Xy4d3!MG6?$Zs*asl zxeuR?d+b$t>(3j-XkkJ8aeIER2YBzGK=|+)Ay?7!U#=Y9-UuTkktNTACI01&F7RUC zV@c_whYwQ7=QPi9)Nc-!{0;=sH*n$v5SDKuBM!COOnQU{%ylm4{bdv3Q#=rqQ+S2k zj`;iQ|5#1*tDo87!FU=+xpV4wedgcaOpT92&D1dZ?hE~&8~XP*|4)nX*J`5w>MaUC z%aVt|2Ise<`?q&I6A>VWAaA! z{ODo-x9|ABrTM?5`Hw^Rf2Zb;UHbp^K)E{;hac`*r1q8C`t?=1l!1xms!7Qp=sZo4I)(~cT8>ijp%cw-b~ji}bx8+ac25vLmd!;t(oghXK? zS$If(K~4u?P)xG<>5SiKb1VaT+ca4CZ+dsedieR+HQQ|&-^A}w{W;=`2F;y|2V-%mR%%~sL>XI zo`o}2wk*dz6_TU=?hYX>qVK{8?nwmiL#p#TXMW%Jzgv5epBD#$y$TSNljkLwK%NfM z`qOcGJ3>k2Va(?Q|9!1-?o#)7P68z@lBMd+W5kb|Jehmp0=*PgjnyfD+vf5mV|?Y| ztU1H+-TuW)4yR6Fk!j!k&v^vizoY7ZUumd8N>I0X z>i22BGas9lWLoAJD6-&&zJv75M>a>L;{R!%#EzEv40(49+-2uN#g=A@X&;-BhdvHU zx8Wd=>_1P{Z(r+C!)c12qlKM0!rfncZ+E6=M*@Fh$%E83vvJYNbKxrYGKV0p5dj&QpX)obB>2M*sb| zlvsG?0;11Cw^ILnriXqqI!*))@n%qV80^>G+Zu5L4no5_gVq0SEr@(YvLu*HCVW*j zK{0STf3N3;g?_x3g(pJFVQj6)_0*rz$)E7-Xf1+%9`?<5`izMiQ9q8f0B*qaqU9ht*x2m%dB4|MOEv5BM|eiI^PIR~DYZ*Es?y zcZCi9`&-e^1q4hdWG-qPRc8P5%>dy!8;!gePo zDL5n*{$)R@NpXNZ+<1<;G@ExOfoRH`7N4E|-nq8AZoEnckICopCmoOO`4~pt{cj+- z!Tmug8)YSU{b&#V`F~8&Z)r*R+$7iEoohj@eapf|IvfkT^4ms=Xc8$Ge>cfDAJW=9 zpBy0AOySx~V*o~djqA?l*2hL|$-cx z)bml7P!51`M>4|flkcB3P*ae$&cQ}C;obf+>7a{Eggo-#S7hl$N%Pg z91?C~H?5uZ_mZBy`zcT{d|$jIv?Dm`&F}C1`5l(B+I3runamPwV$8Hm4g2E~Sbvu1 zx;w9R<*F-RQPaju{m-d?t(s_)@S3% z&5p@^=r%I_T69%fK-cq+FTQi}gle0KMz()ZL6+=<2k;^CB}a~~+?#}bKV~zEav4+{ zDkc46s{cN;)KS=U$kIy)-l({H^+yXKB1e2D9Me!vmgvRb4qxO7#%q0zz??TtVj+Kg z)0n479+Swp=hgkc?U~4}vvrf)e=7+pL%xE zHwJvhW7UYC8~i#9{XHSJt`|uEJ|#LdK}_?G9+S!jF>FvstvuTc-5*fO*)|xhp`VJe z2uI_>#RrQ34n5^T9Ka3={d!PCG=P3R|K)gM^tLt0P^+9zXcCfrMezG5!OLWX|J(b#nV_!5p+lbFs7`fX`YWjwp_cr$ z_DKz&BKz`%xw(g*O0{IZv8*?ms>Z^K0)+}b<5*DsY4~W=IXtzoIDj0qkxxpSTwNP` zL4eOm6cLspz1tgl-S)ogu#nbrS3)%>^qoA>c|8Zr7=4|+H7swj2~dre57_~nTCOG3 zYS=Qo$-Y~Z<@F2svcaZZsYI|Z?qZjNnDD-c>BwjuoiFsZ2MzqXz1pj_B;f?S6K4U) zyPY7uUF&+dPhbGl%xX@`1&HgWV4wk8YP_rJXWLk6rqo-nvq>T0CMgO|+k;kUB$b_nBq+Qylay&D5mYj-4eVdAg~h@(ZQ2Kx$uSI(twM0krqWoBbv%-26P6P(bQg9$DCCZJ^MFN>&v$ zOT`O_4r997r&jAmy_^|S{qk^4d%(Y7vt%600rCX=f5NQV>5MhE2U0`bE zWBHW(WZ)E<;wJB|OJ6{XYvenuUCCdr6h9evaek~$3aD`=ru)7mvae<;r*5w$?Cxz( z^Qp5zImrrZ^^c?8q>o(xGmF-@TPrf&KpLWQ8Y0zk@b-%_bppwg^ z=fHqlPhj?)Z;FKf+^E__(>C~h=_*&zj-j;WPbZ}!=d(<-fthl_@(=`!xnWjQB4E1W zz2^^nxYl8YYAl1Cp2gumouJpgKkb>Z+=bJB+q1ko)GFN@*hrN(Kc1{);3CsUa&<|e zKSEyko`1>EfY0{cP=+k2(6(NwQOCO|6gl7et%uT!YI6JQ@M8R_c=LV3m@Vm8KGa8xk)}Hnufl=1 zVYXB{-zu_h-uvCL?LfPd`Fi#&-VGjMRn9V>w*skQnl2mqN&W=v_g3>j`9R@9#s21% z6yvVzgqJI|ln8U+Sj&Aqb4~-Xog5(%?X+ZMRJm&&rN#N<9Mrm(q-h`QZph0uJ1Sl! z7C8KN74>~I@YDuak!Zjv_AG1zi}n6U<$lGQCCC~lwq6T{W1%O#}B>6G~}>E&>~m2v5|9y*x0qj#by@0j!i zZ^H85abhFG(ZhVVF(7BJ-$eKRbnDPUELxs|9dSGTX*wSCsfnI|fX$_K9{S`A!R}M; zuCMLa;9w(?JoinqZoYXf*KK}nX4+Gj%E`321V)BkzEQSOTeI3JD?INLPaqMh^zMnW zUidec#ey?tyxhiRc8k`i#^5%wuRT7Oi)4jLqARjkQSri8{k_SvCZJmR}@cVpQ zt`ZiK7WO(#W)SyujG-#s<$aFhMmC`>XFb%vy9#GX&-F<7o3o-Px`I%puj^LG#?1 zm3n{si>ep~<#&9cof+Ni%(8{#dVdQ}K}Q?g{G?0J%Fs%%U%n*KUIz32(jK~%+02@Q ziX9@K-bU)XT?otR-JWx-AaSiEpEq$UuCDGp`_u1dlkOnseJXvA*~=GKuELy8Q(3(% zgGJtZq1O;eUr3A=ibCL!_!(a^#?%aph<$lE*=40jt7>=(xP%YFrN``>8B$f=s&|vX zxCW1&s@HLtjWQUNiXWH&CFY`r`A3FmFiEID+nqc+ubWw z@H@vahLX>|Zzk4Z_Uts;zggZ#2TPW(BD4MuZK>N}ThU>nSKvK65B+UIm`+JwyasY; zFTB65!wc(iPj&RwByYdP5ak9o|0zIkDLq|0w{cl6duxF$`cxn6-kssCVJCk752U@E ztWae>gnZ7b|47RWJuYRqTz!GtLYV#-vQ@7+^!=tf;Zzu-5@QOJb#jVN2NN|WBwv%YtEwABEwJAO!GxQ zuKG>`YhT>WPJk|<2!!`$`qaw}lLBpF?j>vYmU2UXWVVEVr>)^GkkeZ}Qew0j0{19Jw2~a}~ zqc(uHb~U~pkJJsoU$s5EsB%I0pn{rBP{6zjCT0h22o%dF)dGY|#d_GGU@t>`H?*KG zh$gV^YaIFeazMu~J2-X)Fy}Wf_79l^2yRBU_y(h+D(can;pFJ2PQ>34-NfZ1C-4@W z0F%~Gek)(wa9@G<7ti&};bE!RisE@YcN6C!jnW15wy{gF?Iq@tn(v1PFpbSl6=hfl zs0uDlAf6k0YKo8TG-Xnq58r1aiWJ_~i}q(bACl9M*IcnZHD8$R8hc65G3H^CWO0m97HHsbyf{$=1}%LJ>YUSVi*vrmH-vR;hcbGfd+;WQ}}3^>0M zn{u1$hMu@`mi)9cpqgz_@orgbiR)gyX>-cvJ~=P_AgU3IfZBjO^b`r8`I?#|w235l9jv?w6-GpEcP3!RcUtKx*9GhE}BQ^D$Q z<^jKSvoDn}xXyeZpOP;oS9j!NgaInr9P}%mI4yJsUSVRk>=!{vn30f>uY__59C@W%`Id8%Ql9q`kDhI+W`6OIF(B&fkvCY7a=w-1#++S?r zUj~4+mMibo?`f#C1Hfr4Iq;2)aU=9l)Q7{W1+BTR==mYqI~!6PooDcn>0dt@t$pfL zx}3?x>3nG9K;gDC`)Iu11%uny3ShB48utEVJm1}~CBG_&`d;yJxi~56@qP~f<$lue z+TaOu(Ohh#@1=+eNyoe0w5uOCX}{Q3f&5Z;>j0>fek5#|Y>w6bAW0k^mJ9sTwatNX zLD%?5P}fV%^?ndzQNhXO9gQAMDWDeIW)`!whF}rfPLjqNGF-YQo@dL@joSVC6vQtD zcSG+Dr+6UCSIh!tT}_8ciLc;-*TT7nK8ZNK@#g<>5|xflVxXv^r#|;RrJ_rMJNni; zv#FRBjrW!DahLd%%Qm^!>PXizl07UC)VD0FpR=5})lKqJGQhdsuUn9N@ReZIX1_!s zGfpYw%tWD<^Io*!E;NdsNe)CH3fflwLnobHoCD#ovraZuIAC71^}5lu8nXZeN^3@K z%N1MZvd+>&pIS8=CjuFNb_{&e+ta-}rMZU;@+V{mA)-%+6pp+3BH@|+S-+e&4_yk9 z&#cm=Gb*IpSF|8z;&Rs5{S`f?xLhToP&5V9rZ@_svyl)9&#=Q za{T&25@}`pt=%!d`KR5~A*S0oD6ubZZScWliqmD0vSCG=KMoxJFbVk0d2~+{@4-{t7#!)E*J+{tNogOI7 z`jj~{)m_GgLBYT2mg2Y}Z%Vcmn3#Zt@B9To+bZ2vWzaY=x67gg*|r|1_p1^qi%kK^ z@~K}V@PJdYg#nL9w-LbFc*kGKvolNn%wXp8*Y7v{X9ZtxN*FpYFd1K)30~{ z>JW>4LT@fuG7BR5NJRB@smuJH%=PB_k+EM<$gr*m1w6H_ULZA1ueABD*}$;HQpr_? zuaC&kkkV`@36#tvK0==H$tN-zTAxj27i^opQk@K?DU>4v7O5R22%ui*EU|t^Wd%o~ zZBcnr__9ch=$yF3y=L={$odx(=t`YXbl=K~{kc z6$-9}yKA)sjOOy5zg_9#)VI&BlPSe}^yrMmi`>ebw~o1= zg@d%6*Q{hOu;v$}vFK^fVys`_0048TVZNPXaOv{orZVn3K_z8H8^Z!)1vBA;U5hf9 zCLkV00d2Mlz(@Y2liM#r0Z?`TkP!GpR@<=^Emcu z^rk=Cb0aBW!ozpi2eVqNeUf()=!H+!>ba)&Ez!|;qW=d)LCy zG5D0FKcstQ``E4#DZU!KHVOr&$W9r4enUx50Ny4W^{vKv??lCVcWK5$LJ zoV23p{+pl$VwZB;6W0I$iURy$?e1GexG>ne4lqRRg{GQ&P&8pdS2R%{(a7R{HUQ!T z8TEl|c)e4WiJoe}&w?Ny5>^I6CCTH}9-Ehg+O57AEi?4yPT}GALQv=^T)7liz zEBSs5)e^!vfjBXn=}{NjVVyC4OF1Cy+~lvmq0ij}>h2APTj|g<)66g99J%dD zcPceba<0~uOpAN7t)gQXLE|ymn<9BglVYg5JtrYW#+)bV@7U!>y-1m<`niBc^pyCX zdaFinsUk69m|g5Ea_)NEH|B9_BP8Jp^&5+}^FJDhxLlA}htU`_{L7|O@%?Qu<5}%i zR~3)tm{Z(0hc|8)J5w2%q&UK+b1sJ_(OW(p^O2-Yj8eE!<)PoFaLuuIKhUNq-cd$R z;q77ClTOfa?Ck^!oA6ohA?GdS=j)NINwi{nAl;}cJ8vkAu$xcW8mnavA#Z>ZX}|OK zzl1VTH@jzjs3ORrxK1~ z9YsHM9PWFXJ<}u^>crs{@zYp@$lzFok9k=??%B+h2xx}TpkjADOO{GMDqTR

Y+e^qD)NU?MlJdHvoykny!e^H5|eNGi`^mA*)xV+ZGr1( z+9fITubTQm*sq$pLA;CaL>lFFw0rQjKe1hLtjnP2VCs|VV=&eq$QoAutXtw6JrK2ovuGjKsn)=>gz8fv)vx19w*_$_20`^VBk~3^XitVHLfV?#*oB}G^A@!*X@X-EAYi+yZ|V*`QewG3xv`wu8q5FG32SgEJ{o8N z4t9ttr)f{qb19eO3vQ@XI+tu6dNJN_dQD#uF=3?j*~I@X>GvJ+4oxSL!b3OLrILaU zjl-G4L(t06U5!_-uu4@z!q?D3IS_C8wLb_7~q`N`s z5RmQ$5u{TA={R(EH`3iD9ny6-DC+aP@Ar*+|Gi@{9C(0p_HXUA_g;IgIp>-(S0Z4# zzVmd&z3hC(3e+?=@w-iQ0O&KKzLz`ia1E>Yyh+TdovQux6<_XQ>u7;q;eCux6v|@- zKd!(>KL$q(BM1W_#Uc+-;bgmFS!UwQ`QWxGen!6=U5`d5$a(r^ByB1CK>33t`~g!8 zB)fPLRI+rV&E#8GdN!}q`6Ik*pA)7)X5GQ~sCd|9E9TXDHbBsf&!TWP7SzcwdOB;# zw_U}j;g5wktefSO7PlXUQixZtI7YXwFj}4OVg)9JBoEo1Ma}f4a^K~;ts}Lu!<>}o z_Et*c_7=$1J{%#mfyWe1IJ33gzgQUL$nioVr9JyHzx})lLMOKtRkogDw8^N~;q#as z#=yS8a`JuziXa6^?-CG-)2sNJsG&q~pk|{)DCw9^bimzqZs~Yk_7r&xJ_9QJBEq;V z3|ytzdM`80x~Sk$iL_ZxD|cIjmI381V3c-PEfSOzrf5M|LDM&>SHwpHAk&reX+05@ zkZtnJtMkF-yC%?}bgD`NY6`IG=9Nu8=xh<*5e6hgY(L6f&r zNaiVNQbd#Y1FUm|y$g^1-n`AJ^{3l#7Q@a=3lnBP6ih(#l9F0&OT2WaYI7o{HMlbx z8k5{C4Q;Q_Newi>Ya?f6N!M-J?syAR5vN9>21#uaVS8(;V?xo^ZI8801V z60M+Iu9EEW|{xL@4cgQ{@$XeZ7GF*$y{jbhT33x+X=o zDg{)9N^+P7JeTCCQTu(7rWRB2RtlVjzmkNlVEK zi+VGlV=I5J=wuhDpe_2Hv8)fKn&YX(aXNT|){%{GkT*`V5SD;M9`5%N=cz~NBa+`w zw{kO*raJSoBoRBWr8p_)xfj(xvPP7hFURHypT~4#2^V&dwyo1l>}9p z7bfEzZLdYCje@b9HYlIkgT5pic`fHgTUJ}o-UnY4d>_s+C2s63jg3^ZxvW09O$UC_ zL|9y@;9?N=g~Ua^Uq~^r)gnM`C|D;a6nbI*v-6Ph^W1uVT;&xyrimMoI6;^nF^s?S zc$vlL?~>8sVqvUxg^1KdyTspi2UP5@EB+)d2#X^yvL*Mhp2T6^Gi(pd%Rb;UCxwUK zs%SD3ihwC7W<4BzlOWe9&){F?7nDTEeaQ(JHv}rLhVP8CN)1D=B{DM`fi5c~F*SP~ z0q72}i>Z)o6cIGX#gMNy@kvf-pzW#dR!yZINe%bUK25+&Q8?xA_z5uZa*AX?p;*Es zrNo%(MTuZSv1hC<4Oxg1GcJZtvrz2gCsR(g$d!`r8qT0+g}O^_{xE4pYBf83TEA$<4bEzyBh z3y{dcy&#h?p{_ojw?da5%4N-TtgWGC)1Hg{yz8}{b0+?!dNH&j;hT!TQ@&F(R^(Xq z6HqWyuVnDr)3AT1i*{3)sJaj(LP+}5EdRaN8Hszv-+1_Wl7U+9cA15log0?g`MiEh zAT_#!PMK2|$j#hQ(!-eyyK{R1Knmn>&RO5LcLnen309PG5P9s9&`Kt9HC|3W4@^}l zrB@edGnwmrnh2&4aKoN6Bi#Ttcwo@m^5D{|9DBJcODM>{8_qSf1({p9cd13D6AA_z zHqKNZef-HYoEQ6=EyK_}>n&05Rt#T5Av50Lg`t&-A`0@zTGV-h(MV0(p|o`Qb&_S( zOdmcMqB<}!c>?ILd3h{{A83b#Gz#JxcN;91+SP&cUE7Osp> zDwW`v_B#_=uc;c6=Tq2J5KvYcJsApFohn%3G_`(EvWyTg@Z?(K(hj{l!$JN1`tX>_ z3KPn>xtEMu*OQsJKnBNcki2SbLlV54HU(8QKT7$MNXH3O77Yd<1P1gVn1b-twyV+J z;p_S&?gw*U_IYyVfO?sh5V%APgNDQgfMYs@v{vcV^-8hlN#*S0KRgF1fY~xqOB}f) zpx9&7V9|GZexfm!j&J=7quCYG zX4g~?mhI-G3J2%%me9oM`yjGJ6J2MsxxK`B(E%=c(RvS_-eV|C@k7-Tx{^Wv2TgW}zo7S5#S|L)*X zhquSOD-Zp(1Fr&rL03Da0mMx(?Q6T@5kX&a;=x_8~yJ+k#EE5MRwqBBP$1W02C9$xOtnyfpSwN`;>?* z#f>4a7F==?FVOiq-bVOK1-~jpQ_DOi1aiKX#gKM}OwK|kM^G!;77)R8ZeC!J7J~nB zs$}y*9rkUe)WZM>hx2)5!s^s8b8HAC`>l*%ypT8`6-BI=On%pI*HJ)E$$zAvsGdI` zn=XgXTr~}@dX5C>*WSng0kqNMW}d+oejK&kX{?A|Py@5uDo6j?-(oyn)hwGCcDnb~ z`GTfO0@o*nnmo%FaFpKV%{cA{B~V)QegX0z`r&4;TUVypW4eX=M7L*wGWHA5q750= znMJY{{|p?pt0qrWImtS~Qm`Vc{A_*-qTmEno3(&&z#j!W=+>Q;TO(oKb30cZSy@83 z4lw9V{xQ-cV5B}!#w{g)WSm9&naz<_SU<;zpaj^gFq%k=T+?3A#c##r_^^QUb?;ri z=-hZ7AC%2(gTmZaGFl*(OrxIimKdv)s8T__d}3e}i?|UzxuhYNJ9lKeu$N1ZzKmpu zuZix#<|s}C)-D?;i@g=(KrWQkdPzaizuZ9gP(sn>A_J&r*dJ9`Pc4Am0l7(9K{0}* zP+-N}63~f(!om=zYLjUl0Z8u3)x0E3WH0r&d%!ZH9*e(}pbu3^|CWC)m@7sbWF^}i z&v#|l#R=&NoHIbi9n{Z^OvW3mE%~o^Jab}JpWpDm{|pD-5!_@nuG0~qo^`J33iRJc zNywDuS5ic`V?9vV7ze(W9l}2WWahZN*qM4dQb880q7l2+-cK5* z`<0aD{KyO1b^%Pscqd*ErD`uuMDdSV&??WfaI>6&)`w-p z3i&#vzpXKn@WB6Gub8FR(og1VjSrKWY>B8wQ36=`yo8DsK_PkEvFq~|+zZdTJkJuR zPXI45nxQ7j0H~V}fxvqL`?D*9_=6djF+l2epuc`(l**qi&DOVP=w2|7bME@Sr7So@ zMI%f8*d2jH74KPqM%VhV&_ZFa{)bqWZJaIl6981K zwy2&Pa~v#=?LV=KooTSNIbPOv%tz`R$35NeU9p$$ka<<8FVT%I{E(<#Dx?dXuK35Y zpuK_c@~DpZ*fWqGHSkeuB4KsAop+vJVIcfX82DfFuI^efpY3e{%IQ(O9z@soJ@Xi8 zpaZ%MScTgn3xcs~T1rXkIm$Ru8ZH+@)9WWIi4rMfWGPK=}$Tr`*fK&^^Q zyz+K);30`bTs(r2MUU=|nZgr*J!=fv&dsJk;edDorbdPV{7$sN9Y|jBsv%)}r^u%fLR)xXMo&3|x`e_wDb%UjP$Tzjy zHAC{L6BbDuy29zqW*~0{YQJARSsP!X)HGh=n;2y*XoEKv(F-&R?xwzqUK8Y-iJq$l zog;OCK3HgKAejx;Sd4nbc1>lq$TFp|jS!fwVt_MT_!+ay47H(eT1hulLO#X!HLwah z!nzV5Dx&QVa$orKyk-y$i|lUoqf9`3Sh|90i6IbC%GLz5zTCTWRTZVy7&}q z8BA2&s3vB`rQ1dVgpuyMuLE~qX)eUO=l& zrv5C#4nQ2z!jJPt0B^#+rrAKbSIZ8ShB~xBuhYSu4OH4un;|D6T3IoP72>bDHi>3ePrSx&?XilSfsuWdP}qkk4tLp zT7Wv!j9hvXp3 z#kuRC9YYp?!|>FcR4dzFDFaG+hlGNcie=9u48=z$F1td{jH1}#nxwC#F^5|$-10FWsmr$fTongD%!BHQyUYNS`c4-w^->(cfd1`Z@%x_L=y zR~}1@|K)7jiI5Ose*vLb{rO%e!|>Vhj7w=WkmP83;1m1;B-q{)eVoL#qwho=JyNBY z%M5#Yd5n}$Eco@+b|p|+E(8FC{i7Lz9C*`af65p@s@79vpq*v{!BWJ?=t{4KrU?(Y zsw7c^b?Lp?B)$w1(JJ2$0V`{qE0QL1zFRGm*u3he^aa`sg?P>3a$0&j(#$z-V$(Kg zjD@SD+a_M_c(Ql+vdzr7{m^)sg?kJBrnH9U3lFmk$?d(50%*IuAB_?eFzIk_ zxxRGl()B;swwz?V&qLw^`+?aC;nUi${lkrwZ{DD3@_A2<^$Zt3FD5&#-E?H6_y+HuqWRT zINTNikop?fzGzR6cmMX2L~qbnevE>?wtkG_4*JA)%tu{z27@=F`=t6CM5o$Ik$ig$ zkoesI?$A?3Vqss${0!MN1U1Vj;PH?6N`EX6@wggTp+@O?BixuqRPxs_MX%DI@!SqG z^yc9{>v7@2-wxyY;|hL1(pa?9+BJ0Y2Ho!l1MGv`2gB1!_h$iu^9YUQ*T^2S1Y;83 zjtn5|Z)gN9$&dbn9MmrHYY^=hanIQP48r#YE&Ems7wJC+0qk*umQAq#OGW`i>+3k4VohL&bQy^=C4Nyjp8ZDUPq7kd`znM_SdrkJ~<9R z0J%OM$!5gA9oMz<=m!1z-9qN=u)pLRIH7Bl0o>hB1b+h1>tqVbZpml*v;tS6&$Y41SF}W$qpAl&3C;KXBa*Y1rkilMW!V zIRqN1wFAiS6%muoUF1I>07_B1BoK>*$Is2C$z-w>rDY2BhdhZe8Z7xFiQ{jhW=%=5 zohjRy*rBYEm^ZCNdL7@wrayc4DE{kEpD6D=YJ%B5S?;nikx1YbZz> z`c8600M^6^XxIWR9>}=~ty%TY>>!HrB<=Noo3R_Vxq2j6RPY#D>m@0%AlohdrY_-? z=3GNwcZIc!5^O;;>p6tK3p5C8^>%jHB-uVT|xeLxH%5&YwvG5QD|?s^2s; z{qW&$q6FYr@xl8Cy+ky@iYJHWB>JYuzc`P;p8fBy7lsA!Kr88z$yam1xf z?81SG7cIEGk@gB=oRXZo45{kioG(4+yQv}k-CF+h5n<5N74quo2|tu>Wo@MyHfD_2 z7&Ws=P8m;jmoA}4c)4BEdu}(<|NNHZ1aUD2G6|VGVE@KL1LW)MXA{$eo}G0Ri*39B zSM&;yP5nQ7EHJ?*P1xL;Q?|TTP;Zu%U_F?$EgCH!;EdGYNHD7NdPVA~=SFh7u>Yrt zMVx`;LpyhC;9s8Ie~*$U_u71NaWDQa`V%K$5-6`8KIi*CJtGuMxfl@PP_XF7{Xfp7 z+ajeaB<|d*3*jHn{jd32d;-KURGUS0{#b-$8}S~&VTw}uUK;JpYSBPutWXfXemN1= zd}5u7nFJ#M8f9$0J=)sru7@Yaw2dd?zPtm3WCuK)<+49im0#;OT7h85tqU;_DB3yh zHan_7oX*a=fI1wWteB?@ynD}Y$qrFo@C4MUA-uO1f>5G=n052%0vA{VyEeJZIAhb= zi)Gr8WWMtU$1_*cF910+pAa;C$N{wj{)Nl|Pv!wV@Y{`-oFlr zuXO34Jx1$w@8`Zc*I|3Vs#mF+(l(vyaz&IX9?7K-sy~OyBe^CV>mM5%_^+v0ZDejA zE^SEy_`kB=c+evR;0dtNR27JSR+A%4IW?`(gSzb+6)ga$l_WSx4f?X!Ts5NZa8ep@ zd=3c1-Db7lPT4!Xliz-KGPxvn$$kz90v>?4LkuWu{y2h!v1YkdwpeaQ3&B}k8ZIV` z@PzNbP|GYUTFv`mk!@vtzE5yjay76E4UfqJPo&7_gZ>WC+W@_)cB2@B0)_E35wJwY zjh!#;il@$Zj%KbPN9=3q zC8<(+sZ(=q>84b;%yP|bj|O+d29U!ne_tJ(JhjlvJres(yf*)W69Drzoj$UXRX`DB zeM!j=C|yH=_T;mEb}9^e;M`<|uRCS3x7%Ys?Ep~9pqz2$-Il~)HptMW9iB5&^C)jGkPEe`t0 z!Bg8F%gb4L*h(7vfWA3ap`B{Ln4Gc#x`!*o5IY}#U%iZJ+*^_fO!yku4|rK70IuWA z43hfA2d}wKnqKr;+M9r!;n?b!^b?=~bQYMVAm8h8Di72T9&CFAPO zhzCuJQX=8jYTvYn!VAXZOi3Qe`44l~N^aSz&P-f0umBmqa6i?>L0dv8zV_`*^W(BPnW&m@o&~^+mZ3a1g*jqKfRHYy3 zdkvhQ1atw_Ic)|OATBKu_1Sg{fOPNqGaK0iWT>8`%yLwF06ah}z!!EPIn4o(_e4=S zJ0Hj8`M%w9xI)Wl(wfdpjam@RU2zCLt9cogliaE|K`ygFg3>@lBrBU%gYNTp{g@6! zm*f`G3n3ExtmWDgXNO18=;Z1Mu%@-yYcsYpgz`U64=0u){$FcK>n#iim)IpXnb;MI z$)Zr+C`xxBQa;4#d6uJWjgkxQCa}>;e^+$J+R67(8d)lwoJ_l`i}qze<@1jL z9pThuO1Dm=_hp5xlPQ9L5Wblxd5>WrL)A{EQo&;E+o3MN`!vn1KJ2*nGGiVH;4za6 ze=IOrZ59JoN-CiYQMXFQ><$%RxbyQ3iE;%{wCVzlFryxe_tsphNs9&d0%d-Vv(L^Y zf$AME%$&10@4CO-z10F3CN|j`&5$v3aPFX%+xT!W2^vmyd+&&V)6FuicVyv(h1LS4~o^5gimhw|8fgUcZD;lZC{h6ftGEdyvlT8u&{gUHAR#Qoik zrcUW)D&A-Kl7Zb8v{#H7D_jv;Tz>@6lw{*KI@j}qZDK>8qp}f1xvA&#Z=|jnfYkK} zg^*a7Z2twt-80aYFM2tMrDZ}!FkdW6K6LiHk3TpKFAr93=3_nf&&whc2g#VHmD)in z!hx#0iZJgwBWz>7@)M(u-;ez61n}{?k7CN(t9_Epu-c>4`=gj_SjrEQ5yh@SSwd20 z)|mG*9gZ&)xF?D$StMSV03|pt1#cL?jFFhJ*Yw< z=4ucsp6K{-o}sjl3DA@yf{SZ*_^dl~vs~!WJ?#=KYDTZwb1JNwM-3c+sGPgerT9J0 zN@q(A^<%K1n#4uF+_$dyjx2F~-JK)Ngz@g)XX=V@|=<;5qr8 zrHtZm9`;YOFm9A9A&@COEv07PUvZAyn?Fm4y(03eT{oR+wBzEwMUa$>g43DhT|f5U z1rGl|6}T_n)mA8H4t`$-B@-5qm&s12d8$9(6ue3GDD(Y~e&&Wtpx%1%HB%KNEfiI} zKr`f6kofJ^{V8LONQ2>fu{sdLlr_;NfM|=c`RH!D5MMi%uQmNwi0yX08_AR1O3<G53Im;j?+KqgdMJQZ$6O!oOxt5}DWh#V4h8GMG{dW*r-Ma5BubnF z4TJFy>|;b`|0r*3d`TQ$C&HKtdlUug2H%O!TpEs87e9rnH(B%dvVF}q_?F)|E_7^z zk(n$acCEX8s>RzLlpv>@=_$nd;8t&YekNJi$5&(=Kpt{G0D{#o@#0G3mRqIiG|H$o zB~Z+f9{$J+hvi1|^aymH+D}@%IxKViA($kIH2z-W-gT8nXz`SR1~Z%%T5iA8a!GRe zv!wprScEmah}BKx>T8v=V6M|Z69qf%)RkAF49uY6U-T?q)ppJD@Unm%M*T$by4L|A761v6F-7Whjy)Yb678*|Wq*N+FC#=7wH?}Hb@s0};UoW6J_C$) z0GN)Y+WZqayKy|e76hNnyb%OnN8-Lw19!qFmOY}}DQGOajw2@k81|&O~-gg%$11jLpuiGHdB`5Axs<-DF>^k}E&%&C%zdMzKFS`);oYA;W-&a-{Fyy3nL=Ts__N=bKg z4B$>J3Z+e7D}PUgRARiKFnuE0SGk(BKbeU~WJENTu6y)Swi#TJN8f4_28_+tR}UN$ zb}Afw7fzH%R}qq~nTy26kfKO=LhUXeQY5xrbEc)~M8;dq?<0@C>62KPDq(hbo+?>l zt+z5;i?G`FRWC-w^vL1;TE@_ZIILV)aAA`UMR)LWyTs*(MT2aEeEWk;<_WA>imml# z_gF`9c*N{$Dc2@(xY9V6?E4Sct=AqP)Loof2km&;jCH1!>a@z2WF{;fZrXBR9nH)% z)V$xD7)moL{%YXZ_eH(FaKo}a1Iv5s+u?S3no=rXMMuTWv`=m){ePPFPe8yJ3m5Yx z2;Lnh8X>-`wkjdnCKEQLNwSJN$Uq_V9@p`#2zr+pWwoRN?(1!XQ@~hf0SJqF=I<%( z5+H#qhTeUj@KiuEh$Q57>>@RFMPF&1IgQ9$m7=#9L3f9u!d|>aG`^;vC#N_Cc+54z zm&uWPra~^fHKeqY1O&gC>*i{-6z{GMq*-~o6wx4jqikyz`wE01OJ@A1Wd{qu+TU`l zl*h#I$}5}4s1@7!VZYh&SsA^)JVZ4_&g#+js`=WGk(8!S64jK9Bqv3ZFGulGPR{#g zEF6ah8xx0(OX)|OAUTjtSq7`iU5;KphhSD4m`w?)hbm`|bQT(VcU66&0=d5dcIhg+E z)LwKRX}mtQ5dvTRKDC*zPwfW}hyI@0yy)g$dKdSmu1=ioFQj49&P~EkkBISoA@S^` z%-0<-1`Y4r<#=f9K9VtTF}%zN2TEYE)l*JKo6_qB?=1$?pAL`n>RtCkA(w7kqBu4j zVEP6mH9HH}%gB;^tD?_-+A;dJ$)>{UjKGj5HROH!oU^=DGXE6b zD3{HclUPPGWvw7%M2JTJvtK+9ZWvjMcN(kkkx=T9jEf67U{Q`j;N4Zq4%B#m1TZWU zOey>TS{w+=Pt#WkHzvy)d$MxBQ9@NN>2OAPHM_$Fm{^#wrU$_{wqwa+q0<>XV4*Hh zL*EjkQ<67GL3%Q3VOhcm~i`rU-8w~Mogz|NC0lIUelI~Y+OG*_B|Gc?BD|C<+(RQ47 z_J)_Qq>?v@rlPVI0heV3yMIA z%}glvh}!b19{vpWd*9*Rr{@+Ey=&(gc1M+A3NyAYxGyh`+AeZ^&WL*Ux7#Uy9oi%; zU}FEX3UR^%4rB`K%~Fm0sO>f3y$n7f?O1{_+N^+^rZnbWO8`vN`N9~N`naVZz4R(S zY@~X~$J|m-bcq_Dg*@x-h**P(I7~!kzOqn*rTu)W5iv$L8}E@-vYEr-2JW2NroU3| zOwNQPVKDBl`-;_BP&I9$zF_B*axgWbv3Q0ujf$TY@8R{c4A1|Fkoj4flvNm`8X{v& zu_Hz$8!G2*#GuWPy1$ZF%lrYGIwNGIApBFcK(?s!1h~#?XE&R?w2K!nQwS|P{W-uO zz#+rjct&oa5>jXXI+&Gg?_1Aey9}qsqM!43UrEHq(;Cs$0}Tv8Op%qQV$U6xp+RJV zYSuSwHv;WJ&ekI?QwJXp_b+;{3T+>oKbLdC}1HK4IyYaS$|R{=`tzX z$;qO7Hn)ITqlRv*HRkL!ofE|Ku4L#vX*sJJ-&_y7nZg!D+>%hxX`-^HL|P?XRocFO zuQ~}f?XmoJ=0d&ER_TuO*%a^YjKwM&34TBzV*<6YO#1M^vDs zFGKgn=Z+8D2GfYD32vESfo?k)P)V4Nz><%thEiq4ir=C)?o>|8o6y`Gm(K!V47t<3 zE%fX%Ng_`NgkuCWi#tjOSul~8^=inDitC^R`?8pkjJ72IEirBSCX`#bcqGobBMbj~ zwG!iSWeBQ;L7fL|6|ebHxB_dT>d~g)Mv>rgw$lE$?H|pV9Mk%)8is@$>qKVcvpKM( znhZP!mClHQiqn<0wXQS-o|+BMawQb6du6CB zn7U%oXIsQWU%Jgvj-sITlctmN^cf9hmM3N*s+gk!TVfc2`S`)D6x__UrJ2hJQ|%mV za~I_yH3@5jB@T8K8!Secu>x+rs`08#sG(7!0b0>>JtSi}a0I6fKK*Joq4D%E-{^49>{uLg*-=z@GmckbT9*`Ww>g~Ocrpc4lg zC@vVfw0(+nqWxkr)2a9fV!5=J8MmKkf@n4aZ>$BNdtxw`4e?qFEp*!F$C7fEj9O#m z=IMp;JZB+28efdsw-`d#IbUuIe+``CLTM5G?aFUpFeAt*+D`x2_tB>YSmW<3ssVZv z?!~+w!h+?NN2ftV0iM>|(;A3MXilV>hF3+Rq_hkwC2w9OoQJQme3Eb$UbvC1ZpS4n23+4#!TT!F!*TSBcmaK1S6SPB>h%l`WKZk&;AXr zr^Ku-?wlMDFXp1DY6p0Wg&>x6Q)6gU?I!|q26gjJz^F2O8=OaBvaP<(CcI3aRN!e( z5s>Z6)Y4@$s;E>-0u6~wIc9tAEYy9NZ1Bf23M1YDYR~wcI=(FgET-5X?8X8e1eivG za?J|!iBSM4a)sh*;+?9X&IAmHYW+|NeM>AnnhSanTp1B2z5#lEUUvinFg+x6~{#a*j(xtmjOc zN~bi~!vcc@`ufW^49aUs1hIe%@e|bVuM|c%5l=l@e-`7Y*4igcEQ;hSZ6Q-D2*%ts z0BsWdb`_=(+vt)4xt`5fY4YYk3wvcF2S)Nzr*{0GF!1Y-78-!up}1o zIB0Y7#IA4^2OFln58O9sU zY4Z!5dCJ&`huUMQhYbJ#cX95>RjG8;s0Z(<=DUVqzAK%rh@P05Uq+05!f$?>S3>MO ztJUy{)M-)M3-8rMqfc;KDXBWsSvv(SrOhQHgl8lauP9K!vYK+`f+{S3qP3p&_e=5HKiNp4hrC>xw7SrkT&mA5j@V0m* z#pZ7iI?>p*z)T4E${eh7XJTV`96XtX1AC_j{+&x+s=@iA`R2})^d)pZlK^o9E#S*c zrgtGxxJ?SMzI_mgfUE2Mr}*x}eh(;z8f;kg*?Ba(5;$WErn^HatO}4NRwuAMA*j;1 z0Cdx#2d_#7`tDigJ6{OtW>iILY^~`i8IRajtV7`Bi&XN|O_o%YUwar^dGxwlpnaTk zPzFQ{^?qrJ)p7a$$Bq7)rdo1Ab^?$KE0yN+B%={iG(|=X=#>eYyr0kKYv|^+&gHV? z-vo&2cST8Ot5?6KRW52Of@h(OQtj#0HGBKHK@Y83%tNV_avL~z z=1_6P*Bj?ruj+Ft7Rg6fbjz?i+wHiK^fnU6XI*|TQJ_YM8S&YhT!r*z#a^#lH%xN0 z3`n5cIA6p>9jU3-+!F`hXJO)CQEt{v-EEp~^tq}tt&ykAxHc=O<=w z1ZO|OVfQJ2LX|}wAt!p`YQkFoW9yswocp{NjxwBa1NEm$toQ7w(mgg4bMkq7UIlAz zD0{$yLKA-RwVt^YMTG4K32{fsQY{~Mpa0&@c;29YfyBu^8bkSKgF|!s%C8|)qxM~m zB&*TW8^+?!7AX)%(`w>SqtvLWN>VEH*01M^|G1edq0;QI?UWQ+M1aK39mm>4U$pc% zsiER$eybtgx=ax=jhLbgS)EJHp-&bD{e?#{mB3S2UmNgvY4d54i4pEA4)Y@#sF_X| z3^J)Sg8R!s;cyI13dgNOrcg!fNGbvaIce4eqvKmI?%rQpqHIeJq}u|GL>h%LOX0pI zaIzyR>gOpHPPaNiXIpEHnW$hUt9g31_?`$6zg{;U^b$doE)fg&A3Z=UfyDJ`Tcy7x zCWmdZKQKHg!ZBU}7#B%gZylj?G5EM6(VZYe;6s)nWQp8La+fJGXKRlTL+8t3wOD#dPYld^ws= zO911!3!<9%Dp&tHXZaQgDLC&r(u)0#&=4`e+>(%C|2oQU7Di_z6n1+f%_(S|rHZI) zw>LRU`GMl-`_;*chS(0L(`g_+KM-PDTt6re{idRr+mJ>-DZbd7D5=|NV^Z+*foXCH zB%AIdX`hxUiFYFtJZ*Giv8?sSJ7lEe9CmvtmQ2t%v47*-h zsWVuv^icEz*52%nMZz^OwBre`_KQiGFV1|>Nz6TXv=B-G`~w~mvuOggDUneVtxFO^ zkk;otfy~EMB>ESiTeAKM&+rrNc_lL z2`nc^B?*H};(N4v*H&dhIetvc?d)EM4P6sHrxWe=_pBnL05SC&_iV|3-T=^Uz@$AS z<&i%kWi+1^A%8T1XPyB~Jbmxa2R{Np5h_|rIKdL$|NKKV@Q2VG@(1LMnsw$DKdBt~ z9PVIG0b;y_|C!X$N7tvnH%t<@&L9xgbeag+ar}K(9+;=Xng9u_z0$*JIEh6wornE2 zu-s$THQBpvZD<`1n?|6H&r9(IqswI#8IZ)1elCRKdHXOVhu|Xi@#5to`kyfAfBA;T z|Josw%Es{iZZ`k(*P6JXLEe+whXdwzynpuff4&is2D`otCJQ_HKQI0}k2mfqVW0u&8EIiz@|DbD0Z;_%k%%S;HM;Dh%_ow8=?PjzyJD^d`V!_jb75=|Lytz zSn$ntc#YZI-Sg*_|K*$i-#OQ(pRk~E%doqi2j3jTGEGvTS4sbaT{GanO!#P)ghy)( z+OzWS>uAWZK$nWP=F~8EwAd(^)oM8#Zx2gB7ZVN{8;!|G9>;Ef`fdLMf6&fK`l<1# z(L4~lViJsfkwMuJMy+-Dnf;k|6Ty7xj57{E!D3&W9f@phjdCFh(7A!MdjPmOX6_E< zsPKc1Sj_D$_8X%b+Ou`&nbqvuRT3QguL>U-#oqtpjOV2YQi_*m-7LWox4u66jAZ9* z+o!25#LOQVhyE@{3;-+p|Bhdf6om|$T3eBQDOif+u@NW1{W&+audp(CbFN z3ynTUlEXFH#k+v{7eMSXndA*rgX}Cei68nlU0wxAwD8-%SZkvng+ z_KGF{-GKb=b}dns&LXI@t1tG%)2yO9pKU$^I=jZT&oCn{KBd?*TFf+X=((I2~ z-Pz4RC%7{J;F|>u&P+gmU736MFZ#n6)X(X68&SD4Ey0JIf3t`2T9Qq4$HaqDoiZNV z3s_qpRx!i2+gp5H;M^O}4q%DS;f4$bJr9#aLMA?En=lL9F3z19V1ScarWp?9+qLh8 zOS!X_W#(7yaFhjbDM@~=XdCxHMGf;arOUTaQjvPYF}gaqy8^3s;GJd0!k>gcP%6}C z8t%l5m`eX;RA7Zggev+Yk71fvInf5wf{xV4#Czs#c zxZG-n#I!O8vNy9a@Z%d{AeIXP+QL8^H2()5I9AW2uIM~;+ewQov07QdN2$bWI1^GQ zb=xma`4p>;9rE?N?Xk@IW~HUS@Ni+ZewfNuwz$WARi*Y6@Yy`Ip=&2J(Wz#mq)}Ue zU>&yHy^HP9d==HI54_zvA0LiM8~~|2;iu!(4taH|$vp=n1~`HN)6~y&Ma(+9@DWR0 zijYSbL0;oc0Cxi!i^HMuJOD2suHLsIOeF#ABKOmcpBp% z;;F9`Vz@RqIBjU#O2K_G-=NpoTC(osr?RDpI-Sz9#+Rjl)Xzk(TFbhT`lBJptFr-R?Dd#$8l0BiMKK7a>WUw zV@349vld9)V^0j0(e%)3WF$oyRzy{!kiJkMCBaaYFSKa6B4r~pOKcQPzrManLct8-Lb&=*== z)+(YXi0&2{4oz;WjzP2vk7qp)4Ot~(hjBpBjRc51|6r+VCCZ{E`Y`K!fCh!Ey5@sw z!~1i0@v}sjrg0j1jaK91UC^Mh?Bo5D58_a{hC7;dD2K;!BVXe|wfufc`c>9?vA56b z92M?&L|m(fCM`R?AD^*}+I3JAbkjib0{%vrtUG9Wk&;EoRE#y7Xi}Xm4 zQZ{;N`W)pkE#)9{%LK@K5)*HWczErvemA38x85M1o%Eo}TR}82=m={UPEUhuXfCe`(je#;rsqI| zB$qv0a2a5eniVvK=Cy@ zFD>c3)^bTXntN!YG4ug;Bm~y$)7{4^CFj|D*GbVLqml72!6rbYx~mGks&Les$jtyc z8a$qoI!BY{_M5xIi=s^kvkc$r4^?7eF^(mq96}S>I{tv{3}~W;=b|5GT7r9>R75M9 zGbyXbXZ0`WrcX7?vpwk8)?OITokG!zJkxFaLaV9ujU6E5p%Z%%&|*kg$u+Zq7IcJ; zTIL}s8K{?mr9R{gahdyxy8B^gR(jbH3?UZ-LYm9R<5OIMJxpxB%DcPMDEDflgf&25 zq5`LZA$$6OEV=?C7fX?Q;VWzh$xO+|Qni7)P>0#Nkc!N@Mt=aDAqY4!t5G7;?}^LX zd#YX~rwQ-iSnsJf$m>*9ZGJQ*D0?2MVdHDD!1WAOcB7ovF4lrk3r`Onlw}_Q27L-q z=4@HZ$BRDS7O;tvCfKD$r_Fx5I`nt|0d2YEm&n%Lh6+$hAotLL*fJLvcK1%>>0T#)ARL{i z-lhY#JJzHp39Fz$3L*Pp8k=cTeA0{lxQ1u6QlxD!on(ajB~u(3d!(?pJ%T6a8a0G1 zmT>TMQH&Lk(GoTV3E)vSsJ*JYGOOs=$e{OkfHVyi3rOT0Yy*HHRe%;t!s;FQEXW6X z>3MqSNDKuHr((GYPW;zbGmeF_oasE`AaKG2*T(GuD$kY63k_))8X}B3O9gn9GC_P? zUftq5O?JoF!}WGKBvb0I9z=8lNUBsb>13l9EbQB~4|zy!E}RZ1FIJurZGmJ%d`~F- zi>h2U42+1Wj~4*z=?4hAx#L#KG82IhFe2vC%1tKHy2{XAzF+lql-kJ@^B7`RZ5@R^ zBA)a?D>}nvCq}(98$bY!>Utgc8a#h}8xEoIUoXEtU5oTTGxP{85Gwom95zoSJ(Ya6 z?}xRFR;Ns!je$$cV(~kbAfLQfVNK8s+76_~SQR1-UjP#2Yq~9LoX8pBog%L#VWNyD znF@K?Sy7ul#IK(Xm#9^i$jBG5J&^BKbi6M=4w)CI%Q{jF+}xQX?|6fQU5TL6a!*d> zq8B@Yl#kE2xLHvRWuocu0X6YYLVt*HfIkGAPPL3A^AuL`0XUk4zs)_YFL&1r_&AFC z_yLp39ePNhY$CYj#zCqR&$`t7$NV>bK%oo~3q1AdrDO9VZ9*=O*TbLtW(%`#}# z2e`sxDX4{uHk-^1OFR@LjYY>6+8EBM&NRAGfy}BrbvT3xU)1%o+D=O>$q>YvL*j#1 zywI*^Q{>pLT=c-j{#mB~9@2{QO7T$9<7jtZ;Um1rby27LWu9QH1z$tKVRdfnw^C(H zTV)lUdI?%a|5-=#ly%TZAyWHfZ^)2E>a2pc^pCN>OMvC@z86(k_;yBkU`Wx;{X5X& zP7gP8Cq-888r!0OIH=XAC31F`1;!7=vuaKf6qmLAU`>=u-T-oW9GZ3F&d8l#=_-T4 z-9k(O?c}1umBt&3W;hdWY}R9h4)#Grew6Z3$I?{hNs8A*Fy~>@4)4lWgw8}~Hl>OU z#k`ixDe~ics#`q9Bl6!+Ii%#3F&!L@Cz8l}zM_-gg~c2-k;dBO*RA@I%F)3o9Q}+U z{L9(~%hwC@Q=fwh^;f8<4<;LcK6sZ$1ha`U6|!kqp_fXUV4f+weCvl#Zc^aZBAwPl z&emxRu&Q~qpF^NV9+?J=)%O!!&}r0&ZA?|pl%+EUwTV>P6-s)0*&Pt0<}j5B@=fMX;*_YAIB}2gMe0*1T^x8XMn;R_n$Iu{(R96z~^6MHxoLYVt@lqyZHzjj@7g{@<(RdI)Iu(G z6Sxc_DD~Z4;Fjwi%)Nko*r`YAdAxM8Rorj;AY#`BDm%6KTa_u#e&2|Sw%3wBR;MTn z3Df&Rt_e^FHF!FJCqQ~8Zp#4B=?6f>gBELOW;PdOS1v)W=OE2u8C^2v)Htw z5e1MWPbNK9UgS=+skngXA2#lYo#vuYG|GgG>b^0GuBewz!T?`j!8wcB2r9)6M=0|^ zjK8n@*yO>)_1^qR%2V&S){9sbc5`206ZY74CBBs4`yjSW{U*S=qDXbd0>3cLHylzf zW=#SxsBWwdCd6|S9}K=t;s9!!%{BB0i<(uzdmabUIm&w0^j`w$Xd8pzu}7@#swkTF zrzEgN#_54EK{~vwb5YR7J0q%v=?iSr_R@gpI$t4=rnZ;f3QSZ5gE2W)ga=}6$%l=q z2xC*+*6aFh#D;J%(F4V*H{7%DrmOO)l%7>Nz>e$uNjv!x0kb6=(6eIvH5)=Sl)AM> znR)*1w!gnQ|IVb9q&RnhLV1aY!kNpZU8PaN>&ZfcT8j`U3noxmc#>r}y6G(XdgjX8A92Jg!s`G% zCob~dZiBK-29Ep&%P;xKy&_&qn)}4B6uqqvI~h<93;SE5-kYnKORZRl-|li~?APZ` zkE%t=-}U$fL31~6cYzX@I8CI9&lZ(imEsHcq||r8c^y2y`Rl?;MMvzB&^zhzo?;c+ z3U9%~*(-pCB*s?cxX+7h0pep8eo<(p0LOUJp1S$y( z9m@a1-dnd-xqaWmN(hM3Y((jl7Le|e5|Hke?rso}Zs`^T>F#b2P`bOjyLlGp9M3tQ z^Lq);_qzOJi|p-vuRZUz<{Wd(F|vKEg4eB4V@e$72hxfVX1*=BKiPX*-uvvrw3Gh} zm^ne7mOy$z|2K#!h!611K^Afx)PKMk7?NVg!}FzHI)ixE>PExKqk!;yK8Z|%h=-QD z%vh#4{&H50{;w^lm_GXl1gso_XYvI!hx8yXf(4Z-yPl^O9_$*!U8wtNe#8Z&asM!Q z{M-&}l3T$ZINekhiwLw>REx3vG_*~WD(H5(>RaAV*^^mPiahvTj&Dk=mi6U3H6GE* zX-`7B3B{0K3=B(6sF!}MiDS3>zC|3~QPfj@=QG8V+pOaODxO^C8Jf3dD)YwOy4XAb zb1!R7yD#4L(mfSf7&@MHHE$|6$818aV7v-0A#mJwsJrn{J#sAG2F=)h+9 zCFXuSkFX`S9CEr#&+t!{GW`%OXE#is{dh56hf6g}^dwq@q93>ghjU%qrMuq>N9SQE4Y}W*>P+?_QzGAMxYq<( z*`VbX2XO;#ByF)J2^956Z*-la79sC0(lRh|h^xV4$f!E9DR>HZiDi6m2&J$g;1>#E zC4*6tXtTyYcD^`3t?*wiYQmFqif7a$?%0nFuEq{zeHB3U>|k>wr>aJw2i1#Wu6m!J_j0Jpy1FJEsboQM}A8#xdgA;4E?d zfTd%c$}JG074Q}p&+%)%;|_h;hp>)af^vNB8^1pLS$2H9&%`_C)0MLIw`Y#yB@@Z8 z$D<~(0-Q;A(s)4_NUzRR2Iv>~paIJfS&<*rfEYD~o_Ttl=`8s%f7!7o9L&a41%9Zq z_N2zo0jGNN>tg$da@YCIVod+r-}|%Y1{vrI)UNw|`&SbMA4U<& zIF{XM(0Kd{o<6diPhx@OHAU)1^&B8um=vOCjAu*JY+RVKy!7l0r8f-HQ+n>OF`$4f zPMeG680x(m|KuY~p~8YSY|EKqs4gC><>%T^d42f%X5E3WDTK=9egil`e{YHz1P@Ig z_(b);arW?LkAO$6PxohCcyT!F-y;M8H-zLLk?T)tKFnZt2QC7zv~>O!3Nc_6^l57r zMM9B?cgZs^n$=;|_?v}bC)RtS1!!_(qTUmX;B#Kosg>`>sldOHN}1_tvPpPr^rbZM zs~MjqkL^Y=no&{Bnz2*iDg!N(g8x#-eqe+5n*U$e z^#YINr`98n8<>T~$p5t1cr#%7Qk%X~$Dj`*x#qTEu|xkpqj*h0ix)@rxMT3kf8QgA zrc`bipc4FMq=oOjJ3t7CKtEfcpw;^CE0z`n)enZm6F>gnxAnWNR`UK)kRWgZR8C^I zQ_=DitFj!Gk8hV?S(Wy(zB<{Q4SW;wd+i9GfW}Lxr|1&OzcKj#HqHnV9)9cl|FF3^+c=aOx-+%h&Czfo0nB&UUPaF34!M#vQV!5bOe{p)iW_7Uvd$`_>xU@ff z52zCNm`Yt<|DuLH+z|mgd~>p;?}JzX1i(#ilGMPMK1)KabLSF3W%HRVxHQ$MvV8M z_P?)!;jt1$(UdJe4u?B^v~cU*fAW+s3A~m0n;l$2W$hWK41hhs{c7T0ZznPWz}}b!&&6Zf3~WC7{jr8c+Y93*`k8Yneys z@*dK1lDIcJ(f`~USX)3lYF~xhuVMa99ZNx-wL#JPF9H(5Td3n${BPGm10PB-{#ja( zp{qe)79ihpA?M`3wE&zaP3di@|AQ8K-amazJ94fWZ-htwKkvUl^Us96eNBDchkO1{ z%o4~WG{bhz=ymbGY&+A%>iFWe-abZ*)v+jh@9(h@$VOsL{jb&$2rvph0(BKaYmH>d z87ntuX~KETbj!*ULc43r8dLg3H1Aexk>PIseiNs5?d`n0DWX^+U(3F=@miIs1G)Q1%Hy-Y%VS=sSa`MS7j)m< z-2~ga;Di}=t|OPa1hC}N3P;NV-es2gr#ls*U=L>@XhhWfa~Xd;+Z68>>TleeHpgw2 zj+w~Y{V!9h(Dr+x`A({NstuFgxfc{Et~y5$_B74QDoK{e2H^fv2Ju=#xwvysDVTDi zmWNl$(b~E_Idn?oO#gUC z{uLk&-q$bvi^s={D)Qs`6IaS}^&Qld@iSUp>G<{M|MP)>H00l#`D9m`cu$jF{6lJQ z$pR8s=x52UIF!GCv3XTO>+VQ*Y2q;GF|v#*RoiXH!4(^HjP8@)zwu|mz-x=P!Ms{* z9TX8hY7HI)q0`dI^kV|rkib@JIVohgzc}~YPkZE zzd!c>tSibWO!=tw-p|eU>|AB&P$pg9@}Yj~&`+0x;e6U`FZDhDL1_sdi!PKo`FYdM zjGJE3ABK%2_cypvVQnpA|7)}hd^o+xBg8B9!yP&z5Nq5y=o3WPh!;%muWRRJGKz7$ z$qD@a=Dk`vp~byDw;X^rVgFV3#rCFp+ABu)zu(6Rn#k~$0aJBA7%U`4`fGO^Mhus2g*nEf7Zw{9@K3+$seBfYCwV`c;BO5fN7nMd5=`IuCUY( z%Y9!+%YGzXzKmclusvz}^Hd980wre_tF^tz|5R%fplU3|V)N0XL+lhG*g{aNwv^6S zt>QDCDwhY!l}J{Lt+1)|20z02W}LTwr5g@%2m)e?pzpIyx1*XrS2^4jd@?z{YUSUH z_{aY^$PYf*feh;D15ig9io>=%HFi5Z%&Hj~R;|fiu5b3cUG00bf8coSOC6e7Q;Y94_xG& zYm=g1lz2`Vxvyove{S~a`|ExmcwFRknw5X!kbhnkIPOXvAmFNiB`~@lvWfO2l^9>X zN|Sp%))Y#WJJ3sDnXw+a#pN;TsVjb` z%VCM_z@J6FF61Q>#<*r`TSeFko}WopPLdF!E`Tb;-yQ_ouTHA|i*Yp6V8}%sgI4Va&U4rC zR{rym99gpGsPK6}P&KrAJ%xdLGxl228nG7N^`PVht7UO3tJ@WZ)wsHldIZ@ys~U$y zhoFDY14RP#>v+YV}*qu|(D0c1E z0bS}bfK2siFvRK<4<&QE`M%(Gjq3KUuGeZYjo@%FkF>85Ui%qOE}6nfQCPk7R60v? zmjv_xd{$TRgumb`eSYqKv6vyKO36{@yJ{T-kx1&EA^-CrCj#x3NO+ROe?K+^p0EE{ z3O0-wbd(sTT)@l-AWt>Tap-V6pQR#ufAM375e?fDeEbjRfP$%moM%`sTKECkNwCqC zMf84sv%SUJ53StPS3KBdj{-YibnVamm*fk$Q$a6XrvEvJ{RzP(G(=*S7C+mb@rFb0 zNr=s&3g(6MYRK^eU2WlVi~DrZ1h!*wD_|-e=f+{ZGeN(}=omjyWzi&($Q)9=`8}~6 z8d(nIKpK}Chs#+G$bQHJsYRii>;0yoR2#fiNcx8~ua?}Ldi6T{AV4q(0*-qU09Ka| z-L}b88oKmsyHG@Ilj_m$GS{wMR zetz3Xa;Zq#cb$1RgWrS$Irge0I)Dln`Ht?mTZ{D+8&cgNQ0x}hT;R#JfplpWYbCwb zY)rz?8lLWWfAce*ajDr8+L*^;uAT@eH9#$U@2>-*n9Q@mckv9Faw$1+?;`+PHjvNz z-4nHJI+HQ#^uMrqw|B*$-$!pgBfJDm=(#tY;np?x=hVSgvWH{KX8vbd)%w73ch%4T z_cQ=X5^sxFRW9Ie-J;bK*vJMy0=v~#xX)^$1#NcwJNV4lZ(*yT*{B7~OtRuQopOmL zYEK!{fn$S4Laa{ZX#t~6^QXq80P+O($k97MeW1KLeYKHrJ|HmKF_7jB10!Vc>Gf2N zjRU>KoU1|1l*ei`VLG3;Gq=+Lu6ns4LiAYuC3?DS7Sh(lTEC`YPld^OlH*|ql5vw{ z@|D=fhDG03?MJKg@eKYOoc-sP|nJZ=3vsnmeGR>f`%p96%@4hSFNH`CQGpz20ErbmC=m|*wm z&E*ebxmWmSBB5660<$#CfB$5u0kn|f`EAOX>WFr5K2Y%Gx|x58ZQZ~@D}BurQYMd$XL4Z?$*U0aPGu_euQfM-BsP=?bem0=Ake!)D*kr|r~91qi9 zYt#p$X^Y2D6VADv)2yaI(UL91okugOAjuVn*MHG>-6;IcVy5?L%_Q02_DCo#e!z^tF>Id+vu8pAi+GRrL6+Yc&w zaAg={PPNHo5-$U?Qq9xUl$t9#f#-3g2VPp4-#{?})cnm^D1g z++rs*p+rJ?k6-ar#J#PiIi#1aX-4X*AtfWQm7!L*>n!j-g4p8++#{PzlYlfzWE)14 zG1~|#MakKGFjY^YW3H{O-gtA{qw><6X{y|5&VLQe;hJL?oAsXX13t1)e+pf0Nnc6>FD>n*S#vE)yC3pCxq5lVb0y!ua>&Jp?}M9h?C(ZC zcmWSm9BWJNoCt9c)Lf?8%Es}4>d{|M({4#k-+EMIv!3zlbKOsAi^E;2KBY zw(Ru?Qc!83CHIL{xyfBu9;?-4e~vDvIvGjv;UYh5Ug5jqjTcPw)>s;j=4OKV@ARIb z%?VGGr;Dsocwtf>A4ZAGzT?FK~Csw*fr%-JbQKYon4n3d`dVKU~-T>|I2jQgi#DEiM@{$2K; zw4N=#&<5tPLqt==D${yz=72_I5b)SkT=|vQ^^whIwJK9{aceX$-qnC4{A-XkJF->v z6VKIh#s}M^p<7|I=x0qM2U2E!B0hRb#9{dNX~sv%4tL~$1F6GOw4?col%Ejxl%T_YTk#<|~*cQx+M3=@SNEK*vTj_AMA3eN$s$s&cwip8WC?_^47v%S~G zZv4_4D5~HT1`#uF;9?M2oB=OX0hG&yHz@Dg@M%`c?NSxII>lx zT+WvsOd&A5;C4f~eMO^E9)(yzBU{&!?`r4dVgeXCGKvK%kP#*?NLX@AtD)>V;oWh6 zVz5$>8?GrI-X1d`4`lSoOap4icWXRt&(MS2Dn!A%8$6yqN?_px#6E%dq(W{KpqFah za80*@-0W9ohb3KJ4Ea+mOqTf_t3m0CEpMG2rLfa7%@%m{R@9h0Kr1yK%m1#H`V5Cv zMk_cC2HgDzoj=eG_=gtRfn&+PCRf6FTOZ=(=j%*yuvMaE72|v_uD9Q1A1v0E6+*f5 zTJKLSSfWzM|I!%Nri$_l!n@e>7_ooTXTM%{#meS!QxDK0`Euub9G?%wnEZFA9kSh~ zt*0>H(MrI#EDGinJ`-d%ECI$V8*Sky(7AGNwlA-!RILF)U`7+#S7kB3wH_#d+*4`V zNxwV+L{q?HAG!p*J4WSQE3bT0l3J}<3*P{zHUATX+vK|zf8Kg_(784zpmZ%A z0U{vD^stE78_UB+7}SzKihPp5hlE*Mm-OF}O#oAl1nP1442(Vy+pv<_Y|pfvx>P;B#UM4Y_Ds4qtkEDlb8#Znj@^h58=q*dc& zpr8qSyVErO)2#u{ydf{SXaQwB&mK6yMw~Dvoy)l9s6E=p#`HKHMICDJ>%uPwRK7oM zm*MIq%bxDTSW4~m*J&_~BY)WydW%_})JH9|Vn3KG?>Kf^OZyki(-WYmNI!kq?rESv zjAB{hI~*+RZ#6I4?{>bj>D~XPcw5DLVE7j(NPqxuhzo#BwC4BeWMGVlPeutzTB$R3 z*?w0e3bC<3@nmV~sXr{@+C{b6r+vLBD#bl_BTSAt@yWnn@r+VCQx)QzKHkWR=>}7B5r$U?4wPc~2vxj*+EW(rOM~A&1{~S!?O-B%{D-LG)2Dl)BSDYhoK-@TT_3E+DN#Qoc zLXYeCy=pXz^~#&n4tFqDPCV{it+~o%G$eAeGdWuka<8MKoA)KiO|+je&V*tH(SymdTGbd88xn`TS1Vp4PC|!E4&nlzggZ{h*DYy!+k5>U z?K8&qs~w4a32h#hs`~Ooc3Ti^vGlr~>~UEfPruM1^xE2KQXyVex~%rCbsP(v$zmuEDhQlQ2}c>=(F z9hmLM&{u)vDF+vp%&>ZbL1R`{8a$mqudoa)#E#AND+Yyi)||YmYO#+HBXuGw5HjeC z-6z}Z(@h9dMWUVo0zHUj)hWBvO3&ZjdAmHV&f#rNcf43!gg_}v?oXB}$`$TtBgPUU zLViNP#&u={>hRe1D2fzhb)~4bQZ+wA^cW!g`x@xuP-r3GQ$H}8>Mzo}@PVPYMGzpE zz)}^e6`kzO3Vr;sut!N^sP`AT3`2^t+j<0^w-p0T#Z+Cx@jbRnwxwInlWy?9?JzRT z?4z6SY8Dy=tl|c7KGgNKwwtuTlM-X-z^aeyJgIA`?ij@YM;n;B;ldOyC=uXavDFx3w;OmTsZpSV}Yinyf&F$Z?ZTxE(Ns ze@T@3I=|9AYw@-`RV5W0&@Z*m+`0f3((B!NL<&lg=8nMGf**j&FLc<2FPu3)wJY}N zbm^!aO1a#8=HgqnYLzF&T!V|^;edd=5RiDt2=bSZVa3%_Qvn#$=NS*>X!Ux>fbzKm z_XwaW9*OZ~Ul#GS#qTJI&8Fis0v=!WU--uNFhxyV;yKr0-HK;aGiST0u`vRfnL=Hd zLnx2ukzJ?Pi#{ZQ&l<9Hhi?sL(%F)_aK5r}_06mLjLI~t>a`8YJ zokXe9X!vSKEc>I^s?uLsnk&>GEj7>GMPyG$jN8XP7RMb(k=6Z=amzL5!T8$j7v8$-k84&`GHxq5+ zBP@y~6HL?b3t$5FYdhV(78lqz$GzZjPYRPSGTSwctynH@_vxT-y@(@eDB778!~yf z54MNk+occn7wSvh4+(uS z_yx~G*}_fScFVmucDEV_LD*MX@?_%E!YTRK&A@0%Z3_IX1$c;|@-OM_cj|!o8YSyI zfj|Nc_42hfPy-L&L9aBOr0k#W&zLUmmtb$y^Z+$=le>iHqlghS1gK|z-Lx?2i`<}y z*VBoM%7sC@Qcp_q7XQ^4d0fqJ#8JV`_{Z5Dr7RW?#(^7>paB|szF&1y@m>1vycDQn zWrR}r)cjE)neI;6rE3d#RH0vmYneT!$L2TRonp%Ln3Ewgn<{7LSg>FFsjSodiI7_X zyVeqX`63Y~uuUB&;XEc|IIGsnprEO(o}6=-ZvbFwn)PDjD~W{A=CC8GqTr~Qm-rhb zWgQpVY_|t(qY^KPF03^(MChY^72yww36y{(L<9ya%WFK4679;4+-b%0c*HnvX3h=u zIHyQqzxlks42D8F$Y|RkeOr`My09MvISyk>3YUVxf*f3L!eg=q1ORTID$uQ z?if7>cz2fqA<$UmVyUXIf?a6mpK*#pfO)-RiaySA;)j<@dyR0*Br(hz7XgHZe)wT8 zXJm%O3dc!gFI^tblX%oupkXuWi@diR`xB^y_vC$x*n44$1Aq?pz!q<~?0)gvn6T;e z+OtI&@HBpggnf1}snIpS6`dw_?zyPam`_)* zlRz-`@%OoClW1Fvex7|RRDCpb|BM)s@aEz;GXNEvwrEAc(4rQ5c++X#sRvq%{%Vvx z;Z>(G!2N^a-fFge>%2exMnMVzLuf{V`d~Z$d3*JuSiBG{k zLJKK`X`{v3o~|1Mgg|kdmh?^scGnAb*>#2)=H)IB$t{?1SYT>7*&NRCk@&y@jQKos zNknU>;Sd#|{Y+4*F+8iEi?2nMM|F6ySrS{X{J zNd77Vb@U>Xce*u$6hVAbB*z)3RSCQ5PFpc6#Rf9Xf^lAJ9i*s z<#@IbfNl zlUSHDNV@Io6PqnE>SYIH>hUVmOHqQ*d1Xsazhz`d@xW;)E=N0ElPNk#cyrvdv>wY= z3);R!N!tlPPdSw&lS(0*bFrv5e%ZTPuevAi_e?@Bbr@>be zFy1*gm$U1S#-9|Te&#(_NaCoLLO|Vj=4n|v2G!3O-JXnKXWL64-XOA^r(3%3YVDR3 z=&l?6{OYsYHE=V$10ES8HJ?mMO($K1zy|2r13Tq;?{L8PBjHpff^q%SGce(L4^vSs zrw`G9@22oGJ=(LcK&K~ly)|>hANskwj@N$Wy+u*;v-GL4+m`ws0_6Niv%?muE${MG z&%J@%!lxTkI-({U3`G;9O?u}w!_*u1@WK6SPg-xYx32mhlqwBK*q6X2d;E~ec$EmE z!Jm4QM*J$9B857r0h21NFjyzU&4mrjd5(ZFI<#&dfPr~xcR%yc{crXN;VpmK3y=y$ zL|9#JQ$@)F6)q|W&DA>z+2SfYBFq?2*AmVrY-)WLysrP0I^-~SnHttX36Z3g-{O{k zc6ng1^$bNi2$RZePSW{ok{QZGj@5hj3l!1WNRGxh!dYuepR*&Q(Z&GZV_0YdCl?-> zxk`UuCaU5I%kw=hKzl}%V6jBir>osiD{8}tLfw@y9r&6yggNa;*=v-Ew}+F4dNa`F zzzgHAVKQF$VW~k(WC6b~C)NE*v_X0uqpB4%#)>wIPIJ;<_@PmSkxvPW%Qu)#`m+W- zn_#A`St1x0t*yHd|8>fQ4XT%To`TtGTLg`}M4R0_9DcCuUEwW*9zTg)^6qmy&Y$&c z*gxD5)M7yzJ1#d{tR1r5_BDk|6XL8M1qP0JXFNQVd9-W9qTyDZW;-m9tnh$H|H!_C zgl!-kBQzXR#Q7(>UoUpx_mF`xpaN8dBxs04s(+jy~}FbV0iKKYjM5-v2!H|Y8$@4f?8zbA*f3e5IsTjrzflM zRfc@4f{{^)z~ztfpcejhAn*}`h&M+_BOfRlgzFK6TF1HkXA@LZm_Ws6RiokRg=V?s zGRz=`_EnxR>#RK53}F5WLn=vNet&@vj%Mp*A6poIOUK@y_QJkzgL^A4 zPNR_SK(I3p*V^GnLx8)jeDU5S7 zm+Sd749hLg+7I&zdsc>RNbKBR)|Lb1QI(p5vt?tt@Sp|=jmp)>7T|dl1o;yrTG- z$vL5b20gOl8wDkbHtQ+8F3=<0E*r>g+@CsM0w7SgpY`3jDVTe-vs~_!`kMIZ@i`Qy zVA(u%#eNZ({n&W1M|yh5wvKqsXyCr7+kzx63twv4=KqNSW$?67Eb2a?7tZ4Lsv+M& zy-G`?VykTpFVHW@+@eP-1TTNo`w>)WgGPxLypZ;I@T`>n3)`LNfm7M+2oY~lPX zO3);WMP^7jVOs}@_{GdCE2QQGm3i@*P-wR;l;;lx;;E@%e+=m6)BYjY5+efD!mWfh zNs{No`$#hi2;4=vo*sOrOePiAKNT3de5na9QSv9Tf)@?N|D85A56*u5@Ul4+Pme;; z-JCyJ@oP$%jB2(cOsbgL#&_oT12%V#m9Y4#`GVgJ@zXok#K)p6qX#lX1x{-zsCi15 zKWn(YXkD+{Z#1J`H=S<)Zq%_nXk249&c(#<>M4QSZ~mP8Gyr?DnfP`DRlkBO>gcI> zJof#ev2(?T;P4noal}}&J*J1|dArCXU39vD@nWZPc1g5lTHp9wy3U@aX0MB8s6zf7 zDFKjgRN>NUJRKwYJ|T?XP^7{54vM-GSexj#Kgn2h{6Hrhy0aBW98V1YabMl~`9Ph6 zRE__9^8j$q&*%1jz5-;E7?IcO?6!>{DZT_b0zhCTeteC1 zF=N{1k1WKY8B1g1_1dq*q6D|K_^eD=3l}@HWt{dYswpm!(Ol%73gJ11Sz(#+w@d!K zBDLppM`Ax(+@6g6s1%2ru9a8pxV)J%Y1C0C@SNIB0Lf0B;nDaG7wvSFBd#*+_ld*(w z+a{3Sg=dl-$oHewI}hlwWV@}a!Yy0?uV6|~P5=gt%uJo@%wn}yfn1(!X=!PMO+9an zxW}#&W`D>ng zp0c*9G%T>UpVBoY>EL~8?hpk&K=SfRP(TFs3wtUpm}-xqxz^z3H2#(-5BcH9icLL{ zO74m0#dRcNw)zfrH(%wn!3y)O&6=r^)3(#ACpKq2RNm3kXi3KpFtqR9qQyw8p z+v8oD%i`e0>XOls3kx)YBLct+8!oD=RhRbbio94NesfSS!cgN_*FU%x^Bn$^I=m3? zexut-eTp5@Z*&=0qd9&gT+;1ve~Wy36hYweHJ(A!{W4k#dvkhZMbzOrx04L(8>gXk zW{tb>)9zRzKG#SxsW=Av>7!G%$h(zDsq2xpK4@gl?4!e3=q}$T0Iu|BC1Loy$7}4} zB2br)vpukSLQ$LT8oZlm(P*a+H!JmYj#>8hR_v66FIt8)YKmx;=6UF@1n@xMct5#;a)z)46n859MzZU{!`cfUeey`p24&u|gsy}wi zMAG5EOgI5dmjqkfK^sDKns)J~TXwbukQxqicTv)`yY=kNHFTZ$m245<55?IA%W{*9 zD(eSXZR169b2mRh8ePm4qG72m-dE`xkujckD_-30nLGGaqq$s5v0*TE!N+BD5Z5Fg zl);p}X-MUcBthI31B%Da0BA}@!l4Sl8SR=5ff@*}=-TRO;EO!-X^3#8`E7T`H{?w} zWfrqa*24DB84G5N9T%DM#?5IH{LwV+n@>^X-a;Ei=S2QeC=-acT!&vuF`wS;YT6|E z=}%MWLrL8WTA4~o>wNsPene=3trSZ4AhLGhUPz)Uo|Dsm1v74vL>u|tT{-K zVi3#*9kVVlt=Jaa?SwysSsoW~NUEUZJCGT6yE9o1`?77KOQ@}{maHik#9_!8DcZK2_Ty^b>aTR((!*z)x8btcR<0UQ3 zK_tU1YT}qd^$nexd*=NONSo*!0iS`Xk%+q*iCRW4mpnW=y!Tt-7&vyS=F=iCgq`sR z+?Eq6pjnL|0AwjIdyg|->7MObaQms7yK7Yi@z8uc;C2BUnRQo-$JA}Nf@OyqeOy;~ zJ^&%-G(N3{O#f$oBqz+t`PJvfdvd9L(Q#vdAs7)*y9CG4nFR~k)T>3j?>u+IC0EzPY8`}6BCc1LftFiSH={v64j^^sQBjR@BJ9g5*tO0+5qR%$;IM<^i zAJnzK&Z?Rp)*FX50(gGUIZ!RUh}lFDsn6Om8d5Yu1V|!=(q(JJl(euYo9MRHfkm24 zDh)^XQzk`ux1fQ7eGc2teeI=u&}V;4=+RC|JGD+uxg@_OlPN~Yf-0XYhcsQpFeO4c zcV=?$x%T{)aH7aD?5}=|RA=VBQg@?Frm=AmV8ry?7t^9_;!N&|)Y|Wo6o1l@`P^RJ zTCAz)H?L7m3C~m>Q78pK`HaB*sna0M3K6zTCPPw^Gqv*B@CA7gd!Nnm_Vg9Q@C| zr1M=W8IU(5tF?0x&8#`qo)6j{$ti#k92+eRh3=}dg>S=Q03uv+IP$n0&4mp zx<O@gk=QFZX`(;0~ihD!LU1JdW6y(@gd73WuJ&oGzy-Unz#a7ASVdq_sX}TwD=sc-V zi)|4t8csuBP~4soKf~u1jLFMdI!crB3L+qDmuA9PNVOOC+_A*|BUCxmwE-jaCpG)# ziJduqCy=!^+LMY4f`UEDGrh*K7)-BuKor#_I#vZ<9SD%zyg z3*pCFB?NL+=IX9rcJZClp7sv0e*ls>d==;-*@MXo!UXQmh=3KqsQ*X%R}Z`tcWLQ& zDhk;`Nj=eZnt?sbJ~Sc?M?EN)WExzX^OwOGMVyV9B}X@cHJ{>fi{UXKSfw3$$;hdp zDBECH&bZ;*KKWcJ+A$3o1jY!Y-1n~h&4r7^m^=rl3v@3zPg-?FB8!HD!nlucu5T{O zGW?g-Uw5x1#<6`F3)R00*w=0-R=)4tF<`#@e;N$(}=6Npk6cFV=;l z(|Ef8*mQGf--KNR>%pTX$F(vzJ24L9I?)K@(7n|#_7@gyc!ap z70gq>yX*5hAfzeamoNRrguyrB9==AlV6;e2OMBq`$TnYy+n^=@ohNeKN)zJ{F^-PY z_0vEisW_zRh8F?1K#2R4$nUI;C2PR&`e_RDF5>0Fiuw5WOue#`uSh=Q0gMnW3T2cw}#LAX57N^Y`OBGYjzcve9l}&&p@LGh$m;(--~zjT3-A^CMQYY zeT&e01j}ZWqcVEajClH$-~#xXx#6nzj@Wn>{-Z7Q9D&PONfD zlX(PJRqgm2CG(Fe&1bVA`3?JxN5&op3y-boo8KMn);j71oB^6^5FCC!H896ho!~j~ zdxo72S(lE^_du}*60hIO^KT?+xyy&Bn=^tIgemf$07N!B63RUCOBGG}q{3(O~@xNQ|gUzZb!&O@l5bik1WL0mx&)C^w8uYAtp3(R=`GYQ`6o zdSdCw-*78YE%Tv5QP%W>tb(aGJZ zv2N>nB#~ZprTYxYhxQa{G~}v1I+M6`T6BZ%yu8%si5o^1nt7SZ;n1k^2+irLUYL`D zH9eV^FuZ!3dcTu(=$iLy<8J>kp#H|eIwk84ox^5Ko9^0h#vSApDP|Q1pS=^?w&FAu zY`%p-eg#r4nv8A2vIXkB$B0;csH{9~&mRuwu=rN%YQ`AXRZvSz%7AX3*1x2q(If~G zKI81TUn`qPdrroOs99mo3SCypBK<;mq#axXF(7p5X5@^&~ zX56pUjpz5~F^Y*S6Hj{SrZa@D9QbKRjJb=RAnjz$y(tNN60*gj@nHiPY4>j5I~{*8 zZu4d0h76pkz83I9l0hb(d0Dlud}i=b#?p3MDr@q5@b{_6Q)TAx7V*A+&Z${cI>W8{ zhu3Q{j9~xtxQ;eQO8xz32F=Y*angOCA3q}?;Nz9%l@jW(4eW~NhBg>O@5e#yc!Va; z3x50oW~S)ALP1Kx;(TZ4XWx^voOz3?x;(1W;n~sQd-v@EtW=4cH`}W3o36Hc^@k-` zO8laCnf3(7ShbxxgX7o21@2bfL@SBO0cUbgEjihexjG=-Z&j7_XJDMfaOxfXjG%&A%WtOQz~Do*W|wUzw!!r728@|S zQ*r|9&(tVjrrt*_{e21torLzjGrPzYL2@!{qx9aRmE-`U^0`K6_rp&PW%SgN5;LT;qx5H)pr`zad&0_2-LIJy4{w^vh&7*=U z35n*lq%)dRD{i&!GbA5!J(@r98R_YjMf?(ce`zgdEL=FA&NG=r1|Njz`9c-r*(x$w zuS?qNVt=H_l}Hw9lD0q(x{LF49_iFlj_-a?B70%^wH^DGL&>LI_{Zz(9B#=p9A=+u z*I$(x!p~TLz8>vr_c@BImvRd=NhNc&880NG7>4l+<2=evj-xGf@6g){y5;1iJ4MLz z?jBm=KR~511*wv{)+BZ(xrB5r5?0>npflUCpF7iL8^-L`&+h=V#amCVaMVm=c!ayT^kJ5>tA{h1SPQ4F7R`S@FTKiiv;_oWnlyu01cNJp0( znD35gDr~8|lcZ%oY%etOAWuFSCnDPyN7&8($Ko^zUJf&8bs>}HK&n>CaBx-ij0|_8 z+(0m6e>`yGFS3DGFB=*F2bpZvPW=3WW>U% zNJrFoKTJkwyLl53rIxg>ApnUg%?S2wIG>wW5w0>zoaD(qie}k=|6V>7KWQ|Mhfndq zSvyi`8&mP68X8YF-Dg>r;pB{amutC?*wUFP`piJXW!q?;4YCN^efpiiCh5H^-+gJeO=3 zi+P@W9s~t$r`t;Mg6o)k+K|40iLoNhc_HQbd26NxxDaap#E);MU5L7Ml*f}^wLCcF zz>WpO`Hc1$SGt@xCGxJ6TQ!RNzmYCRgHIhR@LNjPyV(3P7E7)C@Yw zKRSbf;k09rbIZkUmNLmf19>=aoYYA4A+=# z^okmMUs^6vlBDm#d>067g7WFnUsjI>Q1U`bXXNH$XrfCrLe5gxgup# zh%R!0`0<-!cV=`l&H|nMIGa`kb+5^pX=P2n(|+|!yCX35uMh=Ti?hlYJ7TgZjac3B~K8s z46J@D#79w;JQJI1*>S83-@f#91!|uO&zTL=f4#YAEftg<*Kha;2iaL!4^EJbgK+T!4H?7V(8n2Lba)x_}^cmPX|*%W4ZnA$^TTkg-&MN~|v}CW)cCl@J{V z+cIeUdLf*2`W0%+6@BLGqED^2>Ou(_cG{Y-3Oi_vIqSN7I&L>yb4+M78R?2>;37qW z(Cew!usL1TR3JVcwRf59FxUMaz$b=7yex~UXd=%d;p_HlL%g~dLy@P+n-;j7F{B2@ zDt94cG+v3UlHBRyb!T(nz}=o#f!&(o0W#TM$Yyrs@eo^NP-`l927TQf`m99SGlG?c ziDn7}zP>C#_XniiU)l7raiH9=tH!eKH{GXknb$yMs&CI!f2H|B5G$C8F35q@sbRM0 zZo44&NYjOm3PCaQ({lMFn`O8`bbu;@&4rs~K>sXa}{ebfA?2Upt` z@KbLU>fTRRs!jwTjS3e5H*U|oZxo)*E}9oR{?`{7uJNtv4zs!|Pg{{iX>H{+wN8yA zx2CFBzNC1*Y4F?in0H*3*zQ;4sGwt)PFgx%&1U%Gh3+tWSNf)}3g6OB*hjBIs9(x$ zLS~>MznhrJ=t1A!EcJBXgU9r|LxMH&;iy(P9d9h&shb!esV`8CJxNo`l2jkgl^1vE zH?Pr~KBz4V=~>bg;$)mT%whAxcUM|qywYD@LWwD36~&B#(Po2vsm<2m#q_6pEqDOJ z89j4}sC#3xVW?o|=XH(X)|yQBYu5K#GDMIksN*$G#L!(oK7a8H9ZBd@HS>?K!;L`} zvc?(Fs2PhU=(c=kX;6OYl&}XsQ_yfd)Z3;T?-0$ezTV@Q`Ty8^�(^ZCz9mBq<;W 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%0A&~5PT~7 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 z

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 diff --git a/docs/assets/versions.css b/docs/assets/versions.css index ae75d223d1335..b8bb066929dd0 100644 --- a/docs/assets/versions.css +++ b/docs/assets/versions.css @@ -53,7 +53,7 @@ div[data-md-component=announce]>div#announce-msg>a{ } /* from https://assets.readthedocs.org/static/css/badge_only.css, -most styles have to be overridden here */ +most styles have to be overriden here */ .rst-versions{ position: relative !important; bottom: 0; diff --git a/docs/assets/versions.js b/docs/assets/versions.js index b9f0b13e8d013..274b3b557eae6 100644 --- a/docs/assets/versions.js +++ b/docs/assets/versions.js @@ -61,17 +61,23 @@ window.addEventListener("DOMContentLoaded", function() { var margin = 30; var headerHeight = document.getElementsByClassName("md-header")[0].offsetHeight; const currentVersion = getCurrentVersion(); - if (currentVersion && currentVersion !== "stable") { + if (currentVersion) { if (currentVersion === "latest") { document.querySelector("div[data-md-component=announce]").innerHTML = ""; - } else { + var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin; + document.querySelector("header.md-header").style.top = bannerHeight + "px"; + document.querySelector('style').textContent += + "@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}"; + document.querySelector('style').textContent += + "@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}"; + } else if (currentVersion !== "stable") { document.querySelector("div[data-md-component=announce]").innerHTML = "
You are viewing the docs for a previous version of Argo CD, click here to go to the latest stable version.
"; + var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin; + document.querySelector("header.md-header").style.top = bannerHeight + "px"; + document.querySelector('style').textContent += + "@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}"; + document.querySelector('style').textContent += + "@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}"; } - var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin; - document.querySelector("header.md-header").style.top = bannerHeight + "px"; - document.querySelector('style').textContent += - "@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}"; - document.querySelector('style').textContent += - "@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}"; } }); diff --git a/docs/cli_installation.md b/docs/cli_installation.md index 5a314d4ce6be2..42938bcd751ba 100644 --- a/docs/cli_installation.md +++ b/docs/cli_installation.md @@ -37,17 +37,6 @@ 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. diff --git a/docs/developer-guide/architecture/components.md b/docs/developer-guide/architecture/components.md index e073751da4867..eb2904b531ccb 100644 --- a/docs/developer-guide/architecture/components.md +++ b/docs/developer-guide/architecture/components.md @@ -71,7 +71,7 @@ and the CLI functionalities. ### Application Controller The Application Controller is responsible for reconciling the -Application resource in Kubernetes synchronizing the desired +Application resource in Kubernetes syncronizing the desired application state (provided in Git) with the live state (in Kubernetes). The Application Controller is also responsible for reconciling the Project resource. diff --git a/docs/developer-guide/code-contributions.md b/docs/developer-guide/code-contributions.md index 2d28aaa956b48..b02bf64e15505 100644 --- a/docs/developer-guide/code-contributions.md +++ b/docs/developer-guide/code-contributions.md @@ -103,12 +103,10 @@ 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 8:15AM Pacific Time ([click here to check in your current timezone][1]). We use Zoom to conduct these meetings. +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. * [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 it. - -[1]: https://www.timebie.com/std/pacific.php?q=081500 +for one of the upcoming meetings so that we can plan in the time for discussing it. \ No newline at end of file diff --git a/docs/developer-guide/contributors-quickstart.md b/docs/developer-guide/contributors-quickstart.md index 68cda35b6d08e..0e98fab7ec940 100644 --- a/docs/developer-guide/contributors-quickstart.md +++ b/docs/developer-guide/contributors-quickstart.md @@ -9,9 +9,7 @@ and the [toolchain guide](toolchain-guide.md). ### Install Go - - -Install Go with a version equal to or greater than the version listed in `go.mod` (verify go version with `go version`). +Install version 1.18 or newer (Verify version by running `go version`) ### Clone the Argo CD repo @@ -25,29 +23,16 @@ git clone https://github.com/argoproj/argo-cd.git -### Install or Upgrade a Tool for Running Local Clusters (e.g. kind or minikube) - -#### Installation guide for kind: +### Install or Upgrade `kind` (Optional - Should work with any local cluster) -#### 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 diff --git a/docs/developer-guide/debugging-remote-environment.md b/docs/developer-guide/debugging-remote-environment.md index f87d1a0bb009d..5548d3444af8c 100644 --- a/docs/developer-guide/debugging-remote-environment.md +++ b/docs/developer-guide/debugging-remote-environment.md @@ -21,7 +21,7 @@ curl -sSfL https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/i Connect to one of the services, for example, to debug the main ArgoCD server run: ```shell kubectl config set-context --current --namespace argocd -telepresence helm install --set agent.securityContext={} # Installs telepresence into your cluster +telepresence helm install # Installs telepresence into your cluster telepresence connect # Starts the connection to your cluster (bound to the current namespace) telepresence intercept argocd-server --port 8080:http --env-file .envrc.remote # Starts the interception ``` diff --git a/docs/developer-guide/dependencies.md b/docs/developer-guide/dependencies.md index 2a4c869825e31..410fd1241b1b2 100644 --- a/docs/developer-guide/dependencies.md +++ b/docs/developer-guide/dependencies.md @@ -6,32 +6,31 @@ https://github.com/argoproj/gitops-engine -### Pulling changes from `gitops-engine` +### Pulling changes from `gitops-engine` -After your GitOps Engine PR has been merged, ArgoCD needs to be updated to pull in the version of the GitOps engine that contains your change. Here are the steps: +After your GitOps Engine PR has been merged, ArgoCD needs to be updated to pull in the version of the GitOps engine that contains your change. Here are the steps: -- Retrieve the SHA hash for your commit. You will use this in the next step. -- From the `argo-cd` folder, run the following command +* Retrieve the SHA hash for your commit. You will use this in the next step. +* From the `argo-cd` folder, run the following command - `go get github.com/argoproj/gitops-engine@` + `go get github.com/argoproj/gitops-engine@` - If you get an error message `invalid version: unknown revision` then you got the wrong SHA hash + If you get an error message `invalid version: unknown revision` then you got the wrong SHA hash -- Run: +* Run: - `go mod tidy` + `go mod tidy` -- The following files are changed: +* The following files are changed: - - `go.mod` - - `go.sum` + - `go.mod` + - `go.sum` -- Create an ArgoCD PR with a `refactor:` type in its title for the two file changes. +* Create an ArgoCD PR with a `refactor:` type in its title for the two file changes. ### Tips: - -- See https://github.com/argoproj/argo-cd/pull/4434 as an example -- The PR might require additional, dependent changes in ArgoCD that are directly impacted by the changes made in the engine. +* See https://github.com/argoproj/argo-cd/pull/4434 as an example +* The PR might require additional, dependent changes in ArgoCD that are directly impacted by the changes made in the engine. ## Argo UI Components @@ -46,8 +45,10 @@ If you make changes to the Argo UI component, and your Argo CD changes depend on 1. Make changes to Argo UI and submit the PR request. 2. Also, prepare your Argo CD changes, but don't create the PR just yet. 3. **After** the Argo UI PR has been merged to master, then as part of your Argo CD changes: - - Run `yarn add git+https://github.com/argoproj/argo-ui.git` in the `ui/` directory, and then, - - Check in the regenerated yarn.lock file as part of your Argo CD commit -4. Create the Argo CD PR when you are ready. The PR build and test checks should pass. + - Run `yarn add git+https://github.com/argoproj/argo-ui.git` in the `ui/` directory, and then, + - Check in the regenerated yarn.lock file as part of your Argo CD commit +4. Create the Argo CD PR when you are ready. The PR build and test checks should pass. If your Argo UI change is a 'stand-alone' fix, and you simply want Argo CD to pull in your change, then simply create an Argo CD PR with the yarn.lock file change. + + diff --git a/docs/developer-guide/docs-site.md b/docs/developer-guide/docs-site.md deleted file mode 100644 index 43b3fba747186..0000000000000 --- a/docs/developer-guide/docs-site.md +++ /dev/null @@ -1,25 +0,0 @@ -# Documentation Site - -## Developing And Testing - -The [documentation website](https://argo-cd.readthedocs.io/) 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/). -Making changes to documentation will automatically rebuild and refresh the view. - -Before submitting a PR build the website, to verify that there are no errors building the site -```bash -make build-docs -``` - -## Analytics - -!!! tip - Don't forget to disable your ad-blocker when testing. - -We collect [Google Analytics](https://analytics.google.com/analytics/web/#/report-home/a105170809w198079555p192782995). diff --git a/docs/developer-guide/extensions/proxy-extensions.md b/docs/developer-guide/extensions/proxy-extensions.md index ab4d89c7f8e32..9982a5cdee59a 100644 --- a/docs/developer-guide/extensions/proxy-extensions.md +++ b/docs/developer-guide/extensions/proxy-extensions.md @@ -15,7 +15,7 @@ requests before forwarding to the backend service. 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 +in Argo CD command parameters. The easiest way to to properly enable this feature flag is by adding the `server.enable.proxy.extension` key in the existing `argocd-cmd-params-cm`. For example: @@ -120,7 +120,7 @@ Is the address where the extension backend must be available. 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 overridden by the one in this list. Reserved header +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*) @@ -264,14 +264,6 @@ Note that additional pre-configured headers can be added to outgoing request. See [backend service headers](#extensionsbackendservicesheaders-list) section for more details. -#### `Argocd-Username` - -Will be populated with the username logged in Argo CD. - -#### `Argocd-User-Groups` - -Will be populated with the 'groups' claim from the user logged in Argo CD. - ### Multi Backend Use-Case In some cases when Argo CD is configured to sync with multiple remote diff --git a/docs/developer-guide/extensions/ui-extensions.md b/docs/developer-guide/extensions/ui-extensions.md index ffe4ba936cc74..8d3d9dc4a3882 100644 --- a/docs/developer-guide/extensions/ui-extensions.md +++ b/docs/developer-guide/extensions/ui-extensions.md @@ -6,7 +6,7 @@ in the `argocd-server` Pods that are placed in the `/tmp/extensions` directory a ``` /tmp/extensions ├── example1 -│ └── extension-1.js +│   └── extension-1.js └── example2 └── extension-2.js ``` @@ -73,7 +73,7 @@ registerSystemLevelExtension(component: ExtensionComponent, title: string, optio Below is an example of a simple system level extension: -```javascript +```typescript ((window) => { const component = () => { return React.createElement( @@ -106,7 +106,7 @@ registerStatusPanelExtension(component: StatusPanelExtensionComponent, title: st Below is an example of a simple extension: -```javascript +```typescript ((window) => { const component = () => { return React.createElement( @@ -129,95 +129,32 @@ It is also possible to add an optional flyout widget to your extension. It can b Below is an example of an extension using the flyout widget: - -```javascript +```typescript ((window) => { const component = (props: { - openFlyout: () => any - }) => { + openFlyout: () => any + }) => { return React.createElement( - "div", - { - style: { padding: "10px" }, - onClick: () => props.openFlyout() - }, - "Hello World" + "div", + { + style: { padding: "10px" }, + onClick: () => props.openFlyout() + }, + "Hello World" ); }; const flyout = () => { return React.createElement( - "div", - { style: { padding: "10px" } }, - "This is a flyout" + "div", + { style: { padding: "10px" } }, + "This is a flyout" ); }; window.extensionsAPI.registerStatusPanelExtension( - component, - "My Extension", - "my_extension", - flyout + component, + "My Extension", + "my_extension", + flyout ); })(window); ``` - -## Top Bar Action Menu Extensions - -The top bar panel is the action menu at the top of the application view where the action buttons are displayed like Details, Sync, Refresh. Argo CD allows you to add new button to the top bar action menu of an application. -When the extension button is clicked, the custom widget will be rendered in a flyout panel. - -The extension should be registered using the `extensionsAPI.registerTopBarActionMenuExt` method: - -```typescript -registerTopBarActionMenuExt( - component: TopBarActionMenuExtComponent, - title: string, - id: string, - flyout?: ExtensionComponent, - shouldDisplay: (app?: Application) => boolean = () => true, - iconClassName?: string, - isMiddle = false -) -``` - -The callback function `shouldDisplay` should return true if the extension should be displayed and false otherwise: - -```typescript -const shouldDisplay = (app: Application) => { - return application.metadata?.labels?.['application.environmentLabelKey'] === "prd"; -}; -``` - -Below is an example of a simple extension with a flyout widget: - -```javascript -((window) => { - const shouldDisplay = () => { - return true; - }; - const flyout = () => { - return React.createElement( - "div", - { style: { padding: "10px" } }, - "This is a flyout" - ); - }; - const component = () => { - return React.createElement( - "div", - { - onClick: () => flyout() - }, - "Toolbar Extension Test" - ); - }; - window.extensionsAPI.registerTopBarActionMenuExt( - component, - "Toolbar Extension Test", - "Toolbar_Extension_Test", - flyout, - shouldDisplay, - '', - true - ); -})(window); -``` \ No newline at end of file diff --git a/docs/developer-guide/index.md b/docs/developer-guide/index.md index 08578c0fd0707..c0405c5e0803b 100644 --- a/docs/developer-guide/index.md +++ b/docs/developer-guide/index.md @@ -1,26 +1,10 @@ # Overview !!! warning "You probably don't want to be reading this section of the docs." - This part of the manual is aimed at helping people contribute to Argo CD, the documentation, or to develop third-party applications that interact with Argo CD, e.g. + This part of the manual is aimed at people wanting to develop third-party applications that interact with Argo CD, e.g. * A chat bot * A Slack integration - -## Contributing to Argo CD -* [Code Contribution Guide](code-contributions/) -* [Contributors Quickstart](contributors-quickstart/) -* [Running Argo CD Locally](running-locally/) - -Need help? Start with the [Contributors FAQ](faq/) - -## Contributing to the Documentation -* [Building and Running Documentation Site Locally](docs-site/) - -## Extensions and Third-Party Applications -* [UI Extensions](ui-extensions/) -* [Proxy Extensions](proxy-extensions/) -* [Config Management Plugins](../operator-manual/config-management-plugins/) - -## Contributing to Argo Website -The Argo website is maintained in the [argo-site](https://github.com/argoproj/argo-site) repository. \ No newline at end of file +!!! note + Please make sure you've completed the [getting started guide](../getting_started.md). diff --git a/docs/developer-guide/release-process-and-cadence.md b/docs/developer-guide/release-process-and-cadence.md index 2db177eb90984..737c6eba6a8d9 100644 --- a/docs/developer-guide/release-process-and-cadence.md +++ b/docs/developer-guide/release-process-and-cadence.md @@ -13,11 +13,8 @@ These are the upcoming releases dates: | 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 | Friday, Apr. 5, 2024 | Monday, May 6, 2024 | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [checklist](https://github.com/argoproj/argo-cd/issues/17726) | -| v2.12 | Monday, Jun. 17, 2024 | Monday, Aug. 5, 2024 | [Ishita Sequeira](https://github.com/ishitasequeira) | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [checklist](https://github.com/argoproj/argo-cd/issues/19063) | -| v2.13 | Monday, Sep. 16, 2024 | Monday, Nov. 4, 2024 | [Regina Voloshin](https://github.com/reggie-k) | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [checklist](https://github.com/argoproj/argo-cd/issues/19513) | -| v2.14 | Monday, Dec. 16, 2024 | Monday, Feb. 3, 2025 | | | | -| v2.15 | Monday, Mar. 17, 2025 | Monday, May 5, 2025 | | | | +| 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. @@ -74,7 +71,7 @@ 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). +CVEs in Argo CD code will be patched for all [supported versions](../operator-manual/installation.md#supported-versions). ### Dependencies Lifecycle Policy diff --git a/docs/developer-guide/site.md b/docs/developer-guide/site.md new file mode 100644 index 0000000000000..af32753a323e2 --- /dev/null +++ b/docs/developer-guide/site.md @@ -0,0 +1,26 @@ +# Site + +## Developing And Testing + +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/). + +## Deploying + +```bash +make publish-docs +``` + +## Analytics + +!!! 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 diff --git a/docs/developer-guide/static-code-analysis.md b/docs/developer-guide/static-code-analysis.md index 90798a70f5a32..ef4d72c99a3b6 100644 --- a/docs/developer-guide/static-code-analysis.md +++ b/docs/developer-guide/static-code-analysis.md @@ -2,7 +2,7 @@ We use the following static code analysis tools: -* golangci-lint and eslint for compile time linting +* golangci-lint and tslint for compile time linting * [codecov.io](https://codecov.io/gh/argoproj/argo-cd) - for code coverage * [snyk.io](https://app.snyk.io/org/argoproj/projects) - for image scanning * [sonarcloud.io](https://sonarcloud.io/organizations/argoproj/projects) - for code scans and security alerts diff --git a/docs/developer-guide/toolchain-guide.md b/docs/developer-guide/toolchain-guide.md index 9bba72b456f71..42ca7fac87404 100644 --- a/docs/developer-guide/toolchain-guide.md +++ b/docs/developer-guide/toolchain-guide.md @@ -138,14 +138,6 @@ 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` @@ -312,7 +304,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 -BIN=~/go/bin make install-tools-local +make BIN=~/go/bin 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): diff --git a/docs/faq.md b/docs/faq.md index e98ca95f556b6..5ce6ca134ff1b 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -70,14 +70,14 @@ kubectl -n argocd patch secret argocd-secret \ ``` Another option is to delete both the `admin.password` and `admin.passwordMtime` keys and restart argocd-server. This -will generate a new password as per [the getting started guide](getting_started.md), so either to the name of the pod -(Argo CD 1.8 and earlier) +will generate a new password as per [the getting started guide](getting_started.md), so either to the name of the pod ( +Argo CD 1.8 and earlier) or a randomly generated password stored in a secret (Argo CD 1.9 and later). ## How to disable admin user? -Add `admin.enabled: "false"` to the `argocd-cm` ConfigMap -(see [user management](./operator-manual/user-management/index.md)). +Add `admin.enabled: "false"` to the `argocd-cm` ConfigMap ( +see [user management](./operator-manual/user-management/index.md)). ## Argo CD cannot deploy Helm Chart based applications without internet access, how can I solve it? @@ -110,8 +110,8 @@ to all manifest generators. ## I've configured [cluster secret](./operator-manual/declarative-setup.md#clusters) but it does not show up in CLI/UI, how do I fix it? Check if cluster secret has `argocd.argoproj.io/secret-type: cluster` label. If secret has the label but the cluster is -still not visible then make sure it might be a permission issue. Try to list clusters using `admin` user -(e.g. `argocd login --username admin && argocd cluster list`). +still not visible then make sure it might be a permission issue. Try to list clusters using `admin` user ( +e.g. `argocd login --username admin && argocd cluster list`). ## Argo CD is unable to connect to my cluster, how do I troubleshoot it? @@ -127,7 +127,7 @@ Now you can manually verify that cluster is accessible from the Argo CD pod. ## How Can I Terminate A Sync? -To terminate the sync, click on the "synchronization" then "terminate": +To terminate the sync, click on the "synchronisation" then "terminate": ![Synchronization](assets/synchronization-button.png) ![Terminate](assets/terminate-button.png) @@ -270,7 +270,7 @@ The most common instance of this error is with `env:` fields for `containers`. 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. ## How to rotate Redis secret? -* Delete `argocd-redis` secret in the namespace where Argo CD is installed. +* Delete `argocd-redis` secret in the namespace where Argo CD is installed. ```bash kubectl delete secret argocd-redis -n ``` @@ -291,78 +291,22 @@ kubectl rollout restart statefulset argocd-application-controller ## How to turn off Redis auth if users really want to? -Argo CD default installation is now configured to automatically enable Redis authentication. +Argo CD default installation is now configured automatically enable Redis authentication. If for some reason authenticated Redis does not work for you and you want to use non-authenticated Redis, here are the steps: -1. You need to have your own Redis installation. -2. Configure Argo CD to use your own Redis instance. See this [doc](https://argo-cd.readthedocs.io/en/stable/operator-manual/argocd-cmd-params-cm-yaml/) for the Argo CD configuration. -3. If you already installed Redis shipped with Argo CD, you also need to clean up the existing components: - - * When HA Redis is used: - - - kubectl delete deployment argocd-redis-ha-haproxy - - kubectl delete statefulset argocd-redis-ha-server - - * When non-HA Redis is used: - - - kubectl delete deployment argocd-redis - -4. Remove environment variable `REDIS_PASSWORD` from the following manifests: - * Deployment: argocd-repo-server +* You need to have your own Redis installation. +* Configure Argo CD to use your own Redis instance. See this [doc](https://argo-cd.readthedocs.io/en/stable/operator-manual/argocd-cmd-params-cm-yaml/) for the Argo CD configuration. +* If you already installed Redis shipped with Argo CD, you also need to clean up the existing components: + * When HA Redis is used: + * kubectl delete deployment argocd-redis-ha-haproxy + * kubectl delete statefulset argocd-redis-ha-server + * When non-HA Redis is used: + * kubectl delete deployment argocd-redis +* Remove environment variable `REDIS_PASSWORD` from the following manifests + * Deployment: argocd-repo-server: * Deployment: argocd-server * StatefulSet: argocd-application-controller - + ## How do I provide my own Redis credentials? The Redis password is stored in Kubernetes secret `argocd-redis` with key `auth` in the namespace where Argo CD is installed. -You can config your secret provider to generate Kubernetes secret accordingly. - -## How do I fix `Manifest generation error (cached)`? - -`Manifest generation error (cached)` means that there was an error when generating manifests and that the error message has been cached to avoid runaway retries. - -Doing a hard refresh (ignoring the cached error) can overcome transient issues. But if there's an ongoing reason manifest generation is failing, a hard refresh will not help. - -Instead, try searching the repo-server logs for the app name in order to identify the error that is causing manifest generation to fail. - -## How do I fix `field not declared in schema`? - -For certain features, Argo CD relies on a static (hard-coded) set of schemas for built-in Kubernetes resource types. - -If your manifests use fields which are not present in the hard-coded schemas, you may get an error like `field not -declared in schema`. - -The schema version is based on the Kubernetes libraries version that Argo CD is built against. To find the Kubernetes -version for a given Argo CD version, navigate to this page, where `X.Y.Z` is the Argo CD version: - -``` -https://github.com/argoproj/argo-cd/blob/vX.Y.Z/go.mod -``` - -Then find the Kubernetes version in the `go.mod` file. For example, for Argo CD v2.11.4, the Kubernetes libraries -version is v0.26.11 - -``` - k8s.io/api => k8s.io/api v0.26.11 -``` - -### How do I fix the issue? - -To completely resolve the issue, upgrade to an Argo CD version which contains a static schema supporting all the needed -fields. - -### How do I work around the issue? - -As mentioned above, only certain Argo CD features rely on the static schema: 1) `ignoreDifferences` with -`managedFieldManagers`, 2) server-side apply _without_ server-side diff, and 3) server-side diff _with_ mutation -webhooks. - -If you can avoid using these features, you can avoid triggering the error. The options are as follows: - -1. **Disable `ignoreDifferences` which have `managedFieldsManagers`**: see [diffing docs](user-guide/diffing.md) for - details about that feature. Removing this config could cause undesired diffing behavior. -2. **Disable server-side apply**: see [server-side apply docs](user-guide/sync-options.md#server-side-apply) for details about that - feature. Disabling server-side apply may have undesired effects on sync behavior. Note that you can bypass this issue - if you use server-side diff and [exclude mutation webhooks from the diff](user-guide/diff-strategies.md#mutation-webhooks). - Excluding mutation webhooks from the diff could cause undesired diffing behavior. -3. **Disable mutation webhooks when using server-side diff**: see [server-side diff docs](user-guide/diff-strategies.md#mutation-webhooks) - for details about that feature. Disabling mutation webhooks may have undesired effects on sync behavior. +You can config your secret provider to generate Kubernetes secret accordingly. \ No newline at end of file diff --git a/docs/getting_started.md b/docs/getting_started.md index ce0d9688e7963..c4438b14705d0 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -22,8 +22,12 @@ 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. -!!! tip - If you are not interested in UI, SSO, and multi-cluster features, then you can install only the [core](operator-manual/core.md#installing) Argo CD components. +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 +``` This default installation will have a self-signed certificate and cannot be accessed without a bit of extra work. Do one of: @@ -32,12 +36,6 @@ 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. !!! note @@ -217,12 +215,6 @@ events, and assessed health status. ### Syncing via UI -On the Applications page, click on *Sync* button of the guestbook application: - ![guestbook app](assets/guestbook-app.png) - -A panel will be opened and then, click on *Synchronize* button. - -You can see more details by clicking at the guestbook application: - ![view app](assets/guestbook-tree.png) + diff --git a/docs/operator-manual/app-any-namespace.md b/docs/operator-manual/app-any-namespace.md index 09df16e061314..21bfa5c4f5a0b 100644 --- a/docs/operator-manual/app-any-namespace.md +++ b/docs/operator-manual/app-any-namespace.md @@ -1,5 +1,7 @@ # 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. @@ -11,6 +13,10 @@ Argo CD administrators can define a certain set of namespaces where `Application Some manual steps will need to be performed by the Argo CD administrator in order to enable this feature. +!!! 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 @@ -42,11 +48,8 @@ In order for an application to be managed and reconciled outside the Argo CD's c In order to enable this feature, the Argo CD administrator must reconfigure the `argocd-server` and `argocd-application-controller` workloads to add the `--application-namespaces` parameter to the container's startup command. -The `--application-namespaces` parameter takes a comma-separated list of namespaces where `Applications` are to be allowed in. Each entry of the list supports: +The `--application-namespaces` parameter takes a comma-separated list of namespaces where `Applications` are to be allowed in. Each entry of the list supports shell-style wildcards such as `*`, so for example the entry `app-team-*` would match `app-team-one` and `app-team-two`. To enable all namespaces on the cluster where Argo CD is running on, you can just specify `*`, i.e. `--application-namespaces=*`. -- shell-style wildcards such as `*`, so for example the entry `app-team-*` would match `app-team-one` and `app-team-two`. To enable all namespaces on the cluster where Argo CD is running on, you can just specify `*`, i.e. `--application-namespaces=*`. -- regex, requires wrapping the string in ```/```, example to allow all namespaces except a particular one: ```/^((?!not-allowed).)*$/```. - The startup parameters for both, the `argocd-server` and the `argocd-application-controller` can also be conveniently set up and kept in sync by specifying the `application.namespaces` settings in the `argocd-cmd-params-cm` ConfigMap _instead_ of changing the manifests for the respective workloads. For example: ```yaml diff --git a/docs/operator-manual/app-sync-using-impersonation.md b/docs/operator-manual/app-sync-using-impersonation.md deleted file mode 100644 index 9314f0b376b8e..0000000000000 --- a/docs/operator-manual/app-sync-using-impersonation.md +++ /dev/null @@ -1,131 +0,0 @@ -# Application Sync using impersonation - -!!! warning "Alpha Feature" - This is an experimental, alpha-quality feature that allows you to control the service account used for the sync operation. The configured service account, could have lesser privileges required for creating resources compared to the highly privileged access required for the control plane operations. - -!!! warning - Please read this documentation carefully before you enable this feature. Misconfiguration could lead to potential security issues. - -## Introduction - -Argo CD supports syncing `Application` resources using the same service account used for its control plane operations. This feature enables users to decouple service account used for application sync from the service account used for control plane operations. - -By default, 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. This provides an opportunity for malicious tenants to gain admin level access. Argo CD provides a multi-tenancy model to restrict what each `Application` is authorized to do using `AppProjects`, however it is not secure enough and if Argo CD is compromised, attackers will easily gain `cluster-admin` access to the cluster. - -Some manual steps will need to be performed by the Argo CD administrator in order to enable this feature, as it is disabled by default. - -!!! note - This feature is considered alpha 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. - -### 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. - -## Prerequisites - -In a multi-team/multi-tenant environment, a team/tenant is typically granted access to a target namespace to self-manage their kubernetes resources in a declarative way. -A typical tenant onboarding process looks like below: -1. The platform admin creates a tenant namespace and the service account to be used for creating the resources is also created in the same tenant namespace. -2. The platform admin creates one or more Role(s) to manage kubernetes resources in the tenant namespace -3. The platform admin creates one or more RoleBinding(s) to map the service account to the role(s) created in the previous steps. -4. The platform admin can choose to use either the [apps-in-any-namespace](./app-any-namespace.md) feature or provide access to tenants to create applications in the ArgoCD control plane namespace. -5. If the platform admin chooses apps-in-any-namespace feature, tenants can self-service their Argo applications in their respective tenant namespaces and no additional access needs to be provided for the control plane namespace. - -## Implementation details - -### Overview - -In order for an application to use a different service account for the application sync operation, the following steps needs to be performed: - -1. The impersonation feature flag should be enabled. Please refer the steps provided in [Enable application sync with impersonation feature](#enable-application-sync-with-impersonation-feature) - -2. The `AppProject` referenced by the `.spec.project` field of the `Application` must have the `DestinationServiceAccounts` mapping the destination server and namespace to a service account to be used for the sync operation. Please refer the steps provided in [Configuring destination service accounts](#configuring-destination-service-accounts) - - -### Enable application sync with impersonation feature - -In order to enable this feature, the Argo CD administrator must reconfigure the `application.sync.impersonation.enabled` settings in the `argocd-cm` ConfigMap as below: - -```yaml -data: - application.sync.impersonation.enabled: "true" -``` - -### Disable application sync with impersonation feature - -In order to disable this feature, the Argo CD administrator must reconfigure the `application.sync.impersonation.enabled` settings in the `argocd-cm` ConfigMap as below: - -```yaml -data: - application.sync.impersonation.enabled: "false" -``` - -!!! note - This feature is disabled by default. - -!!! note - This feature can be enabled/disabled only at the system level and once enabled/disabled it is applicable to all Applications managed by ArgoCD. - -## Configuring destination service accounts - -Destination service accounts can be added to the `AppProject` under `.spec.destinationServiceAccounts`. Specify the target destination `server` and `namespace` and provide the service account to be used for the sync operation using `defaultServiceAccount` field. Applications that refer this `AppProject` will use the corresponding service account configured for its destination. - -During the application sync operation, the controller loops through the available `destinationServiceAccounts` in the mapped `AppProject` and tries to find a matching candidate. If there are multiple matches for a destination server and namespace combination, then the first valid match will be considered. If there are no matches, then an error is reported during the sync operation. In order to avoid such sync errors, it is highly recommended that a valid service account may be configured as a catch-all configuration, for all target destinations and kept in lowest order of priority. - -It is possible to specify service accounts along with its namespace. eg: `tenant1-ns:guestbook-deployer`. If no namespace is provided for the service account, then the Application's `spec.destination.namespace` will be used. If no namespace is provided for the service account and the optional `spec.destination.namespace` field is also not provided in the `Application`, then the Application's namespace will be used. - -`DestinationServiceAccounts` associated to a `AppProject` can be created and managed, either declaratively or through the Argo CD API (e.g. using the CLI, the web UI, the REST API, etc). - -### Using declarative yaml - -For declaratively configuring destination service accounts, create an yaml file for the `AppProject` as below and apply the changes using `kubectl apply` command. - -```yaml -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: - - * - 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 - - server: https://kubernetes.default.svc # catch-all configuration - namespace: '*' - defaultServiceAccount: default -``` - -### Using the CLI - -Destination service accounts can be added to an `AppProject` using the ArgoCD CLI. - -For example, to add a destination service account for `in-cluster` and `guestbook` namespace, you can use the following CLI command: - -```shell -argocd proj add-destination-service-account my-project https://kubernetes.default.svc guestbook guestbook-sa -``` - -Likewise, to remove the destination service account from an `AppProject`, you can use the following CLI command: - -```shell -argocd proj remove-destination-service-account my-project https://kubernetes.default.svc guestbook -``` - -### Using the UI - -Similar to the CLI, you can add destination service account when creating or updating an `AppProject` from the UI diff --git a/docs/operator-manual/application.yaml b/docs/operator-manual/application.yaml index 051ca6a1755e3..aa2dea5c65b7c 100644 --- a/docs/operator-manual/application.yaml +++ b/docs/operator-manual/application.yaml @@ -90,19 +90,6 @@ spec: # and decide which Helm binary to use automatically. This field can be either 'v2' or 'v3'. version: v2 - # You can specify the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD uses - # the Kubernetes version of the target cluster. The value must be semver formatted. Do not prefix with `v`. - kubeVersion: 1.30.0 - - # You can specify the Kubernetes resource API versions to pass to Helm when templating manifests. By default, Argo - # CD uses the API versions of the target cluster. The format is [group/]version/kind. - apiVersions: - - traefik.io/v1alpha1/TLSOption - - v1/Service - - # Optional namespace to template with. If left empty, defaults to the app's destination namespace. - namespace: custom-namespace - # kustomize specific config kustomize: # Optional kustomize version. Note: version must be configured in argocd-cm ConfigMap @@ -116,8 +103,6 @@ spec: beep: boop-${ARGOCD_APP_REVISION} # Toggle which enables/disables env variables substitution in commonAnnotations commonAnnotationsEnvsubst: true - forceCommonLabels: false - forceCommonAnnotations: false images: - gcr.io/heptio-images/ks-guestbook-demo:0.2 - my-app=gcr.io/my-repo/my-app:0.1 @@ -125,27 +110,6 @@ spec: replicas: - name: kustomize-guestbook-ui count: 4 - components: - - ../component # relative to the kustomization.yaml (`source.path`). - patches: - - target: - kind: Deployment - name: guestbook-ui - patch: |- - - op: add # Add new element to manifest - path: /spec/template/spec/nodeSelector/ - value: - env: "pro" - - # You can specify the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD uses - # the Kubernetes version of the target cluster. The value must be semver formatted. Do not prefix with `v`. - kubeVersion: 1.30.0 - - # You can specify the Kubernetes resource API versions to pass to Helm when templating manifests. By default, Argo - # CD uses the API versions of the target cluster. The format is [group/]version/kind. - apiVersions: - - traefik.io/v1alpha1/TLSOption - - v1/Service # directory directory: @@ -155,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" diff --git a/docs/operator-manual/applicationset.yaml b/docs/operator-manual/applicationset.yaml index 88264493e248d..d05b08f1101a0 100644 --- a/docs/operator-manual/applicationset.yaml +++ b/docs/operator-manual/applicationset.yaml @@ -3,217 +3,32 @@ 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: - - # 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' - - + - list: + elements: + - cluster: https://kubernetes.default.svc # Determines whether go templating will be used in the `template` field below. - goTemplate: true + goTemplate: false # 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"] - + goTemplateOptions: ["missingkey="] # 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: - # 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 - + # 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. 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 @@ -221,94 +36,3 @@ spec: 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 index f6124f098cb6d..bf3f8ffecfaf1 100644 --- a/docs/operator-manual/applicationset/Appset-Any-Namespace.md +++ b/docs/operator-manual/applicationset/Appset-Any-Namespace.md @@ -25,9 +25,7 @@ This feature can only be enabled and used when your Argo CD ApplicationSet contr ### 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. This means if ApplicationSet controller is configured to allow namespace `appNs` and some user is allowed to create -an ApplicationSet in `appNs` namespace, then the user can install a malicious Pod into the `appNs` namespace as described below -and read out the content of the secret indirectly, thus exfiltrating the secret value. +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: @@ -36,7 +34,6 @@ apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: myapps - namespace: appNs spec: goTemplate: true goTemplateOptions: ["missingkey=error"] @@ -46,7 +43,7 @@ spec: # 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.appNs.svc.cluster.local + 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 @@ -56,7 +53,7 @@ spec: template: ``` -In order to prevent the scenario above 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. +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 @@ -75,7 +72,7 @@ data: 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`. +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_ALLOW_SCM_PROVIDERS` to argocd-cmd-params-cm `applicationsetcontroller.allow.scm.providers` to `false`. ### Overview diff --git a/docs/operator-manual/applicationset/Controlling-Resource-Modification.md b/docs/operator-manual/applicationset/Controlling-Resource-Modification.md index 1636d348cb009..d72cee60ad401 100644 --- a/docs/operator-manual/applicationset/Controlling-Resource-Modification.md +++ b/docs/operator-manual/applicationset/Controlling-Resource-Modification.md @@ -32,19 +32,16 @@ spec: ``` -- Policy `create-only`: Prevents ApplicationSet controller from modifying or deleting Applications. **WARNING**: It doesn't prevent Application controller from deleting Applications according to [ownerReferences](https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/) when deleting ApplicationSet. -- Policy `create-update`: Prevents ApplicationSet controller from deleting Applications. Update is allowed. **WARNING**: It doesn't prevent Application controller from deleting Applications according to [ownerReferences](https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/) when deleting ApplicationSet. +- 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`). -### Policy - `create-only`: Prevent ApplicationSet controller from modifying and deleting Applications - -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: - -**WARNING**: "*deletion*" indicates the case as the result of comparing generated Application between before and after, there are Applications which no longer exist. It doesn't indicate the case Applications are deleted according to ownerReferences to ApplicationSet. See [How to prevent Application controller from deleting Applications when deleting ApplicationSet](#how-to-prevent-application-controller-from-deleting-applications-when-deleting-applicationset) +### 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 ``` @@ -60,12 +57,9 @@ spec: applicationsSync: create-only ``` -### Policy - `create-update`: Prevent ApplicationSet controller from deleting Applications +## 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`: - -**WARNING**: "*deletion*" indicates the case as the result of comparing generated Application between before and after, there are Applications which no longer exist. It doesn't indicate the case Applications are deleted according to ownerReferences to ApplicationSet. See [How to prevent Application controller from deleting Applications when deleting ApplicationSet](#how-to-prevent-application-controller-from-deleting-applications-when-deleting-applicationset) - ``` --policy create-update ``` @@ -83,22 +77,6 @@ spec: applicationsSync: create-update ``` -### How to prevent Application controller from deleting Applications when deleting ApplicationSet - -By default, `create-only` and `create-update` policy isn't effective against preventing deletion of Applications when deleting ApplicationSet. -You must set the finalizer to ApplicationSet to prevent deletion in such case, and use background cascading deletion. -If you use foreground cascading deletion, there's no guarantee to preserve applications. - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: ApplicationSet -metadata: - finalizers: - - resources-finalizer.argocd.argoproj.io -spec: - # (...) -``` - ## Ignore certain changes to Applications The ApplicationSet spec includes an `ignoreApplicationDifferences` field, which allows you to specify which fields of @@ -343,15 +321,3 @@ metadata: data: applicationsetcontroller.log.level: debug ``` - -## Previewing changes - -To preview changes that the ApplicationSet controller would make to Applications, you can create the AppSet in dry-run -mode. This works whether the AppSet already exists or not. - -```shell -argocd appset create --dry-run ./appset.yaml -o json | jq -r '.status.resources[].name' -``` - -The dry-run will populate the returned ApplicationSet's status with the Applications which would be managed with the -given config. You can compare to the existing Applications to see what would change. diff --git a/docs/operator-manual/applicationset/Generators-Cluster.md b/docs/operator-manual/applicationset/Generators-Cluster.md index de769b94deed9..ca1a49aad295b 100644 --- a/docs/operator-manual/applicationset/Generators-Cluster.md +++ b/docs/operator-manual/applicationset/Generators-Cluster.md @@ -64,7 +64,6 @@ In this example, the cluster secret's `name` and `server` fields are used to pop A label selector may be used to narrow the scope of targeted clusters to only those matching a specific label: ```yaml -apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: guestbook @@ -76,7 +75,7 @@ spec: - clusters: selector: matchLabels: - staging: "true" + staging: true # The cluster generator also supports matchExpressions. #matchExpressions: # - key: staging @@ -89,7 +88,6 @@ spec: This would match an Argo CD cluster secret containing: ```yaml -apiVersion: v1 kind: Secret data: # (... fields as above ...) @@ -138,29 +136,6 @@ 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)` diff --git a/docs/operator-manual/applicationset/Generators-Git-File-Globbing.md b/docs/operator-manual/applicationset/Generators-Git-File-Globbing.md index 04efabecebab9..4f8967b5937fa 100644 --- a/docs/operator-manual/applicationset/Generators-Git-File-Globbing.md +++ b/docs/operator-manual/applicationset/Generators-Git-File-Globbing.md @@ -53,7 +53,7 @@ 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 `argocd-cmd-params-cm` ConfigMap. +1. Set `applicationsetcontroller.enable.new.git.file.globbing: true` in the Argo CD ConfigMap. Note that the default may change in the future. diff --git a/docs/operator-manual/applicationset/Generators-Git.md b/docs/operator-manual/applicationset/Generators-Git.md index 19564970749a6..69108d0604280 100644 --- a/docs/operator-manual/applicationset/Generators-Git.md +++ b/docs/operator-manual/applicationset/Generators-Git.md @@ -7,8 +7,6 @@ The Git generator contains two subtypes: the Git directory generator, and Git fi If the `project` field in your ApplicationSet is templated, developers may be able to create Applications under Projects with excessive permissions. For ApplicationSets with a templated `project` field, [the source of truth _must_ be controlled by admins](./Security.md#templated-project-field) - in the case of git generators, PRs must require admin approval. - - Git generator does not support Signature Verification For ApplicationSets with a templated `project` field. - ## Git Generator: Directories diff --git a/docs/operator-manual/applicationset/Generators-List.md b/docs/operator-manual/applicationset/Generators-List.md index fdd2a91559344..e5696f37b9745 100644 --- a/docs/operator-manual/applicationset/Generators-List.md +++ b/docs/operator-manual/applicationset/Generators-List.md @@ -15,8 +15,8 @@ spec: elements: - cluster: engineering-dev url: https://kubernetes.default.svc - # - cluster: engineering-prod - # url: https://kubernetes.default.svc + - cluster: engineering-prod + url: https://kubernetes.default.svc template: metadata: name: '{{.cluster}}-guestbook' @@ -61,7 +61,7 @@ The List generator can also dynamically generate its elements based on a yaml/js apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: - name: elements-yaml + name: elementsYaml namespace: argocd spec: goTemplate: true diff --git a/docs/operator-manual/applicationset/Generators-Matrix.md b/docs/operator-manual/applicationset/Generators-Matrix.md index 91b1bb3abb778..0396b8c0e06d3 100644 --- a/docs/operator-manual/applicationset/Generators-Matrix.md +++ b/docs/operator-manual/applicationset/Generators-Matrix.md @@ -22,8 +22,8 @@ As an example, imagine that we have two clusters: And our application YAMLs are defined in a Git repository: -- [Argo Workflows controller](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/git-generator-directory/cluster-addons/argo-workflows) -- [Prometheus operator](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/git-generator-directory/cluster-addons/prometheus-operator) +- Argo Workflows controller (examples/git-generator-directory/cluster-addons/argo-workflows) +- Prometheus operator (/examples/git-generator-directory/cluster-addons/prometheus-operator) Our goal is to deploy both applications onto both clusters, and, more generally, in the future to automatically deploy new applications in the Git repository, and to new clusters defined within Argo CD, as well. diff --git a/docs/operator-manual/applicationset/Generators-Plugin.md b/docs/operator-manual/applicationset/Generators-Plugin.md index 13e7bcdb01f36..d0888b9949b8e 100644 --- a/docs/operator-manual/applicationset/Generators-Plugin.md +++ b/docs/operator-manual/applicationset/Generators-Plugin.md @@ -77,12 +77,10 @@ metadata: data: token: "$plugin.myplugin.token" # Alternatively $:plugin.myplugin.token baseUrl: "http://myplugin.plugin-ns.svc.cluster.local." - requestTimeout: "60" ``` - `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. -- `requestTimeout`: Timeout of the request to the plugin in seconds (default: 30) ### Store credentials diff --git a/docs/operator-manual/applicationset/Generators-Post-Selector.md b/docs/operator-manual/applicationset/Generators-Post-Selector.md index 5a07cf1db425c..896e89e267d7c 100644 --- a/docs/operator-manual/applicationset/Generators-Post-Selector.md +++ b/docs/operator-manual/applicationset/Generators-Post-Selector.md @@ -1,57 +1,8 @@ # Post Selector all generators -The `selector` field on a generator allows an `ApplicationSet` to post-filter results using [the Kubernetes common labelSelector format](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) and the generated values. +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`: -`matchLabels` is a map of `{key,value}` pairs. This `list` generator generates a set of two `Applications`, which is then filtered using `matchLabels` to only the list element containing the key `env` with value `staging`: -``` -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 -``` - -The `list` generator + `matchLabels` 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. - -A single `{key,value}` in the `matchLabels` map is equivalent to an element of `matchExpressions`, whose `key` field is the "key", the `operator` is "In", and the `values` array contains only the "value". So the same example using `matchExpressions` looks like: -```yaml -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: - matchExpressions: - - key: env - operator: In - values: - - staging -``` - -Valid `operators` include `In`, `NotIn`, `Exists`, and `DoesNotExist`. The `values` set must be non-empty in the case of `In` and `NotIn`. - -## Full Example -In the example, the list generator generates a set of two applications, 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 @@ -85,3 +36,26 @@ spec: 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 2e6dffaaf5f32..e54fc385d7d28 100644 --- a/docs/operator-manual/applicationset/Generators-Pull-Request.md +++ b/docs/operator-manual/applicationset/Generators-Pull-Request.md @@ -84,8 +84,8 @@ spec: generators: - pullRequest: gitlab: - # The GitLab project ID. - project: "12341234" + # The GitLab project. + project: myproject # For self-hosted GitLab (optional) api: https://git.example.com/ # Reference to a Secret containing an access token. (optional) @@ -99,22 +99,17 @@ spec: pullRequestState: opened # If true, skips validating the SCM provider's TLS certificate - useful for self-signed certificates. insecure: false - # Reference to a ConfigMap containing trusted CA certs - useful for self-signed certificates. (optional) - caRef: - configMapName: argocd-tls-certs-cm - key: gitlab-ca requeueAfterSeconds: 1800 template: # ... ``` -* `project`: Required project ID of the GitLab project. +* `project`: Required name 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. -* `caRef`: Optional `ConfigMap` name and key containing the GitLab certificates to trust - useful for self-signed TLS certificates. Possibly reference the ArgoCD CM holding the trusted certs. 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). @@ -175,8 +170,7 @@ spec: repo: myrepository # URL of the Bitbucket Server. Required. api: https://mycompany.bitbucket.org - # Credentials for Basic authentication (App Password). Either basicAuth or bearerToken - # authentication is required to access private repositories + # Credentials for Basic authentication. Required for private repositories. basicAuth: # The username to authenticate with username: myuser @@ -184,19 +178,6 @@ spec: passwordRef: secretName: mypassword key: password - # Credentials for Bearer Token (App Token) authentication. Either basicAuth or bearerToken - # authentication is required to access private repositories - bearerToken: - # Reference to a Secret containing the bearer token. - tokenRef: - secretName: repotoken - key: token - # If true, skips validating the SCM provider's TLS certificate - useful for self-signed certificates. - insecure: true - # Reference to a ConfigMap containing trusted CA certs - useful for self-signed certificates. (optional) - caRef: - configMapName: argocd-tls-certs-cm - key: bitbucket-ca # Labels are not supported by Bitbucket Server, so filtering by label is not possible. # Filter PRs using the source branch name. (optional) filters: @@ -214,13 +195,6 @@ 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. -In case of Bitbucket App Token, go with `bearerToken` section. -* `tokenRef`: A `Secret` name and key containing the app token to use for requests. - -In case self-signed BitBucket Server certificates, the following options can be usefully: -* `insecure`: By default (false) - Skip checking the validity of the SCM's certificate - useful for self-signed TLS certificates. -* `caRef`: Optional `ConfigMap` name and key containing the BitBucket server certificates to trust - useful for self-signed TLS certificates. Possibly reference the ArgoCD CM holding the trusted certs. - ## Bitbucket Cloud Fetch pull requests from a repo hosted on a Bitbucket Cloud. @@ -254,7 +228,6 @@ spec: # Credentials for Bearer Token (App Token) authentication. Either basicAuth or bearerToken # authentication is required to access private repositories bearerToken: - # Reference to a Secret containing the bearer token. tokenRef: secretName: repotoken key: token @@ -378,7 +351,7 @@ spec: helm: parameters: - name: "image.tag" - value: "pull-{{.author}}-{{.head_sha}}" + value: "pull-{{.head_sha}}" project: "my-project" destination: server: https://kubernetes.default.svc @@ -411,7 +384,7 @@ spec: commonLabels: app.kubernetes.io/instance: '{{.branch}}-{{.number}}' images: - - 'ghcr.io/myorg/myrepo:{{.author}}-{{.head_sha}}' + - 'ghcr.io/myorg/myrepo:{{.head_sha}}' project: "my-project" destination: server: https://kubernetes.default.svc @@ -419,7 +392,6 @@ spec: ``` * `number`: The ID number of the pull request. -* `title`: The title 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. @@ -428,7 +400,6 @@ spec: * `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.) -* `author`: The author/creator of the pull request. ## Webhook Configuration diff --git a/docs/operator-manual/applicationset/Generators-SCM-Provider.md b/docs/operator-manual/applicationset/Generators-SCM-Provider.md index d48c07403573a..40c8e552fe573 100644 --- a/docs/operator-manual/applicationset/Generators-SCM-Provider.md +++ b/docs/operator-manual/applicationset/Generators-SCM-Provider.md @@ -98,10 +98,6 @@ spec: key: token # If true, skips validating the SCM provider's TLS certificate - useful for self-signed certificates. insecure: false - # Reference to a ConfigMap containing trusted CA certs - useful for self-signed certificates. (optional) - caRef: - configMapName: argocd-tls-certs-cm - key: gitlab-ca template: # ... ``` @@ -114,7 +110,6 @@ spec: * `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. -* `caRef`: Optional `ConfigMap` name and key containing the GitLab certificates to trust - useful for self-signed TLS certificates. Possibly reference the ArgoCD CM holding the trusted certs. For label filtering, the repository topics are used. @@ -183,8 +178,7 @@ spec: api: https://mycompany.bitbucket.org # If true, scan every branch of every repository. If false, scan only the default branch. Defaults to false. allBranches: true - # Credentials for Basic authentication (App Password). Either basicAuth or bearerToken - # authentication is required to access private repositories + # Credentials for Basic authentication. Required for private repositories. basicAuth: # The username to authenticate with username: myuser @@ -192,19 +186,6 @@ spec: passwordRef: secretName: mypassword key: password - # Credentials for Bearer Token (App Token) authentication. Either basicAuth or bearerToken - # authentication is required to access private repositories - bearerToken: - # Reference to a Secret containing the bearer token. - tokenRef: - secretName: repotoken - key: token - # If true, skips validating the SCM provider's TLS certificate - useful for self-signed certificates. - insecure: true - # Reference to a ConfigMap containing trusted CA certs - useful for self-signed certificates. (optional) - caRef: - configMapName: argocd-tls-certs-cm - key: bitbucket-ca # Support for filtering by labels is TODO. Bitbucket server labels are not supported for PRs, but they are for repos template: # ... @@ -218,13 +199,6 @@ 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. -In case of Bitbucket App Token, go with `bearerToken` section. -* `tokenRef`: A `Secret` name and key containing the app token to use for requests. - -In case self-signed BitBucket Server certificates, the following options can be usefully: -* `insecure`: By default (false) - Skip checking the validity of the SCM's certificate - useful for self-signed TLS certificates. -* `caRef`: Optional `ConfigMap` name and key containing the BitBucket server certificates to trust - useful for self-signed TLS certificates. Possibly reference the ArgoCD CM holding the trusted certs. - Available clone protocols are `ssh` and `https`. ## Azure DevOps diff --git a/docs/operator-manual/applicationset/GoTemplate.md b/docs/operator-manual/applicationset/GoTemplate.md index 022e5f6584238..4a2b6cf55140b 100644 --- a/docs/operator-manual/applicationset/GoTemplate.md +++ b/docs/operator-manual/applicationset/GoTemplate.md @@ -12,8 +12,7 @@ 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: - +Another function has `slugify` function has been added which, by default, sanitizes and smart truncate (means 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. @@ -100,17 +99,6 @@ possible with Go text templates: - name: throw-away value: "{{end}}" -- Signature verification is not supported for the templated `project` field when using the Git generator. - - ::yaml - apiVersion: argoproj.io/v1alpha1 - kind: ApplicationSet - spec: - goTemplate: true - template: - spec: - project: {{.project}} - ## Migration guide @@ -215,11 +203,9 @@ 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. + 1. contains no more than 253 characters + 2. contains only lowercase alphanumeric characters, '-' or '.' + 3. starts and ends with an alphanumeric character - `toYaml` / `fromYaml` / `fromYamlArray` helm like functions diff --git a/docs/operator-manual/applicationset/Template.md b/docs/operator-manual/applicationset/Template.md index 6bc6f24dc0310..9a7cd574453b4 100644 --- a/docs/operator-manual/applicationset/Template.md +++ b/docs/operator-manual/applicationset/Template.md @@ -9,24 +9,21 @@ ApplicationSet is using [fasttemplate](https://github.com/valyala/fasttemplate) An Argo CD Application is created by combining the parameters from the generator with fields of the template (via `{{values}}`), and from that a concrete `Application` resource is produced and applied to the cluster. Here is the template subfield from a Cluster generator: - ```yaml # (...) template: metadata: - name: '{{ .nameNormalized }}-guestbook' + name: '{{cluster}}-guestbook' spec: source: repoURL: https://github.com/infra-team/cluster-deployments.git targetRevision: HEAD - path: guestbook/{{ .nameNormalized }} + path: guestbook/{{cluster}} destination: - server: '{{ .server }}' + server: '{{url}}' namespace: guestbook ``` -For details on all available parameters (like `.name`, `.nameNormalized`, etc.) please refer to the [Cluster Generator docs](./Generators-Cluster.md). - The template subfields correspond directly to [the spec of an Argo CD `Application` resource](../../declarative-setup/#applications): - `project` refers to the [Argo CD Project](../../user-guide/projects.md) in use (`default` may be used here to utilize the default Argo CD Project) @@ -43,7 +40,6 @@ Note: - Referenced clusters must already be defined in Argo CD, for the ApplicationSet controller to use them - Only **one** of `name` or `server` may be specified: if both are specified, an error is returned. -- Signature Verification does not work with the templated `project` field when using git generator. The `metadata` field of template may also be used to set an Application `name`, or to add labels or annotations to the Application. @@ -57,7 +53,7 @@ template as a Helm string literal. For example: ```yaml metadata: - name: '{{`{{ .nameNormalized }}`}}-guestbook' + name: '{{`{{.cluster}}`}}-guestbook' ``` This _only_ applies if you use Helm to deploy your ApplicationSet resources. @@ -89,24 +85,24 @@ spec: spec: project: "default" source: - targetRevision: HEAD + revision: HEAD repoURL: https://github.com/argoproj/argo-cd.git # New path value is generated here: - path: 'applicationset/examples/template-override/{{ .nameNormalized }}-override' + path: 'applicationset/examples/template-override/{{cluster}}-override' destination: {} template: metadata: - name: '{{ .nameNormalized }}-guestbook' + name: '{{cluster}}-guestbook' spec: project: "default" source: repoURL: https://github.com/argoproj/argo-cd.git targetRevision: HEAD - # This 'default' value is not used: it is replaced by the generator's template path, above + # This 'default' value is not used: it is is replaced by the generator's template path, above path: applicationset/examples/template-override/default destination: - server: '{{ .server }}' + server: '{{url}}' namespace: guestbook ``` (*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/template-override).*) @@ -144,15 +140,15 @@ spec: - values.debug.yaml template: metadata: - name: '{{ .nameNormalized }}-deployment' + name: '{{.cluster}}-deployment' spec: project: "default" source: repoURL: https://github.com/infra-team/cluster-deployments.git targetRevision: HEAD - path: guestbook/{{ .nameNormalized }} + path: guestbook/{{ .cluster }} destination: - server: '{{ .server }}' + server: '{{.url}}' namespace: guestbook templatePatch: | spec: diff --git a/docs/operator-manual/argocd-cm.yaml b/docs/operator-manual/argocd-cm.yaml index a8d3be645bcfb..609aa393f2d0a 100644 --- a/docs/operator-manual/argocd-cm.yaml +++ b/docs/operator-manual/argocd-cm.yaml @@ -10,10 +10,6 @@ data: # Argo CD's externally facing base URL (optional). Required when configuring SSO url: https://argo-cd-demo.argoproj.io - # Additional externally facing base URLs (optional) - additionalUrls: | - - https://argo-cd-demo2.argoproj.io - # Enables application status badge feature statusbadge.enabled: "true" @@ -225,16 +221,6 @@ data: # An optional comma-separated list of metadata.labels to observe in the UI. resource.customLabels: tier - # An optional comma-separated list of metadata.labels keys to add to Kubernetes events generated for Applications. - # The keys are compared against the Application and its AppProject. If matched, - # the corresponding labels are added to the generated event. - # In case of a conflict between labels on the Application and AppProject, - # the Application label values are prioritized and added to the event. Supports wildcards. - resource.includeEventLabelKeys: team,env* - # An optional comma-separated list of metadata.labels keys to exclude from Kubernetes events generated for Applications. Supports wildcards. - resource.excludeEventLabelKeys: environment,bu - - resource.compareoptions: | # if ignoreAggregatedRoles set to true then differences caused by aggregated roles in RBAC resources are ignored. ignoreAggregatedRoles: true @@ -249,11 +235,19 @@ data: # can be either empty, "normal" or "strict". By default, it is empty i.e. disabled. resource.respectRBAC: "normal" + # Configuration to add a config management plugin. + configManagementPlugins: | + - name: kasane + init: + command: [kasane, update] + generate: + command: [kasane, show] + # A set of settings that allow enabling or disabling the config management tool. # If unset, each defaults to "true". - kustomize.enabled: "true" - jsonnet.enabled: "true" - helm.enabled: "true" + kustomize.enabled: true + jsonnet.enabled: true + helm.enabled: true # Build options/parameters to use with `kustomize build` (optional) kustomize.buildOptions: --load_restrictor none @@ -314,10 +308,8 @@ 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, 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. + # 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. # ui.bannerposition: "bottom" # Application reconciliation timeout is the max amount of time required to discover if a new manifests version got @@ -334,10 +326,6 @@ data: # cluster.inClusterEnabled indicates whether to allow in-cluster server address. This is enabled by default. cluster.inClusterEnabled: "true" - # The maximum number of pod logs to render in UI. If the application has more than this number of pods, the logs will not be rendered. - # This is to prevent the UI from becoming unresponsive when rendering a large number of logs. Default is 10. - server.maxPodLogsToRender: 10 - # Application pod logs RBAC enforcement enables control over who can and who can't view application pod logs. # When you enable the switch, pod logs will be visible only to admin role by default. Other roles/users will not be able to view them via cli and UI. # When you enable the switch, viewing pod logs for other roles/users will require explicit RBAC allow policies (allow get on logs subresource). @@ -369,7 +357,7 @@ data: - url: https://mycompany.splunk.com?search={{.spec.destination.namespace}} title: Splunk # conditionally show link e.g. for specific project - # github.com/expr-lang/expr is used for evaluation of conditions + # github.com/antonmedv/expr is used for evaluation of conditions - url: https://mycompany.splunk.com?search={{.spec.destination.namespace}} title: Splunk if: spec.project == "default" @@ -425,7 +413,4 @@ data: name: some-cluster server: https://some-cluster # The maximum size of the payload that can be sent to the webhook server. - webhook.maxPayloadSizeMB: 1024 - - # application.sync.impersonation.enabled indicates whether the application sync can be decoupled from control plane service account using impersonation. - application.sync.impersonation.enabled: "false" + webhook.maxPayloadSizeMB: 1024 \ No newline at end of file diff --git a/docs/operator-manual/argocd-cmd-params-cm.yaml b/docs/operator-manual/argocd-cmd-params-cm.yaml index 348677b1cb065..3cb79d85f3150 100644 --- a/docs/operator-manual/argocd-cmd-params-cm.yaml +++ b/docs/operator-manual/argocd-cmd-params-cm.yaml @@ -58,7 +58,7 @@ data: controller.resource.health.persist: "true" # Cache expiration default (default 24h0m0s) controller.default.cache.expiration: "24h0m0s" - # Sharding algorithm used to balance clusters across application controller shards (default "legacy") + # 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" @@ -72,8 +72,6 @@ data: # Diff calculation will be done by running a server side apply dryrun (when # diff cache is unavailable). controller.diff.server.side: "false" - # Enables profile endpoint on the internal metrics port - controller.profile.enabled: "false" ## Server properties # Listen on given address for incoming connections (default "0.0.0.0") @@ -95,8 +93,6 @@ data: # 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" - # Number of webhook requests processed concurrently (default 50) - server.webhook.parallelism.limit: "50" # Set the logging format. One of: text|json (default "text") server.log.format: "text" @@ -138,8 +134,6 @@ data: server.default.cache.expiration: "24h0m0s" # Enable the experimental proxy extension feature server.enable.proxy.extension: "false" - # Enables profile endpoint on the internal metrics port - server.profile.enabled: "false" ## Repo-server properties # Listen on given address for incoming connections (default "0.0.0.0") @@ -171,8 +165,6 @@ data: reposerver.max.combined.directory.manifests.size: '10M' # Paths to be excluded from the tarball streamed to plugins. Separate with ; reposerver.plugin.tar.exclusions: "" - # Enable the repo server to use the 'argocd.argoproj.io/manifest-generate-paths' annotation to guide manifest generation. - reposerver.plugin.use.manifest.generate.paths: "false" # Allow repositories to contain symlinks that leave the boundaries of the repository. # Changing this to "true" will not allow _all_ out-of-bounds symlinks. Those will still be blocked for things like values # files in Helm charts. But symlinks which are not explicitly blocked by other checks will be allowed. @@ -187,35 +179,15 @@ data: reposerver.git.lsremote.parallelism.limit: "0" # Git requests timeout. reposerver.git.request.timeout: "15s" - # Include hidden directories from Git - reposerver.include.hidden.directories: "false" - - # Set the logging format. One of: text|json (default "text") - dexserver.log.format: "text" - # Set the logging level. One of: debug|info|warn|error (default "info") - dexserver.log.level: "info" # Disable TLS on the HTTP endpoint dexserver.disable.tls: "false" ## 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" - # "Modify how application is synced between the generator and the cluster. Default is '' (empty), which means AppSets - # will default to the 'sync' policy (create & update & delete). Explicitly setting the value prevents AppSet-level - # policy overrides unless overrides are explicitly enabled (see option below). Explicit options are: - # 'create-only', 'create-update' (no deletion), 'create-delete' (no update)" - applicationsetcontroller.policy: "" - # If applicationsetcontroller.policy is empty, this flag has no effect. If applicationsetcontroller.policy is set, - # this flag controls whether the policy set in the controller can be overridden by the ApplicationSet resource - # (i.e. the spec.syncPlicy.applicationSync field). Set it to "true" to allow overrides. "" or "false" will disable - # overrides. (default "") - applicationsetcontroller.enable.policy.override: "" - # Max concurrent reconciliation limit for the controller (default 10) - applicationsetcontroller.concurrent.reconciliations.max: "10" - # Enable new globbing in Git files generator (default "false") - # See https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/Generators-Git-File-Globbing/ - applicationsetcontroller.enable.new.git.file.globbing: "false" + # "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 applicationsetcontroller.debug: "false" # Set the logging format. One of: text|json (default "text") @@ -239,8 +211,6 @@ data: 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" - # Number of webhook requests processed concurrently (default 50) - applicationsetcontroller.webhook.parallelism.limit: "50" ## Argo CD Notifications Controller Properties # Set the logging level. One of: debug|info|warn|error (default "info") @@ -249,5 +219,3 @@ data: notificationscontroller.log.format: "text" # Enable self-service notifications config. Used in conjunction with apps-in-any-namespace. (default "false") notificationscontroller.selfservice.enabled: "false" - # Disable TLS on connections to repo server - notificationscontroller.repo.server.plaintext: "false" diff --git a/docs/operator-manual/argocd-repositories.yaml b/docs/operator-manual/argocd-repositories.yaml index 8f48429ebd60b..b6aa0715c389d 100644 --- a/docs/operator-manual/argocd-repositories.yaml +++ b/docs/operator-manual/argocd-repositories.yaml @@ -12,7 +12,6 @@ stringData: url: https://github.com/argoproj/argocd-example-apps password: my-password username: my-username - project: my-project 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" @@ -43,7 +42,6 @@ metadata: stringData: url: https://storage.googleapis.com/istio-prerelease/daily-build/master-latest-daily/charts name: istio.io - project: my-project type: helm --- apiVersion: v1 diff --git a/docs/operator-manual/cluster-bootstrapping.md b/docs/operator-manual/cluster-bootstrapping.md index 9a06098db8670..7a43800da2478 100644 --- a/docs/operator-manual/cluster-bootstrapping.md +++ b/docs/operator-manual/cluster-bootstrapping.md @@ -119,29 +119,4 @@ metadata: - resources-finalizer.argocd.argoproj.io spec: ... -``` - -### Ignoring differences in child applications - -To allow changes in child apps without triggering an out-of-sync status, or modification for debugging etc, the app of apps pattern works with [diff customization](../user-guide/diffing/). The example below shows how to ignore changes to syncPolicy and other common values. - -```yaml -spec: - ... - syncPolicy: - ... - syncOptions: - - RespectIgnoreDifferences=true - ... - ignoreDifferences: - - group: "*" - kind: "Application" - namespace: "*" - jsonPointers: - # Allow manually disabling auto sync for apps, useful for debugging. - - /spec/syncPolicy/automated - # These are automatically updated on a regular basis. Not ignoring last applied configuration since it's used for computing diffs after normalization. - - /metadata/annotations/argocd.argoproj.io~1refresh - - /operation - ... -``` +``` diff --git a/docs/operator-manual/config-management-plugins.md b/docs/operator-manual/config-management-plugins.md index 1d115261db643..7c86075ff2f7f 100644 --- a/docs/operator-manual/config-management-plugins.md +++ b/docs/operator-manual/config-management-plugins.md @@ -178,7 +178,7 @@ entrypoint. You can use either off-the-shelf or custom-built plugin image as sid containers: - name: my-plugin command: [/var/run/argocd/argocd-cmp-server] # Entrypoint should be Argo CD lightweight CMP server i.e. argocd-cmp-server - image: ubuntu # This can be off-the-shelf or custom-built image + image: busybox # This can be off-the-shelf or custom-built image securityContext: runAsNonRoot: true runAsUser: 999 @@ -359,16 +359,6 @@ You can set it one of three ways: For option 1, the flag can be repeated multiple times. For option 2 and 3, you can specify multiple globs by separating them with semicolons. -## Application manifests generation using argocd.argoproj.io/manifest-generate-paths - -To enhance the application manifests generation process, you can enable the use of the `argocd.argoproj.io/manifest-generate-paths` annotation. When this flag is enabled, the resources specified by this annotation will be passed to the CMP server for generating application manifests, rather than sending the entire repository. This can be particularly useful for monorepos. - -You can set it one of three ways: - -1. The `--plugin-use-manifest-generate-paths` argument on the repo server. -2. The `reposerver.plugin.use.manifest.generate.paths` key if you are using `argocd-cmd-params-cm` -3. Directly setting `ARGOCD_REPO_SERVER_PLUGIN_USE_MANIFEST_GENERATE_PATHS` environment variable on the repo server to `true`. - ## Migrating from argocd-cm plugins Installing plugins by modifying the argocd-cm ConfigMap is deprecated as of v2.4 and has been completely removed starting in v2.8. @@ -468,7 +458,7 @@ Plugins configured with argocd-cm ran on the Argo CD image. This gave it access 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 ubuntu, busybox, or alpine/k8s) or design your own base image with the tools your plugin needs. For +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. ### Test the plugin diff --git a/docs/operator-manual/core.md b/docs/operator-manual/core.md index 79b2530cfe340..01b394d6e9d8c 100644 --- a/docs/operator-manual/core.md +++ b/docs/operator-manual/core.md @@ -12,7 +12,6 @@ installation: - Argo CD RBAC model - Argo CD API -- Argo CD Notification Controller - OIDC based authentication The following features will be partially available (see the @@ -26,7 +25,7 @@ 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 + another CLI to automate my deployments. I want instead rely in Kubernetes API only. - As a cluster admin, I don't want to provide Argo CD UI or Argo CD CLI to developers. diff --git a/docs/operator-manual/declarative-setup.md b/docs/operator-manual/declarative-setup.md index be939570adf71..c1f5ba2b2d3bd 100644 --- a/docs/operator-manual/declarative-setup.md +++ b/docs/operator-manual/declarative-setup.md @@ -30,7 +30,7 @@ For each specific kind of ConfigMap and Secret resource, there is only a single |------------------------------------------------------------------|-------------|--------------------------| | [`application.yaml`](../user-guide/application-specification.md) | Application | Example application spec | | [`project.yaml`](./project-specification.md) | AppProject | Example project spec | -| [`argocd-repositories.yaml`](./argocd-repositories-yaml.md) | Secret | Repository credentials | +| - | 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. @@ -176,7 +176,6 @@ spec: Repository details are stored in secrets. To configure a repo, create a secret which contains repository details. Consider using [bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets) to store an encrypted secret definition as a Kubernetes manifest. Each repository must have a `url` field and, depending on whether you connect using HTTPS, SSH, or GitHub App, `username` and `password` (for HTTPS), `sshPrivateKey` (for SSH), or `githubAppPrivateKey` (for GitHub App). -Credentials can be scoped to a project using the optional `project` field. When omitted, the credential will be used as the default for all projects without a scoped credential. !!!warning When using [bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets) the labels will be removed and have to be readded as described here: https://github.com/bitnami-labs/sealed-secrets#sealedsecrets-as-templates-for-secrets @@ -196,7 +195,6 @@ stringData: url: https://github.com/argoproj/private-repo password: my-password username: my-username - project: my-project ``` Example for SSH: @@ -470,9 +468,9 @@ data: ### Configure repositories with proxy -Proxy for your repository can be specified in the `proxy` field of the repository secret, along with a corresponding `noProxy` config. Argo CD uses this proxy/noProxy config to access the repository and do related helm/kustomize operations. Argo CD looks for the standard proxy environment variables in the repository server if the custom proxy config is absent. +Proxy for your repository can be specified in the `proxy` field of the repository secret, along with other repository configurations. Argo CD uses this proxy to access the repository. Argo CD looks for the standard proxy environment variables in the repository server if the custom proxy is absent. -An example repository with proxy and noProxy: +An example repository with proxy: ```yaml apiVersion: v1 @@ -486,13 +484,10 @@ stringData: type: git url: https://github.com/argoproj/private-repo proxy: https://proxy-server-url:8888 - noProxy: ".internal.example.com,company.org,10.123.0.0/16" password: my-password username: my-username ``` -A note on noProxy: Argo CD uses exec to interact with different tools such as helm and kustomize. Not all of these tools support the same noProxy syntax as the [httpproxy go package](https://cs.opensource.google/go/x/net/+/internal-branch.go1.21-vendor:http/httpproxy/proxy.go;l=38-50) does. In case you run in trouble with noProxy not beeing respected you might want to try using the full domain instead of a wildcard pattern or IP range to find a common syntax that all tools support. - ### Legacy behaviour In Argo CD version 2.0 and earlier, repositories were stored as part of the `argocd-cm` config map. For @@ -554,7 +549,6 @@ 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: @@ -675,9 +669,9 @@ extended to allow assumption of multiple roles, either as an explicit array of r "Statement" : { "Effect" : "Allow", "Action" : "sts:AssumeRole", - "Resource" : [ - ":role/" - ] + "Principal" : { + "AWS" : ":role/" + } } } ``` @@ -737,140 +731,6 @@ data: "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", "--role-arn", "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 Argo CD to read the file at the provided path and use the credentials defined within to authenticate to AWS. -The profile must be mounted in both the `argocd-server` and `argocd-application-controller` components in order for this to work. -For example, the following values can be defined in a Helm-based Argo CD 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): @@ -928,17 +788,6 @@ In addition to the environment variables above, argocd-k8s-auth accepts two extr 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. **Add Annotations to Service Account** Add `"azure.workload.identity/client-id": "$CLIENT_ID"` and `"azure.workload.identity/tenant-id": "$TENANT_ID"` annotations to the `argocd-application-controller` and `argocd-server` service accounts using the details from the federated credential. - -4. **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 @@ -957,9 +806,9 @@ stringData: "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 + "AZURE_TENANT_ID": "fill in tenant id", + "AZURE_FEDERATED_TOKEN_FILE": "/opt/path/to/federated_file.json", + "AZURE_AUTHORITY_HOST": "https://login.microsoftonline.com/", "AAD_LOGIN_METHOD": "workloadidentity" }, "args": ["azure"], @@ -1046,7 +895,7 @@ stringData: ## Resource Exclusion/Inclusion -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/*` and `coordination.k8s.io/Lease` 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. @@ -1137,22 +986,6 @@ data: Custom Labels configured with `resource.customLabels` (comma separated string) will be displayed in the UI (for any resource that defines them). -## Labels on Application Events - -An optional comma-separated list of `metadata.labels` keys can be configured with `resource.includeEventLabelKeys` to add to Kubernetes events generated for Argo CD Applications. When events are generated for Applications containing the specified labels, the controller adds the matching labels to the event. This establishes an easy link between the event and the application, allowing for filtering using labels. In case of conflict between labels on the Application and AppProject, the Application label values are prioritized and added to the event. - -```yaml - resource.includeEventLabelKeys: team,env* -``` - -To exclude certain labels from events, use the `resource.excludeEventLabelKeys` key, which takes a comma-separated list of `metadata.labels` keys. - -```yaml - resource.excludeEventLabelKeys: environment,bu -``` - -Both `resource.includeEventLabelKeys` and `resource.excludeEventLabelKeys` support wildcards. - ## SSO & RBAC * SSO configuration details: [SSO](./user-management/index.md) @@ -1168,7 +1001,7 @@ Example of `kustomization.yaml`: ```yaml # additional resources like ingress rules, cluster and repository secrets. resources: -- github.com/argoproj/argo-cd//manifests/cluster-install?ref=stable +- github.com/argoproj/argo-cd//manifests/cluster-install?ref=v1.0.1 - clusters-secrets.yaml - repos-secrets.yaml diff --git a/docs/operator-manual/deep_links.md b/docs/operator-manual/deep_links.md index 74c3196f8612a..c166a1d25d75d 100644 --- a/docs/operator-manual/deep_links.md +++ b/docs/operator-manual/deep_links.md @@ -26,7 +26,7 @@ Each link in the list has five subfields: 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 [expr-lang/expr](https://github.com/expr-lang/expr/tree/master/docs) for evaluating conditions + 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. @@ -63,7 +63,7 @@ An example `argocd-cm.yaml` file with deep links and their variations : - 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/expr-lang/expr is used for evaluation of conditions + # github.com/antonmedv/expr is used for evaluation of conditions - url: https://mycompany.splunk.com?search={{.app.spec.destination.namespace}} title: Splunk if: application.spec.project == "default" @@ -75,9 +75,4 @@ An example `argocd-cm.yaml` file with deep links and their variations : - url: https://mycompany.splunk.com?search={{.resource.metadata.name}}&env={{.project.metadata.labels.env}} title: Splunk if: resource.kind == "Pod" || resource.kind == "Deployment" - - # sample checking a tag exists that contains - or / and how to alternatively access it - - url: https://mycompany.splunk.com?tag={{ index .resource.metadata.labels "some.specific.kubernetes.like/tag" }} - title: Tag Service - if: resource.metadata.labels["some.specific.kubernetes.like/tag"] != nil && resource.metadata.labels["some.specific.kubernetes.like/tag"] != "" ``` diff --git a/docs/operator-manual/health.md b/docs/operator-manual/health.md index 107f2f3f92cdb..8566d6460e6db 100644 --- a/docs/operator-manual/health.md +++ b/docs/operator-manual/health.md @@ -38,19 +38,21 @@ metadata: app.kubernetes.io/name: argocd-cm app.kubernetes.io/part-of: argocd data: - resource.customizations.health.argoproj.io_Application: | - hs = {} - hs.status = "Progressing" - hs.message = "" - if obj.status ~= nil then - if obj.status.health ~= nil then - hs.status = obj.status.health.status - if obj.status.health.message ~= nil then - hs.message = obj.status.health.message + resource.customizations: | + argoproj.io/Application: + health.lua: | + hs = {} + hs.status = "Progressing" + hs.message = "" + if obj.status ~= nil then + if obj.status.health ~= nil then + hs.status = obj.status.health.status + if obj.status.health.message ~= nil then + hs.message = obj.status.health.message + end + end end - end - end - return hs + return hs ``` ## Custom Health Checks @@ -66,7 +68,9 @@ There are two ways to configure a custom health check. The next two sections des Custom health checks can be defined in ```yaml - resource.customizations.health._: | + resource.customizations: | + : + health.lua: | ``` field of `argocd-cm`. If you are using argocd-operator, this is overridden by [the argocd-operator resourceCustomizations](https://argocd-operator.readthedocs.io/en/latest/reference/argocd/#resource-customizations). @@ -74,44 +78,49 @@ The following example demonstrates a health check for `cert-manager.io/Certifica ```yaml data: - resource.customizations.health.cert-manager.io_Certificate: | - 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 + resource.customizations: | + cert-manager.io/Certificate: + health.lua: | + 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 - end - end - hs.status = "Progressing" - hs.message = "Waiting for certificate" - return hs + hs.status = "Progressing" + hs.message = "Waiting for certificate" + return hs ``` - In order to prevent duplication of the custom health check for potentially multiple resources, it is also possible to specify a wildcard in the resource kind, and anywhere in the resource group, like this: ```yaml - resource.customizations.health.ec2.aws.crossplane.io_*: | - ... + resource.customizations: | + ec2.aws.crossplane.io/*: + health.lua: | + ... ``` ```yaml - resource.customizations.health.*.aws.crossplane.io_*: | - ... + resource.customizations: | + "*.aws.crossplane.io/*": + health.lua: | + ... ``` !!!important - Please, note that there can be ambiguous resolution of wildcards, see [#16905](https://github.com/argoproj/argo-cd/issues/16905) + 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: @@ -124,13 +133,15 @@ The custom health check might return one of the following health statuses: By default health typically returns `Progressing` status. NOTE: As a security measure, access to the standard Lua libraries will be disabled by default. Admins can control access by -setting `resource.customizations.useOpenLibs._`. In the following example, standard libraries are enabled for health check of `cert-manager.io/Certificate`. +setting `resource.customizations.useOpenLibs.`. In the following example, standard libraries are enabled for health check of `cert-manager.io/Certificate`. ```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 @@ -163,31 +174,6 @@ The [PR#1139](https://github.com/argoproj/argo-cd/pull/1139) is an example of Ce Please note that bundled health checks with wildcards are not supported. -## Overriding Go-Based Health Checks - -Health checks for some resources were [hardcoded as Go code](https://github.com/argoproj/gitops-engine/tree/master/pkg/health) -because Lua support was introduced later. Also, the logic of health checks for some resources were too complex, so it -was easier to implement it in Go. - -It is possible to override health checks for built-in resource. Argo will prefer the configured health check over the -Go-based built-in check. - -The following resources have Go-based health checks: - -* PersistentVolumeClaim -* Pod -* Service -* apiregistration.k8s.io/APIService -* apps/DaemonSet -* apps/Deployment -* apps/ReplicaSet -* apps/StatefulSet -* argoproj.io/Workflow -* autoscaling/HorizontalPodAutoscaler -* batch/Job -* extensions/Ingress -* networking.k8s.io/Ingress - ## Health Checks An Argo CD App's health is inferred from the health of its immediate child resources (the resources represented in diff --git a/docs/operator-manual/high_availability.md b/docs/operator-manual/high_availability.md index ddcce80fab25a..a532200216d9b 100644 --- a/docs/operator-manual/high_availability.md +++ b/docs/operator-manual/high_availability.md @@ -82,16 +82,10 @@ spec: ``` * 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, consistent-hashing]: -- `legacy` mode uses an `uid` based distribution (non-uniform). -- `round-robin` uses an equal distribution across all shards. -- `consistent-hashing` uses the consistent hashing with bounded loads algorithm which tends to equal distribution and also reduces cluster or application reshuffling in case of additions or removals of shards or clusters. +* 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. -The `--sharding-method` parameter can also be overridden 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 Features" - 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. - The `consistent-hashing` shard distribution algorithm is an experimental feature. Extensive benchmark have been documented on the [CNOE blog](https://cnoe.io/blog/argo-cd-application-scalability) with encouraging results. Community feedback is highly appreciated before moving this feature to a production ready state. +!!! 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 @@ -130,13 +124,9 @@ stringData: 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. -* `ARGOCD_APPLICATION_TREE_SHARD_SIZE` - environment variable controlling the max number of resources stored in one Redis - key. Splitting application tree into multiple keys helps to reduce the amount of traffic between the controller and Redis. - The default value is 0, which means that the application tree is stored in a single Redis key. The reasonable value is 100. - **metrics** -* `argocd_app_reconcile` - reports application reconciliation duration in seconds. Can be used to build reconciliation duration heat map to get a high-level reconciliation performance picture. +* `argocd_app_reconcile` - reports application reconciliation duration. Can be used to build reconciliation duration heat map to get a high-level reconciliation performance picture. * `argocd_app_k8s_request_total` - number of k8s requests per application. The number of fallback Kubernetes API queries - useful to identify which application has a resource with non-preferred version and causes performance issues. @@ -180,29 +170,25 @@ 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:** 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 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 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. * **Multiple Kustomize applications in same repository with [parameter overrides](../user-guide/parameters.md):** sorry, no workaround for now. -### Manifest Paths Annotation +### Webhook and Manifest Paths Annotation Argo CD aggressively caches generated manifests and uses the repository commit SHA as a cache key. A new commit to the Git repository invalidates the cache for all applications configured in the repository. This can negatively affect repositories with multiple applications. You can use [webhooks](https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/webhook.md) and the `argocd.argoproj.io/manifest-generate-paths` Application CRD annotation to solve this problem and improve performance. -The `argocd.argoproj.io/manifest-generate-paths` annotation contains a semicolon-separated list of paths within the Git repository that are used during manifest generation. It will use the paths specified in the annotation to compare the last cached revision to the latest commit. If no modified files match the paths specified in `argocd.argoproj.io/manifest-generate-paths`, then it will not trigger application reconciliation and the existing cache will be considered valid for the new commit. +The `argocd.argoproj.io/manifest-generate-paths` annotation contains a semicolon-separated list of paths within the Git repository that are used during manifest generation. The webhook compares paths specified in the annotation with the changed files specified in the webhook payload. If no modified files match the paths specified in `argocd.argoproj.io/manifest-generate-paths`, then the webhook will not trigger application reconciliation and the existing cache will be considered valid for the new commit. Installations that use a different repository for each application are **not** subject to this behavior and will likely get no benefit from using these annotations. -Similarly, applications referencing an external Helm values file will not get the benefits of this feature when an unrelated change happens in the external source. - -For webhooks, the comparison is done using the files specified in the webhook event payload instead. - !!! note - Application manifest paths annotation support for webhooks depends on the git provider used for the Application. It is currently only supported for GitHub, GitLab, and Gogs based repos. + Application manifest paths annotation support depends on the git provider used for the Application. It is currently only supported for GitHub, GitLab, and Gogs based repos. * **Relative path** The annotation might contain a relative path. In this case the path is considered relative to the path specified in the application source: @@ -258,28 +244,6 @@ spec: # ... ``` -* **Glob paths** The annotation might contain a glob pattern path, which can be any pattern supported by the [Go filepath Match function](https://pkg.go.dev/path/filepath#Match): - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: guestbook - namespace: argocd - annotations: - # resolves to any file matching the pattern of *-secret.yaml in the top level shared folder - argocd.argoproj.io/manifest-generate-paths: "/shared/*-secret.yaml" -spec: - source: - repoURL: https://github.com/argoproj/argocd-example-apps.git - targetRevision: HEAD - path: guestbook -# ... -``` - -!!! note - If application manifest generation using the `argocd.argoproj.io/manifest-generate-paths` annotation feature is enabled, only the resources specified by this annotation will be sent to the CMP server for manifest generation, rather than the entire repository. To determine the appropriate resources, a common root path is calculated based on the paths provided in the annotation. The application path serves as the deepest path that can be selected as the root. - ### 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. @@ -378,17 +342,3 @@ Not all HTTP responses are eligible for retries. The following conditions will n * Responses with a status code indicating client errors (4xx) except for 429 Too Many Requests. * Responses with the status code 501 Not Implemented. - - -## CPU/Memory Profiling - -Argo CD optionally exposes a profiling endpoint that can be used to profile the CPU and memory usage of the Argo CD component. -The profiling endpoint is available on metrics port of each component. See [metrics](./metrics.md) for more information about the port. -For security reasons the profiling endpoint is disabled by default. The endpoint can be enabled by setting the `server.profile.enabled` -or `controller.profile.enabled` key of [argocd-cmd-params-cm](argocd-cmd-params-cm.yaml) ConfigMap to `true`. -Once the endpoint is enabled you can use go profile tool to collect the CPU and memory profiles. Example: - -```bash -$ kubectl port-forward svc/argocd-metrics 8082:8082 -$ go tool pprof http://localhost:8082/debug/pprof/heap -``` diff --git a/docs/operator-manual/ingress.md b/docs/operator-manual/ingress.md index 652458c32f093..5ea947345d507 100644 --- a/docs/operator-manual/ingress.md +++ b/docs/operator-manual/ingress.md @@ -12,8 +12,7 @@ There are several ways how Ingress can be configured. The Ambassador Edge Stack can be used as a Kubernetes ingress controller with [automatic TLS termination](https://www.getambassador.io/docs/latest/topics/running/tls/#host) and routing capabilities for both the CLI and the UI. -The API server should be run with TLS disabled. Edit the `argocd-server` deployment to add the `--insecure` flag to the argocd-server command, or simply set `server.insecure: "true"` in the `argocd-cmd-params-cm` ConfigMap [as described here](server-commands/additional-configuration-method.md). Given the `argocd` CLI includes the port number in the request `host` header, 2 Mappings are required. -Note: Disabling TLS in not required if you are using grpc-web +The API server should 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). Given the `argocd` CLI includes the port number in the request `host` header, 2 Mappings are required. ### Option 1: Mapping CRD for Host-based Routing ```yaml @@ -25,7 +24,7 @@ metadata: spec: host: argocd.example.com prefix: / - service: https://argocd-server:443 + service: argocd-server:443 --- apiVersion: getambassador.io/v2 kind: Mapping @@ -61,25 +60,7 @@ metadata: spec: prefix: /argo-cd rewrite: /argo-cd - service: https://argocd-server:443 -``` - -Example of `argocd-cmd-params-cm` configmap -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: argocd-cmd-params-cm - namespace: argocd - labels: - app.kubernetes.io/name: argocd-cmd-params-cm - app.kubernetes.io/part-of: argocd -data: - ## Server properties - # Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from / (default "/") - server.basehref: "/argo-cd" - # Used if Argo CD is running behind reverse proxy under subpath different from / - server.rootpath: "/argo-cd" + service: argocd-server:443 ``` Login with the `argocd` CLI using the extra `--grpc-web-root-path` flag for non-root paths. @@ -185,43 +166,6 @@ 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 @@ -369,7 +313,7 @@ the API server -- one for gRPC and the other for HTTP/HTTPS. However it allows T happen at the ingress controller. -## [Traefik (v3.0)](https://docs.traefik.io/) +## [Traefik (v2.2)](https://docs.traefik.io/) Traefik can be used as an edge router and provide [TLS](https://docs.traefik.io/user-guides/grpc/) termination within the same deployment. @@ -379,7 +323,7 @@ The API server should be run with TLS disabled. Edit the `argocd-server` deploym ### IngressRoute CRD ```yaml -apiVersion: traefik.io/v1alpha1 +apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: argocd-server @@ -395,7 +339,7 @@ spec: - name: argocd-server port: 80 - kind: Rule - match: Host(`argocd.example.com`) && Header(`Content-Type`, `application/grpc`) + match: Host(`argocd.example.com`) && Headers(`Content-Type`, `application/grpc`) priority: 11 services: - name: argocd-server @@ -471,7 +415,7 @@ Once we create this service, we can configure the Ingress to conditionally route ``` ## [Istio](https://www.istio.io) -You can put Argo CD behind Istio using following configurations. Here we will achieve both serving Argo CD behind istio and using subpath on Istio +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 @@ -511,7 +455,7 @@ spec: - --staticassets - /shared/app - --redis - - argocd-redis:6379 + - argocd-redis-ha-haproxy:6379 - --insecure - --basehref - /argocd @@ -529,7 +473,7 @@ After that install Argo CD (there should be only 3 yml file defined above in cu kubectl apply -k ./ -n argocd --wait=true ``` -Be sure you create secret for Istio ( in our case secretname is argocd-server-tls on argocd Namespace). After that we create Istio Resources +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 @@ -617,7 +561,7 @@ Edit the `--insecure` flag in the `argocd-server` command of the argocd-server d ### Creating a service -Now you need an externally accessible service. This is practically the same as the internal service Argo CD has, but with Google Cloud annotations. Note that this service is annotated to use a [Network Endpoint Group](https://cloud.google.com/load-balancing/docs/negs) (NEG) to allow your load balancer to send traffic directly to your pods without using kube-proxy, so remove the `neg` annotation if that's not what you want. +Now you need an externally accessible service. This is practically the same as the internal service Argo CD has, but with Google Cloud annotations. Note that this service is annotated to use a [Network Endpoint Group](https://cloud.google.com/load-balancing/docs/negs) (NEG) to allow your load balancer to send traffic directly to your pods without using kube-proxy, so remove the `neg` annotation it that's not what you want. The service: diff --git a/docs/operator-manual/installation.md b/docs/operator-manual/installation.md index 70494298c1391..5782e5660868f 100644 --- a/docs/operator-manual/installation.md +++ b/docs/operator-manual/installation.md @@ -21,9 +21,6 @@ Not recommended for production use. This type of installation is typically used in (i.e. kubernetes.svc.default). It will still be able to deploy to external clusters with inputted credentials. - > Note: The ClusterRoleBinding in the installation manifest is bound to a ServiceAccount in the argocd namespace. - > Be cautious when modifying the namespace, as changing it may cause permission-related errors unless the ClusterRoleBinding is correctly adjusted to reflect the new namespace. - * [namespace-install.yaml](https://github.com/argoproj/argo-cd/blob/master/manifests/namespace-install.yaml) - Installation of Argo CD which requires only namespace level privileges (does not need cluster roles). Use this manifest set if you do not need Argo CD to deploy applications in the same cluster that Argo CD runs in, and will rely solely @@ -81,29 +78,6 @@ resources: 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). -#### Installing Argo CD in a Custom Namespace -If you want to install Argo CD in a namespace other than the default argocd, you can use Kustomize to apply a patch that updates the ClusterRoleBinding to reference the correct namespace for the ServiceAccount. This ensures that the necessary permissions are correctly set in your custom namespace. - -Below is an example of how to configure your kustomization.yaml to install Argo CD in a custom namespace: -```yaml -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -namespace: -resources: - - https://raw.githubusercontent.com/argoproj/argo-cd/v2.7.2/manifests/install.yaml - -patches: - - patch: |- - - op: replace - path: /subjects/0/namespace - value: - target: - kind: ClusterRoleBinding -``` - -This patch ensures that the ClusterRoleBinding correctly maps to the ServiceAccount in your custom namespace, preventing any permission-related issues during the deployment. - ## Helm The Argo CD can be installed using [Helm](https://helm.sh/). The Helm chart is currently community maintained and available at diff --git a/docs/operator-manual/metrics.md b/docs/operator-manual/metrics.md index 02a490998307a..cfd2a8a8093ac 100644 --- a/docs/operator-manual/metrics.md +++ b/docs/operator-manual/metrics.md @@ -8,10 +8,9 @@ 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 Argo CD. | -| `argocd_app_condition` | gauge | Report Applications conditions. It contains the conditions currently present in the application status. | | `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 in seconds. | +| `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. | @@ -31,8 +30,6 @@ to deleted resources, you can schedule a metrics reset to clean the history with an application controller flag. Example: `--metrics-cache-expiration="24h0m0s"`. - - ### Exposing Application labels as Prometheus metrics There are use-cases where Argo CD Applications contain labels that are desired to be exposed as Prometheus metrics. @@ -63,45 +60,6 @@ argocd_app_labels{label_business_unit="bu-id-1",label_team_name="my-team",name=" argocd_app_labels{label_business_unit="bu-id-2",label_team_name="another-team",name="my-app-3",namespace="argocd",project="important-project"} 1 ``` -### Exposing Application conditions as Prometheus metrics - -There are use-cases where Argo CD Applications contain conditions that are desired to be exposed as Prometheus metrics. -Some examples are: - -* Hunting orphaned resources across all deployed applications -* Knowing which resources are excluded from ArgoCD - -As the Application conditions are specific to each company, this feature is disabled by default. To enable it, add the -`--metrics-application-conditions` flag to the Argo CD application controller. - -The example below will expose the Argo CD Application condition `OrphanedResourceWarning` and `ExcludedResourceWarning` to Prometheus: - -```yaml - containers: - - command: - - argocd-application-controller - - --metrics-application-conditions - - OrphanedResourceWarning - - --metrics-application-conditions - - ExcludedResourceWarning -``` - -## Application Set Controller metrics - -The Application Set controller exposes the following metrics for application sets. - -| Metric | Type | Description | -|--------|:----:|-------------| -| `argocd_appset_info` | gauge | Information about Application Sets. It contains labels for the name and namespace of an application set as well as `Resource_update_status` that reflects the `ResourcesUpToDate` property | -| `argocd_appset_reconcile` | histogram | Application reconciliation performance in seconds. It contains labels for the name and namespace of an applicationset | -| `argocd_appset_labels` | gauge | Applicationset labels translated to Prometheus labels. Disabled by default | -| `argocd_appset_owned_applications` | gauge | Number of applications owned by the applicationset. It contains labels for the name and namespace of an applicationset. | - -Similar to the same metric in application controller (`argocd_app_labels`) the metric `argocd_appset_labels` is disabled by default. You can enable it by providing the `–metrics-applicationset-labels` argument to the applicationset controller. - -Once enabled it works exactly the same as application controller metrics (label_ appended to normalized label name). -Available labels include Name, Namespace + all labels enabled by the command line options and their value (exactly like application controller metrics described in the previous section). - ## API Server Metrics Metrics about API Server API request and response activity (request totals, response codes, etc...). Scraped at the `argocd-server-metrics:8083/metrics` endpoint. @@ -112,8 +70,6 @@ Scraped at the `argocd-server-metrics:8083/metrics` endpoint. | `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. @@ -123,7 +79,6 @@ 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_repo_pending_request_total` | gauge | Number of pending requests requiring repository lock | @@ -213,8 +168,6 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: argocd-redis-haproxy-metrics - labels: - release: prometheus-operator spec: selector: matchLabels: @@ -223,7 +176,7 @@ spec: - port: http-exporter-port ``` -For notifications controller, you need to additionally add following: +For notifications controller, you need to additionally add following: ```yaml apiVersion: monitoring.coreos.com/v1 diff --git a/docs/operator-manual/notifications/catalog.md b/docs/operator-manual/notifications/catalog.md index f4d88d2cf6aeb..8f413ac7eb5b3 100644 --- a/docs/operator-manual/notifications/catalog.md +++ b/docs/operator-manual/notifications/catalog.md @@ -1,9 +1,4 @@ # Triggers and Templates Catalog -## Getting Started -* Install Triggers and Templates from the catalog - ```bash - kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/notifications_catalog/install.yaml - ``` ## Triggers | NAME | DESCRIPTION | TEMPLATE | |------------------------|---------------------------------------------------------------|-----------------------------------------------------| @@ -67,7 +62,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -94,7 +90,8 @@ 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}}" @@ -148,7 +145,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -171,7 +169,8 @@ 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}}" @@ -225,7 +224,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -252,7 +252,8 @@ 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}}" @@ -306,7 +307,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -333,7 +335,8 @@ 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}}" @@ -391,7 +394,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -414,7 +418,8 @@ 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}}" @@ -467,7 +472,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -494,7 +500,8 @@ 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 ebde62a62afb0..3d614e4e53a55 100644 --- a/docs/operator-manual/notifications/functions.md +++ b/docs/operator-manual/notifications/functions.md @@ -12,44 +12,6 @@ Golang [Time](https://golang.org/pkg/time/#Time). Parses specified string using RFC3339 layout. Returns an instance of Golang [Time](https://golang.org/pkg/time/#Time). -
-Time related constants. - -**Durations** - -``` - time.Nanosecond = 1 - time.Microsecond = 1000 * Nanosecond - time.Millisecond = 1000 * Microsecond - time.Second = 1000 * Millisecond - time.Minute = 60 * Second - time.Hour = 60 * Minute -``` - -**Timestamps** - -Used when formatting time instances as strings (e.g. `time.Now().Format(time.RFC3339)`). - -``` - time.Layout = "01/02 03:04:05PM '06 -0700" // The reference time, in numerical order. - time.ANSIC = "Mon Jan _2 15:04:05 2006" - time.UnixDate = "Mon Jan _2 15:04:05 MST 2006" - time.RubyDate = "Mon Jan 02 15:04:05 -0700 2006" - time.RFC822 = "02 Jan 06 15:04 MST" - time.RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone - time.RFC850 = "Monday, 02-Jan-06 15:04:05 MST" - time.RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" - time.RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone - time.RFC3339 = "2006-01-02T15:04:05Z07:00" - time.RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" - time.Kitchen = "3:04PM" - // Handy time stamps. - time.Stamp = "Jan _2 15:04:05" - time.StampMilli = "Jan _2 15:04:05.000" - time.StampMicro = "Jan _2 15:04:05.000000" - time.StampNano = "Jan _2 15:04:05.000000000" -``` - ### **strings** String related functions. @@ -86,16 +48,6 @@ 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/grafana-dashboard.json b/docs/operator-manual/notifications/grafana-dashboard.json index 19af42cf39d06..5d04f9116aa16 100644 --- a/docs/operator-manual/notifications/grafana-dashboard.json +++ b/docs/operator-manual/notifications/grafana-dashboard.json @@ -60,7 +60,7 @@ "steppedLine": false, "targets": [ { - "expr": "sum(increase(argocd_notifications_trigger_eval_total[$interval])) by (name)", + "expr": "sum(increase(argocd_notifications_trigger_eval_total[$interval])) by (notifier)", "refId": "A" } ], @@ -146,7 +146,7 @@ "steppedLine": false, "targets": [ { - "expr": "sum(increase(argocd_notifications_deliveries_total[$interval])) by (service)", + "expr": "sum(increase(argocd_notifications_deliveries_total[$interval])) by (notifier)", "refId": "A" } ], diff --git a/docs/operator-manual/notifications/index.md b/docs/operator-manual/notifications/index.md index 002f67249c616..eccca906ae91b 100644 --- a/docs/operator-manual/notifications/index.md +++ b/docs/operator-manual/notifications/index.md @@ -93,7 +93,7 @@ data: apiVersion: v1 kind: Secret metadata: - name: argocd-notifications-secret + name: argo-cd-notification-secret type: Opaque data: pagerduty-key-my-service: diff --git a/docs/operator-manual/notifications/monitoring.md b/docs/operator-manual/notifications/monitoring.md index 3d8b4c41ea34d..a0aabbaae1f09 100644 --- a/docs/operator-manual/notifications/monitoring.md +++ b/docs/operator-manual/notifications/monitoring.md @@ -13,8 +13,8 @@ The following metrics are available: Number of delivered notifications. Labels: -* `trigger` - trigger name -* `service` - notification service name +* `template` - notification template name +* `notifier` - notification service name * `succeeded` - flag that indicates if notification was successfully sent or failed ### `argocd_notifications_trigger_eval_total` diff --git a/docs/operator-manual/notifications/services/alertmanager.md b/docs/operator-manual/notifications/services/alertmanager.md index 033a76a29ea65..e0f9d7e4e7889 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: argocd-notifications-cm + name: 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: argocd-notifications-cm + name: data: service.alertmanager: | targets: @@ -89,7 +89,7 @@ stringData: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.alertmanager: | targets: @@ -110,7 +110,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.alertmanager: | targets: diff --git a/docs/operator-manual/notifications/services/awssqs.md b/docs/operator-manual/notifications/services/awssqs.md index 5331533826348..6b744f4744b93 100755 --- a/docs/operator-manual/notifications/services/awssqs.md +++ b/docs/operator-manual/notifications/services/awssqs.md @@ -1,8 +1,8 @@ -# AWS SQS +# AWS SQS ## Parameters -This notification service is capable of sending simple messages to AWS SQS queue. +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 @@ -30,7 +30,7 @@ metadata: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.awssqs: | region: "us-east-2" @@ -63,7 +63,7 @@ stringData: ### 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. +Ensure following list of environment variables are injected via OIDC, or other 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: @@ -89,7 +89,7 @@ metadata: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.awssqs: | queue: "myqueue" @@ -104,16 +104,3 @@ data: - 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 7fd3f0e22379c..b81ab6cde8b4c 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: argocd-notifications-cm + name: data: service.email.gmail: | username: $email-username @@ -36,7 +36,7 @@ Without authentication: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.email.example: | host: smtp.example.com @@ -52,7 +52,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: 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 4cd2523908ba1..be76ab150d1a1 100755 --- a/docs/operator-manual/notifications/services/github.md +++ b/docs/operator-manual/notifications/services/github.md @@ -4,29 +4,27 @@ The GitHub notification service changes commit status using [GitHub Apps](https://docs.github.com/en/developers/apps) and requires specifying the following settings: -- `appID` - the app id -- `installationID` - the app installation id -- `privateKey` - the app private key -- `enterpriseBaseURL` - optional URL, e.g. https://git.example.com/api/v3 - -> ⚠️ _NOTE:_ Specifying `/api/v3` in the `enterpriseBaseURL` is required until [argoproj/notifications-engine#205](https://github.com/argoproj/notifications-engine/issues/205) is resolved. +* `appID` - the app id +* `installationID` - the app installation id +* `privateKey` - the app private key +* `enterpriseBaseURL` - optional URL, e.g. https://git.example.com/ ## Configuration 1. Create a GitHub Apps using https://github.com/settings/apps/new -1. 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) -1. Generate a private key, and download it automatically - ![3](https://user-images.githubusercontent.com/18019529/108397926-d4a36300-725b-11eb-83fe-74795c8c3e03.png) -1. Install app to account -1. Store privateKey in `argocd-notifications-secret` Secret and configure GitHub integration - in `argocd-notifications-cm` ConfigMap +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) +4. Install app to account +5. Store privateKey in `argocd-notifications-secret` Secret and configure GitHub integration +in `argocd-notifications-cm` ConfigMap ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.github: | appID: @@ -78,8 +76,6 @@ template.app-deployed: | logURL: "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}?operation=true" requiredContexts: [] autoMerge: true - transientEnvironment: false - reference: v1.0.0 pullRequestComment: content: | Application {{.app.metadata.name}} is now running new version of deployments manifests. @@ -87,11 +83,9 @@ template.app-deployed: | ``` **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. -- Reference is optional. When set, it will be used as the ref to deploy. If not set, the revision will be used as the ref to deploy. diff --git a/docs/operator-manual/notifications/services/googlechat.md b/docs/operator-manual/notifications/services/googlechat.md index 821c23023e863..885ce685a4511 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: argocd-notifications-cm + name: data: service.googlechat: | webhooks: diff --git a/docs/operator-manual/notifications/services/grafana.md b/docs/operator-manual/notifications/services/grafana.md index 1f3e77701f044..a36672d0fa423 100755 --- a/docs/operator-manual/notifications/services/grafana.md +++ b/docs/operator-manual/notifications/services/grafana.md @@ -21,7 +21,7 @@ Available parameters : apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: 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 d1f187e955b9c..98e0d0fd7b82f 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: argocd-notifications-cm + name: data: service.mattermost: | apiURL: diff --git a/docs/operator-manual/notifications/services/newrelic.md b/docs/operator-manual/notifications/services/newrelic.md index b0c7e340c9b28..d98288a846422 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: argocd-notifications-cm + name: data: service.newrelic: | apiURL: diff --git a/docs/operator-manual/notifications/services/opsgenie.md b/docs/operator-manual/notifications/services/opsgenie.md index 2cc1ebff62abf..c590a4ac979b6 100755 --- a/docs/operator-manual/notifications/services/opsgenie.md +++ b/docs/operator-manual/notifications/services/opsgenie.md @@ -7,58 +7,22 @@ To be able to send notifications with argocd-notifications you have to create an 3. Click "Teams" in the Menu on the left 4. Select the team that you want to notify 5. In the teams configuration menu select "Integrations" -6. Click "Add Integration" in the top right corner +6. click "Add Integration" in the top right corner 7. Select "API" integration 8. Give your integration a name, copy the "API key" and safe it somewhere for later -9. Click "Edit" in the integration settings -10. Make sure the checkbox for "Create and Update Access" is selected, disable the other checkboxes to remove unnecessary permissions -11. Click "Save" at the bottom -12. Click "Turn on integration" in the top right corner -13. 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). -14. 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. -15. You can find the example `argocd-notifications-cm` configuration at the below. - -| **Option** | **Required** | **Type** | **Description** | **Example** | -| ------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------- | -------------------------------- | -| `description` | True | `string` | Description field of the alert that is generally used to provide a detailed information about the alert. | `Hello from Argo CD!` | -| `priority` | False | `string` | Priority level of the alert. Possible values are P1, P2, P3, P4 and P5. Default value is P3. | `P1` | -| `alias` | False | `string` | Client-defined identifier of the alert, that is also the key element of Alert De-Duplication. | `Life is too short for no alias` | -| `note` | False | `string` | Additional note that will be added while creating the alert. | `Error from Argo CD!` | +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. ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.opsgenie: | apiUrl: apiKeys: : - template.opsgenie: | - message: | - [Argo CD] Application {{.app.metadata.name}} has a problem. - opsgenie: - description: | - Application: {{.app.metadata.name}} - Health Status: {{.app.status.health.status}} - Operation State Phase: {{.app.status.operationState.phase}} - Sync Status: {{.app.status.sync.status}} - priority: P1 - alias: {{.app.metadata.name}} - note: Error from Argo CD! - trigger.on-a-problem: | - - description: Application has a problem. - send: - - opsgenie - when: app.status.health.status == 'Degraded' or app.status.operationState.phase in ['Error', 'Failed'] or app.status.sync.status == 'Unknown' -``` - -16. Add annotation in application yaml file to enable notifications for specific Argo CD app. -```yaml - apiVersion: argoproj.io/v1alpha1 - kind: Application - metadata: - annotations: - notifications.argoproj.io/subscribe.on-a-problem.opsgenie: ``` \ No newline at end of file diff --git a/docs/operator-manual/notifications/services/pagerduty.md b/docs/operator-manual/notifications/services/pagerduty.md index c6e1e41dac81d..3b507e7fdba58 100755 --- a/docs/operator-manual/notifications/services/pagerduty.md +++ b/docs/operator-manual/notifications/services/pagerduty.md @@ -26,7 +26,7 @@ stringData: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.pagerduty: | token: $pagerdutyToken @@ -41,7 +41,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: template.rollout-aborted: | message: Rollout {{.rollout.metadata.name}} is aborted. diff --git a/docs/operator-manual/notifications/services/pagerduty_v2.md b/docs/operator-manual/notifications/services/pagerduty_v2.md index 549cdc937b150..01eee28fc0c9b 100755 --- a/docs/operator-manual/notifications/services/pagerduty_v2.md +++ b/docs/operator-manual/notifications/services/pagerduty_v2.md @@ -28,7 +28,7 @@ stringData: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.pagerdutyv2: | serviceKeys: @@ -43,7 +43,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: template.rollout-aborted: | message: Rollout {{.rollout.metadata.name}} is aborted. diff --git a/docs/operator-manual/notifications/services/pushover.md b/docs/operator-manual/notifications/services/pushover.md index a09b3660f9233..37cb20b277dcc 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 `argocd-notifications-cm` ConfigMap: +2. Store the API key in `` Secret and define the secret name in `` ConfigMap: ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: 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 b84861b54bd8f..f1157050139d0 100755 --- a/docs/operator-manual/notifications/services/rocketchat.md +++ b/docs/operator-manual/notifications/services/rocketchat.md @@ -4,7 +4,7 @@ The Rocket.Chat notification service configuration includes following settings: -* `email` - the Rocker.Chat user's SAMAccountName +* `email` - the Rocker.Chat user's email * `password` - the Rocker.Chat user's password * `alias` - optional alias that should be used to post message * `icon` - optional message icon @@ -25,7 +25,7 @@ The Rocket.Chat notification service configuration includes following settings: 4. Copy username and password that you was created for bot user 5. Create a public or private channel, or a team, for this example `my_channel` 6. Add your bot to this channel **otherwise it won't work** -7. Store email and password in argocd-notifications-secret Secret +7. Store email and password in argocd_notifications-secret Secret ```yaml apiVersion: v1 @@ -43,7 +43,7 @@ stringData: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.rocketchat: | email: $rocketchat-email diff --git a/docs/operator-manual/notifications/services/slack.md b/docs/operator-manual/notifications/services/slack.md index 41bdddd7617c4..0f3fdf1739210 100755 --- a/docs/operator-manual/notifications/services/slack.md +++ b/docs/operator-manual/notifications/services/slack.md @@ -15,7 +15,6 @@ The Slack notification service configuration includes following settings: | `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 @@ -49,7 +48,7 @@ The Slack notification service configuration includes following settings: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.slack: | token: $slack-token diff --git a/docs/operator-manual/notifications/services/teams.md b/docs/operator-manual/notifications/services/teams.md index 0e44456d4de19..8b8c6b819c795 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: argocd-notifications-cm + name: data: service.teams: | recipientUrls: diff --git a/docs/operator-manual/notifications/services/telegram.md b/docs/operator-manual/notifications/services/telegram.md index d370e4fc2359b..953c2a9fca0bf 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 `argocd-notifications-cm` ConfigMap: +in `` ConfigMap: ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.telegram: | token: $telegram-token @@ -33,12 +33,3 @@ metadata: annotations: notifications.argoproj.io/subscribe.on-sync-succeeded.telegram: -1000000000000 ``` - -If your private chat contains threads, you can optionally specify a thread id by seperating it with a `|`: -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - annotations: - notifications.argoproj.io/subscribe.on-sync-succeeded.telegram: -1000000000000|2 -``` diff --git a/docs/operator-manual/notifications/services/webex.md b/docs/operator-manual/notifications/services/webex.md index eba4c5e11b8dc..440ed1ddc738f 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: argocd-notifications-cm + name: 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 4b8ca38a685ad..965098402236f 100755 --- a/docs/operator-manual/notifications/services/webhook.md +++ b/docs/operator-manual/notifications/services/webhook.md @@ -31,7 +31,7 @@ Use the following steps to configure webhook: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.webhook.: | url: https:/// @@ -50,7 +50,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: template.github-commit-status: | webhook: @@ -82,7 +82,7 @@ metadata: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.webhook.github: | url: https://api.github.com @@ -97,7 +97,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.webhook.github: | url: https://api.github.com @@ -128,7 +128,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.webhook.jenkins: | url: http:///job//build?token= @@ -145,7 +145,7 @@ type: Opaque apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.webhook.form: | url: https://form.example.com @@ -166,7 +166,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: argocd-notifications-cm + name: data: service.webhook.slack_webhook: | url: https://hooks.slack.com/services/xxxxx diff --git a/docs/operator-manual/notifications/triggers.md b/docs/operator-manual/notifications/triggers.md index 49a6244777959..c3e2dc601296b 100644 --- a/docs/operator-manual/notifications/triggers.md +++ b/docs/operator-manual/notifications/triggers.md @@ -1,7 +1,7 @@ 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 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: @@ -71,7 +71,7 @@ When one repo is used to sync multiple applications, the `oncePer: app.status.sy ### oncePer -The `oncePer` field is supported like as follows. +The `oncePer` filed is supported like as follows. ```yaml apiVersion: argoproj.io/v1alpha1 diff --git a/docs/operator-manual/notifications/troubleshooting-errors.md b/docs/operator-manual/notifications/troubleshooting-errors.md index ecfcf7151c0ce..f76bb7a2b0d3f 100644 --- a/docs/operator-manual/notifications/troubleshooting-errors.md +++ b/docs/operator-manual/notifications/troubleshooting-errors.md @@ -39,55 +39,3 @@ You need to check your argocd-notifications controller version. For instance, th ### notification service 'xxxx' is not supported You have not defined `xxxx` in `argocd-notifications-cm` or to fail to parse settings. - -### GitHub.repoURL (\u003cno value\u003e) does not have a / using the configuration - -You probably have an Application with [multiple sources](https://argo-cd.readthedocs.io/en/stable/user-guide/multiple_sources/): - -```yaml -spec: - sources: # <- multiple sources - - repoURL: https://github.com/exampleOrg/first.git - path: sources/example - - repoURL: https://github.com/exampleOrg/second.git - targetRevision: "{{branch}}" -``` - -So standard notification template won't work (`{{.app.spec.source.repoURL}}`). You should choose a single source instead: - -```yaml -template.example: | - github: - repoURLPath: "{{ (index .app.spec.sources 0).repoURL }}" -``` - -## config referenced xxx, but key does not exist in secret - -- If you are using a custom secret, check that the secret is in the same namespace -- You have added the label: `app.kubernetes.io/part-of: argocd` to the secret -- You have tried restarting argocd-notifications controller - -### Example: -Secret: -```yaml -apiVersion: v1 -kind: Secret -metadata: - name: argocd-slackbot - namespace: - labels: - app.kubernetes.io/part-of: argocd -type: Opaque -data: - slack-token: -``` -ConfigMap -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: argocd-notifications-cm -data: - service.slack: | - token: $argocd-slackbot:slack-token -``` diff --git a/docs/operator-manual/notifications/troubleshooting.md b/docs/operator-manual/notifications/troubleshooting.md index 616cd4b024e82..6e144bb0c9985 100644 --- a/docs/operator-manual/notifications/troubleshooting.md +++ b/docs/operator-manual/notifications/troubleshooting.md @@ -16,7 +16,7 @@ Additionally, you can specify `:empty` to use empty secret with no notification ```bash argocd admin notifications trigger get \ - --config-map ./argocd-notifications-cm.yaml --secret :empty + --config-map ./argocd admin notifications-cm.yaml --secret :empty ``` * Trigger notification using in-cluster config map and secret: diff --git a/docs/operator-manual/rbac.md b/docs/operator-manual/rbac.md index e85be535bd826..b1d386fb5eb8e 100644 --- a/docs/operator-manual/rbac.md +++ b/docs/operator-manual/rbac.md @@ -1,288 +1,195 @@ # RBAC Configuration -The RBAC feature enables restrictions 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 +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). 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. -There are two main components where RBAC configuration can be defined: - -- The global RBAC config map (see [argo-rbac-cm.yaml](argocd-rbac-cm-yaml.md)) -- The [AppProject's roles](../user-guide/projects.md#project-roles) - ## Basic Built-in Roles Argo CD has two pre-defined roles but RBAC configuration allows defining roles and groups (see below). -- `role:readonly`: read-only access to all resources -- `role:admin`: unrestricted access to all resources +* `role:readonly` - read-only access to all resources +* `role:admin` - unrestricted access to all resources These default built-in role definitions can be seen in [builtin-policy.csv](https://github.com/argoproj/argo-cd/blob/master/assets/builtin-policy.csv) -## Default Policy for Authenticated Users - -When a user is authenticated in Argo CD, it will be granted the role specified in `policy.default`. - -!!! warning "Restricting Default Permissions" - - **All authenticated users get _at least_ the permissions granted by the default policies. This access cannot be blocked - by a `deny` rule.** It is recommended to create a new `role:authenticated` with the minimum set of permissions possible, - then grant permissions to individual roles as needed. - -## Anonymous Access - -Enabling anonymous access to the Argo CD instance allows users to assume the default role permissions specified by `policy.default` **without being authenticated**. - -The anonymous access to Argo CD can be enabled using the `users.anonymous.enabled` field in `argocd-cm` (see [argocd-cm.yaml](argocd-cm-yaml.md)). +### RBAC Permission Structure -!!! warning - - When enabling anonymous access, consider creating a new default role and assigning it to the default policies - with `policy.default: role:unauthenticated`. - -## RBAC Model Structure - -The model syntax is based on [Casbin](https://casbin.org/docs/overview). There are two different types of syntax: one for assigning policies, and another one for assigning users to internal roles. - -**Group**: Allows to assign authenticated users/groups to internal roles. +Breaking down the permissions definition differs slightly between applications and every other resource type in Argo CD. -Syntax: `g, , ` +* All resources *except* application-specific permissions (see next bullet): -- ``: The entity to whom the role will be assigned. It can be a local user or a user authenticated with SSO. - When SSO is used, the `user` will be based on the `sub` claims, while the group is one of the values returned by the `scopes` configuration. -- ``: The internal role to which the entity will be assigned. + `p, , , , ` -**Policy**: Allows to assign permissions to an entity. +* Applications, applicationsets, logs, and exec (which belong to an `AppProject`): -Syntax: `p, , , , , ` + `p, , , , /` -- ``: The entity to whom the policy will be assigned -- ``: The type of resource on which the action is performed. -- ``: The operation that is being performed on the resource. -- ``: The object identifier representing the resource on which the action is performed. Depending on the resource, the object's format will vary. -- ``: Whether this policy should grant or restrict the operation on the target object. One of `allow` or `deny`. +### RBAC Resources and Actions -Below is a table that summarizes all possible resources and which actions are valid for each of them. +Resources: `clusters`, `projects`, `applications`, `applicationsets`, +`repositories`, `certificates`, `accounts`, `gpgkeys`, `logs`, `exec`, +`extensions` -| Resource\Action | get | create | update | delete | sync | action | override | invoke | -| :------------------ | :-: | :----: | :----: | :----: | :--: | :----: | :------: | :----: | -| **applications** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| **applicationsets** | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | -| **clusters** | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | -| **projects** | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | -| **repositories** | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | -| **accounts** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | -| **certificates** | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | -| **gpgkeys** | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | -| **logs** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| **exec** | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| **extensions** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | +Actions: `get`, `create`, `update`, `delete`, `sync`, `override`,`action/` -### Application-Specific Policy +Note that `sync`, `override`, and `action/` only have meaning for the `applications` resource. -Some policy only have meaning within an application. It is the case with the following resources: +#### Application resources -- `applications` -- `applicationsets` -- `logs` -- `exec` +The resource path for application objects is of the form +`/`. -While they can be set in the global configuration, they can also be configured in [AppProject's roles](../user-guide/projects.md#project-roles). -The expected `` value in the policy structure is replaced by `/`. - -For instance, these policies would grant `example-user` access to get any applications, -but only be able to see logs in `my-app` application part of the `example-project` project. - -```csv -p, example-user, applications, get, *, allow -p, example-user, logs, get, example-project/my-app, allow -``` - -#### Application in Any Namespaces - -When [application in any namespace](app-any-namespace.md) is enabled, the expected `` value in the policy structure is replaced by `//`. -Since multiple applications could have the same name in the same project, the policy below makes sure to restrict access only to `app-namespace`. - -```csv -p, example-user, applications, get, */app-namespace/*, allow -p, example-user, logs, get, example-project/app-namespace/my-app, allow -``` - -### The `applications` resource - -The `applications` resource is an [Application-Specific Policy](#application-specific-policy). - -#### Fine-grained Permissions for `update`/`delete` action - -The `update` and `delete` actions, when granted on an application, will allow the user to perform the operation on the application itself **and** all of its resources. -It can be desirable to only allow `update` or `delete` on specific resources within an application. - -To do so, when the action if performed on an application's resource, the `` will have the `////` format. - -For instance, to grant access to `example-user` to only delete Pods in the `prod-app` Application, the policy could be: - -```csv -p, example-user, applications, delete/*/Pod/*, default/prod-app, allow -``` - -If we want to grant access to the user to update all resources of an application, but not the application itself: - -```csv -p, example-user, applications, update/*, default/prod-app, allow -``` - -If we want to explicitly deny delete of the application, but allow the user to delete Pods: - -```csv -p, example-user, applications, delete, default/prod-app, deny -p, example-user, applications, delete/*/Pod/*, default/prod-app, allow -``` - -!!! note - - It is not possible to deny fine-grained permissions for a sub-resource if the action was **explicitly allowed on the application**. - For instance, the following policies will **allow** a user to delete the Pod and any other resources in the application: - - ```csv - p, example-user, applications, delete, default/prod-app, allow - p, example-user, applications, delete/*/Pod/*, default/prod-app, deny - ``` +Delete access to sub-resources of a project, such as a rollout or a pod, cannot +be managed granularly. `/` grants access to all +subresources of an application. #### The `action` action The `action` action corresponds to either built-in resource customizations defined [in the Argo CD repository](https://github.com/argoproj/argo-cd/tree/master/resource_customizations), or to [custom resource actions](resource_actions.md#custom-resource-actions) defined by you. +The `action` path is of the form `action///`. For +example, a resource customization path +`resource_customizations/extensions/DaemonSet/actions/restart/action.lua` +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)). -See the [resource actions documentation](resource_actions.md#built-in-actions) for a list of built-in actions. - -The `` has the `action///` format. - -For example, a resource customization path `resource_customizations/extensions/DaemonSet/actions/restart/action.lua` -corresponds to the `action` path `action/extensions/DaemonSet/restart`. If the resource is not under a group (for example, Pods or ConfigMaps), -then the path will be `action//Pod/action-name`. - -The following policies allows the user to perform any action on the DaemonSet resources, as well as the `maintenance-off` action on a Pod: +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 -p, example-user, applications, action/extensions/DaemonSet/*, default/*, allow -``` - -To allow the user to perform any actions: - -```csv -p, example-user, applications, action/*, default/*, allow ``` -#### The `override` action +#### The `exec` resource -When granted along with the `sync` action, the override action will allow a user to synchronize local manifests to the Application. -These manifests will be used instead of the configured source, until the next sync is performed. +`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`. -### The `applicationsets` resource +See [Web-based Terminal](web_based_terminal.md) for more info. -The `applicationsets` resource is an [Application-Specific policy](#application-specific-policy). +#### The `applicationsets` resource [ApplicationSets](applicationset/index.md) provide a declarative way to automatically create/update/delete Applications. -Allowing the `create` action on the resource 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. -!!! note - - In v2.5, it is not possible to create an ApplicationSet with a templated Project field (e.g. `project: {{path.basename}}`) - via the API (or, by extension, the CLI). Disallowing templated projects makes project restrictions via RBAC safe: - -With the resource being application-specific, the `` of the applicationsets policy will have the format `/`. -However, since an ApplicationSet does belong to any project, the `` value represents the projects in which the ApplicationSet will be able to create Applications. - -With the following policy, a `dev-group` user will be unable to create an ApplicationSet capable of creating Applications -outside the `dev-project` project. +In v2.5, it is not possible to create an ApplicationSet with a templated Project field (e.g. `project: {{path.basename}}`) +via the API (or, by extension, the CLI). Disallowing templated projects makes project restrictions via RBAC safe: ```csv p, dev-group, applicationsets, *, dev-project/*, allow ``` -### The `logs` resource - -The `logs` resource is an [Application-Specific Policy](#application-specific-policy). - -When granted with the `get` action, this policy allows a user to see Pod's logs of an application via -the Argo CD UI. The functionality is similar to `kubectl logs`. - -### The `exec` resource - -The `exec` resource is an [Application-Specific Policy](#application-specific-policy). - -When granted with the `create` action, this policy allows a user to `exec` into Pods of an application via -the Argo CD UI. The functionality is similar to `kubectl exec`. - -See [Web-based Terminal](web_based_terminal.md) for more info. +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 +#### The `extensions` resource -With the `extensions` resource, it is possible to 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 **needs to have read permission on the application** where the request is originated from. +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, it will allow the `example-user` to invoke the `httpbin` extensions in all -applications under the `default` project. +Consider the example below: ```csv -p, example-user, applications, get, default/*, allow -p, example-user, extensions, invoke, httpbin, allow +g, ext, role:extension +p, role:extension, applications, get, default/httpbin-app, allow +p, role:extension, extensions, invoke, httpbin, allow ``` -### The `deny` effect - -When `deny` is used as an effect in a policy, it will be effective if the policy matches. -Even if more specific policies with the `allow` effect match as well, the `deny` will have priority. - -The order in which the policies appears in the policy file configuration has no impact, and the result is deterministic. +Explanation: -## Policies Evaluation and Matching +* *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. -The evaluation of access is done in two parts: validating against the default policy configuration, then validating against the policies for the current user. +**Note 1**: that for extensions requests to be allowed, the policy defined +in the *line2* is also required. -**If an action is allowed or denied by the default policies, then this effect will be effective without further evaluation**. -When the effect is undefined, the evaluation will continue with subject-specific policies. +**Note 2**: `invoke` is a new action introduced specifically to be used +with the `extensions` resource. The current actions for `extensions` +are `*` or `invoke`. -The access will be evaluated for the user, then for each configured group that the user is part of. +## Tying It All Together -The matching engine, configured in `policy.matchMode`, can use two different match modes to compare the values of tokens: +Additional roles and groups can be configured in `argocd-rbac-cm` ConfigMap. The example below +configures a custom role, named `org-admin`. The role is assigned to any user which belongs to +`your-github-org:your-team` group. All other users get the default policy of `role:readonly`, +which cannot modify Argo CD settings. -- `glob`: based on the [`glob` package](https://pkg.go.dev/github.com/gobwas/glob). -- `regex`: based on the [`regexp` package](https://pkg.go.dev/regexp). +!!! warning + 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. -When all tokens match during the evaluation, the effect will be returned. The evaluation will continue until all matching policies are evaluated, or until a policy with the `deny` effect matches. -After all policies are evaluated, if there was at least one `allow` effect and no `deny`, access will be granted. +*ArgoCD ConfigMap `argocd-rbac-cm` Example:* -### Glob matching +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-rbac-cm + namespace: argocd +data: + policy.default: role:readonly + policy.csv: | + p, role:org-admin, applications, *, */*, allow + p, role:org-admin, clusters, get, *, allow + p, role:org-admin, repositories, get, *, allow + 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 +``` -When `glob` is used, the policy tokens are treated as single terms, without separators. +---- -Consider the following policy: +Another `policy.csv` example might look as follows: -``` -p, example-user, applications, action/extensions/*, default/*, allow +```csv +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 ``` -When the `example-user` executes the `extensions/DaemonSet/test` action, the following `glob` matches will happen: +This example defines a *role* called `staging-db-admin` with nine *permissions* that allow users with that role to perform the following *actions*: -1. The current user `example-user` matches the token `example-user`. -2. The value `applications` matches the token `applications`. -3. The value `action/extensions/DaemonSet/test` matches `action/extensions/*`. Note that `/` is not treated as a separator and the use of `**` is not necessary. -4. The value `default/my-app` matches `default/*`. +* `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`. -## Using SSO Users/Groups - -The `scopes` field controls which OIDC scopes to examine during RBAC enforcement (in addition to `sub` scope). -If omitted, it defaults to `'[groups]'`. The scope value can be a string, or a list of strings. - -For more information on `scopes` please review the [User Management Documentation](user-management/index.md). +!!! 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. -The following example shows targeting `email` as well as `groups` from your OIDC provider. +Following example shows targeting `email` as well as `groups` from your OIDC provider. ```yaml apiVersion: v1 @@ -302,73 +209,24 @@ data: scopes: '[groups, email]' ``` -This can be useful to associate users' emails and groups directly in AppProject. - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: team-beta-project - namespace: argocd -spec: - roles: - - name: admin - description: Admin privileges to team-beta - policies: - - p, proj:team-beta-project:admin, applications, *, *, allow - groups: - - user@example.org # Value from the email scope - - my-org:team-beta # Value from the groups scope -``` - -## Local Users/Accounts - -[Local users](user-management/index.md#local-usersaccounts) are assigned access by either grouping them with a role or by assigning policies directly -to them. - -The example below shows how to assign a policy directly to a local user. - -```yaml -p, my-local-user, applications, sync, my-project/*, allow -``` - -This example shows how to assign a role to a local user. - -```yaml -g, my-local-user, role:admin -``` - -!!! warning "Ambiguous Group Assignments" - - If you have [enabled SSO](user-management/index.md#sso), any SSO user with a scope that matches a local user will be - added to the same roles as the local user. For example, if local user `sally` is assigned to `role:admin`, and if an - SSO user has a scope which happens to be named `sally`, that SSO user will also be assigned to `role:admin`. - - An example of where this may be a problem is if your SSO provider is an SCM, and org members are automatically - granted scopes named after the orgs. If a user can create or add themselves to an org in the SCM, they can gain the - permissions of the local user with the same name. - - To avoid ambiguity, if you are using local users and SSO, it is recommended to assign policies directly to local - users, and not to assign roles to local users. In other words, instead of using `g, my-local-user, role:admin`, you - should explicitly assign policies to `my-local-user`: - - ```yaml - p, my-local-user, *, *, *, allow - ``` +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`. +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. +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 ConfigMap. +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 @@ -383,21 +241,96 @@ data: g, my-org:team-qa, role:tester ``` +## Anonymous Access + +The anonymous access to Argo CD can be enabled using `users.anonymous.enabled` field in `argocd-cm` (see [argocd-cm.yaml](argocd-cm.yaml)). +The anonymous users get default role permissions specified by `policy.default` in `argocd-rbac-cm.yaml`. For read-only access you'll want `policy.default: role:readonly` as above + ## Validating and testing your RBAC policies If you want to ensure that your RBAC policies are working as expected, you can -use the [`argocd admin settings rbac` command](../user-guide/commands/argocd_admin_settings_rbac.md) to validate them. -This tool allows you to test whether a certain role or subject can perform the requested action with a policy -that's not live yet in the system, i.e. from a local file or config map. -Additionally, it can be used against the live RBAC configuration in the cluster your Argo CD is running in. +use the `argocd admin settings rbac` command to validate them. This tool allows you to +test whether a certain role or subject can perform the requested action with a +policy that's not live yet in the system, i.e. from a local file or config map. +Additionally, it can be used against the live policy in the cluster your Argo +CD is running in. + +To check whether your new policy is valid and understood by Argo CD's RBAC +implementation, you can use the `argocd admin settings rbac validate` command. ### Validating a policy -To check whether your new policy configuration is valid and understood by Argo CD's RBAC implementation, -you can use the [`argocd admin settings rbac validate` command](../user-guide/commands/argocd_admin_settings_rbac_validate.md). +To validate a policy stored in a local text file: + +```shell +argocd admin settings rbac validate --policy-file somepolicy.csv +``` + +To validate a policy stored in a local K8s ConfigMap definition in a YAML file: + +```shell +argocd admin settings rbac validate --policy-file argocd-rbac-cm.yaml +``` + +To validate a policy stored in K8s, used by Argo CD in namespace `argocd`, +ensure that your current context in `~/.kube/config` is pointing to your +Argo CD cluster and give appropriate namespace: + +```shell +argocd admin settings rbac validate --namespace argocd +``` ### Testing a policy To test whether a role or subject (group or local user) has sufficient permissions to execute certain actions on certain resources, you can -use the [`argocd admin settings rbac can` command](../user-guide/commands/argocd_admin_settings_rbac_can.md). +use the `argocd admin settings rbac can` command. Its general syntax is + +```shell +argocd admin settings rbac can SOMEROLE ACTION RESOURCE SUBRESOURCE [flags] +``` + +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: + +```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-admin` and associates the group `db-admins` with it. +Policy is stored locally as `policy.csv`: + +You can test against the role: + +```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-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-admin get applications --policy-file policy.csv --default-role role:readonly +Yes +``` + +Or against the group defined: + +```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 index ebf8983a39a97..a956cd9cf7b28 100644 --- a/docs/operator-manual/reconcile.md +++ b/docs/operator-manual/reconcile.md @@ -4,8 +4,7 @@ By default, an Argo CD Application is refreshed every time a resource that belon 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). -For untracked resources, you can [use the argocd.argoproj.io/ignore-resource-updates annotations](#ignoring-updates-for-untracked-resources) +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. @@ -110,57 +109,5 @@ data: jqPathExpressions: # Ignore lastTransitionTime for conditions; helpful when SharedResourceWarnings are being regularly updated but not # actually changing in content. - - .status?.conditions[]?.lastTransitionTime -``` - -## Ignoring updates for untracked resources - -ArgoCD will only apply `ignoreResourceUpdates` configuration to tracked resources of an application. This means dependant resources, such as a `ReplicaSet` and `Pod` created by a `Deployment`, will not ignore any updates and trigger a reconcile of the application for any changes. - -If you want to apply the `ignoreResourceUpdates` configuration to an untracked resource, you can add the -`argocd.argoproj.io/ignore-resource-updates=true` annotation in the dependent resources manifest. - -## Example - -### CronJob - -```yaml -apiVersion: batch/v1 -kind: CronJob -metadata: - name: hello - namespace: test-cronjob -spec: - schedule: "* * * * *" - jobTemplate: - metadata: - annotations: - argocd.argoproj.io/ignore-resource-updates: "true" - spec: - template: - metadata: - annotations: - argocd.argoproj.io/ignore-resource-updates: "true" - spec: - containers: - - name: hello - image: busybox:1.28 - imagePullPolicy: IfNotPresent - command: - - /bin/sh - - -c - - date; echo Hello from the Kubernetes cluster - restartPolicy: OnFailure -``` - -The resource updates will be ignored based on your the `ignoreResourceUpdates` configuration in the `argocd-cm` configMap: - -`argocd-cm`: -```yaml -resource.customizations.ignoreResourceUpdates.batch_Job: | - jsonPointers: - - /status -resource.customizations.ignoreResourceUpdates.Pod: | - jsonPointers: - - /status + - .status.conditions[].lastTransitionTime ``` diff --git a/docs/operator-manual/resource_actions.md b/docs/operator-manual/resource_actions.md index 0a4ea2cb3936a..b720f589ae8d0 100644 --- a/docs/operator-manual/resource_actions.md +++ b/docs/operator-manual/resource_actions.md @@ -5,14 +5,6 @@ Argo CD allows operators to define custom actions which users can perform on spe Operators can add actions to custom resources in form of a Lua script and expand those capabilities. -## Built-in Actions - -The following are actions that are built-in to Argo CD. Each action name links to its Lua script definition: - -{!docs/operator-manual/resource_actions_builtin.md!} - -See the [RBAC documentation](rbac.md#the-action-action) for information on how to control access to these actions. - ## Custom Resource Actions Argo CD supports custom resource actions written in [Lua](https://www.lua.org/). This is useful if you: @@ -80,20 +72,6 @@ The `discovery.lua` script must return a table where the key name represents the 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`. -By default, defining a resource action customization will override any built-in action for this resource kind. If you want to retain the built-in actions, you can set the `mergeBuiltinActions` key to `true`. Your custom actions will have precedence over the built-in actions. -```yaml -resource.customizations.actions.argoproj.io_Rollout: | - mergeBuiltinActions: true - discovery.lua: | - actions = {} - actions["do-things"] = {} - return actions - definitions: - - name: do-things - action.lua: | - return obj -``` - #### Creating new resources with a custom action !!! important diff --git a/docs/operator-manual/resource_actions_builtin.md b/docs/operator-manual/resource_actions_builtin.md deleted file mode 100644 index 46230a879a875..0000000000000 --- a/docs/operator-manual/resource_actions_builtin.md +++ /dev/null @@ -1,50 +0,0 @@ -- [apps/DaemonSet/restart](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/apps/DaemonSet/actions/restart/action.lua) -- [apps/Deployment/pause](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/apps/Deployment/actions/pause/action.lua) -- [apps/Deployment/restart](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/apps/Deployment/actions/restart/action.lua) -- [apps/Deployment/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/apps/Deployment/actions/resume/action.lua) -- [apps/StatefulSet/restart](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/apps/StatefulSet/actions/restart/action.lua) -- [argoproj.io/AnalysisRun/terminate](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/argoproj.io/AnalysisRun/actions/terminate/action.lua) -- [argoproj.io/CronWorkflow/create-workflow](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/argoproj.io/CronWorkflow/actions/create-workflow/action.lua) -- [argoproj.io/Rollout/abort](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/argoproj.io/Rollout/actions/abort/action.lua) -- [argoproj.io/Rollout/promote-full](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/argoproj.io/Rollout/actions/promote-full/action.lua) -- [argoproj.io/Rollout/restart](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/argoproj.io/Rollout/actions/restart/action.lua) -- [argoproj.io/Rollout/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/argoproj.io/Rollout/actions/resume/action.lua) -- [argoproj.io/Rollout/retry](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/argoproj.io/Rollout/actions/retry/action.lua) -- [argoproj.io/WorkflowTemplate/create-workflow](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/argoproj.io/WorkflowTemplate/actions/create-workflow/action.lua) -- [batch/CronJob/create-job](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/batch/CronJob/actions/create-job/action.lua) -- [external-secrets.io/ExternalSecret/refresh](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/external-secrets.io/ExternalSecret/actions/refresh/action.lua) -- [external-secrets.io/PushSecret/push](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/external-secrets.io/PushSecret/actions/push/action.lua) -- [helm.toolkit.fluxcd.io/HelmRelease/reconcile](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/reconcile/action.lua) -- [helm.toolkit.fluxcd.io/HelmRelease/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/resume/action.lua) -- [helm.toolkit.fluxcd.io/HelmRelease/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/suspend/action.lua) -- [image.toolkit.fluxcd.io/ImageRepository/reconcile](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/reconcile/action.lua) -- [image.toolkit.fluxcd.io/ImageRepository/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/resume/action.lua) -- [image.toolkit.fluxcd.io/ImageRepository/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/suspend/action.lua) -- [image.toolkit.fluxcd.io/ImageUpdateAutomation/reconcile](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/reconcile/action.lua) -- [image.toolkit.fluxcd.io/ImageUpdateAutomation/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/resume/action.lua) -- [image.toolkit.fluxcd.io/ImageUpdateAutomation/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/suspend/action.lua) -- [kustomize.toolkit.fluxcd.io/Kustomization/reconcile](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/reconcile/action.lua) -- [kustomize.toolkit.fluxcd.io/Kustomization/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/resume/action.lua) -- [kustomize.toolkit.fluxcd.io/Kustomization/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/suspend/action.lua) -- [notification.toolkit.fluxcd.io/Alert/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/resume/action.lua) -- [notification.toolkit.fluxcd.io/Alert/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/suspend/action.lua) -- [notification.toolkit.fluxcd.io/Provider/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/resume/action.lua) -- [notification.toolkit.fluxcd.io/Provider/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/suspend/action.lua) -- [notification.toolkit.fluxcd.io/Receiver/reconcile](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/reconcile/action.lua) -- [notification.toolkit.fluxcd.io/Receiver/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/resume/action.lua) -- [notification.toolkit.fluxcd.io/Receiver/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/suspend/action.lua) -- [source.toolkit.fluxcd.io/Bucket/reconcile](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/reconcile/action.lua) -- [source.toolkit.fluxcd.io/Bucket/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/resume/action.lua) -- [source.toolkit.fluxcd.io/Bucket/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/suspend/action.lua) -- [source.toolkit.fluxcd.io/GitRepository/reconcile](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/reconcile/action.lua) -- [source.toolkit.fluxcd.io/GitRepository/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/resume/action.lua) -- [source.toolkit.fluxcd.io/GitRepository/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/suspend/action.lua) -- [source.toolkit.fluxcd.io/HelmChart/reconcile](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/reconcile/action.lua) -- [source.toolkit.fluxcd.io/HelmChart/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/resume/action.lua) -- [source.toolkit.fluxcd.io/HelmChart/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/suspend/action.lua) -- [source.toolkit.fluxcd.io/HelmRepository/reconcile](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/reconcile/action.lua) -- [source.toolkit.fluxcd.io/HelmRepository/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/resume/action.lua) -- [source.toolkit.fluxcd.io/HelmRepository/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/suspend/action.lua) -- [source.toolkit.fluxcd.io/OCIRepository/reconcile](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/reconcile/action.lua) -- [source.toolkit.fluxcd.io/OCIRepository/resume](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/resume/action.lua) -- [source.toolkit.fluxcd.io/OCIRepository/suspend](https://github.com/argoproj/argo-cd/blob/master/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/suspend/action.lua) diff --git a/docs/operator-manual/secret-management.md b/docs/operator-manual/secret-management.md index 95343789c4868..aa224e20ff742 100644 --- a/docs/operator-manual/secret-management.md +++ b/docs/operator-manual/secret-management.md @@ -19,14 +19,13 @@ Here are some ways people are doing GitOps secrets: * [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) -* [argocd-secret-replacer](https://github.com/mmalyska/argocd-secret-replacer) For discussion, see [#1364](https://github.com/argoproj/argo-cd/issues/1364) ## Mitigating Risks of Secret-Injection Plugins -Argo CD caches the manifests generated by plugins, along with the injected secrets, in its Redis instance. Those -manifests are also available via the repo-server API (a gRPC service). This means that the secrets are available to +Argo CD caches the manifests generated by plugins, along with the injected secrets, in its Redis instance. Those +manifests are also available via the repo-server API (a gRPC service). This means that the secrets are available to anyone who has access to the Redis instance or to the repo-server. Consider these steps to mitigate the risks of secret-injection plugins: @@ -34,4 +33,5 @@ Consider these steps to mitigate the risks of secret-injection plugins: 1. Set up network policies to prevent direct access to Argo CD components (Redis and the repo-server). Make sure your cluster supports those network policies and can actually enforce them. 2. Consider running Argo CD on its own cluster, with no other applications running on it. - +3. [Enable password authentication on the Redis instance](https://github.com/argoproj/argo-cd/issues/3130) (currently + only supported for non-HA Argo CD installations). diff --git a/docs/operator-manual/security.md b/docs/operator-manual/security.md index 9d05c45cb7c74..47c5d3aa1accc 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.md) policy. Any matched rule +and comparing each group against the roles/rules in the [RBAC](../rbac) policy. Any matched rule permits access to the API request. ## TLS @@ -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-roles/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/argocd-application-controller-role.yaml) in order to: 1. watch & operate on cluster state diff --git a/docs/operator-manual/server-commands/argocd-application-controller.md b/docs/operator-manual/server-commands/argocd-application-controller.md index f0d1a01dbd02d..caab2770e07aa 100644 --- a/docs/operator-manual/server-commands/argocd-application-controller.md +++ b/docs/operator-manual/server-commands/argocd-application-controller.md @@ -31,7 +31,6 @@ argocd-application-controller [flags] --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. - --enable-k8s-event none Enable ArgoCD to use k8s event. For disabling all events, set the value as none. (e.g --enable-k8s-event=none), For enabling specific events, set the value as `event reason`. (e.g --enable-k8s-event=StatusRefreshed,ResourceCreated) (default [all]) --gloglevel int Set the glog logging level -h, --help help for argocd-application-controller --ignore-normalizer-jq-execution-timeout-seconds duration Set ignore normalizer JQ execution timeout @@ -40,7 +39,6 @@ argocd-application-controller [flags] --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-conditions strings List of Application conditions that will be added to the argocd_application_conditions metric --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) @@ -72,7 +70,7 @@ argocd-application-controller [flags] --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, consistent-hashing] (default "legacy") + --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 diff --git a/docs/operator-manual/server-commands/argocd-repo-server.md b/docs/operator-manual/server-commands/argocd-repo-server.md index 12e4d34d14028..e180d5a65dbba 100644 --- a/docs/operator-manual/server-commands/argocd-repo-server.md +++ b/docs/operator-manual/server-commands/argocd-repo-server.md @@ -23,7 +23,6 @@ argocd-repo-server [flags] --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 - --include-hidden-directories Include hidden directories from Git --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") @@ -35,7 +34,6 @@ argocd-repo-server [flags] --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. - --plugin-use-manifest-generate-paths Pass the resources described in argocd.argoproj.io/manifest-generate-paths value to the cmpserver to generate the application manifests. --port int Listen on given port for incoming connections (default 8081) --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. @@ -47,12 +45,11 @@ argocd-repo-server [flags] --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") --streamed-manifest-max-tar-size string Maximum size of streamed manifest archives (default "100M") - --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") + --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") ``` diff --git a/docs/operator-manual/server-commands/argocd-server.md b/docs/operator-manual/server-commands/argocd-server.md index 0fe1e2d3ca45e..acb7c47f629b3 100644 --- a/docs/operator-manual/server-commands/argocd-server.md +++ b/docs/operator-manual/server-commands/argocd-server.md @@ -25,94 +25,74 @@ argocd-server [flags] ### Options ``` - --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 - --appset-allowed-scm-providers strings 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) - --appset-enable-new-git-file-globbing Enable new globbing in Git files generator. - --appset-enable-scm-providers Enable retrieving information from SCM providers, used by the SCM and PR generators (Default: true) (default true) - --appset-scm-root-ca-path string Provide Root CA Path for self-signed TLS Certificates - --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-k8s-event none Enable ArgoCD to use k8s event. For disabling all events, set the value as none. (e.g --enable-k8s-event=none), For enabling specific events, set the value as `event reason`. (e.g --enable-k8s-event=StatusRefreshed,ResourceCreated) (default [all]) - --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") - --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 - --webhook-parallelism-limit int Number of webhook requests processed concurrently (default 50) - --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-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") ``` ### SEE ALSO diff --git a/docs/operator-manual/signed-release-assets.md b/docs/operator-manual/signed-release-assets.md index 3c42b27fd4e10..9aec6bb071047 100644 --- a/docs/operator-manual/signed-release-assets.md +++ b/docs/operator-manual/signed-release-assets.md @@ -7,21 +7,20 @@ *** ## 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 | +| 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 | +| 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 @@ -32,8 +31,7 @@ Argo CD container images are signed by [cosign](https://github.com/sigstore/cosi 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 \ ---certificate-github-workflow-repository "argoproj/argo-cd" \ -quay.io/argoproj/argocd:v2.11.3 | jq +quay.io/argoproj/argocd:v2.7.0 | jq ``` The command should output the following if the container image was correctly verified: ```bash @@ -94,7 +92,7 @@ The attestation payload contains a non-forgeable provenance which is base64 enco ```bash slsa-verifier verify-image "$IMAGE" \ --source-uri github.com/argoproj/argo-cd \ - --source-tag v2.7.0 \ + --source-tag v2.7.0 --print-provenance | jq ``` diff --git a/docs/operator-manual/tested-kubernetes-versions.md b/docs/operator-manual/tested-kubernetes-versions.md index 73475e8523f5b..b24ffe305cdd9 100644 --- a/docs/operator-manual/tested-kubernetes-versions.md +++ b/docs/operator-manual/tested-kubernetes-versions.md @@ -1,2 +1,5 @@ -This page is populated for released Argo CD versions. Use the version selector to view this table for a specific -version. +| Argo CD version | Kubernetes versions | +|-----------------|---------------------| +| 2.10 | v1.28, v1.27, v1.26, v1.25 | +| 2.9 | v1.28, v1.27, v1.26, v1.25 | +| 2.8 | v1.27, v1.26, v1.25, v1.24 | diff --git a/docs/operator-manual/upgrading/2.10-2.11.md b/docs/operator-manual/upgrading/2.10-2.11.md deleted file mode 100644 index ea06a89e6d7d7..0000000000000 --- a/docs/operator-manual/upgrading/2.10-2.11.md +++ /dev/null @@ -1,58 +0,0 @@ -# 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. - -## Egress NetworkPolicy for `argocd-redis` and `argocd-redis-ha-haproxy` - -Starting with Argo CD 2.11.2, the NetworkPolicy for the `argocd-redis` and `argocd-redis-ha-haproxy` dropped Egress restrictions. This change was made -to allow access to the Kubernetes API to create a secret to secure Redis access. - -To retain similar networking restrictions as before 2.11.2, you can add an Egress rule to allow access only to the -Kubernetes API and access needed by Redis itself. The Egress rule for Kubernetes access will depend entirely on your -Kubernetes setup. The access for Redis itself can be allowed by adding the following to the -`argocd-redis-network-policy` NetworkPolicy: - -```diff -kind: NetworkPolicy -apiVersion: networking.k8s.io/v1 -metadata: - name: argocd-redis-network-policy -spec: - policyTypes: - - Ingress -+ - Egress -+ egress: -+ - ports: -+ - port: 53 -+ protocol: UDP -+ - port: 53 -+ protocol: TCP -``` - -```diff -kind: NetworkPolicy -apiVersion: networking.k8s.io/v1 -metadata: - name: argocd-redis-ha-haproxy -spec: - policyTypes: - - Ingress -+ - Egress -+ egress: -+ - ports: -+ - port: 6379 -+ protocol: TCP -+ - port: 26379 -+ protocol: TCP -+ to: -+ - podSelector: -+ matchLabels: -+ app.kubernetes.io/name: argocd-redis-ha -+ - ports: -+ - port: 53 -+ protocol: UDP -+ - port: 53 -+ protocol: TCP -``` \ No newline at end of file diff --git a/docs/operator-manual/upgrading/2.11-2.12.md b/docs/operator-manual/upgrading/2.11-2.12.md deleted file mode 100644 index deb538feb1915..0000000000000 --- a/docs/operator-manual/upgrading/2.11-2.12.md +++ /dev/null @@ -1,59 +0,0 @@ -# v2.11 to 2.12 - -## Cluster secret scoping changes - -From Argo CD 2.12, there have been some changes to the use of cluster secrets where a `project` is a non-empty value. -Previously, an `Application` or `ApplicationSet` would use any cluster secret matching the URL of the `repoUrl` field. -From 2.12, we now check to see whether the project field of an application _also_ matches the project field of the cluster -secret. What this means is that if you have a cluster secret scoped to `project-a`, an application scoped to `project-b` -can no longer make use of the secret. If you have a cluster secret that's intended to be used by applications in multiple -projects, you need to **unset** the `project` field. - -This also applies when using the Git generator in applicationsets; since an applicationset is not scoped to a particular -project any cluster secrets it makes use of also needs to be globally scoped (i.e. any secret needs to have an unset -`project`). - -## Upgraded Helm Version - -Note that bundled Helm version has been upgraded from 3.14.4 to 3.15.2. - -## Image Registry Change for `redis` and `haproxy` - -Argo CD 2.12 upgraded its [upstream redis-ha Helm chart](https://artifacthub.io/packages/helm/dandydev-charts/redis-ha/) -version from 4.22.3 to 4.26.6. - -As part of the upgrade, the image registry for `redis` and `haproxy` was changed from DockerHub to ECR. - -Make sure that the registry change will work for your environment. One example of a problem would be that your -environment can use Cosign to verify the image signature for DockerHub but not for ECR. You would need to make sure your -Image Validation policy includes the AWS ECR as an approved registry. Please validate that the registry change is -acceptable before upgrading. - -## Server-Side Apply Management of ApplicationSet Fields - -### Summary - -If you are using server-side apply with multiple field managers to manage a single `selector` or `labelSelector` field -in an ApplicationSet, that field management must be changed to be atomic starting with 2.12. - -### Details - -Argo CD 2.12 upgraded its controller-gen version from 0.4.1 to 0.14.0. As part of that change, several ApplicationSet -CRD fields now have `x-kubernetes-map-type: atomic`. - -Each of the affected fields is a label selector with two child keys: `matchLabels` and `matchExpressions`. - -Prior to this change, two field managers could manage the `matchLabels` and `matchExpressions` fields independently. -Starting with the 2.12 CRD, a single field manager must manage both of those fields. This behavior is in line with the -upstream behavior of the label selector struct. - -See the [Kubernetes server-side apply merge strategy docs](https://kubernetes.io/docs/reference/using-api/server-side-apply/#merge-strategy) -for more information about the fields' behavior. - -The affected ApplicationSet fields are the following (jq selector syntax): - -* `.spec.generators[].selector` -* `.spec.generators[].cluster.selector` -* `.spec.generators[].clusterDecisionResource.labelSelector` -* `.spec.generators[].matrix.generators[].selector` -* `.spec.generators[].merge.generators[].selector` diff --git a/docs/operator-manual/upgrading/2.12-2.13.md b/docs/operator-manual/upgrading/2.12-2.13.md deleted file mode 100644 index 14b26f22a2d70..0000000000000 --- a/docs/operator-manual/upgrading/2.12-2.13.md +++ /dev/null @@ -1,69 +0,0 @@ -# v2.12 to 2.13 - -## Custom Resource Actions for Flux Resources - -[`Custom Resource Actions`](../resource_actions.md#Custom-Resource-Actions) have been added for Flux Resources. -The following actions are now available: - -| Custom Resource | Supported Actions | -|-----------------------|----------------------------------| -| HelmRelease | `Suspend`, `Resume`, `Reconcile` | -| ImageRepository | `Suspend`, `Resume`, `Reconcile` | -| ImageUpdateAutomation | `Suspend`, `Resume`, `Reconcile` | -| Kustomization | `Suspend`, `Resume`, `Reconcile` | -| Alert | `Suspend`, `Resume` | -| Provider | `Suspend`, `Resume` | -| Receiver | `Suspend`, `Resume`, `Reconcile` | -| Bucket | `Suspend`, `Resume`, `Reconcile` | -| GitRepository | `Suspend`, `Resume`, `Reconcile` | -| HelmChart | `Suspend`, `Resume`, `Reconcile` | -| HelmRepository | `Suspend`, `Resume`, `Reconcile` | -| OCIRepository | `Suspend`, `Resume`, `Reconcile` | - -If you want to use these actions do not forget to update the permissions (RBAC) for your Argo CD instance. - -## Custom Resource Health for Flux Resources - -[`Custom Resource Health`](../health.md#custom-health-checks) has been added for Flux Resources. -The following Flux resources now support health checks: -- HelmRelease -- ImagePolicy -- ImageRepository -- ImageUpdateAutomation -- Kustomization -- Receiver -- Bucket -- GitRepository -- HelmChart -- HelmRepository -- OCIRepository - -## Upgraded Dex Version - -Dex [v2.39.0](https://github.com/dexidp/dex/releases/tag/v2.39.0) included a breaking change for the LDAP connector: - -> The validation of username and password in the LDAP connector is much more strict now. -> As of today, Dex uses the EscapeFilter function to check for special characters in credentials and prevent injections by denying such requests. - -## Updated Job name for manually started CronJob jobs - -The naming of Jobs that are manually started from CronJobs (using Argo CD) was changed. Instead of the previous postfix `-YYYYMMDDHHmm` (4-digit year), manually started Jobs now receive postfix `-YYMMDDHHmm` (2-digit year). - -The format of Jobs that are started from a CronJob on schedule (by Kubernetes) is not handled by Argo CD and remains unchanged. - -## Change in Log File Extension for Downloaded Logs - -The default extension for log files generated by Argo CD when using the "Download Logs" feature has been changed from `.txt` to `.log`. This change aligns with industry standards and improves compatibility with various log management tools and IDEs that offer enhanced features for `.log` files. - -**Impact:** -- Users and systems that rely on the `.txt` extension will need to adjust their workflows. -- Automated scripts and processes that specifically target `.txt` log files should be updated to handle `.log` files. - -**Benefits:** -- Improved readability and parsing in IDEs and log management tools. -- Consistency with standard log file conventions. - -If you have any custom scripts or tools that depend on the `.txt` extension, please update them accordingly. -## Added proxy to kustomize - -Proxy config set on repository credentials / repository templates is now passed down to the `kustomize build` command. diff --git a/docs/operator-manual/upgrading/2.4-2.5.md b/docs/operator-manual/upgrading/2.4-2.5.md index 5ae6772dad9f9..8971c7cd8e3a4 100644 --- a/docs/operator-manual/upgrading/2.4-2.5.md +++ b/docs/operator-manual/upgrading/2.4-2.5.md @@ -86,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.7. +~~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. @@ -151,7 +151,7 @@ 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.7, the `--server-side-generate` argument will become the default, and client-side generation will be supported as an alternative. +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`). diff --git a/docs/operator-manual/upgrading/overview.md b/docs/operator-manual/upgrading/overview.md index 290ef05638d88..742c7b191b57a 100644 --- a/docs/operator-manual/upgrading/overview.md +++ b/docs/operator-manual/upgrading/overview.md @@ -5,7 +5,7 @@ This section contains information on upgrading Argo CD. Before upgrading please make sure to read details about the breaking changes between Argo CD versions. -Argo CD uses semver-like versioning that ensures the following rules: +Argo CD uses the semver versioning and ensures that following rules: * The patch release does not introduce any breaking changes. So if you are upgrading from v1.5.1 to v1.5.3 there should be no special instructions to follow. @@ -37,9 +37,6 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/ -* [v2.12 to v2.13](./2.12-2.13.md) -* [v2.11 to v2.12](./2.11-2.12.md) -* [v2.10 to v2.11](./2.10-2.11.md) * [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) diff --git a/docs/operator-manual/user-management/auth0.md b/docs/operator-manual/user-management/auth0.md index c20b5f5af30c9..411517df05e06 100644 --- a/docs/operator-manual/user-management/auth0.md +++ b/docs/operator-manual/user-management/auth0.md @@ -39,7 +39,6 @@ data: issuer: https://..auth0.com/ clientID: clientSecret: - domain_hint: requestedScopes: - openid - profile diff --git a/docs/operator-manual/user-management/google.md b/docs/operator-manual/user-management/google.md index 366a1e9863d76..7113e51018ca2 100644 --- a/docs/operator-manual/user-management/google.md +++ b/docs/operator-manual/user-management/google.md @@ -142,6 +142,17 @@ 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: @@ -200,7 +211,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 (note that the `type` is now `google` instead of `oidc`): +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: dex.config: | connectors: @@ -218,20 +229,6 @@ 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 index c4019964d7a4d..0fd78b1aaf62f 100644 --- a/docs/operator-manual/user-management/identity-center.md +++ b/docs/operator-manual/user-management/identity-center.md @@ -1,7 +1,7 @@ # Identity Center (AWS SSO) !!! note "Are you using this? Please contribute!" - If you're using this IdP please consider [contributing](../../developer-guide/docs-site.md) to this document. + 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: diff --git a/docs/operator-manual/user-management/index.md b/docs/operator-manual/user-management/index.md index 8616764172988..496dd17a83e9f 100644 --- a/docs/operator-manual/user-management/index.md +++ b/docs/operator-manual/user-management/index.md @@ -172,8 +172,6 @@ kubectl edit configmap argocd-cm -n argocd ``` * In the `url` key, input the base URL of Argo CD. In this example, it is `https://argocd.example.com` -* (Optional): If Argo CD should be accessible via multiple base URLs you may - specify any additional base URLs via the `additionalUrls` key. * In the `dex.config` key, add the `github` connector to the `connectors` sub field. See Dex's [GitHub connector](https://github.com/dexidp/website/blob/main/content/docs/connectors/github.md) documentation for explanation of the fields. A minimal config should populate the clientID, @@ -502,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 or secret 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 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 10551321d976a..6f0c99de0dec2 100644 --- a/docs/operator-manual/user-management/keycloak.md +++ b/docs/operator-manual/user-management/keycloak.md @@ -125,9 +125,3 @@ In this example we give the role _role:admin_ to all users in the group _ArgoCDA You can now login using our new Keycloak OIDC authentication: ![Keycloak ArgoCD login](../../assets/keycloak-login.png "Keycloak ArgoCD login") - -## Troubleshoot -If ArgoCD auth returns 401 or when the login attempt leads to the loop, then restart the argocd-server pod. -``` -kubectl rollout restart deployment argocd-server -n argocd -``` diff --git a/docs/operator-manual/user-management/microsoft.md b/docs/operator-manual/user-management/microsoft.md index 72a3a3ce77980..33a6b3e945940 100644 --- a/docs/operator-manual/user-management/microsoft.md +++ b/docs/operator-manual/user-management/microsoft.md @@ -1,16 +1,13 @@ # Microsoft -!!! note "" - Entra ID was formerly known as Azure AD. +* [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) -* [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) +## Azure AD SAML Enterprise App Auth using Dex +### Configure a new Azure AD Enterprise App -## 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` +1. From the `Azure Active Directory` > `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. @@ -34,9 +31,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 Entra ID Enterprise App +### Configure Argo to use the new Azure AD 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 Entra ID 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: data: url: https://my-argo-cd-url @@ -59,7 +56,7 @@ groupsAttr: Group 2. Edit `argocd-rbac-cm` to configure permissions, similar to example below. - - Use Entra ID `Group IDs` for assigning roles. + - Use Azure AD `Group IDs` for assigning roles. - See [RBAC Configurations](../rbac.md) for more detailed scenarios. # example policy @@ -73,11 +70,11 @@ p, role:org-admin, repositories, delete, *, allow g, "84ce98d1-e359-4f3b-85af-985b458de3c6", role:org-admin # (azure group assigned to role) -## Entra ID App Registration Auth using OIDC -### Configure a new Entra ID App registration -#### Add a new Entra ID App registration +## Azure AD App Registration Auth using OIDC +### Configure a new Azure AD App registration +#### Add a new Azure AD App registration -1. From the `Microsoft Entra ID` > `App registrations` menu, choose `+ New registration` +1. From the `Azure Active Directory` > `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`. @@ -95,29 +92,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 Entra ID App registration +#### Add credentials a new Azure AD 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 Entra ID Application +#### Setup permissions for Azure AD 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: - ![Entra ID API permissions](../../assets/azure-api-permissions.png "Entra ID API permissions") + ![Azure AD API permissions](../../assets/azure-api-permissions.png "Azure AD API permissions") 3. From the `Token Configuration` menu, choose `+ Add groups claim` - ![Entra ID token configuration](../../assets/azure-token-configuration.png "Entra ID token configuration") + ![Azure AD token configuration](../../assets/azure-token-configuration.png "Azure AD token configuration") -### Associate an Entra ID group to your Entra ID App registration +### Associate an Azure AD group to your 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. +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. 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 Entra ID App registration +### Configure Argo to use the new Azure AD App registration 1. Edit `argocd-cm` and configure the `data.oidc.config` and `data.url` section: @@ -133,7 +130,6 @@ requestedIDTokenClaims: groups: essential: true - value: "SecurityGroup" requestedScopes: - openid - profile @@ -161,7 +157,7 @@ p, role:org-admin, repositories, delete, *, allow g, "84ce98d1-e359-4f3b-85af-985b458de3c6", role:org-admin -4. Mapping role from jwt token to argo. +4. Mapping role from jwt token to argo If you want to map the roles from the jwt token to match the default roles (readonly and admin) then you must change the scope variable in the rbac-configmap. policy.default: role:readonly @@ -177,7 +173,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. -## Entra ID App Registration Auth using Dex +## Azure AD App Registration Auth using Dex Configure a new AD App Registration, as above. Then, add the `dex.config` to `argocd-cm`: @@ -204,9 +200,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 Microsoft Entra ID account. You’ll see the ArgoCD applications screen. +3. Click `LOGIN VIA AZURE` button to log in with your Azure Active Directory 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 Entra ID 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 Azure AD 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 4fd7ea0dc734c..09d7099d19954 100644 --- a/docs/operator-manual/user-management/okta.md +++ b/docs/operator-manual/user-management/okta.md @@ -1,7 +1,7 @@ # Okta !!! note "Are you using this? Please contribute!" - If you're using this IdP please consider [contributing](../../developer-guide/docs-site.md) to this document. + If you're using this IdP please consider [contributing](../../developer-guide/site.md) to this document. A working Single Sign-On configuration using Okta via at least two methods was achieved using: @@ -118,101 +118,34 @@ data: ## OIDC (without 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. +!!! 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) - 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. - 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: + 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. 1. On the `Okta Admin` page, navigate to the Okta API Management at `Security > API`. -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. - -### CLI login - -In order to login with the CLI `argocd login https://argocd.example.com --sso`, Okta requires a separate dedicated App Integration: - -1. Create a new `Create App Integration`, and choose `OIDC`, and then `Single-Page Application`. -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 `http://localhost:8085/auth/callback`. - 1. `Sign-out redirect URIs`: Add `http://localhost:8085`. - 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. - 1. Copy the `Client ID` from the newly created app; `cliClientID: ` will be used in your `argocd-cm` ConfigMap. -1. Edit your Authorization Server `Access Policies`: - 1. Navigate to the Okta API Management at `Security > API`. - 1. Choose your existing `Authorization Server` that was created previously. - 1. Click `Access Policies` > `Edit Policy`. - 1. Assign your newly created `App Integration` by filling in the text box and clicking `Update Policy`. - ![Edit Policy](../../assets/okta-auth-policy-edit.png) - -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: + ![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: ```yaml -url: https://argocd.example.com oidc.config: | name: Okta - # this is the authorization server URI - issuer: https://example.okta.com/oauth2/aus9abcdefgABCDEFGd7 - clientID: 0oa9abcdefgh123AB5d7 - cliClientID: gfedcba0987654321GEFDCBA # Optional if using the CLI for SSO - clientSecret: ABCDEFG1234567890abcdefg + issuer: https://yourorganization.oktapreview.com + clientID: 0oaltaqg3oAIf2NOa0h3 + clientSecret: ZXF_CfUc-rtwNfzFecGquzdeJ_MxM4sGc8pDT2Tg6t 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/onelogin.md b/docs/operator-manual/user-management/onelogin.md index fa417d42de308..21432d7312732 100644 --- a/docs/operator-manual/user-management/onelogin.md +++ b/docs/operator-manual/user-management/onelogin.md @@ -1,7 +1,7 @@ # OneLogin !!! note "Are you using this? Please contribute!" - If you're using this IdP please consider [contributing](../../developer-guide/docs-site.md) to this document. + If you're using this IdP please consider [contributing](../../developer-guide/site.md) to this document.
diff --git a/docs/operator-manual/web_based_terminal.md b/docs/operator-manual/web_based_terminal.md index 3fc5807586be1..5c791e9faa00f 100644 --- a/docs/operator-manual/web_based_terminal.md +++ b/docs/operator-manual/web_based_terminal.md @@ -13,20 +13,10 @@ Kubernetes), then the user effectively has the same privileges as that ServiceAc ## Enabling the terminal -1. In the `argocd-cm` ConfigMap, set the `exec.enabled` key to `"true"`. This enables the exec feature in Argo CD. - - ``` - apiVersion: v1 - kind: ConfigMap - metadata: - name: argocd-cm - namespace: # Replace with your actual namespace - data: - exec.enabled: "true" - ``` +1. Set the `exec.enabled` key to `"true"` on the `argocd-cm` ConfigMap. 2. Patch the `argocd-server` Role (if using namespaced Argo) or ClusterRole (if using clustered Argo) to allow `argocd-server` -to `exec` into pods +to exec into pods - apiGroups: - "" @@ -34,24 +24,14 @@ to `exec` into pods - pods/exec verbs: - create - If you'd like to perform the patch imperatively, you can use the following command: - - - For namespaced Argo - ``` - kubectl patch role -n argocd - type='json' -p='[{"op": "add", "path": "/rules/-", "value": {"apiGroups": ["*"], "resources": ["pods/exec"], "verbs": ["create"]}}]' - ``` - - For clustered Argo - ```` - kubectl patch clusterrole - type='json' -p='[{"op": "add", "path": "/rules/-", "value": {"apiGroups": ["*"], "resources": ["pods/exec"], "verbs": ["create"]}}]' - ``` -3. Add RBAC rules to allow your users to `create` the `exec` resource i.e. - p, role:myrole, exec, create, */*, allow +3. Add RBAC rules to allow your users to `create` the `exec` resource, i.e. - This can be added either to the `argocd-cm` `Configmap` manifest or an `AppProject` manifest. + p, role:myrole, exec, create, */*, allow - See [RBAC Configuration](rbac.md#exec-resource) for more info. + +See [RBAC Configuration](rbac.md#exec-resource) for more info. ## Changing allowed shells diff --git a/docs/operator-manual/webhook.md b/docs/operator-manual/webhook.md index 92789e983d3b3..9275817a9e084 100644 --- a/docs/operator-manual/webhook.md +++ b/docs/operator-manual/webhook.md @@ -99,13 +99,3 @@ stringData: ``` 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/application-name-identifier.md b/docs/proposals/application-name-identifier.md index 0554c4139b817..3d425e9432dbc 100644 --- a/docs/proposals/application-name-identifier.md +++ b/docs/proposals/application-name-identifier.md @@ -31,7 +31,7 @@ managed (i.e. reconciled from Git). The default label used is the well-known label `app.kubernetes.io/instance`. This proposal suggests to introduce the `trackingMethod` setting that allows -controlling how application resources are identified and allows switching to +controlling how applicaton resources are identified and allows switching to using the annotation instead of `app.kubernetes.io/instance` label. ## Motivation diff --git a/docs/proposals/applicationset-plugin-generator.md b/docs/proposals/applicationset-plugin-generator.md index 616ef13efcd2b..6a3b2ec484c8a 100644 --- a/docs/proposals/applicationset-plugin-generator.md +++ b/docs/proposals/applicationset-plugin-generator.md @@ -89,7 +89,7 @@ data: 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). +- token is used a 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 diff --git a/docs/proposals/config-management-plugin-v2.md b/docs/proposals/config-management-plugin-v2.md index d0d2b993c7d08..549ed3967ef49 100644 --- a/docs/proposals/config-management-plugin-v2.md +++ b/docs/proposals/config-management-plugin-v2.md @@ -21,7 +21,6 @@ for using additional tools such as cdk8s, Tanka, jkcfg, QBEC, Dhall, pulumi, etc ## Summary Currently, Argo CD provides first-class support for Helm, Kustomize and Jsonnet/YAML. The support includes: - - Bundled binaries (maintainers periodically upgrade binaries) - An ability to override parameters using UI/CLI - The applications are discovered in Git repository and auto-suggested during application creation in UI @@ -42,7 +41,6 @@ The goals for config management plugin enhancement are, #### Improve Installation Experience The current Config Management plugin installation experience requires two changes: - - An entry in configManagementPlugins in the Argo CD configmap (i.e. argocd-cm) - Either an init container with a volume mount that adds a new binary into Argo CD repo server pod, or a rebuild of the argocd image, which contains the necessary tooling @@ -68,14 +66,13 @@ to additional config management tools. ### Non-Goals -- We aren't planning on changing the existing support for native plugins as of now. +* We aren't planning on changing the existing support for native plugins as of now. ## Proposal We have drafted the solution to the problem statement as **running configuration management plugin tools as sidecar in the argocd-repo-server**. All it means that Argo CD Config Management Plugin 2.0 will be, - - A user-supplied container image with all the necessary tooling installed in it. - It will run as a sidecar in the repo server deployment and will have shared access to the git repositories. - It will contain a CMP YAML specification file describing how to render manifests. @@ -83,7 +80,6 @@ All it means that Argo CD Config Management Plugin 2.0 will be, based on the CMP specification file. This mechanism will provide the following benefits over the existing solution, - - Plugin owners control their execution environment, packaging whatever dependent binaries required. - An Argo CD user who wants to use additional config management tools does not have to go through the hassle of building a customized argocd-repo-server in order to install required dependencies. @@ -91,9 +87,9 @@ a customized argocd-repo-server in order to install required dependencies. ### Use cases -- UC1: As an Argo CD user, I would like to use first-class support provided for additional tools to generate and manage deployable kubernetes manifests -- UC2: As an Argo CD operator, I want to have smooth experience while installing additional tools such as cdk8s, Tanka, jkcfg, QBEC, Dhall, pulumi, etc. -- UC3: As a plugin owner, I want to have some control over the execution environment as I want to package whatever dependent binaries required. +* UC1: As an Argo CD user, I would like to use first-class support provided for additional tools to generate and manage deployable kubernetes manifests +* UC2: As an Argo CD operator, I want to have smooth experience while installing additional tools such as cdk8s, Tanka, jkcfg, QBEC, Dhall, pulumi, etc. +* UC3: As a plugin owner, I want to have some control over the execution environment as I want to package whatever dependent binaries required. ### Implementation Details @@ -147,7 +143,6 @@ volumes: Plugins will be configured via a ConfigManagementPlugin manifest located inside the plugin container, placed at a well-known location (e.g. /home/argocd/plugins/plugin.yaml). Argo CD is agnostic to the mechanism of how the plugin.yaml would be placed, but various options can be used on how to place this file, including: - - Baking the file into the plugin image as part of docker build - Volume mapping the file through a configmap. @@ -267,27 +262,26 @@ volumes: After upgrading to CMP v2, an Argo CD operator will have to make following changes, -- In order to install a plugin, an Argo CD operator will simply have to patch argocd-repo-server +* In order to install a plugin, an Argo CD operator will simply have to patch argocd-repo-server to run config management plugin container as a sidecar, with argocd-cmp-server as it’s entrypoint: - ```bash - # A plugin is a container image which runs as a sidecar, with the execution environment - # necessary to render manifests. To install a plugin, - containers: - - name: cdk8s - command: [/var/run/argocd/argocd-cmp-server] - image: docker.ui/cdk8s/cdk8s:latest - volumeMounts: - - mountPath: /var/run/argocd - name: var-files - ``` - -- Plugins will be configured via a ConfigManagementPlugin manifest located inside the plugin container, placed at a +```bash +# A plugin is a container image which runs as a sidecar, with the execution environment +# necessary to render manifests. To install a plugin, +containers: +- name: cdk8s + command: [/var/run/argocd/argocd-cmp-server] + image: docker.ui/cdk8s/cdk8s:latest + volumeMounts: + - mountPath: /var/run/argocd + name: var-files +``` + +* Plugins will be configured via a ConfigManagementPlugin manifest located inside the plugin container, placed at a well-known location (e.g. /plugin.yaml). Argo CD is agnostic to the mechanism of how the plugin.yaml would be placed, but various options can be used on how to place this file, including: - Baking the file into the plugin image as part of docker build - Volume mapping the file through a configmap. - (For more details please refer to [implementation details](#configuration)) ## Drawbacks @@ -296,34 +290,34 @@ 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. -- The format of the CMP manifest is a new "contract" that would need to adhere the usual Argo CD compatibility promises in future. +* 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. +* The format of the CMP manifest is a new "contract" that would need to adhere the usual Argo CD compatibility promises in future. + ## Alternatives 1. ConfigManagementPlugin as CRD. Have a CR which the human operator creates: - ```bash - apiVersion: argoproj.io/v1alpha1 - kind: ConfigManagementPlugin - metadata: - name: cdk8s - spec: - name: cdk8s - image: docker.ui/cdk8s/cdk8s:latest - version: v1.0 - init: - command: [cdk8s, init] - generate: - command: [sh, -c, "cdk8s synth && cat dist/*.yaml"] - discovery: - find: - - command: [find . -name main.ts] - glob: "**/*/main.ts" - check: - - command: [-f ./main.ts] - glob: "main.ts" - ``` - +```bash +apiVersion: argoproj.io/v1alpha1 +kind: ConfigManagementPlugin +metadata: + name: cdk8s +spec: + name: cdk8s + image: docker.ui/cdk8s/cdk8s:latest + version: v1.0 + init: + command: [cdk8s, init] + generate: + command: [sh, -c, "cdk8s synth && cat dist/*.yaml"] + discovery: + find: + - command: [find . -name main.ts] + glob: "**/*/main.ts" + check: + - command: [-f ./main.ts] + glob: "main.ts" +``` 2. Something magically patches the relevant manifest to add the sidecar. diff --git a/docs/proposals/decouple-application-sync-user-using-impersonation.md b/docs/proposals/decouple-application-sync-user-using-impersonation.md deleted file mode 100644 index 050c8d6b0a635..0000000000000 --- a/docs/proposals/decouple-application-sync-user-using-impersonation.md +++ /dev/null @@ -1,641 +0,0 @@ ---- -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. - -```shell -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 sync operation would fail with an appropriate error message. Users can configure a catch all service account matching all destinations to avoid such sync errors. -- 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. -- The feature can be enabled/disabled only at the system level. Once enabled/disabled, it is applicable to all ArgoCD `Applications`. - -### 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 sync operation will fail with an error. Users can configure a catch all service account matching all destinations to avoid such sync errors. - -```yaml -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 - - server: '* - namespace: '*' - defaultServiceAccount: default # catch all service account to be used when all other matches fail. -``` - -### 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 `application.sync.impersonation.enabled: "true"` in the Argo CD ConfigMap. Default value of `application.sync.impersonation.enabled` would be `"false"` and user has to explicitly override it to use this feature. -- 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. -```shell -kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/install.yaml -n argocd -``` - -- Enable the impersonation feature in ArgoCD. -```shell -kubectl patch cm argocd-cm -n argocd --type json --patch '[{ "op": "add", "path": "/data/application.sync.impersonation.enabled", "value": "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`. -```shell -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 -```yaml -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. -```shell -kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/install.yaml -n argocd -``` - -- Enable the impersonation feature in ArgoCD. -```shell -kubectl patch cm argocd-cm -n argocd --type json --patch '[{ "op": "add", "path": "/data/application.sync.impersonation.enabled", "value": "true" }]' -``` - -- Create a namespace called `guestbook` and a service account called `guestbook-deployer`. -```shell -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`. -```shell -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`. -```yaml -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. -```shell -kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/install.yaml -n argocd -``` - -- Enable the impersonation feature in ArgoCD. -```shell -kubectl patch cm argocd-cm -n argocd --type json --patch '[{ "op": "add", "path": "/data/application.sync.impersonation.enabled", "value": "true" }]' -``` - -- Add the remote cluster as a destination to argocd -```shell -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`. -```shell -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`. - -```shell -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. -```yaml -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 - 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. -```shell -kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/install.yaml -n argocd -``` - -- Enable the impersonation feature in ArgoCD. -```shell -kubectl patch cm argocd-cm -n argocd --type json --patch '[{ "op": "add", "path": "/data/application.sync.impersonation.enabled", "value": "true" }]' -``` - -- In the remote cluster, create a service account called `argocd-admin` -```shell -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`. -```shell -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`. -```shell -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`. -```yaml -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: -```yaml - ... - 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. -```yaml - ... - 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`. - -#### Application does not have a `spec.destination.namespace` field -`spec.destination.namespace` is an optional field in an `Application`. If the user does not specify it, the application controller will use the service account in the Application's namespace for the sync operation. User's also have the option of specifying the service account along with its namespace, in which case the service account in the user specified namespace will be used for the sync operation. - -eg: - -```yaml -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 ---- -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 -``` -In the above example, since `spec.destination.namespace` is not specified, Application's namespace `argocd` is used for scoping the service account. So the service account `system:serviceaccount:argocd:guestbook-deployer` will be used for the sync operation. - -In the above example, If the matching service account is specified with a namespace, eg: `guestbook:guestbook-deployer`, then the service account `system:serviceaccount:guestbook:guestbook-deployer` will be used for the sync operation. - -### 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. - -```yaml -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/images/current-summary-tab.png b/docs/proposals/images/current-summary-tab.png deleted file mode 100644 index b9934ea592f36a7cdd3ce147973c7d05e7312566..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115933 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

ZHN<=`1HW(}+nBJVT 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%0DdRh{`v>Fg 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 IcOE_aUv+h;f&c&j diff --git a/docs/proposals/images/history-and-rollback-button.png b/docs/proposals/images/history-and-rollback-button.png deleted file mode 100644 index bea82323a1e4d80ec4d776c80dc53b3696b35ce0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20446 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 diff --git a/docs/proposals/images/history-rollback-contents.png b/docs/proposals/images/history-rollback-contents.png deleted file mode 100644 index 3070a8e19d6279554dde26a3ca2a6333fd308eb4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128640 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ⅆ||#n4jH(M8z`oGK|1Ut9w)dp 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 JYTdZ|zX0#1RZIW? diff --git a/docs/proposals/multiple-sources-for-applications-ui.md b/docs/proposals/multiple-sources-for-applications-ui.md deleted file mode 100644 index 09b868db0d5ef..0000000000000 --- a/docs/proposals/multiple-sources-for-applications-ui.md +++ /dev/null @@ -1,226 +0,0 @@ ---- -title: Proposal for support multi-source apps in the UI -authors: - - "@keithchong" -sponsors: - - TBD -reviewers: - - "@alexmt" - - "@crenshaw-dev" - - "@ishitasequeira" - - "@jannfis" - - "@rbreeze" -approvers: - - "@jannfis" - - "@alexmt" - - "@crenshaw-dev" - -creation-date: 2024-02-06 -last-updated: 2024-02-06 ---- - -# UI Support for Multiple Sources in Applications - -This is the proposal for the UI changes to support multiple sources for an Application. - -Related Issues: -* [Proposal: Support multiple sources for an application](https://github.com/argoproj/argo-cd/blob/master/docs/proposals/multiple-sources-for-applications.md) -* [Issue for the Proposal: Support multiple sources for an application](https://github.com/argoproj/argo-cd/issues/677) - -## Summary - -This is a follow-on proposal to supporting Multiple Sources for Applications, but for the UI. - -The above [original](https://github.com/argoproj/argo-cd/blob/master/docs/proposals/multiple-sources-for-applications.md#changes-to-ui) ‘core’ proposal deferred -any design changes for the UI to a separate feature or secondary proposal. The proposal implementation that was made in [PR 10432](https://github.com/argoproj/argo-cd/pull/10432) -enabled the UI to tolerate multi-source applications with the new Sources field, while still supporting the original Source field. - -Here are the current restrictions and limitations of the UI when applications with multiple sources are used: - -1. The application’s details page (for [example](https://cd.apps.argoproj.io/applications/argocd/guestbook?view=tree&node=argoproj.io%2FApplication%2Fargocd%2Fguestbook%2F0&resource=)) -currently shows one ApplicationSource, regardless of whether the application has one source or multiple sources. With the PR 10432 implementation, if the application has multiple sources, -the UI displays only the first of the sources. Also, in particular, in the Summary tab, the source parameters are non-editable. - -2. History and Rollback is disabled for multi-source applications. The button is disabled. Jorge has submitted a PR for -rollback which includes [controller and UI changes](https://github.com/argoproj/argo-cd/pull/14124). - - - -3. The New Application dialog currently only allows users to provide one source. - -Thus, multiple source applications are not considered first class citizens in the UI. - -Note, see the [Open Questions](https://github.com/argoproj/argo-cd/docs/proposals/multiple-sources-for-applications-ui.md#open-questions) -section for concerns regarding the priority or value of some of the above changes. - -## Motivation - -The motivation behind this change is to add a more complete story for the multiple source feature. The UI should support -the creation of multiple source applications, and also support the viewing and editing of parameters from all sources. The three -points in the summary above are the base or core changes that need to be addressed. - -### Goals - -The goals of the proposal are: - -- Provide first-class support of multiple sources for applications in the UI (e.g. address the aforementioned restrictions) -- Outline stages of implementation that will help ease PR review, and reduce the risk of introducing regressions/issues. - - -### Non-goals -* The design changes for the Argo CD CLI is beyond the scope of this proposal (The server APIs can probably be reused) - -## Proposal - -As mentioned in the previous summary section, the application source parameters are surfaced in the UI in three locations. -The Resource details pages, specifically, the Summary and Parameters tabs, the deployment history, and the Application -Create panel page. These pages should be updated. - -### Resource Details - -The following describes the current behavior and proposed changes for the Summary tab and the Parameters Tab. - -#### i) Summary Tab - -_Current Behavior:_ - -The current Summary tab includes source-related information, including the repository. For example, in Figure 1 below, -the REPO URL and PATH. - - - -Figure 1: The current Summary tab - -_Proposed Change:_ - -To support multiple sources, the source-related information, from a single-source-based design, will be ‘pulled out’ -and put into a new tab called **Sources**, and it will be combined with the **Parameters** tab (more details following). -The new **Sources** tab will allow users to view all the information related to each source, including the repo URL -and path, chart and revision for Helm, etc. - -The view should show one source at a time (similar to what the UI is doing now, which only shows one source), but with -widgets to allow users to cycle (via pagination or combo selector?) through each source. There are API calls to retrieve -the data for each source. - - - -Figure 2. The new SOURCES tab will allow access to view all sources and application parameters. - -#### ii) Parameters Tab -_Current Behavior:_ - -The Parameters tab shows the application parameters for the application’s repository details type or source. These can -be Helm, Kustomize, Directory or Plugin (CMP). - -_Proposed Change:_ - -The Parameter tab will be removed but the contents of the current parameters tab will be ‘reused’ and will be shown in -the new **SOURCES** tab as described above. The parameters and parameter values will be shown for whatever source is -selected by the user. - -#### iii) Update/Edit Capability in the New Sources Tab - -The above points describe how all the sources will be rendered. However, the Sources tab should be the page to allow -users to delete and add sources. (You can currently change the repo URL and path from the Summary tab, or manually edit -the application by hand, in the Manifest tab, but this is not considered as ‘guided’ editing.) - -_Current Behavior:_ - -The current form-based UI doesn’t support deleting a chosen/desired source of a multi-source application. It, -obviously, does not support deleting the only source in a single-source application. - -_Proposed Change:_ - -In addition to adding the new SOURCES tab from section i) and ii), two new buttons (_Add Source_ and _Delete Source_) will -be added to the page. For the _Add Source_ button, a separate dialog/panel will need to appear to allow the user to -input the parameters or other information. - -Validation of any newly added source should prevent users from adding the same resource, and prevent users from -deleting all sources, etc. - -### History and Rollback - -Current Behavior: The History and Rollback button for multi-source apps is disabled. It's only enabled -for single-source apps, and shows source information as shown in Figure 3. - - - -Figure 3: Source information in History - -Jorge has submitted a [PR](https://github.com/argoproj/argo-cd/pull/14124) for rollback which includes controller and UI changes. -This can be treated as a separate, independent proposal. - -Other related changes pertain to the Last Synced Details. The Sync Details panel needs to be updated to show sync info -from multiple sources. See [Issue 13215](https://github.com/argoproj/argo-cd/issues/13215). - -### New App Dialog - -_Current Behavior:_ - -The dialog currently allows users to ‘quickly’ create a single source application.. - -_Proposed Changes:_ - -Make the form view of the dialog support adding, updating and viewing of multiple sources. The issue with the current -single source New App wizard is that it can lead to loss of “input” provided by the user. The content in the form-based -editor and the YAML editor (accessed via the Edit as YAML button) must match. If the user provides multiple sources in -the YAML editor, and then switches back to the form view, the form will only show the first source. The other sources -are effectively ‘lost’. Furthermore, if the user switches back to the YAML editor, only one source will be shown as well. - -The design and changes (React components) from the new Sources tab can likely be reused in this dialog. - -Other Changes. This includes the underlying plumbing to create an app using the Sources field of the Application CR, so that the -deprecated Source field can be removed in the future. - - - -### Use cases - -The use cases involves those areas in the UI where the current source is displayed. These have been described -in the Summary and Proposal sections. - - -### Implementation Details - -The implementation plan can be divided into different stages. Read-only capability can be provided first and it will -be the safest change. The UI currently is not showing all the sources for the multi-source application so this should -be the highest priority. (Before you can edit, you have to first display it.) - -Here are the general enhancements to be implemented (Upstream issues to be opened if not already): - -1. Create new Sources tab to replace Parameters tab so that all sources can be displayed (Read-only) -2. Update History and Rollback to show a summary of all sources of an application - As mentioned above, this is already covered by Jorge’s [PR](https://github.com/argoproj/argo-cd/pull/14124) -3. Add _Add Source_ and _Delete Source_ buttons to Sources tab. This will depend on #1 above. (Update and Delete) -4. Update New App dialog. (Creation) - - Support adding multiple sources in New App dialog. (This will likely depend on the Components from #1 and #3) - - Use Sources field instead of Source field. Clean up code. - -### Security Considerations -None - -### Risks and Mitigations -None - -### Upgrade / Downgrade Strategy -If downgraded, the UI will revert to showing just the first source. - -## Drawbacks -None - -## Open Questions - -Supporting multiple sources in the New App dialog may not be ‘worth’ the effort? The drawback is that switching from the -YAML editor and form editor can lead to loss of information. - -Users can simply edit the application manifest to add their sources by hand. - - -## Appendix -Multiple sources can be shown as a list of collapsible cards or sections, one below the other, under one page of the -SOURCES tab. However, this can be cumbersome especially when a source, like Helm, has many source parameters. -so it'll be difficult to find the desired source. Perhaps showing one source per page will be better. - -Appendix Figure 1: Zoomed out view of the Helm source parameter list - - diff --git a/docs/proposals/native-oci-support.md b/docs/proposals/native-oci-support.md index 7ec0053729c2e..64918fde8904e 100644 --- a/docs/proposals/native-oci-support.md +++ b/docs/proposals/native-oci-support.md @@ -126,10 +126,10 @@ Consider the following in developing an upgrade/downgrade strategy for this enha ## 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. +* 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 registires 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. +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. \ No newline at end of file diff --git a/docs/proposals/project-scoped-repository-enhancements.md b/docs/proposals/project-scoped-repository-enhancements.md deleted file mode 100644 index 85b0251326d71..0000000000000 --- a/docs/proposals/project-scoped-repository-enhancements.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -title: Project scoped repository credential enhancements -authors: - - "@blakepettersson" -sponsors: - - TBD -reviewers: - - "@alexmt" - - "@jsoref" - - "@christianh814" - - "@wanghong230" - - "@yyzxw" -approvers: - - "@alexmt" - -creation-date: 2024-05-17 -last-updated: 2024-06-04 ---- - -# Project scoped repository credential enhancements - -## Summary - -This is to allow the possibility to have multiple repository credentials which share the same URL. Currently, multiple repository -credentials sharing the same URL is disallowed by the Argo CD API. - -## Motivation - -This is to allow the possibility to have multiple repository credentials which share the same URL. Currently, multiple repository -credentials sharing the same URL is disallowed by the Argo CD API. If the credentials are added directly to the `argocd` -namespace, we "get around" `argocd-server` returning an error, but this still does not work since the first secret that -matches a repository URL is the one that gets returned, and the order is also undefined. - -The reason why we want this is due to the fact that in a multi-tenant environment, multiple teams may want to -independently use the same repositories without needing to ask an Argo CD admin to add the repository for them, and then -add the necessary RBAC in the relevant `AppProject`s to prevent other teams from having access to the repository -credentials. In other words, this will enable more self-service capabilities for dev teams. - -### Goals - -The goal of this proposal is to allow multiple app projects to have the ability to have separate repository credentials -which happen to share the same URL. - -### Non-Goals - -- Having multiple repository secrets sharing the same URL _within the same_ `AppProject`. -- Allowing a single repository credential to be used in multiple `AppProject`s. -- Preventing non project-scoped repository credentials from being used by an Application. -- Extending this to repository credential templates. - -## Proposal - -There are a few parts to this proposal. - -We need to distinguish between a user accessing a repository via the API/CLI/UI and an application retrieving repository -credentials. In the first case, we need to maintain backwards compatibility for API consumers. The current behaviour -is that the API will return the first repository found matching the URL given. Since we now want to allow the same URL -to potentially be in multiple projects, we need to do some minor changes. - -* If there is only one matching repository with the same URL, and assuming the user is allowed to access it _and_ there is -no app project given as a parameter, use that repository ignoring any project-scope. This is in line with the -current behavior. -* If there is only one matching repository with the same URL, and assuming the user is allowed to access it _and_ there is -an app project given as a parameter, use that repository only if it also matches the app project given. -* If there are multiple repositories with the same URL and assuming the user is allowed to access them, then setting a -project parameter would be required, since there would otherwise be no way to determine which of the credentials a user -wants to access. This is not a breaking change since this adds functionality which has previously not existed. - -This change would apply when we retrieve a _single_ repository credential, or when we delete a repository credential. -For listing repository credentials, nothing changes - the logic would be the same as today. - -In the case of selecting a suitable repository for an application, the logic would differ slightly. What instead happens -is that the lookup would first attempt to find the first `repository` secret which matches the `project` -and repository URL of the requesting application. If there are no credentials which match the requested `project`, it -will fall back to returning the first unscoped credential, i.e, the first credential with an empty `project` parameter. - -When it comes to mutating a repository credential we need to strictly match the project to which the repository belongs, since -there would otherwise be a risk of changing (inadvertently or otherwise) a credential not belonging to the correct project. -This can be done without any breaking changes. - -The second part is specifically for when we imperatively create repository secrets. Currently, when we create a repository -secret in the UI/CLI, a suffix gets generated which is a hash of the repository URL. This mechanism will be extended to -also hash the repository _project_. - -On the API server side no major changes are anticipated to the public API. The only change we need to do from the API -perspective is to add an `appProject` parameter when retrieving or deleting a repository credential. To preserve backwards -compatibility this option is optional and would only be a required parameter if multiple repository credentials are -found for the same URL. - -Finally, we need to change the way the cache keys for the repository paths are generated in the repo-server -(see `Security Considerations`). - -### Security Considerations - -* Special care needs to be taken in order not to inadvertently expose repository credentials belonging to other `AppProject`s. -Access to repositories are covered by RBAC checks on the project, so we should be good. -* We need to change how the cache keys for the checked out repository paths are generated on the repo-server side, the -reason being that we do not want separate `AppProject`s sharing the same paths of sources which have been downloaded. -With this change there is a potential for multiple `AppProject`s to have rendered/downloaded different manifests due to -having different sets of credentials, so to mitigate that we need to check out a separate copy of the repository per -`AppProject`. - -### Risks and Mitigations - -### Upgrade / Downgrade Strategy - -When upgrading no changes need to happen - the repository credentials will work as before. On the other hand, when -downgrading to an older version we need to consider that the existing order in which multiple credentials with the same -URL gets returned is undefined. This means that deleting the credentials before downgrading to an older version would be -advisable. - -## Drawbacks - -* It will be more difficult to reason about how a specific repository credential gets selected. There could be scenarios -where a repository has both a global repository credential and a scoped credential for the project to which the -application belongs. -* There will be more secrets proliferating in the `argocd` namespace. This has the potential to increase maintenance burden -to keeping said secrets safe, and it also makes it harder to have a bird's eye view from an Argo CD admin's perspective. -* Depending on the number of projects making use of distinct credentials for the same repository URL, loading the correct -credentials from the repository secrets has the potential to scale linearly with the number of app projects (in the worst case -scenario we would need to loop through all the credentials before finding the correct credential to load). This is likely -a non-issue in practice. -* Also depending on the number of projects making use of distinct credentials for the same repository URL, this will -imply that for each `AppProject` sharing the same repository URL, a separate copy of the repository will be checked out. -This has potential implications in terms of memory consumption, sync times, CPU load times etc. This is something -of which an Argo CD admin will need to be mindful. - -## Alternatives - -To keep the existing behavior of having a single repository credential shared by multiple `AppProject`s. It would be up -to the Argo CD admins to ensure that a specific repository credential cannot be used by unauthorized parties. \ No newline at end of file diff --git a/docs/proposals/resource-deletion-with-approval.md b/docs/proposals/resource-deletion-with-approval.md deleted file mode 100644 index 4d843ce08fa38..0000000000000 --- a/docs/proposals/resource-deletion-with-approval.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -title: Neat-enhancement-idea -authors: -- "@alexmt" - sponsors: -- TBD - reviewers: -- "@jessesuen" -- TBD - approvers: -- "@jessesuen" -- TBD - -creation-date: 2020-04-19 -last-updated: 2020-04-19 - ---- -# Neat Enhancement Idea - -Support manual approval for pruning and deleting Kubernetes resources during application syncing/deletion. - -## Summary - -Introduce Kubernetes resource-level annotations that require manual user approval using Argo CD UI/CLI/API before the -resource is pruned or deleted. The annotations should be respected while Argo CD attempts to synchronize or delete the -application. - -## Motivation - -We’ve seen cases where Argo CD deleted Kubernetes resources due to a bug or misconfiguration.​ Examples include [corrupted -data](https://github.com/argoproj/argo-cd/issues/4423) in Redis, user errors -([1](https://github.com/argoproj/argo-cd/issues/9093), [2](https://github.com/argoproj/argo-cd/issues/4844)) -and [bug](https://github.com/argoproj/argo-cd/issues/3473) in the automation on top of Argo CD. These examples don’t -mean Argo CD is not reliable; however, there are cases where misbehavior is catastrophic, and erroneous deletion is not -acceptable. Examples include the app-of-apps pattern where Argo CD is used to manage itself, or namespaces in production -clusters. - -### Goals - -The goals of a proposal ares: - -#### Allow developers to mark resources that require manual approval before application deletion. - -Developers should be able to add an annotation to resources that require manual approval before deletion. The annotation -should be respected by Argo CD when it attempts to delete the application. - -#### Allow developers to mark resources that require manual approval before pruning - -Developers should be able to add an annotation to resources that require manual approval before pruning. The annotation -should be respected by Argo CD when it attempts to prune extra resources while syncing the application. - -### Non-Goals - -#### Implement automatic self check while deleting resources - -We've made our best effort to implement corrected behavior, and as of now, we are not aware of any bugs that cause -erroneous deletion. The goal of this proposal is to provide a safety net for cases where deletion is not acceptable. - -## Proposal - -It is proposed to introduce two new sync options for Argo CD applications: `Prune=confirm` and `Delete=confirm`. Options would -protect resources from accidental deletion during cascading application deletion as well as during sync operations. - -### Introduce `confirm` option for Prune sync option. - -Argo CD already supports `argocd.argoproj.io/sync-options: Prune=false` sync option that prevents resource deletion while syncing -the application. This, however, is not ideal since it prevents implementing fully automated workflows that include resource deletion. - -In order to improve the situation, we propose to introduce `confirm` option for Prune sync option. When `confirm` option is set, Argo CD should pause the sync operation -**before deleting any app resources** and wait for the user to confirm the deletion. The confirmation can be done in a very friendly way using Argo CD UI, CLI or API. - -* **Sync Operation status**. I suggest not to introduce new sync operation states to avoid disturbing the existing automation around syncing (CI pipelines, scripts etc). - If Argo CD is waiting for the operation state should remain `Progressing`. Once the user confirms the deletion, the operation should resume. -* **Sync Waves**. The sync wave shuold be "paused" while Argo CD is waiting for the user to confirm the deletion. No difference from waiting for the resource to became healthy. - -### Introduce `confirm` option for Delete sync option. - -Similarly to `Prune` sync option we need to introduce `confirm` value for `Delete` sync option: `argocd.argoproj.io/sync-options: Delete=confirm`. The `confirm` option -should pause the sync operation **before deleting any app resources** and wait for the user to confirm the deletion. The confirmation can be done in a very friendly way -using Argo CD UI, CLI or API. - - -### Friendly prunning/deletion manual approval - -Since we know Argo CD is often used to implement fully automated developer workflows that include resource deletion, the -deletion approval process should be as painless as possible. This way, platform administrators can instruct end users to -apply the new prune/delete option to resources that require special care without significantly disturbing the developer -experience. - -In both cases where Argo CD requires manual approval, the user should be able to approve the deletion using Argo CD UI, -CLI, or API. The approval process should be as simple as possible and should not require the user to understand the -internals of Argo CD. - -#### New `requiresDeletionApproval` resource field in application status - -A new field `requiresDeletionApproval` should be added to the `status.resources` list items. The field should be set to `true` when the resource deletion approval is required. - -```yaml - - health: - status: Healthy - kind: Service - name: guestbook-ui - namespace: default - status: OutOfSync - version: v1 - requiresPruning: true - requiresDeletionApproval: true # new field that indicates that deletion approval is required -``` - -The Argo CD UI, CLI should visualize the `requiresDeletionApproval` field so that the user can easily discover which resources require manual approval. - -#### Approve deletion resource action - -The Argo CD UI, CLI should bundle the `Approve Deletion` [resource action](https://argo-cd.readthedocs.io/en/stable/operator-manual/resource_actions/) -that would allow the user to approve the deletion. The action should patch the resource with the `argocd.argoproj.io/deletion-approved: true` annotation. -Once annotation is applied the Argo CD should proceed with the deletion. - -The main reason to use the action is that we can reuse existing [RBAC](https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/) to control who can approve the deletion. - -#### UI/CLI Convinience to approve all resources - -The Argo CD UI should provide a convinient way to approve resources that require manual approval. The existing user interface will provide a button that allows end user -execute the `Approve Deletion` action and approve resources one by one. In addition to the single resource approval, the UI should provide a way to approve all resources -that require manual approval. The new button should execute the `Approve Deletion` action for all resources that require manual approval. - -Argo CD CLI would no need changes since existing `argocd app actions run` command allows to execute an action against multiple resources. - -#### Require deletion approval notification - -The default Argo CD notification catalog should include a trigger and notification template that notifies the user when -deletion approval is required. The notification template should include a list of resources that require approval. - - -#### Declarative approval - -The user should be able to approve resource deletion without using the UI or CLI by manually adding the `argocd.argoproj.io/deletion-approved: true` annotation to the resource. - -### Use cases - -Add a list of detailed use cases this enhancement intends to take care of. - -## Use case 1: - -As a developer, I would like to mark resources that require manual pruning approval so I can prevent the accidental deletion of critical resources. - -## Use case 2: - -As a developer, I would like to mark resources that require manual deletion approval so I can prevent the accidental deletion of critical resources. - - -### Security Considerations - -The resource approval would require a mechanism to control who can approve the deletion. The proposal to use -resource-level actions solves this problem and allows us to reuse the existing RBAC model. - -### Risks and Mitigations - -None. - -### Upgrade / Downgrade Strategy - -In case of rollback to the previous version the sync option would be ignored and the resources would be deleted as before. - -## Open Issues - -The proposal would require end users to learn about the new behavior and adjust their workflows. It includes a set of -enhancements aimed at minimizing the impact on end users. - -## Alternatives - -None. \ No newline at end of file diff --git a/docs/proposals/sync-timeout.md b/docs/proposals/sync-timeout.md deleted file mode 100644 index 5d8e5c3b3d86d..0000000000000 --- a/docs/proposals/sync-timeout.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -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 26b5dc2049e05..5ffcd4ff0221b 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,9 +1,7 @@ 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.15.1 -jinja2==3.1.4 +pygments==2.15.0 +jinja2==3.0.3 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 b9e7582682786..984cd3460c17d 100644 --- a/docs/snyk/index.md +++ b/docs/snyk/index.md @@ -13,66 +13,63 @@ recent minor releases. | | Critical | High | Medium | Low | |---:|:--------:|:----:|:------:|:---:| -| [go.mod](master/argocd-test.html) | 0 | 0 | 1 | 0 | -| [ui/yarn.lock](master/argocd-test.html) | 0 | 0 | 1 | 0 | -| [dex:v2.41.1](master/ghcr.io_dexidp_dex_v2.41.1.html) | 0 | 0 | 0 | 1 | -| [haproxy:2.6.17-alpine](master/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html) | 0 | 0 | 2 | 3 | -| [redis:7.0.15-alpine](master/public.ecr.aws_docker_library_redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 | -| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 3 | 8 | -| [redis:7.0.15-alpine](master/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 | +| [go.mod](master/argocd-test.html) | 0 | 0 | 6 | 0 | +| [ui/yarn.lock](master/argocd-test.html) | 0 | 0 | 0 | 0 | +| [dex:v2.37.0](master/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 0 | 3 | 1 | +| [haproxy:2.6.14-alpine](master/haproxy_2.6.14-alpine.html) | 0 | 0 | 0 | 1 | +| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 4 | 16 | +| [redis:7.0.11-alpine](master/redis_7.0.11-alpine.html) | 1 | 0 | 3 | 1 | | [install.yaml](master/argocd-iac-install.html) | - | - | - | - | | [namespace-install.yaml](master/argocd-iac-namespace-install.html) | - | - | - | - | -### v2.13.0-rc2 +### v2.9.0-rc3 | | Critical | High | Medium | Low | |---:|:--------:|:----:|:------:|:---:| -| [go.mod](v2.13.0-rc2/argocd-test.html) | 0 | 0 | 1 | 0 | -| [ui/yarn.lock](v2.13.0-rc2/argocd-test.html) | 0 | 0 | 1 | 0 | -| [dex:v2.41.1](v2.13.0-rc2/ghcr.io_dexidp_dex_v2.41.1.html) | 0 | 0 | 0 | 1 | -| [haproxy:2.6.17-alpine](v2.13.0-rc2/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html) | 0 | 0 | 2 | 3 | -| [redis:7.0.15-alpine](v2.13.0-rc2/public.ecr.aws_docker_library_redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 | -| [argocd:v2.13.0-rc2](v2.13.0-rc2/quay.io_argoproj_argocd_v2.13.0-rc2.html) | 0 | 0 | 3 | 8 | -| [redis:7.0.15-alpine](v2.13.0-rc2/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 | -| [install.yaml](v2.13.0-rc2/argocd-iac-install.html) | - | - | - | - | -| [namespace-install.yaml](v2.13.0-rc2/argocd-iac-namespace-install.html) | - | - | - | - | +| [go.mod](v2.9.0-rc3/argocd-test.html) | 0 | 2 | 6 | 0 | +| [ui/yarn.lock](v2.9.0-rc3/argocd-test.html) | 0 | 0 | 0 | 0 | +| [dex:v2.37.0](v2.9.0-rc3/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 0 | 3 | 1 | +| [haproxy:2.6.14-alpine](v2.9.0-rc3/haproxy_2.6.14-alpine.html) | 0 | 0 | 0 | 1 | +| [argocd:v2.9.0-rc3](v2.9.0-rc3/quay.io_argoproj_argocd_v2.9.0-rc3.html) | 0 | 0 | 4 | 16 | +| [redis:7.0.11-alpine](v2.9.0-rc3/redis_7.0.11-alpine.html) | 1 | 0 | 3 | 1 | +| [install.yaml](v2.9.0-rc3/argocd-iac-install.html) | - | - | - | - | +| [namespace-install.yaml](v2.9.0-rc3/argocd-iac-namespace-install.html) | - | - | - | - | -### v2.12.3 +### v2.8.5 | | Critical | High | Medium | Low | |---:|:--------:|:----:|:------:|:---:| -| [go.mod](v2.12.3/argocd-test.html) | 0 | 0 | 2 | 0 | -| [ui/yarn.lock](v2.12.3/argocd-test.html) | 0 | 1 | 2 | 0 | -| [dex:v2.38.0](v2.12.3/ghcr.io_dexidp_dex_v2.38.0.html) | 0 | 0 | 6 | 6 | -| [haproxy:2.6.17-alpine](v2.12.3/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html) | 0 | 0 | 2 | 3 | -| [redis:7.0.15-alpine](v2.12.3/public.ecr.aws_docker_library_redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 | -| [argocd:v2.12.3](v2.12.3/quay.io_argoproj_argocd_v2.12.3.html) | 0 | 0 | 8 | 8 | -| [redis:7.0.15-alpine](v2.12.3/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 | -| [install.yaml](v2.12.3/argocd-iac-install.html) | - | - | - | - | -| [namespace-install.yaml](v2.12.3/argocd-iac-namespace-install.html) | - | - | - | - | +| [go.mod](v2.8.5/argocd-test.html) | 0 | 0 | 6 | 0 | +| [ui/yarn.lock](v2.8.5/argocd-test.html) | 0 | 0 | 0 | 0 | +| [dex:v2.37.0](v2.8.5/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 0 | 3 | 1 | +| [haproxy:2.6.14-alpine](v2.8.5/haproxy_2.6.14-alpine.html) | 0 | 0 | 0 | 1 | +| [argocd:v2.8.5](v2.8.5/quay.io_argoproj_argocd_v2.8.5.html) | 0 | 0 | 4 | 16 | +| [redis:7.0.11-alpine](v2.8.5/redis_7.0.11-alpine.html) | 1 | 0 | 3 | 1 | +| [install.yaml](v2.8.5/argocd-iac-install.html) | - | - | - | - | +| [namespace-install.yaml](v2.8.5/argocd-iac-namespace-install.html) | - | - | - | - | -### v2.11.8 +### v2.7.14 | | Critical | High | Medium | Low | |---:|:--------:|:----:|:------:|:---:| -| [go.mod](v2.11.8/argocd-test.html) | 0 | 1 | 3 | 0 | -| [ui/yarn.lock](v2.11.8/argocd-test.html) | 0 | 1 | 2 | 0 | -| [dex:v2.38.0](v2.11.8/ghcr.io_dexidp_dex_v2.38.0.html) | 0 | 0 | 6 | 6 | -| [haproxy:2.6.14-alpine](v2.11.8/haproxy_2.6.14-alpine.html) | 0 | 1 | 7 | 6 | -| [argocd:v2.11.8](v2.11.8/quay.io_argoproj_argocd_v2.11.8.html) | 0 | 0 | 8 | 16 | -| [redis:7.0.15-alpine](v2.11.8/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 | -| [install.yaml](v2.11.8/argocd-iac-install.html) | - | - | - | - | -| [namespace-install.yaml](v2.11.8/argocd-iac-namespace-install.html) | - | - | - | - | +| [go.mod](v2.7.14/argocd-test.html) | 0 | 3 | 5 | 0 | +| [ui/yarn.lock](v2.7.14/argocd-test.html) | 0 | 1 | 0 | 0 | +| [dex:v2.37.0](v2.7.14/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 0 | 3 | 1 | +| [haproxy:2.6.14-alpine](v2.7.14/haproxy_2.6.14-alpine.html) | 0 | 0 | 0 | 1 | +| [argocd:v2.7.14](v2.7.14/quay.io_argoproj_argocd_v2.7.14.html) | 0 | 2 | 8 | 20 | +| [redis:7.0.11-alpine](v2.7.14/redis_7.0.11-alpine.html) | 1 | 0 | 3 | 1 | +| [install.yaml](v2.7.14/argocd-iac-install.html) | - | - | - | - | +| [namespace-install.yaml](v2.7.14/argocd-iac-namespace-install.html) | - | - | - | - | -### v2.10.16 +### v2.6.15 | | Critical | High | Medium | Low | |---:|:--------:|:----:|:------:|:---:| -| [go.mod](v2.10.16/argocd-test.html) | 0 | 1 | 4 | 0 | -| [ui/yarn.lock](v2.10.16/argocd-test.html) | 0 | 1 | 2 | 0 | -| [dex:v2.37.0](v2.10.16/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 1 | 10 | 6 | -| [haproxy:2.6.14-alpine](v2.10.16/haproxy_2.6.14-alpine.html) | 0 | 1 | 7 | 6 | -| [argocd:v2.10.16](v2.10.16/quay.io_argoproj_argocd_v2.10.16.html) | 0 | 0 | 12 | 20 | -| [redis:7.0.15-alpine](v2.10.16/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 | -| [install.yaml](v2.10.16/argocd-iac-install.html) | - | - | - | - | -| [namespace-install.yaml](v2.10.16/argocd-iac-namespace-install.html) | - | - | - | - | +| [go.mod](v2.6.15/argocd-test.html) | 0 | 3 | 5 | 0 | +| [ui/yarn.lock](v2.6.15/argocd-test.html) | 0 | 1 | 0 | 0 | +| [dex:v2.37.0](v2.6.15/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 0 | 3 | 1 | +| [haproxy:2.6.14-alpine](v2.6.15/haproxy_2.6.14-alpine.html) | 0 | 0 | 0 | 1 | +| [argocd:v2.6.15](v2.6.15/quay.io_argoproj_argocd_v2.6.15.html) | 0 | 2 | 8 | 20 | +| [redis:7.0.11-alpine](v2.6.15/redis_7.0.11-alpine.html) | 1 | 0 | 3 | 1 | +| [install.yaml](v2.6.15/argocd-iac-install.html) | - | - | - | - | +| [namespace-install.yaml](v2.6.15/argocd-iac-namespace-install.html) | - | - | - | - | diff --git a/docs/snyk/master/argocd-iac-install.html b/docs/snyk/master/argocd-iac-install.html index 4ffca011eadd2..28be7b9bb102b 100644 --- a/docs/snyk/master/argocd-iac-install.html +++ b/docs/snyk/master/argocd-iac-install.html @@ -456,7 +456,7 @@

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

Snyk test report

-
44 total issues
+
40 total issues
@@ -482,54 +482,8 @@

Snyk test report

-
-

Role or ClusterRole with dangerous permissions

-
- -
- high severity -
- -
- -
- -
- -

Impact

-

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

- -

Remediation

-

Consider removing these permissions

- - -
-
- - - -
-

Role or ClusterRole with dangerous permissions

+

Role with dangerous permissions

@@ -553,17 +507,17 @@

Role or ClusterRole with dangerous permissions

  • - Line number: 22070 + Line number: 20316

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -575,7 +529,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -599,17 +553,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 22157 + Line number: 20393

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -621,7 +575,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -645,17 +599,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 22185 + Line number: 20421

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -667,7 +621,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -684,24 +638,24 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: [DocId: 13] - rules[1] + rules[3] resources
  • - Line number: 22215 + Line number: 20469

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -713,7 +667,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -730,24 +684,24 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: [DocId: 13] - rules[3] + rules[1] resources
  • - Line number: 22233 + Line number: 20451

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -759,7 +713,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -783,63 +737,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 22251 -
  • - - -
    - -

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 15] - - rules[0] - - resources - -
    • - -
    • - Line number: 22273 + Line number: 20485

    Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -866,59 +774,7 @@

    Container could be running with outdated image

  • Introduced through: - [DocId: 48] - - spec - - template - - spec - - initContainers[secret-init] - - imagePullPolicy - -
  • - -
  • - Line number: 23345 -
  • - - -
    - -

    Impact

    -

    The container may run with outdated or unauthorized image

    - -

    Remediation

    -

    Set `imagePullPolicy` attribute to `Always`

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-42 -
    • - -
    • Introduced through: - [DocId: 49] + [DocId: 45] spec @@ -933,7 +789,7 @@

      Container could be running with outdated image

    • - Line number: 23644 + Line number: 21642
    @@ -970,7 +826,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 45] + [DocId: 41] input @@ -991,7 +847,7 @@

    Container has no CPU limit

  • - Line number: 22882 + Line number: 20969
  • @@ -1028,7 +884,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 46] + [DocId: 42] input @@ -1049,7 +905,7 @@

    Container has no CPU limit

  • - Line number: 23151 + Line number: 21220
  • @@ -1086,7 +942,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 46] + [DocId: 42] input @@ -1107,7 +963,7 @@

    Container has no CPU limit

  • - Line number: 23105 + Line number: 21186
  • @@ -1144,7 +1000,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 47] + [DocId: 43] input @@ -1165,7 +1021,7 @@

    Container has no CPU limit

  • - Line number: 23211 + Line number: 21280
  • @@ -1202,7 +1058,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 48] + [DocId: 44] input @@ -1223,7 +1079,7 @@

    Container has no CPU limit

  • - Line number: 23316 + Line number: 21373
  • @@ -1260,7 +1116,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 48] + [DocId: 45] input @@ -1270,7 +1126,7 @@

    Container has no CPU limit

    spec - initContainers[secret-init] + initContainers[copyutil] resources @@ -1281,7 +1137,7 @@

    Container has no CPU limit

  • - Line number: 23340 + Line number: 21642
  • @@ -1318,7 +1174,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 49] + [DocId: 45] input @@ -1328,7 +1184,7 @@

    Container has no CPU limit

    spec - initContainers[copyutil] + containers[argocd-repo-server] resources @@ -1339,7 +1195,7 @@

    Container has no CPU limit

  • - Line number: 23644 + Line number: 21430
  • @@ -1376,7 +1232,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 49] + [DocId: 46] input @@ -1386,7 +1242,7 @@

    Container has no CPU limit

    spec - containers[argocd-repo-server] + containers[argocd-server] resources @@ -1397,7 +1253,7 @@

    Container has no CPU limit

  • - Line number: 23397 + Line number: 21727
  • @@ -1434,7 +1290,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 50] + [DocId: 47] input @@ -1444,7 +1300,7 @@

    Container has no CPU limit

    spec - containers[argocd-server] + containers[argocd-application-controller] resources @@ -1455,7 +1311,7 @@

    Container has no CPU limit

  • - Line number: 23729 + Line number: 22043
  • @@ -1477,7 +1333,7 @@

    Remediation

    -

    Container has no CPU limit

    +

    Container is running with multiple open ports

    @@ -1488,13 +1344,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-36
    • Introduced through: - [DocId: 51] - - input + [DocId: 42] spec @@ -1502,40 +1356,36 @@

      Container has no CPU limit

      spec - containers[argocd-application-controller] - - resources - - limits + containers[dex] - cpu + ports
    • - Line number: 24119 + Line number: 21200

    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.

    +

    Increases the attack surface of the application and the container.

    Remediation

    -

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

    +

    Reduce `ports` count to 2


    -

    Container is running with multiple open ports

    +

    Container is running without liveness probe

    @@ -1546,11 +1396,11 @@

    Container is running with multiple open ports

    • - Public ID: SNYK-CC-K8S-36 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 46] + [DocId: 41] spec @@ -1558,31 +1408,31 @@

      Container is running with multiple open ports

      spec - containers[dex] + containers[argocd-applicationset-controller] - ports + livenessProbe
    • - Line number: 23131 + Line number: 20969

    Impact

    -

    Increases the attack surface of the application and the container.

    +

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

    Remediation

    -

    Reduce `ports` count to 2

    +

    Add `livenessProbe` attribute


    @@ -1602,7 +1452,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 45] + [DocId: 42] spec @@ -1610,14 +1460,14 @@

    Container is running without liveness probe

    spec - containers[argocd-applicationset-controller] + initContainers[copyutil] livenessProbe
  • - Line number: 22882 + Line number: 21220
  • @@ -1654,7 +1504,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 46] + [DocId: 42] spec @@ -1669,7 +1519,7 @@

    Container is running without liveness probe

  • - Line number: 23105 + Line number: 21186
  • @@ -1706,7 +1556,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 48] + [DocId: 44] spec @@ -1721,7 +1571,7 @@

    Container is running without liveness probe

  • - Line number: 23316 + Line number: 21373
  • @@ -1743,7 +1593,7 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container is running without liveness probe

    @@ -1754,49 +1604,43 @@

    Container is running without memory limit

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

    Impact

    -

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

    +

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

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Add `livenessProbe` attribute


    @@ -1816,7 +1660,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 46] + [DocId: 41] input @@ -1826,7 +1670,7 @@

    Container is running without memory limit

    spec - containers[dex] + containers[argocd-applicationset-controller] resources @@ -1837,7 +1681,7 @@

    Container is running without memory limit

  • - Line number: 23105 + Line number: 20969
  • @@ -1874,7 +1718,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 46] + [DocId: 42] input @@ -1884,7 +1728,7 @@

    Container is running without memory limit

    spec - initContainers[copyutil] + containers[dex] resources @@ -1895,7 +1739,7 @@

    Container is running without memory limit

  • - Line number: 23151 + Line number: 21186
  • @@ -1932,7 +1776,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 47] + [DocId: 42] input @@ -1942,7 +1786,7 @@

    Container is running without memory limit

    spec - containers[argocd-notifications-controller] + initContainers[copyutil] resources @@ -1953,7 +1797,7 @@

    Container is running without memory limit

  • - Line number: 23211 + Line number: 21220
  • @@ -1990,7 +1834,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 48] + [DocId: 43] input @@ -2000,7 +1844,7 @@

    Container is running without memory limit

    spec - containers[redis] + containers[argocd-notifications-controller] resources @@ -2011,7 +1855,7 @@

    Container is running without memory limit

  • - Line number: 23316 + Line number: 21280
  • @@ -2048,7 +1892,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 48] + [DocId: 44] input @@ -2058,7 +1902,7 @@

    Container is running without memory limit

    spec - initContainers[secret-init] + containers[redis] resources @@ -2069,7 +1913,7 @@

    Container is running without memory limit

  • - Line number: 23340 + Line number: 21373
  • @@ -2106,7 +1950,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 49] + [DocId: 45] input @@ -2127,7 +1971,7 @@

    Container is running without memory limit

  • - Line number: 23644 + Line number: 21642
  • @@ -2164,7 +2008,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 49] + [DocId: 45] input @@ -2185,7 +2029,7 @@

    Container is running without memory limit

  • - Line number: 23397 + Line number: 21430
  • @@ -2222,7 +2066,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 50] + [DocId: 46] input @@ -2243,7 +2087,7 @@

    Container is running without memory limit

  • - Line number: 23729 + Line number: 21727
  • @@ -2280,7 +2124,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 51] + [DocId: 47] input @@ -2301,7 +2145,7 @@

    Container is running without memory limit

  • - Line number: 24119 + Line number: 22043
  • @@ -2338,7 +2182,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 45] + [DocId: 41] input @@ -2357,7 +2201,7 @@

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

  • - Line number: 23029 + Line number: 21110
  • @@ -2394,7 +2238,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 46] + [DocId: 42] input @@ -2413,7 +2257,7 @@

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

  • - Line number: 23159 + Line number: 21228
  • @@ -2450,7 +2294,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 46] + [DocId: 42] input @@ -2469,7 +2313,7 @@

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

  • - Line number: 23134 + Line number: 21203
  • @@ -2506,7 +2350,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 47] + [DocId: 43] input @@ -2525,7 +2369,7 @@

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

  • - Line number: 23250 + Line number: 21307
  • @@ -2562,7 +2406,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 48] + [DocId: 44] input @@ -2581,63 +2425,7 @@

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

  • - Line number: 23333 -
  • - - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 23347 + Line number: 21383
    @@ -2674,7 +2462,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 49] + [DocId: 45] input @@ -2693,7 +2481,7 @@

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

  • - Line number: 23651 + Line number: 21649
  • @@ -2730,7 +2518,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 49] + [DocId: 45] input @@ -2749,7 +2537,7 @@

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

  • - Line number: 23617 + Line number: 21615
  • @@ -2786,7 +2574,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 50] + [DocId: 46] input @@ -2805,7 +2593,7 @@

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

  • - Line number: 24020 + Line number: 21953
  • @@ -2842,7 +2630,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 51] + [DocId: 47] input @@ -2861,7 +2649,7 @@

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

  • - Line number: 24320 + Line number: 22191
  • diff --git a/docs/snyk/master/argocd-iac-namespace-install.html b/docs/snyk/master/argocd-iac-namespace-install.html index a78881186e589..e043d126f446c 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

    -

    September 22nd 2024, 12:21:16 am (UTC+00:00)

    +

    October 29th 2023, 12:17:54 am (UTC+00:00)

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

    Snyk test report

    -
    43 total issues
    +
    40 total issues

    @@ -483,7 +483,7 @@

    Snyk test report

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -514,10 +514,10 @@

    Role or ClusterRole with dangerous permissions


    Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -529,7 +529,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -553,17 +553,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 164 + Line number: 154

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -575,7 +575,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -599,17 +599,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 192 + Line number: 182

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -621,7 +621,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -638,24 +638,24 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: [DocId: 10] - rules[1] + rules[3] resources
  • - Line number: 222 + Line number: 230

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -667,7 +667,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -684,24 +684,24 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: [DocId: 10] - rules[3] + rules[1] resources
  • - Line number: 240 + Line number: 212

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -713,7 +713,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -737,63 +737,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 258 -
  • - - -
    - -

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 12] - - rules[0] - - resources - -
    • - -
    • - Line number: 280 + Line number: 246

    Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -820,59 +774,7 @@

    Container could be running with outdated image

  • Introduced through: - [DocId: 39] - - spec - - template - - spec - - initContainers[secret-init] - - imagePullPolicy - -
  • - -
  • - Line number: 1138 -
  • - - -
    - -

    Impact

    -

    The container may run with outdated or unauthorized image

    - -

    Remediation

    -

    Set `imagePullPolicy` attribute to `Always`

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

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

      Container could be running with outdated image

    • - Line number: 1437 + Line number: 1298
    @@ -924,7 +826,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 36] + [DocId: 34] input @@ -945,7 +847,7 @@

    Container has no CPU limit

  • - Line number: 675 + Line number: 625
  • @@ -982,7 +884,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 37] + [DocId: 35] input @@ -1003,7 +905,7 @@

    Container has no CPU limit

  • - Line number: 944 + Line number: 876
  • @@ -1040,7 +942,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 37] + [DocId: 35] input @@ -1061,7 +963,7 @@

    Container has no CPU limit

  • - Line number: 898 + Line number: 842
  • @@ -1098,7 +1000,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 38] + [DocId: 36] input @@ -1119,7 +1021,7 @@

    Container has no CPU limit

  • - Line number: 1004 + Line number: 936
  • @@ -1156,7 +1058,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 39] + [DocId: 37] input @@ -1177,7 +1079,7 @@

    Container has no CPU limit

  • - Line number: 1109 + Line number: 1029
  • @@ -1214,7 +1116,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -1224,7 +1126,7 @@

    Container has no CPU limit

    spec - initContainers[secret-init] + initContainers[copyutil] resources @@ -1235,7 +1137,7 @@

    Container has no CPU limit

  • - Line number: 1133 + Line number: 1298
  • @@ -1272,7 +1174,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 40] + [DocId: 38] input @@ -1282,7 +1184,7 @@

    Container has no CPU limit

    spec - initContainers[copyutil] + containers[argocd-repo-server] resources @@ -1293,7 +1195,7 @@

    Container has no CPU limit

  • - Line number: 1437 + Line number: 1086
  • @@ -1330,7 +1232,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 40] + [DocId: 39] input @@ -1340,7 +1242,7 @@

    Container has no CPU limit

    spec - containers[argocd-repo-server] + containers[argocd-server] resources @@ -1351,7 +1253,7 @@

    Container has no CPU limit

  • - Line number: 1190 + Line number: 1383
  • @@ -1388,7 +1290,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 41] + [DocId: 40] input @@ -1398,7 +1300,7 @@

    Container has no CPU limit

    spec - containers[argocd-server] + containers[argocd-application-controller] resources @@ -1409,7 +1311,7 @@

    Container has no CPU limit

  • - Line number: 1522 + Line number: 1699
  • @@ -1431,7 +1333,7 @@

    Remediation

    -

    Container has no CPU limit

    +

    Container is running with multiple open ports

    @@ -1442,13 +1344,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-36
    • Introduced through: - [DocId: 42] - - input + [DocId: 35] spec @@ -1456,40 +1356,36 @@

      Container has no CPU limit

      spec - containers[argocd-application-controller] - - resources - - limits + containers[dex] - cpu + ports
    • - Line number: 1912 + Line number: 856

    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.

    +

    Increases the attack surface of the application and the container.

    Remediation

    -

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

    +

    Reduce `ports` count to 2


    -

    Container is running with multiple open ports

    +

    Container is running without liveness probe

    @@ -1500,11 +1396,11 @@

    Container is running with multiple open ports

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

      Container is running with multiple open ports

      spec - containers[dex] + containers[argocd-applicationset-controller] - ports + livenessProbe
    • - Line number: 924 + Line number: 625

    Impact

    -

    Increases the attack surface of the application and the container.

    +

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

    Remediation

    -

    Reduce `ports` count to 2

    +

    Add `livenessProbe` attribute


    @@ -1556,7 +1452,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 36] + [DocId: 35] spec @@ -1564,14 +1460,14 @@

    Container is running without liveness probe

    spec - containers[argocd-applicationset-controller] + initContainers[copyutil] livenessProbe
  • - Line number: 675 + Line number: 876
  • @@ -1608,7 +1504,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 37] + [DocId: 35] spec @@ -1623,7 +1519,7 @@

    Container is running without liveness probe

  • - Line number: 898 + Line number: 842
  • @@ -1660,7 +1556,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 39] + [DocId: 37] spec @@ -1675,7 +1571,7 @@

    Container is running without liveness probe

  • - Line number: 1109 + Line number: 1029
  • @@ -1697,7 +1593,7 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container is running without liveness probe

    @@ -1708,13 +1604,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 36] - - input + [DocId: 38] spec @@ -1722,35 +1616,31 @@

      Container is running without memory limit

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

    Impact

    -

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

    +

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

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Add `livenessProbe` attribute


    @@ -1770,7 +1660,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 37] + [DocId: 34] input @@ -1780,7 +1670,7 @@

    Container is running without memory limit

    spec - containers[dex] + containers[argocd-applicationset-controller] resources @@ -1791,7 +1681,7 @@

    Container is running without memory limit

  • - Line number: 898 + Line number: 625
  • @@ -1828,7 +1718,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 37] + [DocId: 35] input @@ -1838,7 +1728,7 @@

    Container is running without memory limit

    spec - initContainers[copyutil] + containers[dex] resources @@ -1849,7 +1739,7 @@

    Container is running without memory limit

  • - Line number: 944 + Line number: 842
  • @@ -1886,7 +1776,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 38] + [DocId: 35] input @@ -1896,7 +1786,7 @@

    Container is running without memory limit

    spec - containers[argocd-notifications-controller] + initContainers[copyutil] resources @@ -1907,7 +1797,7 @@

    Container is running without memory limit

  • - Line number: 1004 + Line number: 876
  • @@ -1944,7 +1834,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 39] + [DocId: 36] input @@ -1954,7 +1844,7 @@

    Container is running without memory limit

    spec - containers[redis] + containers[argocd-notifications-controller] resources @@ -1965,7 +1855,7 @@

    Container is running without memory limit

  • - Line number: 1109 + Line number: 936
  • @@ -2002,7 +1892,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 39] + [DocId: 37] input @@ -2012,7 +1902,7 @@

    Container is running without memory limit

    spec - initContainers[secret-init] + containers[redis] resources @@ -2023,7 +1913,7 @@

    Container is running without memory limit

  • - Line number: 1133 + Line number: 1029
  • @@ -2060,7 +1950,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 40] + [DocId: 38] input @@ -2081,7 +1971,7 @@

    Container is running without memory limit

  • - Line number: 1437 + Line number: 1298
  • @@ -2118,7 +2008,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 40] + [DocId: 38] input @@ -2139,7 +2029,7 @@

    Container is running without memory limit

  • - Line number: 1190 + Line number: 1086
  • @@ -2176,7 +2066,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 41] + [DocId: 39] input @@ -2197,7 +2087,7 @@

    Container is running without memory limit

  • - Line number: 1522 + Line number: 1383
  • @@ -2234,7 +2124,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 42] + [DocId: 40] input @@ -2255,7 +2145,7 @@

    Container is running without memory limit

  • - Line number: 1912 + Line number: 1699
  • @@ -2292,7 +2182,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 36] + [DocId: 34] input @@ -2311,7 +2201,7 @@

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

  • - Line number: 822 + Line number: 766
  • @@ -2348,7 +2238,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 37] + [DocId: 35] input @@ -2367,7 +2257,7 @@

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

  • - Line number: 952 + Line number: 884
  • @@ -2404,7 +2294,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 37] + [DocId: 35] input @@ -2423,7 +2313,7 @@

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

  • - Line number: 927 + Line number: 859
  • @@ -2460,7 +2350,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 38] + [DocId: 36] input @@ -2479,7 +2369,7 @@

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

  • - Line number: 1043 + Line number: 963
  • @@ -2516,7 +2406,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 39] + [DocId: 37] input @@ -2535,7 +2425,7 @@

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

  • - Line number: 1126 + Line number: 1039
  • @@ -2572,63 +2462,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 39] - - input - - spec - - template - - spec - - initContainers[secret-init] - - securityContext - - runAsUser - -
  • - -
  • - Line number: 1140 -
  • - - -
    - -

    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] + [DocId: 38] input @@ -2647,7 +2481,7 @@

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

    • - Line number: 1444 + Line number: 1305
    @@ -2684,7 +2518,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 40] + [DocId: 38] input @@ -2703,7 +2537,7 @@

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

  • - Line number: 1410 + Line number: 1271
  • @@ -2740,7 +2574,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 41] + [DocId: 39] input @@ -2759,7 +2593,7 @@

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

  • - Line number: 1813 + Line number: 1609
  • @@ -2796,7 +2630,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 42] + [DocId: 40] input @@ -2815,7 +2649,7 @@

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

  • - Line number: 2113 + Line number: 1847
  • diff --git a/docs/snyk/master/argocd-test.html b/docs/snyk/master/argocd-test.html index 0c91d6f1cb159..1b2486932df9e 100644 --- a/docs/snyk/master/argocd-test.html +++ b/docs/snyk/master/argocd-test.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,20 +456,19 @@

    Snyk test report

    -

    September 22nd 2024, 12:18:54 am (UTC+00:00)

    +

    October 29th 2023, 12:14:38 am (UTC+00:00)

    Scanned the following paths:
      -
    • /argo-cd/argoproj/argo-cd/v2/go.mod (gomodules)
    • -
    • /argo-cd/ui/yarn.lock (yarn)
    • +
    • /argo-cd/argoproj/argo-cd/v2 (gomodules)
    • /argo-cd (yarn)
    -
    2 known vulnerabilities
    -
    4 vulnerable dependency paths
    -
    2132 dependencies
    +
    6 known vulnerabilities
    +
    19 vulnerable dependency paths
    +
    1965 dependencies

    @@ -478,7 +477,7 @@

    Snyk test report

    -

    Regular Expression Denial of Service (ReDoS)

    +

    LGPL-3.0 license

    @@ -489,21 +488,193 @@

    Regular Expression Denial of Service (ReDoS)

    • - Manifest file: /argo-cd ui/yarn.lock + 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

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + Package Manager: golang
    • - Package Manager: npm + 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 +
    + +
    + +
      +
    • + Package Manager: golang
    • - Vulnerable module: + Module: - path-to-regexp + 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 - argo-cd-ui@1.0.0, react-router@4.3.1 and others
    @@ -515,39 +686,97 @@

    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 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - react-router@4.3.1 + github.com/xanzy/go-gitlab@0.91.1 - path-to-regexp@1.8.0 + github.com/hashicorp/go-retryablehttp@0.7.4
    • Introduced through: - argo-cd-ui@1.0.0 + github.com/argoproj/argo-cd/v2@0.0.0 - react-router-dom@4.3.1 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - react-router@4.3.1 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - path-to-regexp@1.8.0 + github.com/hashicorp/go-retryablehttp@0.7.4
    • Introduced through: - argo-cd-ui@1.0.0 + github.com/argoproj/argo-cd/v2@0.0.0 - argo-ui@1.0.0 + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf - react-router-dom@4.3.1 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - react-router@4.3.1 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - path-to-regexp@1.8.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/cmd@#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/api@#9dcecdc3eebf + + 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/controller@#9dcecdc3eebf + + 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 @@ -558,96 +787,17 @@

      Detailed paths


      -

      Overview

      -

      Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) when including multiple regular expression parameters in a single segment, which will produce the regular expression /^\/([^\/]+?)-([^\/]+?)\/?$/, if two parameters within a single segment are separated by a character other than a / or .. Poor performance will block the event loop and can lead to a DoS.

      -

      Note: - While the 8.0.0 release has completely eliminated the vulnerable functionality, prior versions that have received the patch to mitigate backtracking may still be vulnerable if custom regular expressions are used. So it is strongly recommended for regular expression input to be controlled to avoid malicious performance degradation in those versions. This behavior is enforced as of version 7.1.0 via the strict option, which returns an error if a dangerous regular expression is detected.

      -

      Workaround

      -

      This vulnerability can be avoided by using a custom regular expression for parameters after the first in a segment, which excludes - and /.

      -

      PoC

      -
      /a${'-a'.repeat(8_000)}/a
      -        
      -

      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 path-to-regexp to version 0.1.10, 1.9.0, 3.3.0, 6.3.0, 8.0.0 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')

    +

    MPL-2.0 license

    @@ -658,21 +808,188 @@

    Concurrent Execution using Shared Resource with Improper
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod + Package Manager: golang
    • +
    • + Module: + + github.com/hashicorp/go-cleanhttp +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/hashicorp/go-retryablehttp@0.7.4 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + 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@#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/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/api@#9dcecdc3eebf + + 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/controller@#9dcecdc3eebf + + 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 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/Azure/azure-sdk-for-go/sdk/azidentity + 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/Azure/kubelogin/pkg/token@0.0.20 and others
    @@ -686,9 +1003,7 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/Azure/kubelogin/pkg/token@0.0.20 - - github.com/Azure/azure-sdk-for-go/sdk/azidentity@1.1.0 + github.com/gosimple/slug@1.13.1 @@ -699,41 +1014,12 @@

    Detailed paths


    -

    Overview

    -

    github.com/Azure/azure-sdk-for-go/sdk/azidentity is a module that provides Microsoft Entra ID (formerly Azure Active Directory) token authentication support across the Azure SDK. It includes a set of TokenCredential implementations, which can be used with Azure SDK clients supporting token authentication.

    -

    Affected versions of this package are vulnerable to Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') in the authentication process. An attacker can elevate privileges by exploiting race conditions during the token validation steps. This is only exploitable if the application is configured to use multiple threads or processes for handling authentication requests.

    -

    Notes:

    -
      -
    1. An attacker who successfully exploited the vulnerability could elevate privileges and read any file on the file system with SYSTEM access permissions;

      -
    2. -
    3. An attacker who successfully exploits this vulnerability can only obtain read access to the system files by exploiting this vulnerability. The attacker cannot perform write or delete operations on the files;

      -
    4. -
    5. The vulnerability exists in the following credential types: DefaultAzureCredential and ManagedIdentityCredential;

      -
    6. -
    7. The vulnerability exists in the following credential types:

      -
    8. -
    -

    ManagedIdentityApplication (.NET)

    -

    ManagedIdentityApplication (Java)

    -

    ManagedIdentityApplication (Node.js)

    -

    Remediation

    -

    Upgrade github.com/Azure/azure-sdk-for-go/sdk/azidentity to version 1.6.0 or higher.

    -

    References

    - +

    MPL-2.0 license


    diff --git a/docs/snyk/v2.12.3/ghcr.io_dexidp_dex_v2.38.0.html b/docs/snyk/master/ghcr.io_dexidp_dex_v2.37.0.html similarity index 56% rename from docs/snyk/v2.12.3/ghcr.io_dexidp_dex_v2.38.0.html rename to docs/snyk/master/ghcr.io_dexidp_dex_v2.37.0.html index c40058cb449a2..167a203368fb3 100644 --- a/docs/snyk/v2.12.3/ghcr.io_dexidp_dex_v2.38.0.html +++ b/docs/snyk/master/ghcr.io_dexidp_dex_v2.37.0.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,22 +456,19 @@

    Snyk test report

    -

    September 22nd 2024, 12:24:06 am (UTC+00:00)

    +

    October 29th 2023, 12:14:53 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)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (apk)
    • ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 (gomodules)
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (gomodules)
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (gomodules)
    -
    18 known vulnerabilities
    -
    85 vulnerable dependency paths
    -
    829 dependencies
    +
    28 known vulnerabilities
    +
    79 vulnerable dependency paths
    +
    786 dependencies
    @@ -479,32 +476,29 @@

    Snyk test report

    -
    -

    Allocation of Resources Without Limits or Throttling

    +
    +

    Out-of-bounds Write

    -
    - high severity +
    + critical severity

    • - Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate -
    • -
    • - Package Manager: golang + Package Manager: alpine:3.18
    • Vulnerable module: - golang.org/x/net/http2 + busybox/busybox
    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* and golang.org/x/net/http2@v0.19.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 and busybox/busybox@1.36.1-r0
    @@ -517,18 +511,51 @@

    Detailed paths

    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* + docker-image|ghcr.io/dexidp/dex@v2.37.0 - golang.org/x/net/http2@v0.19.0 + busybox/busybox@1.36.1-r0
    • Introduced through: - github.com/dexidp/dex@* + 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 - golang.org/x/net/http2@v0.20.0 + busybox/ssl_client@1.36.1-r0 @@ -539,49 +566,47 @@

      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 Allocation of Resources Without Limits or Throttling when reading header data from CONTINUATION frames. As part of the HPACK flow, all incoming HEADERS and CONTINUATION frames are read even if their payloads exceed MaxHeaderBytes and will be discarded. An attacker can send excessive data over a connection to render it unresponsive.

      +

      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 golang.org/x/net/http2 to version 0.23.0 or higher.

      +

      Upgrade Alpine:3.18 busybox to version 1.36.1-r1 or higher.

      References


    -
    -

    Out-of-bounds Write

    +
    +

    Denial of Service (DoS)

    -
    - medium severity +
    + high severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • Vulnerable module: - openssl/libcrypto3 + google.golang.org/grpc
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/grpc@v1.46.2
    @@ -594,75 +619,104 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - openssl/libcrypto3@3.1.4-r2 + google.golang.org/grpc@v1.46.2
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - apk-tools/apk-tools@2.14.0-r5 + github.com/dexidp/dex@* - openssl/libcrypto3@3.1.4-r2 + google.golang.org/grpc@v1.56.1
    • -
    • - 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 - - +
    + +

    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 +
    + +
    + +
      +
    • + 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

    + +
    -

    CVE-2024-0727

    +

    Improper Authentication

    @@ -738,7 +768,7 @@

    CVE-2024-0727

    • - Package Manager: alpine:3.19 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -748,7 +778,7 @@

      CVE-2024-0727

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1
    @@ -761,75 +791,75 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - busybox/ssl_client@1.36.1-r15 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - busybox/ssl_client@1.36.1-r15 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1 @@ -841,47 +871,46 @@

      Detailed paths


      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.

      +

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine:3.18. + 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.19 openssl to version 3.1.4-r5 or higher.

      +

      Upgrade Alpine:3.18 openssl to version 3.1.1-r2 or higher.

      References


    -

    Infinite loop

    +

    Inefficient Regular Expression Complexity

    @@ -892,20 +921,17 @@

    Infinite loop

    • - Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate -
    • -
    • - Package Manager: golang + Package Manager: alpine:3.18
    • Vulnerable module: - google.golang.org/protobuf/internal/encoding/json + openssl/libcrypto3
    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/internal/encoding/json@v1.31.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1
    @@ -918,18 +944,75 @@

    Detailed paths

    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* + docker-image|ghcr.io/dexidp/dex@v2.37.0 - google.golang.org/protobuf/internal/encoding/json@v1.31.0 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - github.com/dexidp/dex@* + 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 - google.golang.org/protobuf/internal/encoding/json@v1.32.0 + openssl/libssl3@3.1.1-r1 @@ -940,28 +1023,57 @@

      Detailed paths


      -

      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.

      +

      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 google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

      +

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

      References


    -

    Stack-based Buffer Overflow

    +

    Excessive Iteration

    @@ -972,20 +1084,17 @@

    Stack-based Buffer Overflow

    • - Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate -
    • -
    • - Package Manager: golang + Package Manager: alpine:3.18
    • Vulnerable module: - google.golang.org/protobuf/encoding/protojson + openssl/libcrypto3
    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1
    @@ -998,9 +1107,75 @@

    Detailed paths

    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* + 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 - google.golang.org/protobuf/encoding/protojson@v1.31.0 + openssl/libssl3@3.1.1-r1 @@ -1011,25 +1186,56 @@

      Detailed paths


      -

      Overview

      -

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

      +

      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 google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

      +

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

      References


    -

    Infinite loop

    +

    Cross-site Scripting (XSS)

    @@ -1039,21 +1245,18 @@

    Infinite loop


      -
    • - 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 + golang.org/x/net/html
    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + github.com/dexidp/dex@* and golang.org/x/net/html@v0.11.0
    @@ -1064,20 +1267,11 @@

    Infinite loop

    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 + golang.org/x/net/html@v0.11.0 @@ -1089,27 +1283,82 @@

      Detailed paths


      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.

      +

      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 google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

      +

      Upgrade golang.org/x/net/html to version 0.13.0 or higher.

      References


    -

    Insertion of Sensitive Information into Log File

    +

    MPL-2.0 license

    @@ -1119,21 +1368,18 @@

    Insertion of Sensitive Information into Log File


      -
    • - Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate -
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/hashicorp/go-retryablehttp + github.com/hashicorp/vault/sdk/helper/certutil
    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-retryablehttp@v0.7.1 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/sdk/helper/certutil@v0.5.0
    @@ -1148,36 +1394,100 @@

    Detailed paths

    Introduced through: github.com/hairyhenderson/gomplate/v3@* - github.com/hashicorp/go-retryablehttp@v0.7.1 + 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 + + + +
  • + + +
    +
    -

    Overview

    -

    Affected versions of this package are vulnerable to Insertion of Sensitive Information into Log File due to not sanitizing urls when writing them to the log file. This could lead to an attacker writing sensitive HTTP basic auth credentials to the log file.

    -

    Remediation

    -

    Upgrade github.com/hashicorp/go-retryablehttp to version 0.7.7 or higher.

    -

    References

    - +

    MPL-2.0 license


    -

    Improper Handling of Highly Compressed Data (Data Amplification)

    +

    MPL-2.0 license

    @@ -1187,21 +1497,18 @@

    Improper Handling of Highly Compressed Data (Data Amplif
      -
    • - Manifest file: ghcr.io/dexidp/dex:v2.38.0/dexidp/dex /usr/local/bin/dex -
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/go-jose/go-jose/v3 + github.com/hashicorp/vault/api
    • Introduced through: - github.com/dexidp/dex@* and github.com/go-jose/go-jose/v3@v3.0.1 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/api@v1.6.0
    @@ -1214,9 +1521,9 @@

    Detailed paths

    • Introduced through: - github.com/dexidp/dex@* + github.com/hairyhenderson/gomplate/v3@* - github.com/go-jose/go-jose/v3@v3.0.1 + github.com/hashicorp/vault/api@v1.6.0 @@ -1227,26 +1534,17 @@

      Detailed paths


      -

      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

      - +

      MPL-2.0 license


    -

    Out-of-bounds Write

    +

    MPL-2.0 license

    @@ -1257,17 +1555,17 @@

    Out-of-bounds Write

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - busybox/busybox + github.com/hashicorp/serf/coordinate
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and busybox/busybox@1.36.1-r15 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/serf/coordinate@v0.9.7
    @@ -1280,51 +1578,9 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - busybox/busybox@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 - - busybox/busybox-binsh@1.36.1-r15 - - busybox/busybox@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - busybox/busybox-binsh@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 - - busybox/busybox-binsh@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/ssl_client@1.36.1-r15 + github.com/hashicorp/serf/coordinate@v0.9.7 @@ -1335,26 +1591,17 @@

      Detailed paths


      -

      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.19 relevant fixed versions and status.

      -

      A heap-buffer-overflow was discovered in BusyBox v.1.36.1 in the next_token function at awk.c:1159.

      -

      Remediation

      -

      Upgrade Alpine:3.19 busybox to version 1.36.1-r16 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Use After Free

    +

    MPL-2.0 license

    @@ -1365,17 +1612,17 @@

    Use After Free

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - busybox/busybox + github.com/hashicorp/hcl/v2
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and busybox/busybox@1.36.1-r15 + github.com/dexidp/dex@* and github.com/hashicorp/hcl/v2@v2.13.0
    @@ -1388,51 +1635,72 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/dexidp/dex@* - busybox/busybox@1.36.1-r15 + github.com/hashicorp/hcl/v2@v2.13.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/dexidp/dex@* - alpine-baselayout/alpine-baselayout@3.4.3-r2 + github.com/hashicorp/hcl/v2/ext/customdecode@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* - busybox/busybox-binsh@1.36.1-r15 + github.com/hashicorp/hcl/v2/ext/tryfunc@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* - busybox/busybox@1.36.1-r15 + github.com/hashicorp/hcl/v2/gohcl@v2.13.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/dexidp/dex@* - busybox/busybox-binsh@1.36.1-r15 + github.com/hashicorp/hcl/v2/hclparse@v2.13.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/dexidp/dex@* - alpine-baselayout/alpine-baselayout@3.4.3-r2 + github.com/hashicorp/hcl/v2/hclsyntax@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* - busybox/busybox-binsh@1.36.1-r15 + github.com/hashicorp/hcl/v2/hclwrite@v2.13.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/dexidp/dex@* - busybox/ssl_client@1.36.1-r15 + github.com/hashicorp/hcl/v2/json@v2.13.0 @@ -1443,26 +1711,17 @@

      Detailed paths


      -

      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.19 relevant fixed versions and status.

      -

      A use-after-free vulnerability was discovered in BusyBox v.1.36.1 via a crafted awk pattern in the awk.c copyvar function.

      -

      Remediation

      -

      Upgrade Alpine:3.19 busybox to version 1.36.1-r19 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Use After Free

    +

    MPL-2.0 license

    @@ -1473,17 +1732,17 @@

    Use After Free

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - busybox/busybox + github.com/hashicorp/hcl
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and busybox/busybox@1.36.1-r15 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/hcl@v1.0.0
    @@ -1496,51 +1755,45 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/busybox@1.36.1-r15 + github.com/hashicorp/hcl@v1.0.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 - - busybox/busybox-binsh@1.36.1-r15 + github.com/hairyhenderson/gomplate/v3@* - busybox/busybox@1.36.1-r15 + github.com/hashicorp/hcl/hcl/parser@v1.0.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/busybox-binsh@1.36.1-r15 + github.com/hashicorp/hcl/hcl/strconv@v1.0.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 + github.com/hairyhenderson/gomplate/v3@* - busybox/busybox-binsh@1.36.1-r15 + github.com/hashicorp/hcl/hcl/token@v1.0.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/ssl_client@1.36.1-r15 + github.com/hashicorp/hcl/json/parser@v1.0.0 @@ -1551,26 +1804,17 @@

      Detailed paths


      -

      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.19 relevant fixed versions and status.

      -

      A use-after-free vulnerability in BusyBox v.1.36.1 allows attackers to cause a denial of service via a crafted awk pattern in the awk.c evaluate function.

      -

      Remediation

      -

      Upgrade Alpine:3.19 busybox to version 1.36.1-r19 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Use After Free

    +

    MPL-2.0 license

    @@ -1581,17 +1825,17 @@

    Use After Free

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - busybox/busybox + github.com/hashicorp/golang-lru/simplelru
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and busybox/busybox@1.36.1-r15 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/golang-lru/simplelru@v0.5.4
    @@ -1604,51 +1848,9 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - busybox/busybox@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 - - busybox/busybox-binsh@1.36.1-r15 - - busybox/busybox@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - busybox/busybox-binsh@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 - - busybox/busybox-binsh@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/ssl_client@1.36.1-r15 + github.com/hashicorp/golang-lru/simplelru@v0.5.4 @@ -1659,47 +1861,38 @@

      Detailed paths


      -

      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.19 relevant fixed versions and status.

      -

      A use-after-free vulnerability was discovered in xasprintf function in xfuncs_printf.c:344 in BusyBox v.1.36.1.

      -

      Remediation

      -

      Upgrade Alpine:3.19 busybox to version 1.36.1-r17 or higher.

      -

      References

      - +

      MPL-2.0 license


    -
    -

    CVE-2023-6237

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - openssl/libcrypto3 + github.com/hashicorp/go-version
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-version@v1.5.0
    @@ -1712,75 +1905,246 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - openssl/libcrypto3@3.1.4-r2 + github.com/hashicorp/go-version@v1.5.0
    • -
    • - 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 - - +
    + +

    MPL-2.0 license

    -
  • -
  • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 +
    + + + +
  • +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@* - openssl/libssl3@3.1.4-r2 + github.com/hashicorp/go-sockaddr@v1.0.2
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - apk-tools/apk-tools@2.14.0-r5 + github.com/hashicorp/go-sockaddr/template@v1.0.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@* - openssl/libssl3@3.1.4-r2 + github.com/hashicorp/go-secure-stdlib/strutil@v0.1.2
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/ssl_client@1.36.1-r15 + github.com/hashicorp/go-secure-stdlib/parseutil@v0.1.5 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@* - openssl/libssl3@3.1.4-r2 + github.com/hashicorp/go-secure-stdlib/mlock@v0.1.2 @@ -1791,71 +2155,332 @@

      Detailed paths


      -

      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: Checking excessively long invalid RSA public keys may take - a long time.

      -

      Impact summary: Applications that use the function EVP_PKEY_public_check() - to check RSA public keys may experience long delays. Where the key that - is being checked has been obtained from an untrusted source this may lead - to a Denial of Service.

      -

      When function EVP_PKEY_public_check() is called on RSA public keys, - a computation is done to confirm that the RSA modulus, n, is composite. - For valid RSA keys, n is a product of two or more large primes and this - computation completes quickly. However, if n is an overly large prime, - then this computation would take a long time.

      -

      An application that calls EVP_PKEY_public_check() and supplies an RSA key - obtained from an untrusted source could be vulnerable to a Denial of Service - attack.

      -

      The function EVP_PKEY_public_check() is not called from other OpenSSL - functions however it is called from the OpenSSL pkey command line - application. For that reason that application is also vulnerable if used - with the '-pubin' and '-check' options on untrusted data.

      -

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

      -

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

      -

      Remediation

      -

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

      -

      References

      - +

      MPL-2.0 license

      + +
      + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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


    -
    -

    CVE-2024-2511

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - openssl/libcrypto3 + github.com/hashicorp/errwrap
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/errwrap@v1.1.0
    @@ -1868,75 +2493,9 @@

    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 + github.com/hairyhenderson/gomplate/v3@* - openssl/libssl3@3.1.4-r2 + github.com/hashicorp/errwrap@v1.1.0 @@ -1947,67 +2506,38 @@

      Detailed paths


      -

      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: Some non-default TLS server configurations can cause unbounded - memory growth when processing TLSv1.3 sessions

      -

      Impact summary: An attacker may exploit certain server configurations to trigger - unbounded memory growth that would lead to a Denial of Service

      -

      This problem can occur in TLSv1.3 if the non-default SSL_OP_NO_TICKET option is - being used (but not if early_data support is also configured and the default - anti-replay protection is in use). In this case, under certain conditions, the - session cache can get into an incorrect state and it will fail to flush properly - as it fills. The session cache will continue to grow in an unbounded manner. A - malicious client could deliberately create the scenario for this failure to - force a Denial of Service. It may also happen by accident in normal operation.

      -

      This issue only affects TLS servers supporting TLSv1.3. It does not affect TLS - clients.

      -

      The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue. OpenSSL - 1.0.2 is also not affected by this issue.

      -

      Remediation

      -

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

      -

      References

      - +

      MPL-2.0 license


    -
    -

    CVE-2024-4603

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - openssl/libcrypto3 + github.com/hashicorp/consul/api
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/consul/api@v1.13.0
    @@ -2020,75 +2550,9 @@

    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 + github.com/hairyhenderson/gomplate/v3@* - openssl/libssl3@3.1.4-r2 + github.com/hashicorp/consul/api@v1.13.0 @@ -2099,75 +2563,38 @@

      Detailed paths


      -

      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: Checking excessively long DSA keys or parameters may be very - slow.

      -

      Impact summary: Applications that use the functions EVP_PKEY_param_check() - or EVP_PKEY_public_check() to check a DSA public key or DSA 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 functions EVP_PKEY_param_check() or EVP_PKEY_public_check() perform - various checks on DSA parameters. Some of those computations take a long time - if the modulus (p parameter) is too large.

      -

      Trying to use a very large modulus is slow and OpenSSL will not allow using - public keys with a modulus which is over 10,000 bits in length for signature - verification. However the key and parameter check functions do not limit - the modulus size when performing the checks.

      -

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

      -

      These functions are not called by OpenSSL itself on untrusted DSA keys so - only applications that directly call these functions may be vulnerable.

      -

      Also vulnerable are the OpenSSL pkey 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 affected by this issue.

      -

      Remediation

      -

      Upgrade Alpine:3.19 openssl to version 3.1.5-r0 or higher.

      -

      References

      - +

      MPL-2.0 license


    -
    -

    CVE-2024-5535

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - openssl/libcrypto3 + github.com/gosimple/slug
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/hairyhenderson/gomplate/v3@* and github.com/gosimple/slug@v1.12.0
    @@ -2180,75 +2607,9 @@

    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 + github.com/hairyhenderson/gomplate/v3@* - openssl/libssl3@3.1.4-r2 + github.com/gosimple/slug@v1.12.0 @@ -2259,110 +2620,38 @@

      Detailed paths


      -

      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: Calling the OpenSSL API function SSL_select_next_proto with an - empty supported client protocols buffer may cause a crash or memory contents to - be sent to the peer.

      -

      Impact summary: A buffer overread can have a range of potential consequences - such as unexpected application beahviour or a crash. In particular this issue - could result in up to 255 bytes of arbitrary private data from memory being sent - to the peer leading to a loss of confidentiality. However, only applications - that directly call the SSL_select_next_proto function with a 0 length list of - supported client protocols are affected by this issue. This would normally never - be a valid scenario and is typically not under attacker control but may occur by - accident in the case of a configuration or programming error in the calling - application.

      -

      The OpenSSL API function SSL_select_next_proto is typically used by TLS - applications that support ALPN (Application Layer Protocol Negotiation) or NPN - (Next Protocol Negotiation). NPN is older, was never standardised and - is deprecated in favour of ALPN. We believe that ALPN is significantly more - widely deployed than NPN. The SSL_select_next_proto function accepts a list of - protocols from the server and a list of protocols from the client and returns - the first protocol that appears in the server list that also appears in the - client list. In the case of no overlap between the two lists it returns the - first item in the client list. In either case it will signal whether an overlap - between the two lists was found. In the case where SSL_select_next_proto is - called with a zero length client list it fails to notice this condition and - returns the memory immediately following the client list pointer (and reports - that there was no overlap in the lists).

      -

      This function is typically called from a server side application callback for - ALPN or a client side application callback for NPN. In the case of ALPN the list - of protocols supplied by the client is guaranteed by libssl to never be zero in - length. The list of server protocols comes from the application and should never - normally be expected to be of zero length. In this case if the - SSL_select_next_proto function has been called as expected (with the list - supplied by the client passed in the client/client_len parameters), then the - application will not be vulnerable to this issue. If the application has - accidentally been configured with a zero length server list, and has - accidentally passed that zero length server list in the client/client_len - parameters, and has additionally failed to correctly handle a "no overlap" - response (which would normally result in a handshake failure in ALPN) then it - will be vulnerable to this problem.

      -

      In the case of NPN, the protocol permits the client to opportunistically select - a protocol when there is no overlap. OpenSSL returns the first client protocol - in the no overlap case in support of this. The list of client protocols comes - from the application and should never normally be expected to be of zero length. - However if the SSL_select_next_proto function is accidentally called with a - client_len of 0 then an invalid memory pointer will be returned instead. If the - application uses this output as the opportunistic protocol then the loss of - confidentiality will occur.

      -

      This issue has been assessed as Low severity because applications are most - likely to be vulnerable if they are using NPN instead of ALPN - but NPN is not - widely used. It also requires an application configuration or programming error. - Finally, this issue would not typically be under attacker control making active - exploitation unlikely.

      -

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

      -

      Due to the low severity of this issue we are not issuing new releases of - OpenSSL at this time. The fix will be included in the next releases when they - become available.

      -

      Remediation

      -

      Upgrade Alpine:3.19 openssl to version 3.1.6-r0 or higher.

      -

      References

      - +

      MPL-2.0 license


    -
    -

    CVE-2024-4741

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - openssl/libcrypto3 + github.com/go-sql-driver/mysql
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/dexidp/dex@* and github.com/go-sql-driver/mysql@v1.7.1
    @@ -2375,75 +2664,9 @@

    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 + github.com/dexidp/dex@* - openssl/libssl3@3.1.4-r2 + github.com/go-sql-driver/mysql@v1.7.1 @@ -2454,20 +2677,17 @@

      Detailed paths


      -

      NVD Description

      -

      This vulnerability has not been analyzed by NVD yet.

      -

      Remediation

      -

      Upgrade Alpine:3.19 openssl to version 3.1.6-r0 or higher.

      +

      MPL-2.0 license


    -

    CVE-2024-6119

    +

    CVE-2023-5363

    @@ -2478,7 +2698,7 @@

    CVE-2024-6119

    • - Package Manager: alpine:3.19 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -2488,7 +2708,7 @@

      CVE-2024-6119

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1
    @@ -2501,75 +2721,75 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - busybox/ssl_client@1.36.1-r15 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - busybox/ssl_client@1.36.1-r15 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1 @@ -2582,41 +2802,55 @@

      Detailed paths

      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: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

      -

      Impact summary: Abnormal termination of an application can a cause a denial of - service.

      -

      Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

      -

      Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

      -

      TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

      -

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

      + 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.19 openssl to version 3.1.7-r0 or higher.

      +

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

      References


    diff --git a/docs/snyk/v2.13.0-rc2/ghcr.io_dexidp_dex_v2.41.1.html b/docs/snyk/master/haproxy_2.6.14-alpine.html similarity index 70% rename from docs/snyk/v2.13.0-rc2/ghcr.io_dexidp_dex_v2.41.1.html rename to docs/snyk/master/haproxy_2.6.14-alpine.html index 6a121eec05819..19c8202ec7564 100644 --- a/docs/snyk/v2.13.0-rc2/ghcr.io_dexidp_dex_v2.41.1.html +++ b/docs/snyk/master/haproxy_2.6.14-alpine.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,55 +456,58 @@

    Snyk test report

    -

    September 22nd 2024, 12:21:32 am (UTC+00:00)

    +

    October 29th 2023, 12:15:02 am (UTC+00:00)

    - Scanned the following paths: + Scanned the following path:
      -
    • ghcr.io/dexidp/dex:v2.41.1/dexidp/dex (apk)
    • -
    • ghcr.io/dexidp/dex:v2.41.1/hairyhenderson/gomplate/v4//usr/local/bin/gomplate (gomodules)
    • -
    • ghcr.io/dexidp/dex:v2.41.1/dexidp/dex//usr/local/bin/docker-entrypoint (gomodules)
    • -
    • ghcr.io/dexidp/dex:v2.41.1/dexidp/dex//usr/local/bin/dex (gomodules)
    • +
    • haproxy:2.6.14-alpine (apk)
    -
    2 known vulnerabilities
    -
    8 vulnerable dependency paths
    -
    969 dependencies
    +
    1 known vulnerabilities
    +
    9 vulnerable dependency paths
    +
    18 dependencies
    - +
    + + + + + + + +
    Project docker-image|haproxy
    Path haproxy:2.6.14-alpine
    Package Manager apk
    +
    -
    -

    Insertion of Sensitive Information into Log File

    +
    +

    CVE-2023-5363

    -
    - medium severity +
    + low severity

    • - Manifest file: ghcr.io/dexidp/dex:v2.41.1/hairyhenderson/gomplate/v4 /usr/local/bin/gomplate -
    • -
    • - Package Manager: golang + Package Manager: alpine:3.18
    • Vulnerable module: - google.golang.org/grpc/metadata + openssl/libcrypto3
    • Introduced through: - github.com/hairyhenderson/gomplate/v4@* and google.golang.org/grpc/metadata@v1.64.0 + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0
    @@ -517,140 +520,97 @@

    Detailed paths

    • Introduced through: - github.com/hairyhenderson/gomplate/v4@* + docker-image|haproxy@2.6.14-alpine - google.golang.org/grpc/metadata@v1.64.0 + openssl/libcrypto3@3.1.2-r0
    • -
    - -
    - -
    - -

    Overview

    -

    google.golang.org/grpc/metadata is a package that defines the structure of the metadata supported by the gRPC library

    -

    Affected versions of this package are vulnerable to Insertion of Sensitive Information into Log File in the form of gRPC metadata. If the metadata contains sensitive information an attacker can expose it.

    -

    Remediation

    -

    Upgrade google.golang.org/grpc/metadata to version 1.64.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-6119

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: alpine:3.20 -
    • -
    • - Vulnerable module: - - openssl/libcrypto3 -
    • - -
    • Introduced through: - - docker-image|ghcr.io/dexidp/dex@v2.41.1 and openssl/libcrypto3@3.3.1-r3 - -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - openssl/libcrypto3@3.3.1-r3 + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.1-r3 + openssl/libcrypto3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - busybox/ssl_client@1.36.1-r29 + busybox/ssl_client@1.36.1-r2 - openssl/libcrypto3@3.3.1-r3 + openssl/libcrypto3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - apk-tools/apk-tools@2.14.4-r0 + .haproxy-rundeps@20230809.001942 - openssl/libssl3@3.3.1-r3 + openssl/libssl3@3.1.2-r0 - openssl/libcrypto3@3.3.1-r3 + openssl/libcrypto3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + 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.3.1-r3 + openssl/libssl3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.1-r3 + openssl/libssl3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - busybox/ssl_client@1.36.1-r29 + busybox/ssl_client@1.36.1-r2 - openssl/libssl3@3.3.1-r3 + openssl/libssl3@3.1.2-r0 @@ -663,41 +623,55 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      Issue summary: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

      -

      Impact summary: Abnormal termination of an application can a cause a denial of - service.

      -

      Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

      -

      Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

      -

      TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

      -

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

      + 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.20 openssl to version 3.3.2-r0 or higher.

      +

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

      References


    diff --git a/docs/snyk/master/quay.io_argoproj_argocd_latest.html b/docs/snyk/master/quay.io_argoproj_argocd_latest.html index b01bd7de71714..c9b59ef5e997f 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,23 +456,19 @@

    Snyk test report

    -

    September 22nd 2024, 12:19:38 am (UTC+00:00)

    +

    October 29th 2023, 12:15:33 am (UTC+00:00)

    Scanned the following paths:
      -
    • 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)
    • +
    • quay.io/argoproj/argocd:latest/argoproj/argocd (deb)
    • quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2 (gomodules)
    • quay.io/argoproj/argocd:latest (gomodules)
    • quay.io/argoproj/argocd:latest/helm/v3 (gomodules)
    • quay.io/argoproj/argocd:latest/git-lfs/git-lfs (gomodules)
    -
    11 known vulnerabilities
    -
    65 vulnerable dependency paths
    -
    2355 dependencies
    +
    28 known vulnerabilities
    +
    96 vulnerable dependency paths
    +
    2235 dependencies
    @@ -480,32 +476,29 @@

    Snyk test report

    -
    -

    CVE-2024-41996

    +
    +

    Denial of Service (DoS)

    -
    - medium severity +
    + high severity

    • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: golang
    • Vulnerable module: - openssl/libssl3t64 + golang.org/x/net/http2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and openssl/libssl3t64@3.0.13-0ubuntu3.4 + helm.sh/helm/v3@* and golang.org/x/net/http2@v0.13.0
    @@ -518,135 +511,86 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest + helm.sh/helm/v3@* - openssl/libssl3t64@3.0.13-0ubuntu3.4 + golang.org/x/net/http2@v0.13.0
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - coreutils@9.4-3ubuntu6 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - cyrus-sasl2/libsasl2-modules@2.1.28+dfsg1-5ubuntu3.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - +
  • - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - libfido2/libfido2-1@1.14.0-1build3 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - +
    + +

    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

    + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - openssh/openssh-client@1:9.6p1-3ubuntu13.5 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - +
    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - ca-certificates@20240203 - - openssl@3.0.13-0ubuntu3.4 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - libssh/libssh-4@0.10.6-2build2 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - +
  • +
    +

    CVE-2020-22916

    +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - +
    + medium severity +
    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - openldap/libldap2@2.6.7+dfsg-1~exp1ubuntu8 - - cyrus-sasl2/libsasl2-2@2.1.28+dfsg1-5ubuntu3.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - +
    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - openssl@3.0.13-0ubuntu3.4 - - +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: -
    • + 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 - ca-certificates@20240203 - - openssl@3.0.13-0ubuntu3.4 + xz-utils/liblzma5@5.2.5-2ubuntu1 @@ -658,28 +602,32 @@

      Detailed paths


      NVD Description

      -

      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:24.04 relevant fixed versions and status.

      -

      Validating the order of the public keys in the Diffie-Hellman Key Agreement Protocol, when an approved safe prime is used, allows remote attackers (from the client side) to trigger unnecessarily expensive server-side DHE modular-exponentiation calculations. The client may cause asymmetric resource consumption. The basic attack scenario is that the client must claim that it can only communicate with DHE, and the server must be configured to allow DHE and validate the order of the public key.

      +

      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.

      +

      ** DISPUTED ** 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:24.04 openssl.

      +

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

      References


  • -

    Information Exposure

    +

    Out-of-bounds Write

    @@ -690,21 +638,18 @@

    Information Exposure

    • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - libgcrypt20 + perl/perl-modules-5.34
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and libgcrypt20@1.10.3-2build1 + docker-image|quay.io/argoproj/argocd@latest, git@1:2.34.1-1ubuntu1.10 and others
    @@ -718,29 +663,11 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - libgcrypt20@1.10.3-2build1 - - - - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - gnupg2/dirmngr@2.4.4-2ubuntu17 - - libgcrypt20@1.10.3-2build1 - - - -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest + git@1:2.34.1-1ubuntu1.10 - gnupg2/gpg@2.4.4-2ubuntu17 + perl@5.34.0-3ubuntu1.2 - libgcrypt20@1.10.3-2build1 + perl/perl-modules-5.34@5.34.0-3ubuntu1.2 @@ -749,22 +676,13 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - gnupg2/gpg-agent@2.4.4-2ubuntu17 - - libgcrypt20@1.10.3-2build1 - - - -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest + git@1:2.34.1-1ubuntu1.10 - apt@2.7.14build2 + perl@5.34.0-3ubuntu1.2 - apt/libapt-pkg6.0t64@2.7.14build2 + perl/libperl5.34@5.34.0-3ubuntu1.2 - libgcrypt20@1.10.3-2build1 + perl/perl-modules-5.34@5.34.0-3ubuntu1.2 @@ -773,11 +691,11 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - apt@2.7.14build2 + git@1:2.34.1-1ubuntu1.10 - gnupg2/gpgv@2.4.4-2ubuntu17 + perl@5.34.0-3ubuntu1.2 - libgcrypt20@1.10.3-2build1 + perl/libperl5.34@5.34.0-3ubuntu1.2 @@ -786,11 +704,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - gnupg2/gpg@2.4.4-2ubuntu17 - - gnupg2/gpgconf@2.4.4-2ubuntu17 + git@1:2.34.1-1ubuntu1.10 - libgcrypt20@1.10.3-2build1 + perl@5.34.0-3ubuntu1.2 @@ -799,17 +715,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - apt@2.7.14build2 - - adduser@3.137ubuntu1 - - shadow/passwd@1:4.13+dfsg1-4ubuntu3 - - pam/libpam-modules@1.5.3-5ubuntu5.1 - - systemd/libsystemd0@255.4-1ubuntu8.4 - - libgcrypt20@1.10.3-2build1 + perl/perl-base@5.34.0-3ubuntu1.2 @@ -821,28 +727,27 @@

    Detailed paths


    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:24.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.

    +

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

    +

    In Perl 5.34.0, function S_find_uninit_var in sv.c has a stack-based crash that can lead to remote code execution or local privilege escalation.

    Remediation

    -

    There is no fixed version for Ubuntu:24.04 libgcrypt20.

    +

    There is no fixed version for Ubuntu:22.04 perl.

    References


  • -

    CVE-2024-26462

    +

    Access of Uninitialized Pointer

    @@ -853,10 +758,7 @@

    CVE-2024-26462

    • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: @@ -866,8 +768,8 @@

      CVE-2024-26462

    • Introduced through: + docker-image|quay.io/argoproj/argocd@latest and krb5/libk5crypto3@1.19.2-2ubuntu0.2 - docker-image|quay.io/argoproj/argocd@latest, git@1:2.43.0-1ubuntu7.1 and others
    @@ -881,13 +783,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + krb5/libk5crypto3@1.19.2-2ubuntu0.2 @@ -896,15 +792,19 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + libnsl/libnsl2@1.3.0-2build2 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + krb5/libk5crypto3@1.19.2-2ubuntu0.2 @@ -913,13 +813,21 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + krb5/libk5crypto3@1.19.2-2ubuntu0.2 @@ -928,15 +836,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 @@ -945,17 +845,19 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 + adduser@3.118ubuntu5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + shadow/passwd@1:4.8.1-2ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + pam/libpam-modules@1.4.0-11ubuntu2.3 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + libnsl/libnsl2@1.3.0-2build2 - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + krb5/libkrb5-3@1.19.2-2ubuntu0.2 @@ -964,13 +866,18 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + + +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + openssh/openssh-client@1:8.9p1-3ubuntu0.4 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 @@ -979,9 +886,11 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - openssh/openssh-client@1:9.6p1-3ubuntu13.5 + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 @@ -990,11 +899,13 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 + git@1:2.34.1-1ubuntu1.10 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 @@ -1003,13 +914,17 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + pam/libpam-modules@1.4.0-11ubuntu2.3 - libssh/libssh-4@0.10.6-2build2 + libnsl/libnsl2@1.3.0-2build2 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 @@ -1018,7 +933,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - krb5/krb5-locales@1.20.1-6ubuntu2.1 + krb5/libkrb5support0@1.19.2-2ubuntu0.2 @@ -1031,50 +946,51 @@

    Detailed paths

    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:24.04 relevant fixed versions and status.

    -

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

    + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    lib/kadm5/kadm_rpc_xdr.c in MIT Kerberos 5 (aka krb5) before 1.20.2 and 1.21.x before 1.21.1 frees an uninitialized pointer. A remote authenticated user can trigger a kadmind crash. This occurs because _xdr_kadm5_principal_ent_rec does not validate the relationship between n_key_data and the key_data array count.

    Remediation

    -

    There is no fixed version for Ubuntu:24.04 krb5.

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    References


  • -
    -

    Release of Invalid Pointer or Reference

    +
    +

    LGPL-3.0 license

    -
    - low severity +
    + medium severity

    • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile + Package Manager: golang
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: + Module: - patch + gopkg.in/retry.v1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and patch@2.7.6-7build3 + github.com/argoproj/argo-cd/v2@* and gopkg.in/retry.v1@v1.0.3
    @@ -1087,9 +1003,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest + github.com/argoproj/argo-cd/v2@* - patch@2.7.6-7build3 + gopkg.in/retry.v1@v1.0.3 @@ -1100,51 +1016,38 @@

      Detailed paths


      -

      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:24.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:24.04 patch.

      -

      References

      - +

      LGPL-3.0 license


    -
    -

    Double Free

    +
    +

    Memory Leak

    -
    - low severity +
    + medium severity

    • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - patch + glibc/libc-bin
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and patch@2.7.6-7build3 + docker-image|quay.io/argoproj/argocd@latest and glibc/libc-bin@2.35-0ubuntu3.4
    @@ -1159,7 +1062,16 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - patch@2.7.6-7build3 + glibc/libc-bin@2.35-0ubuntu3.4 + + + + +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + glibc/libc6@2.35-0ubuntu3.4 @@ -1171,56 +1083,55 @@

    Detailed paths


    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:24.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.

    +

    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.

    +

    A flaw was found in the GNU C Library. A recent fix for CVE-2023-4806 introduced the potential for a memory leak, which may result in an application crash.

    Remediation

    -

    There is no fixed version for Ubuntu:24.04 patch.

    +

    There is no fixed version for Ubuntu:22.04 glibc.

    References


  • -
    -

    CVE-2024-26458

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: golang
    • - Vulnerable module: + Module: - krb5/libk5crypto3 + github.com/r3labs/diff
    • Introduced through: + github.com/argoproj/argo-cd/v2@* and github.com/r3labs/diff@v1.1.0 - docker-image|quay.io/argoproj/argocd@latest, git@1:2.43.0-1ubuntu7.1 and others
    @@ -1230,17 +1141,1353 @@

    CVE-2024-26458

    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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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

    + +
    + + + +
    +
    +

    CVE-2022-46908

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + sqlite3/libsqlite3-0 +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@latest, gnupg2/gpg@2.2.27-3ubuntu2.1 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + sqlite3/libsqlite3-0@3.37.2-2ubuntu0.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    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

    + + +
    + + + +
    +
    +

    Arbitrary Code Injection

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + shadow/passwd +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and shadow/passwd@1:4.8.1-2ubuntu2.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + openssh/openssh-client@1:8.9p1-3ubuntu0.4 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + shadow/login@1:4.8.1-2ubuntu2.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream shadow package and not the shadow package as distributed by Ubuntu:22.04. + 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

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + procps/libprocps8 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and procps/libprocps8@2:3.3.17-6ubuntu2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + procps/libprocps8@2:3.3.17-6ubuntu2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + procps@2:3.3.17-6ubuntu2 + + procps/libprocps8@2:3.3.17-6ubuntu2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + procps@2:3.3.17-6ubuntu2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    Under some circumstances, this weakness allows a user who has access to run the “ps” utility on a machine, the ability to write almost unlimited amounts of unfiltered data into the process heap.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 procps.

    +

    References

    + + +
    + + + +
    +
    +

    Uncontrolled Recursion

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + pcre3/libpcre3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + 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:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + patch +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and patch@2.7.6-7build2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + 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:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + patch +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and patch@2.7.6-7build2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + 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:22.04. + 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-28531

    +
    + +
    + 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.4 + +
    • +
    + +
    + + +

    Detailed paths

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

    NVD Description

    +

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

    +

    ssh-add in OpenSSH before 9.3 adds smartcard keys to ssh-agent without the intended per-hop destination constraints. The earliest affected version is 8.9.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 openssh.

    +

    References

    + + +
    + + + +
    +
    +

    NULL Pointer Dereference

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + openldap/libldap-2.5-0 +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@latest, gnupg2/dirmngr@2.2.27-3ubuntu2.1 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 + + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + openldap/libldap-common@2.5.16+dfsg-0ubuntu0.22.04.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    A vulnerability was found in openldap. This security flaw causes a null pointer dereference in ber_memalloc_x() function.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 openldap.

    +

    References

    + + +
    + + + +
    +
    +

    Resource Exhaustion

    +
    + +
    + low severity +
    + +
    + +
      +
    • + 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

    + + +
    + + + +
    +
    +

    Integer Overflow or Wraparound

    +
    + +
    + low severity +
    + +
    + +
      +
    • + 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.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libk5crypto3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + 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.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + krb5/libk5crypto3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + 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.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + krb5/libkrb5-3@1.19.2-2ubuntu0.2 + + krb5/libk5crypto3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libkrb5-3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + 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.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + krb5/libkrb5-3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + openssh/openssh-client@1:8.9p1-3ubuntu0.4 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + 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.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libkrb5support0@1.19.2-2ubuntu0.2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnupg2/gpgv +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and gnupg2/gpgv@2.2.27-3ubuntu2.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + apt@2.4.10 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpgconf@2.2.27-3ubuntu2.1 @@ -1249,15 +2496,11 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpgsm@2.2.27-3ubuntu2.1 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpgconf@2.2.27-3ubuntu2.1 @@ -1266,13 +2509,7 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 @@ -1281,15 +2518,9 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 @@ -1298,17 +2529,11 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 @@ -1317,13 +2542,7 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 @@ -1332,9 +2551,9 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - openssh/openssh-client@1:9.6p1-3ubuntu13.5 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 @@ -1343,11 +2562,7 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 @@ -1356,13 +2571,9 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - libssh/libssh-4@0.10.6-2build2 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 @@ -1371,84 +2582,18 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - krb5/krb5-locales@1.20.1-6ubuntu2.1 + gnupg2/gpg@2.2.27-3ubuntu2.1
    • -
    - -
    - -
    - -

    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:24.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:24.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-26461

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@latest, git@1:2.43.0-1ubuntu7.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpg@2.2.27-3ubuntu2.1 @@ -1457,15 +2602,11 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpg@2.2.27-3ubuntu2.1 @@ -1474,13 +2615,11 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/gpg@2.2.27-3ubuntu2.1 @@ -1489,15 +2628,7 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 @@ -1506,17 +2637,22 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 @@ -1525,13 +2661,20 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 @@ -1540,9 +2683,9 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - openssh/openssh-client@1:9.6p1-3ubuntu13.5 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 @@ -1551,11 +2694,18 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 @@ -1564,13 +2714,18 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest - libssh/libssh-4@0.10.6-2build2 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpgsm@2.2.27-3ubuntu2.1 @@ -1579,7 +2734,7 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - krb5/krb5-locales@1.20.1-6ubuntu2.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -1591,27 +2746,31 @@

      Detailed paths


      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:24.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.

      +

      Note: Versions mentioned in the description apply only to the upstream gnupg2 package and not the gnupg2 package as distributed by Ubuntu:22.04. + 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:24.04 krb5.

      +

      There is no fixed version for Ubuntu:22.04 gnupg2.

      References


    -

    Out-of-bounds Write

    +

    Allocation of Resources Without Limits or Throttling

    @@ -1622,20 +2781,17 @@

    Out-of-bounds Write

    • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - gnupg2/gpgv + glibc/libc-bin
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and gnupg2/gpgv@2.4.4-2ubuntu17 + docker-image|quay.io/argoproj/argocd@latest and glibc/libc-bin@2.35-0ubuntu3.4
    @@ -1650,7 +2806,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - gnupg2/gpgv@2.4.4-2ubuntu17 + glibc/libc-bin@2.35-0ubuntu3.4 @@ -1659,42 +2815,78 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - apt@2.7.14build2 - - gnupg2/gpgv@2.4.4-2ubuntu17 + glibc/libc6@2.35-0ubuntu3.4 -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - gnupg2/dirmngr@2.4.4-2ubuntu17 - - gnupg2/gpgconf@2.4.4-2ubuntu17 - - + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - gnupg2/gpg-agent@2.4.4-2ubuntu17 - - gnupg2/gpgconf@2.4.4-2ubuntu17 - - +
  • - +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + git/git-man +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@latest, git@1:2.34.1-1ubuntu1.10 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    -

    Allocation of Resources Without Limits or Throttling

    +

    Uncontrolled Recursion

    @@ -1768,20 +2949,17 @@

    Allocation of Resources Without Limits or Throttling

  • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile -
  • -
  • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
  • Vulnerable module: - glibc/libc-bin + gcc-12/libstdc++6
  • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and glibc/libc-bin@2.39-0ubuntu8.3 + docker-image|quay.io/argoproj/argocd@latest and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04
  • @@ -1796,7 +2974,40 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - glibc/libc-bin@2.39-0ubuntu8.3 + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + + +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + apt@2.4.10 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + apt@2.4.10 + + apt/libapt-pkg6.0@2.4.10 + + 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 @@ -1805,7 +3016,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - glibc/libc6@2.39-0ubuntu8.3 + gcc-12/libgcc-s1@12.3.0-1ubuntu1~22.04 @@ -1817,23 +3028,23 @@

    Detailed paths


    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:24.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.

    +

    Note: Versions mentioned in the description apply only to the upstream gcc-12 package and not the gcc-12 package as distributed by Ubuntu:22.04. + 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:24.04 glibc.

    +

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

    References


  • @@ -1849,21 +3060,18 @@

    Improper Input Validation

    • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - git/git-man + coreutils
    • Introduced through: + docker-image|quay.io/argoproj/argocd@latest and coreutils@8.32-4.1ubuntu1 - docker-image|quay.io/argoproj/argocd@latest, git@1:2.43.0-1ubuntu7.1 and others
    @@ -1877,29 +3085,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.43.0-1ubuntu7.1 - - git/git-man@1:2.43.0-1ubuntu7.1 - - - - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - git@1:2.43.0-1ubuntu7.1 - - - -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - git-lfs@3.4.1-1ubuntu0.1 - - git@1:2.43.0-1ubuntu7.1 + coreutils@8.32-4.1ubuntu1 @@ -1911,27 +3097,29 @@

    Detailed paths


    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:24.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).

    +

    Note: Versions mentioned in the description apply only to the upstream coreutils package and not the coreutils package as distributed by Ubuntu:22.04. + 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:24.04 git.

    +

    There is no fixed version for Ubuntu:22.04 coreutils.

    References


  • -

    Improper Input Validation

    +

    Out-of-bounds Write

    @@ -1942,20 +3130,17 @@

    Improper Input Validation

    • - Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - coreutils + bash
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and coreutils@9.4-3ubuntu6 + docker-image|quay.io/argoproj/argocd@latest and bash@5.1-6ubuntu1
    @@ -1970,7 +3155,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - coreutils@9.4-3ubuntu6 + bash@5.1-6ubuntu1 @@ -1982,25 +3167,21 @@

    Detailed paths


    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:24.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.

    +

    Note: Versions mentioned in the description apply only to the upstream bash package and not the bash package as distributed by Ubuntu:22.04. + 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

    -

    There is no fixed version for Ubuntu:24.04 coreutils.

    +

    There is no fixed version for Ubuntu:22.04 bash.

    References


    diff --git a/docs/snyk/v2.13.0-rc2/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html b/docs/snyk/master/redis_7.0.11-alpine.html similarity index 64% rename from docs/snyk/v2.13.0-rc2/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html rename to docs/snyk/master/redis_7.0.11-alpine.html index 8197e5ec4909e..5409d26e74695 100644 --- a/docs/snyk/v2.13.0-rc2/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html +++ b/docs/snyk/master/redis_7.0.11-alpine.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,18 +456,18 @@

    Snyk test report

    -

    September 22nd 2024, 12:21:37 am (UTC+00:00)

    +

    October 29th 2023, 12:15:46 am (UTC+00:00)

    Scanned the following path:
      -
    • public.ecr.aws/docker/library/haproxy:2.6.17-alpine/docker/library/haproxy (apk)
    • +
    • redis:7.0.11-alpine (apk)
    5 known vulnerabilities
    -
    42 vulnerable dependency paths
    +
    41 vulnerable dependency paths
    18 dependencies
    @@ -476,8 +476,8 @@

    Snyk test report

    - - + + @@ -485,19 +485,19 @@

    Snyk test report

    -
    -

    Use After Free

    +
    +

    Out-of-bounds Write

    -
    - medium severity +
    + critical severity

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -507,7 +507,7 @@

      Use After Free

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and busybox/busybox@1.36.1-r28 + docker-image|redis@7.0.11-alpine and busybox/busybox@1.36.1-r0
    @@ -520,62 +520,51 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/busybox@1.36.1-r28 + busybox/busybox@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 + alpine-baselayout/alpine-baselayout@3.4.3-r1 - busybox/busybox-binsh@1.36.1-r28 - - busybox/busybox@1.36.1-r28 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + busybox/busybox-binsh@1.36.1-r0 - busybox/busybox-binsh@1.36.1-r28 + busybox/busybox@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 - - busybox/busybox-binsh@1.36.1-r28 + busybox/busybox-binsh@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + alpine-baselayout/alpine-baselayout@3.4.3-r1 - busybox/busybox-binsh@1.36.1-r28 + busybox/busybox-binsh@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 @@ -588,24 +577,24 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      A use-after-free vulnerability in BusyBox v.1.36.1 allows attackers to cause a denial of service via a crafted awk pattern in the awk.c evaluate function.

      + 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.20 busybox to version 1.36.1-r29 or higher.

      +

      Upgrade Alpine:3.18 busybox to version 1.36.1-r1 or higher.

      References


    -

    Use After Free

    +

    Improper Authentication

    @@ -616,17 +605,17 @@

    Use After Free

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: - busybox/busybox + openssl/libcrypto3
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and busybox/busybox@1.36.1-r28 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -639,62 +628,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine - busybox/busybox@1.36.1-r28 + .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|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 + .redis-rundeps@20230614.215749 - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1 - busybox/busybox@1.36.1-r28 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 + .redis-rundeps@20230614.215749 - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + apk-tools/apk-tools@2.14.0-r2 - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 - busybox/ssl_client@1.36.1-r28 + openssl/libssl3@3.1.1-r1 @@ -706,36 +730,57 @@

      Detailed paths


      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.20 relevant fixed versions and status.

      -

      A use-after-free vulnerability was discovered in BusyBox v.1.36.1 via a crafted awk pattern in the awk.c copyvar function.

      +

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine:3.18. + 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.20 busybox to version 1.36.1-r29 or higher.

      +

      Upgrade Alpine:3.18 openssl to version 3.1.1-r2 or higher.

      References


    -
    -

    CVE-2024-4741

    +
    +

    Inefficient Regular Expression Complexity

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -745,7 +790,7 @@

      CVE-2024-4741

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and openssl/libcrypto3@3.3.0-r2 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -758,108 +803,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - openssl/libcrypto3@3.3.0-r2 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - .haproxy-rundeps@20240524.005458 + docker-image|redis@7.0.11-alpine - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + .redis-rundeps@20230614.215749 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 @@ -871,30 +905,67 @@

      Detailed paths


      NVD Description

      -

      This vulnerability has not been analyzed by NVD yet.

      +

      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.20 openssl to version 3.3.0-r3 or higher.

      +

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

      +

      References

      +
    -
    -

    CVE-2024-5535

    +
    +

    Excessive Iteration

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -904,7 +975,7 @@

      CVE-2024-5535

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and openssl/libcrypto3@3.3.0-r2 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -917,108 +988,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 - - openssl/libcrypto3@3.3.0-r2 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + .redis-rundeps@20230614.215749 - .haproxy-rundeps@20240524.005458 + openssl/libssl3@3.1.1-r1 - openssl/libssl3@3.3.0-r2 - - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 @@ -1031,87 +1091,54 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      Issue summary: Calling the OpenSSL API function SSL_select_next_proto with an - empty supported client protocols buffer may cause a crash or memory contents to - be sent to the peer.

      -

      Impact summary: A buffer overread can have a range of potential consequences - such as unexpected application beahviour or a crash. In particular this issue - could result in up to 255 bytes of arbitrary private data from memory being sent - to the peer leading to a loss of confidentiality. However, only applications - that directly call the SSL_select_next_proto function with a 0 length list of - supported client protocols are affected by this issue. This would normally never - be a valid scenario and is typically not under attacker control but may occur by - accident in the case of a configuration or programming error in the calling - application.

      -

      The OpenSSL API function SSL_select_next_proto is typically used by TLS - applications that support ALPN (Application Layer Protocol Negotiation) or NPN - (Next Protocol Negotiation). NPN is older, was never standardised and - is deprecated in favour of ALPN. We believe that ALPN is significantly more - widely deployed than NPN. The SSL_select_next_proto function accepts a list of - protocols from the server and a list of protocols from the client and returns - the first protocol that appears in the server list that also appears in the - client list. In the case of no overlap between the two lists it returns the - first item in the client list. In either case it will signal whether an overlap - between the two lists was found. In the case where SSL_select_next_proto is - called with a zero length client list it fails to notice this condition and - returns the memory immediately following the client list pointer (and reports - that there was no overlap in the lists).

      -

      This function is typically called from a server side application callback for - ALPN or a client side application callback for NPN. In the case of ALPN the list - of protocols supplied by the client is guaranteed by libssl to never be zero in - length. The list of server protocols comes from the application and should never - normally be expected to be of zero length. In this case if the - SSL_select_next_proto function has been called as expected (with the list - supplied by the client passed in the client/client_len parameters), then the - application will not be vulnerable to this issue. If the application has - accidentally been configured with a zero length server list, and has - accidentally passed that zero length server list in the client/client_len - parameters, and has additionally failed to correctly handle a "no overlap" - response (which would normally result in a handshake failure in ALPN) then it - will be vulnerable to this problem.

      -

      In the case of NPN, the protocol permits the client to opportunistically select - a protocol when there is no overlap. OpenSSL returns the first client protocol - in the no overlap case in support of this. The list of client protocols comes - from the application and should never normally be expected to be of zero length. - However if the SSL_select_next_proto function is accidentally called with a - client_len of 0 then an invalid memory pointer will be returned instead. If the - application uses this output as the opportunistic protocol then the loss of - confidentiality will occur.

      -

      This issue has been assessed as Low severity because applications are most - likely to be vulnerable if they are using NPN instead of ALPN - but NPN is not - widely used. It also requires an application configuration or programming error. - Finally, this issue would not typically be under attacker control making active - exploitation unlikely.

      -

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

      -

      Due to the low severity of this issue we are not issuing new releases of - OpenSSL at this time. The fix will be included in the next releases when they - become available.

      + 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.20 openssl to version 3.3.1-r1 or higher.

      +

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

      References


    -

    CVE-2024-6119

    +

    CVE-2023-5363

    @@ -1122,7 +1149,7 @@

    CVE-2024-6119

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -1132,7 +1159,7 @@

      CVE-2024-6119

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and openssl/libcrypto3@3.3.0-r2 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -1145,108 +1172,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - openssl/libcrypto3@3.3.0-r2 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - .haproxy-rundeps@20240524.005458 + docker-image|redis@7.0.11-alpine - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + .redis-rundeps@20230614.215749 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 @@ -1259,41 +1275,55 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      Issue summary: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

      -

      Impact summary: Abnormal termination of an application can a cause a denial of - service.

      -

      Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

      -

      Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

      -

      TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

      -

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

      + 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.20 openssl to version 3.3.2-r0 or higher.

      +

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

      References


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

    Snyk test report

    - -

    September 22nd 2024, 12:19:42 am (UTC+00:00)

    -
    -
    - Scanned the following paths: -
      -
    • redis:7.0.15-alpine (apk)
    • -
    • redis:7.0.15-alpine/tianon/gosu//usr/local/bin/gosu (gomodules)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    - -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.10.16/argocd-iac-install.html b/docs/snyk/v2.10.16/argocd-iac-install.html deleted file mode 100644 index caf0aceb5972f..0000000000000 --- a/docs/snyk/v2.10.16/argocd-iac-install.html +++ /dev/null @@ -1,2891 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:30:18 am (UTC+00:00)

    -
    -
    - Scanned the following path: -
      -
    • /argo-cd/manifests/install.yaml (Kubernetes)
    • -
    -
    - -
    -
    44 total issues
    -
    -
    -
    -
    - -
    -
    Project docker-image|public.ecr.aws/docker/library/haproxy
    Path public.ecr.aws/docker/library/haproxy:2.6.17-alpine/docker/library/haproxy
    Project docker-image|redis
    Path redis:7.0.11-alpine
    Package Manager apk
    - - - - - -
    Project manifests/install.yaml
    Path /argo-cd/manifests/install.yaml
    Project Type Kubernetes
    -
    -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-42 -
    • - -
    • Introduced through: - [DocId: 48] - - spec - - template - - spec - - initContainers[secret-init] - - imagePullPolicy - -
    • - -
    • - Line number: 21827 -
    • -
    - -
    - -

    Impact

    -

    The container may run with outdated or unauthorized image

    - -

    Remediation

    -

    Set `imagePullPolicy` attribute to `Always`

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

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

    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: 45] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21388 -
    • -
    - -
    - -

    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 - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21639 -
    • -
    - -
    - -

    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[dex] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21605 -
    • -
    - -
    - -

    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-notifications-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21699 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21798 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21822 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22108 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21879 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22193 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22544 -
    • -
    - -
    - -

    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: 46] - - spec - - template - - spec - - containers[dex] - - ports - -
    • - -
    • - Line number: 21619 -
    • -
    - -
    - -

    Impact

    -

    Increases the attack surface of the application and the container.

    - -

    Remediation

    -

    Reduce `ports` count to 2

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

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

    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: 46] - - spec - - template - - spec - - containers[dex] - - livenessProbe - -
    • - -
    • - Line number: 21605 -
    • -
    - -
    - -

    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: 48] - - spec - - template - - spec - - containers[redis] - - livenessProbe - -
    • - -
    • - Line number: 21798 -
    • -
    - -
    - -

    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: 45] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21388 -
    • -
    - -
    - -

    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[dex] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21605 -
    • -
    - -
    - -

    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 - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21639 -
    • -
    - -
    - -

    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-notifications-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21699 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21798 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21822 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22108 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21879 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22193 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22544 -
    • -
    - -
    - -

    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'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-applicationset-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21529 -
    • -
    - -
    - -

    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: 21647 -
    • -
    - -
    - -

    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[dex] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21622 -
    • -
    - -
    - -

    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-notifications-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21732 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21815 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21829 -
    • -
    - -
    - -

    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] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22115 -
    • -
    - -
    - -

    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] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22081 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22454 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22745 -
    • -
    - -
    - -

    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.10.16/ghcr.io_dexidp_dex_v2.37.0.html b/docs/snyk/v2.10.16/ghcr.io_dexidp_dex_v2.37.0.html deleted file mode 100644 index f6beb50189acb..0000000000000 --- a/docs/snyk/v2.10.16/ghcr.io_dexidp_dex_v2.37.0.html +++ /dev/null @@ -1,4361 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:28:32 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)
    • -
    -
    - -
    -
    33 known vulnerabilities
    -
    138 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

    - - -
    - - - -
    -
    -

    Allocation of Resources Without Limits or Throttling

    -
    - -
    - 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 Allocation of Resources Without Limits or Throttling when reading header data from CONTINUATION frames. As part of the HPACK flow, all incoming HEADERS and CONTINUATION frames are read even if their payloads exceed MaxHeaderBytes and will be discarded. An attacker can send excessive data over a connection to render it unresponsive.

    -

    Remediation

    -

    Upgrade golang.org/x/net/http2 to version 0.23.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

    - - -
    - - - -
    -
    -

    Insertion of Sensitive Information into Log File

    -
    - -
    - 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/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 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    Affected versions of this package are vulnerable to Insertion of Sensitive Information into Log File due to not sanitizing urls when writing them to the log file. This could lead to an attacker writing sensitive HTTP basic auth credentials to the log file.

    -

    Remediation

    -

    Upgrade github.com/hashicorp/go-retryablehttp to version 0.7.7 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    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

    - - -
    - - - -
    -
    -

    Out-of-bounds Write

    -
    - -
    - medium 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.

    -

    A heap-buffer-overflow was discovered in BusyBox v.1.36.1 in the next_token function at awk.c:1159.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r6 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - medium 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.

    -

    A use-after-free vulnerability was discovered in xasprintf function in xfuncs_printf.c:344 in BusyBox v.1.36.1.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r7 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - medium 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.

    -

    A use-after-free vulnerability in BusyBox v.1.36.1 allows attackers to cause a denial of service via a crafted awk pattern in the awk.c evaluate function.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r7 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - medium 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.

    -

    A use-after-free vulnerability was discovered in BusyBox v.1.36.1 via a crafted awk pattern in the awk.c copyvar function.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r7 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

    -

    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 invalid RSA public keys may take - a long time.

    -

    Impact summary: Applications that use the function EVP_PKEY_public_check() - to check RSA public keys may experience long delays. Where the key that - is being checked has been obtained from an untrusted source this may lead - to a Denial of Service.

    -

    When function EVP_PKEY_public_check() is called on RSA public keys, - a computation is done to confirm that the RSA modulus, n, is composite. - For valid RSA keys, n is a product of two or more large primes and this - computation completes quickly. However, if n is an overly large prime, - then this computation would take a long time.

    -

    An application that calls EVP_PKEY_public_check() and supplies an RSA key - obtained from an untrusted source could be vulnerable to a Denial of Service - attack.

    -

    The function EVP_PKEY_public_check() is not called from other OpenSSL - functions however it is called from the OpenSSL pkey command line - application. For that reason that application is also vulnerable if used - with the '-pubin' and '-check' options on untrusted data.

    -

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

    -

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

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-2511

    -
    - -
    - 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

    -

    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: Some non-default TLS server configurations can cause unbounded - memory growth when processing TLSv1.3 sessions

    -

    Impact summary: An attacker may exploit certain server configurations to trigger - unbounded memory growth that would lead to a Denial of Service

    -

    This problem can occur in TLSv1.3 if the non-default SSL_OP_NO_TICKET option is - being used (but not if early_data support is also configured and the default - anti-replay protection is in use). In this case, under certain conditions, the - session cache can get into an incorrect state and it will fail to flush properly - as it fills. The session cache will continue to grow in an unbounded manner. A - malicious client could deliberately create the scenario for this failure to - force a Denial of Service. It may also happen by accident in normal operation.

    -

    This issue only affects TLS servers supporting TLSv1.3. It does not affect TLS - clients.

    -

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue. OpenSSL - 1.0.2 is also not affected by this issue.

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-4603

    -
    - -
    - 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

    -

    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 DSA keys or parameters may be very - slow.

    -

    Impact summary: Applications that use the functions EVP_PKEY_param_check() - or EVP_PKEY_public_check() to check a DSA public key or DSA 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 functions EVP_PKEY_param_check() or EVP_PKEY_public_check() perform - various checks on DSA parameters. Some of those computations take a long time - if the modulus (p parameter) is too large.

    -

    Trying to use a very large modulus is slow and OpenSSL will not allow using - public keys with a modulus which is over 10,000 bits in length for signature - verification. However the key and parameter check functions do not limit - the modulus size when performing the checks.

    -

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

    -

    These functions are not called by OpenSSL itself on untrusted DSA keys so - only applications that directly call these functions may be vulnerable.

    -

    Also vulnerable are the OpenSSL pkey 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 affected by this issue.

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-5535

    -
    - -
    - 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

    -

    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: Calling the OpenSSL API function SSL_select_next_proto with an - empty supported client protocols buffer may cause a crash or memory contents to - be sent to the peer.

    -

    Impact summary: A buffer overread can have a range of potential consequences - such as unexpected application beahviour or a crash. In particular this issue - could result in up to 255 bytes of arbitrary private data from memory being sent - to the peer leading to a loss of confidentiality. However, only applications - that directly call the SSL_select_next_proto function with a 0 length list of - supported client protocols are affected by this issue. This would normally never - be a valid scenario and is typically not under attacker control but may occur by - accident in the case of a configuration or programming error in the calling - application.

    -

    The OpenSSL API function SSL_select_next_proto is typically used by TLS - applications that support ALPN (Application Layer Protocol Negotiation) or NPN - (Next Protocol Negotiation). NPN is older, was never standardised and - is deprecated in favour of ALPN. We believe that ALPN is significantly more - widely deployed than NPN. The SSL_select_next_proto function accepts a list of - protocols from the server and a list of protocols from the client and returns - the first protocol that appears in the server list that also appears in the - client list. In the case of no overlap between the two lists it returns the - first item in the client list. In either case it will signal whether an overlap - between the two lists was found. In the case where SSL_select_next_proto is - called with a zero length client list it fails to notice this condition and - returns the memory immediately following the client list pointer (and reports - that there was no overlap in the lists).

    -

    This function is typically called from a server side application callback for - ALPN or a client side application callback for NPN. In the case of ALPN the list - of protocols supplied by the client is guaranteed by libssl to never be zero in - length. The list of server protocols comes from the application and should never - normally be expected to be of zero length. In this case if the - SSL_select_next_proto function has been called as expected (with the list - supplied by the client passed in the client/client_len parameters), then the - application will not be vulnerable to this issue. If the application has - accidentally been configured with a zero length server list, and has - accidentally passed that zero length server list in the client/client_len - parameters, and has additionally failed to correctly handle a "no overlap" - response (which would normally result in a handshake failure in ALPN) then it - will be vulnerable to this problem.

    -

    In the case of NPN, the protocol permits the client to opportunistically select - a protocol when there is no overlap. OpenSSL returns the first client protocol - in the no overlap case in support of this. The list of client protocols comes - from the application and should never normally be expected to be of zero length. - However if the SSL_select_next_proto function is accidentally called with a - client_len of 0 then an invalid memory pointer will be returned instead. If the - application uses this output as the opportunistic protocol then the loss of - confidentiality will occur.

    -

    This issue has been assessed as Low severity because applications are most - likely to be vulnerable if they are using NPN instead of ALPN - but NPN is not - widely used. It also requires an application configuration or programming error. - Finally, this issue would not typically be under attacker control making active - exploitation unlikely.

    -

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

    -

    Due to the low severity of this issue we are not issuing new releases of - OpenSSL at this time. The fix will be included in the next releases when they - become available.

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-4741

    -
    - -
    - 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.6-r0 or higher.

    - -
    - - - -
    -
    -

    CVE-2024-6119

    -
    - -
    - 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

    -

    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: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

    -

    Impact summary: Abnormal termination of an application can a cause a denial of - service.

    -

    Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

    -

    Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

    -

    TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

    -

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

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -
    -
    - - - diff --git a/docs/snyk/v2.10.16/haproxy_2.6.14-alpine.html b/docs/snyk/v2.10.16/haproxy_2.6.14-alpine.html deleted file mode 100644 index 9020dc61d54b1..0000000000000 --- a/docs/snyk/v2.10.16/haproxy_2.6.14-alpine.html +++ /dev/null @@ -1,2741 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:28:38 am (UTC+00:00)

    -
    -
    - Scanned the following path: -
      -
    • haproxy:2.6.14-alpine (apk)
    • -
    -
    - -
    -
    14 known vulnerabilities
    -
    110 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

    - - -
    - - - -
    -
    -

    Out-of-bounds Write

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: alpine:3.18 -
    • -
    • - Vulnerable module: - - busybox/busybox -
    • - -
    • Introduced through: - - docker-image|haproxy@2.6.14-alpine and busybox/busybox@1.36.1-r2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/ssl_client@1.36.1-r2 - - - -
    • -
    - -
    - -
    - -

    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.

    -

    A heap-buffer-overflow was discovered in BusyBox v.1.36.1 in the next_token function at awk.c:1159.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r6 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: alpine:3.18 -
    • -
    • - Vulnerable module: - - busybox/busybox -
    • - -
    • Introduced through: - - docker-image|haproxy@2.6.14-alpine and busybox/busybox@1.36.1-r2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/ssl_client@1.36.1-r2 - - - -
    • -
    - -
    - -
    - -

    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.

    -

    A use-after-free vulnerability was discovered in xasprintf function in xfuncs_printf.c:344 in BusyBox v.1.36.1.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r7 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: alpine:3.18 -
    • -
    • - Vulnerable module: - - busybox/busybox -
    • - -
    • Introduced through: - - docker-image|haproxy@2.6.14-alpine and busybox/busybox@1.36.1-r2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/ssl_client@1.36.1-r2 - - - -
    • -
    - -
    - -
    - -

    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.

    -

    A use-after-free vulnerability in BusyBox v.1.36.1 allows attackers to cause a denial of service via a crafted awk pattern in the awk.c evaluate function.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r7 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: alpine:3.18 -
    • -
    • - Vulnerable module: - - busybox/busybox -
    • - -
    • Introduced through: - - docker-image|haproxy@2.6.14-alpine and busybox/busybox@1.36.1-r2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/ssl_client@1.36.1-r2 - - - -
    • -
    - -
    - -
    - -

    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.

    -

    A use-after-free vulnerability was discovered in BusyBox v.1.36.1 via a crafted awk pattern in the awk.c copyvar function.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r7 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

    -

    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 invalid RSA public keys may take - a long time.

    -

    Impact summary: Applications that use the function EVP_PKEY_public_check() - to check RSA public keys may experience long delays. Where the key that - is being checked has been obtained from an untrusted source this may lead - to a Denial of Service.

    -

    When function EVP_PKEY_public_check() is called on RSA public keys, - a computation is done to confirm that the RSA modulus, n, is composite. - For valid RSA keys, n is a product of two or more large primes and this - computation completes quickly. However, if n is an overly large prime, - then this computation would take a long time.

    -

    An application that calls EVP_PKEY_public_check() and supplies an RSA key - obtained from an untrusted source could be vulnerable to a Denial of Service - attack.

    -

    The function EVP_PKEY_public_check() is not called from other OpenSSL - functions however it is called from the OpenSSL pkey command line - application. For that reason that application is also vulnerable if used - with the '-pubin' and '-check' options on untrusted data.

    -

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

    -

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

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-2511

    -
    - -
    - 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

    -

    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: Some non-default TLS server configurations can cause unbounded - memory growth when processing TLSv1.3 sessions

    -

    Impact summary: An attacker may exploit certain server configurations to trigger - unbounded memory growth that would lead to a Denial of Service

    -

    This problem can occur in TLSv1.3 if the non-default SSL_OP_NO_TICKET option is - being used (but not if early_data support is also configured and the default - anti-replay protection is in use). In this case, under certain conditions, the - session cache can get into an incorrect state and it will fail to flush properly - as it fills. The session cache will continue to grow in an unbounded manner. A - malicious client could deliberately create the scenario for this failure to - force a Denial of Service. It may also happen by accident in normal operation.

    -

    This issue only affects TLS servers supporting TLSv1.3. It does not affect TLS - clients.

    -

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue. OpenSSL - 1.0.2 is also not affected by this issue.

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-4603

    -
    - -
    - 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

    -

    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 DSA keys or parameters may be very - slow.

    -

    Impact summary: Applications that use the functions EVP_PKEY_param_check() - or EVP_PKEY_public_check() to check a DSA public key or DSA 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 functions EVP_PKEY_param_check() or EVP_PKEY_public_check() perform - various checks on DSA parameters. Some of those computations take a long time - if the modulus (p parameter) is too large.

    -

    Trying to use a very large modulus is slow and OpenSSL will not allow using - public keys with a modulus which is over 10,000 bits in length for signature - verification. However the key and parameter check functions do not limit - the modulus size when performing the checks.

    -

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

    -

    These functions are not called by OpenSSL itself on untrusted DSA keys so - only applications that directly call these functions may be vulnerable.

    -

    Also vulnerable are the OpenSSL pkey 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 affected by this issue.

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-5535

    -
    - -
    - 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

    -

    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: Calling the OpenSSL API function SSL_select_next_proto with an - empty supported client protocols buffer may cause a crash or memory contents to - be sent to the peer.

    -

    Impact summary: A buffer overread can have a range of potential consequences - such as unexpected application beahviour or a crash. In particular this issue - could result in up to 255 bytes of arbitrary private data from memory being sent - to the peer leading to a loss of confidentiality. However, only applications - that directly call the SSL_select_next_proto function with a 0 length list of - supported client protocols are affected by this issue. This would normally never - be a valid scenario and is typically not under attacker control but may occur by - accident in the case of a configuration or programming error in the calling - application.

    -

    The OpenSSL API function SSL_select_next_proto is typically used by TLS - applications that support ALPN (Application Layer Protocol Negotiation) or NPN - (Next Protocol Negotiation). NPN is older, was never standardised and - is deprecated in favour of ALPN. We believe that ALPN is significantly more - widely deployed than NPN. The SSL_select_next_proto function accepts a list of - protocols from the server and a list of protocols from the client and returns - the first protocol that appears in the server list that also appears in the - client list. In the case of no overlap between the two lists it returns the - first item in the client list. In either case it will signal whether an overlap - between the two lists was found. In the case where SSL_select_next_proto is - called with a zero length client list it fails to notice this condition and - returns the memory immediately following the client list pointer (and reports - that there was no overlap in the lists).

    -

    This function is typically called from a server side application callback for - ALPN or a client side application callback for NPN. In the case of ALPN the list - of protocols supplied by the client is guaranteed by libssl to never be zero in - length. The list of server protocols comes from the application and should never - normally be expected to be of zero length. In this case if the - SSL_select_next_proto function has been called as expected (with the list - supplied by the client passed in the client/client_len parameters), then the - application will not be vulnerable to this issue. If the application has - accidentally been configured with a zero length server list, and has - accidentally passed that zero length server list in the client/client_len - parameters, and has additionally failed to correctly handle a "no overlap" - response (which would normally result in a handshake failure in ALPN) then it - will be vulnerable to this problem.

    -

    In the case of NPN, the protocol permits the client to opportunistically select - a protocol when there is no overlap. OpenSSL returns the first client protocol - in the no overlap case in support of this. The list of client protocols comes - from the application and should never normally be expected to be of zero length. - However if the SSL_select_next_proto function is accidentally called with a - client_len of 0 then an invalid memory pointer will be returned instead. If the - application uses this output as the opportunistic protocol then the loss of - confidentiality will occur.

    -

    This issue has been assessed as Low severity because applications are most - likely to be vulnerable if they are using NPN instead of ALPN - but NPN is not - widely used. It also requires an application configuration or programming error. - Finally, this issue would not typically be under attacker control making active - exploitation unlikely.

    -

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

    -

    Due to the low severity of this issue we are not issuing new releases of - OpenSSL at this time. The fix will be included in the next releases when they - become available.

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-4741

    -
    - -
    - 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.6-r0 or higher.

    - -
    - - - -
    -
    -

    CVE-2024-6119

    -
    - -
    - 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

    -

    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: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

    -

    Impact summary: Abnormal termination of an application can a cause a denial of - service.

    -

    Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

    -

    Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

    -

    TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

    -

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

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -
    -
    - - - diff --git a/docs/snyk/v2.10.16/redis_7.0.15-alpine.html b/docs/snyk/v2.10.16/redis_7.0.15-alpine.html deleted file mode 100644 index a425e2171384a..0000000000000 --- a/docs/snyk/v2.10.16/redis_7.0.15-alpine.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:28:59 am (UTC+00:00)

    -
    -
    - Scanned the following paths: -
      -
    • redis:7.0.15-alpine (apk)
    • -
    • redis:7.0.15-alpine/tianon/gosu//usr/local/bin/gosu (gomodules)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    - -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.11.8/argocd-iac-install.html b/docs/snyk/v2.11.8/argocd-iac-install.html deleted file mode 100644 index e3753708b8045..0000000000000 --- a/docs/snyk/v2.11.8/argocd-iac-install.html +++ /dev/null @@ -1,2891 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:28:02 am (UTC+00:00)

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

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-42 -
    • - -
    • Introduced through: - [DocId: 48] - - spec - - template - - spec - - initContainers[secret-init] - - imagePullPolicy - -
    • - -
    • - Line number: 21991 -
    • -
    - -
    - -

    Impact

    -

    The container may run with outdated or unauthorized image

    - -

    Remediation

    -

    Set `imagePullPolicy` attribute to `Always`

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

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

    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: 45] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21552 -
    • -
    - -
    - -

    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 - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21803 -
    • -
    - -
    - -

    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[dex] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21769 -
    • -
    - -
    - -

    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-notifications-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21863 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21962 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21986 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22278 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22043 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22363 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22714 -
    • -
    - -
    - -

    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: 46] - - spec - - template - - spec - - containers[dex] - - ports - -
    • - -
    • - Line number: 21783 -
    • -
    - -
    - -

    Impact

    -

    Increases the attack surface of the application and the container.

    - -

    Remediation

    -

    Reduce `ports` count to 2

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

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

    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: 46] - - spec - - template - - spec - - containers[dex] - - livenessProbe - -
    • - -
    • - Line number: 21769 -
    • -
    - -
    - -

    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: 48] - - spec - - template - - spec - - containers[redis] - - livenessProbe - -
    • - -
    • - Line number: 21962 -
    • -
    - -
    - -

    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: 45] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21552 -
    • -
    - -
    - -

    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[dex] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21769 -
    • -
    - -
    - -

    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 - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21803 -
    • -
    - -
    - -

    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-notifications-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21863 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21962 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21986 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22278 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22043 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22363 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22714 -
    • -
    - -
    - -

    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'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-applicationset-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21693 -
    • -
    - -
    - -

    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: 21811 -
    • -
    - -
    - -

    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[dex] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21786 -
    • -
    - -
    - -

    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-notifications-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21896 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21979 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21993 -
    • -
    - -
    - -

    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] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22285 -
    • -
    - -
    - -

    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] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22251 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22624 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22915 -
    • -
    - -
    - -

    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.11.8/haproxy_2.6.14-alpine.html b/docs/snyk/v2.11.8/haproxy_2.6.14-alpine.html deleted file mode 100644 index 51e9cc5e39b95..0000000000000 --- a/docs/snyk/v2.11.8/haproxy_2.6.14-alpine.html +++ /dev/null @@ -1,2741 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:26:22 am (UTC+00:00)

    -
    -
    - Scanned the following path: -
      -
    • haproxy:2.6.14-alpine (apk)
    • -
    -
    - -
    -
    14 known vulnerabilities
    -
    110 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

    - - -
    - - - -
    -
    -

    Out-of-bounds Write

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: alpine:3.18 -
    • -
    • - Vulnerable module: - - busybox/busybox -
    • - -
    • Introduced through: - - docker-image|haproxy@2.6.14-alpine and busybox/busybox@1.36.1-r2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/ssl_client@1.36.1-r2 - - - -
    • -
    - -
    - -
    - -

    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.

    -

    A heap-buffer-overflow was discovered in BusyBox v.1.36.1 in the next_token function at awk.c:1159.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r6 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: alpine:3.18 -
    • -
    • - Vulnerable module: - - busybox/busybox -
    • - -
    • Introduced through: - - docker-image|haproxy@2.6.14-alpine and busybox/busybox@1.36.1-r2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/ssl_client@1.36.1-r2 - - - -
    • -
    - -
    - -
    - -

    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.

    -

    A use-after-free vulnerability was discovered in xasprintf function in xfuncs_printf.c:344 in BusyBox v.1.36.1.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r7 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: alpine:3.18 -
    • -
    • - Vulnerable module: - - busybox/busybox -
    • - -
    • Introduced through: - - docker-image|haproxy@2.6.14-alpine and busybox/busybox@1.36.1-r2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/ssl_client@1.36.1-r2 - - - -
    • -
    - -
    - -
    - -

    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.

    -

    A use-after-free vulnerability in BusyBox v.1.36.1 allows attackers to cause a denial of service via a crafted awk pattern in the awk.c evaluate function.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r7 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: alpine:3.18 -
    • -
    • - Vulnerable module: - - busybox/busybox -
    • - -
    • Introduced through: - - docker-image|haproxy@2.6.14-alpine and busybox/busybox@1.36.1-r2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - busybox/busybox@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - alpine-baselayout/alpine-baselayout@3.4.3-r1 - - busybox/busybox-binsh@1.36.1-r2 - - - -
    • -
    • - Introduced through: - docker-image|haproxy@2.6.14-alpine - - busybox/ssl_client@1.36.1-r2 - - - -
    • -
    - -
    - -
    - -

    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.

    -

    A use-after-free vulnerability was discovered in BusyBox v.1.36.1 via a crafted awk pattern in the awk.c copyvar function.

    -

    Remediation

    -

    Upgrade Alpine:3.18 busybox to version 1.36.1-r7 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

    -

    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 invalid RSA public keys may take - a long time.

    -

    Impact summary: Applications that use the function EVP_PKEY_public_check() - to check RSA public keys may experience long delays. Where the key that - is being checked has been obtained from an untrusted source this may lead - to a Denial of Service.

    -

    When function EVP_PKEY_public_check() is called on RSA public keys, - a computation is done to confirm that the RSA modulus, n, is composite. - For valid RSA keys, n is a product of two or more large primes and this - computation completes quickly. However, if n is an overly large prime, - then this computation would take a long time.

    -

    An application that calls EVP_PKEY_public_check() and supplies an RSA key - obtained from an untrusted source could be vulnerable to a Denial of Service - attack.

    -

    The function EVP_PKEY_public_check() is not called from other OpenSSL - functions however it is called from the OpenSSL pkey command line - application. For that reason that application is also vulnerable if used - with the '-pubin' and '-check' options on untrusted data.

    -

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

    -

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

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-2511

    -
    - -
    - 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

    -

    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: Some non-default TLS server configurations can cause unbounded - memory growth when processing TLSv1.3 sessions

    -

    Impact summary: An attacker may exploit certain server configurations to trigger - unbounded memory growth that would lead to a Denial of Service

    -

    This problem can occur in TLSv1.3 if the non-default SSL_OP_NO_TICKET option is - being used (but not if early_data support is also configured and the default - anti-replay protection is in use). In this case, under certain conditions, the - session cache can get into an incorrect state and it will fail to flush properly - as it fills. The session cache will continue to grow in an unbounded manner. A - malicious client could deliberately create the scenario for this failure to - force a Denial of Service. It may also happen by accident in normal operation.

    -

    This issue only affects TLS servers supporting TLSv1.3. It does not affect TLS - clients.

    -

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue. OpenSSL - 1.0.2 is also not affected by this issue.

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-4603

    -
    - -
    - 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

    -

    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 DSA keys or parameters may be very - slow.

    -

    Impact summary: Applications that use the functions EVP_PKEY_param_check() - or EVP_PKEY_public_check() to check a DSA public key or DSA 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 functions EVP_PKEY_param_check() or EVP_PKEY_public_check() perform - various checks on DSA parameters. Some of those computations take a long time - if the modulus (p parameter) is too large.

    -

    Trying to use a very large modulus is slow and OpenSSL will not allow using - public keys with a modulus which is over 10,000 bits in length for signature - verification. However the key and parameter check functions do not limit - the modulus size when performing the checks.

    -

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

    -

    These functions are not called by OpenSSL itself on untrusted DSA keys so - only applications that directly call these functions may be vulnerable.

    -

    Also vulnerable are the OpenSSL pkey 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 affected by this issue.

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-5535

    -
    - -
    - 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

    -

    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: Calling the OpenSSL API function SSL_select_next_proto with an - empty supported client protocols buffer may cause a crash or memory contents to - be sent to the peer.

    -

    Impact summary: A buffer overread can have a range of potential consequences - such as unexpected application beahviour or a crash. In particular this issue - could result in up to 255 bytes of arbitrary private data from memory being sent - to the peer leading to a loss of confidentiality. However, only applications - that directly call the SSL_select_next_proto function with a 0 length list of - supported client protocols are affected by this issue. This would normally never - be a valid scenario and is typically not under attacker control but may occur by - accident in the case of a configuration or programming error in the calling - application.

    -

    The OpenSSL API function SSL_select_next_proto is typically used by TLS - applications that support ALPN (Application Layer Protocol Negotiation) or NPN - (Next Protocol Negotiation). NPN is older, was never standardised and - is deprecated in favour of ALPN. We believe that ALPN is significantly more - widely deployed than NPN. The SSL_select_next_proto function accepts a list of - protocols from the server and a list of protocols from the client and returns - the first protocol that appears in the server list that also appears in the - client list. In the case of no overlap between the two lists it returns the - first item in the client list. In either case it will signal whether an overlap - between the two lists was found. In the case where SSL_select_next_proto is - called with a zero length client list it fails to notice this condition and - returns the memory immediately following the client list pointer (and reports - that there was no overlap in the lists).

    -

    This function is typically called from a server side application callback for - ALPN or a client side application callback for NPN. In the case of ALPN the list - of protocols supplied by the client is guaranteed by libssl to never be zero in - length. The list of server protocols comes from the application and should never - normally be expected to be of zero length. In this case if the - SSL_select_next_proto function has been called as expected (with the list - supplied by the client passed in the client/client_len parameters), then the - application will not be vulnerable to this issue. If the application has - accidentally been configured with a zero length server list, and has - accidentally passed that zero length server list in the client/client_len - parameters, and has additionally failed to correctly handle a "no overlap" - response (which would normally result in a handshake failure in ALPN) then it - will be vulnerable to this problem.

    -

    In the case of NPN, the protocol permits the client to opportunistically select - a protocol when there is no overlap. OpenSSL returns the first client protocol - in the no overlap case in support of this. The list of client protocols comes - from the application and should never normally be expected to be of zero length. - However if the SSL_select_next_proto function is accidentally called with a - client_len of 0 then an invalid memory pointer will be returned instead. If the - application uses this output as the opportunistic protocol then the loss of - confidentiality will occur.

    -

    This issue has been assessed as Low severity because applications are most - likely to be vulnerable if they are using NPN instead of ALPN - but NPN is not - widely used. It also requires an application configuration or programming error. - Finally, this issue would not typically be under attacker control making active - exploitation unlikely.

    -

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

    -

    Due to the low severity of this issue we are not issuing new releases of - OpenSSL at this time. The fix will be included in the next releases when they - become available.

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-4741

    -
    - -
    - 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.6-r0 or higher.

    - -
    - - - -
    -
    -

    CVE-2024-6119

    -
    - -
    - 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

    -

    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: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

    -

    Impact summary: Abnormal termination of an application can a cause a denial of - service.

    -

    Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

    -

    Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

    -

    TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

    -

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

    -

    Remediation

    -

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

    -

    References

    - - -
    - - - -
    -
    -
    -
    - - - diff --git a/docs/snyk/v2.11.8/redis_7.0.15-alpine.html b/docs/snyk/v2.11.8/redis_7.0.15-alpine.html deleted file mode 100644 index 1a6806f71cefa..0000000000000 --- a/docs/snyk/v2.11.8/redis_7.0.15-alpine.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:26:43 am (UTC+00:00)

    -
    -
    - Scanned the following paths: -
      -
    • redis:7.0.15-alpine (apk)
    • -
    • redis:7.0.15-alpine/tianon/gosu//usr/local/bin/gosu (gomodules)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    - -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.12.3/argocd-iac-install.html b/docs/snyk/v2.12.3/argocd-iac-install.html deleted file mode 100644 index 268b77b876e08..0000000000000 --- a/docs/snyk/v2.12.3/argocd-iac-install.html +++ /dev/null @@ -1,2891 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:25:50 am (UTC+00:00)

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

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-42 -
    • - -
    • Introduced through: - [DocId: 48] - - spec - - template - - spec - - initContainers[secret-init] - - imagePullPolicy - -
    • - -
    • - Line number: 22039 -
    • -
    - -
    - -

    Impact

    -

    The container may run with outdated or unauthorized image

    - -

    Remediation

    -

    Set `imagePullPolicy` attribute to `Always`

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

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

    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: 45] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21600 -
    • -
    - -
    - -

    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 - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21851 -
    • -
    - -
    - -

    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[dex] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21817 -
    • -
    - -
    - -

    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-notifications-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 21911 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22010 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22034 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22338 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22091 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22423 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22774 -
    • -
    - -
    - -

    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: 46] - - spec - - template - - spec - - containers[dex] - - ports - -
    • - -
    • - Line number: 21831 -
    • -
    - -
    - -

    Impact

    -

    Increases the attack surface of the application and the container.

    - -

    Remediation

    -

    Reduce `ports` count to 2

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

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

    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: 46] - - spec - - template - - spec - - containers[dex] - - livenessProbe - -
    • - -
    • - Line number: 21817 -
    • -
    - -
    - -

    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: 48] - - spec - - template - - spec - - containers[redis] - - livenessProbe - -
    • - -
    • - Line number: 22010 -
    • -
    - -
    - -

    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: 45] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21600 -
    • -
    - -
    - -

    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[dex] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21817 -
    • -
    - -
    - -

    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 - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21851 -
    • -
    - -
    - -

    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-notifications-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 21911 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22010 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22034 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22338 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22091 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22423 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22774 -
    • -
    - -
    - -

    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'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-applicationset-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21741 -
    • -
    - -
    - -

    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: 21859 -
    • -
    - -
    - -

    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[dex] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21834 -
    • -
    - -
    - -

    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-notifications-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 21944 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22027 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22041 -
    • -
    - -
    - -

    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] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22345 -
    • -
    - -
    - -

    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] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22311 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22684 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 22975 -
    • -
    - -
    - -

    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.12.3/argocd-test.html b/docs/snyk/v2.12.3/argocd-test.html deleted file mode 100644 index c5ace95defe54..0000000000000 --- a/docs/snyk/v2.12.3/argocd-test.html +++ /dev/null @@ -1,1086 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:23:57 am (UTC+00:00)

    -
    -
    - Scanned the following paths: -
      -
    • /argo-cd/argoproj/argo-cd/v2/go.mod (gomodules)
    • -
    • /argo-cd/ui/yarn.lock (yarn)
    • -
    -
    - -
    -
    5 known vulnerabilities
    -
    7 vulnerable dependency paths
    -
    2061 dependencies
    -
    -
    -
    -
    - -
    -
    -
    -

    Prototype Pollution

    -
    - -
    - high severity -
    - -
    - -
      -
    • - Manifest file: /argo-cd ui/yarn.lock -
    • -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - dompurify -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, redoc@2.0.0-rc.64 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - redoc@2.0.0-rc.64 - - dompurify@2.3.6 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    dompurify is a DOM-only XSS sanitizer for HTML, MathML and SVG.

    -

    Affected versions of this package are vulnerable to Prototype Pollution due to improper user input sanitization through the depth-checking mechanism, an attacker can exploit this vulnerability by using special nesting techniques to create a malicious HTML file.

    -

    Details

    -

    Prototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as __proto__, constructor and prototype. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values. Properties on the Object.prototype are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.

    -

    There are two main ways in which the pollution of prototypes occurs:

    -
      -
    • Unsafe Object recursive merge

      -
    • -
    • Property definition by path

      -
    • -
    -

    Unsafe Object recursive merge

    -

    The logic of a vulnerable recursive merge function follows the following high-level model:

    -
    merge (target, source)
    -        
    -          foreach property of source
    -        
    -            if property exists and is an object on both the target and the source
    -        
    -              merge(target[property], source[property])
    -        
    -            else
    -        
    -              target[property] = source[property]
    -        
    -
    - -

    When the source object contains a property named __proto__ defined with Object.defineProperty() , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of Object and the source of Object as defined by the attacker. Properties are then copied on the Object prototype.

    -

    Clone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: merge({},source).

    -

    lodash and Hoek are examples of libraries susceptible to recursive merge attacks.

    -

    Property definition by path

    -

    There are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: theFunction(object, path, value)

    -

    If the attacker can control the value of “path”, they can set this value to __proto__.myValue. myValue is then assigned to the prototype of the class of the object.

    -

    Types of attacks

    -

    There are a few methods by which Prototype Pollution can be manipulated:

    - - - - - - - - - - - - - - - - - - - - - - - -
    TypeOriginShort description
    Denial of service (DoS)ClientThis is the most likely attack.
    DoS occurs when Object holds generic functions that are implicitly called for various operations (for example, toString and valueOf).
    The attacker pollutes Object.prototype.someattr and alters its state to an unexpected value such as Int or Object. In this case, the code fails and is likely to cause a denial of service.
    For example: if an attacker pollutes Object.prototype.toString by defining it as an integer, if the codebase at any point was reliant on someobject.toString() it would fail.
    Remote Code ExecutionClientRemote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.
    For example: eval(someobject.someattr). In this case, if the attacker pollutes Object.prototype.someattr they are likely to be able to leverage this in order to execute code.
    Property InjectionClientThe attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.
    For example: if a codebase checks privileges for someuser.isAdmin, then when the attacker pollutes Object.prototype.isAdmin and sets it to equal true, they can then achieve admin privileges.
    -

    Affected environments

    -

    The following environments are susceptible to a Prototype Pollution attack:

    -
      -
    • Application server

      -
    • -
    • Web server

      -
    • -
    • Web browser

      -
    • -
    -

    How to prevent

    -
      -
    1. Freeze the prototype— use Object.freeze (Object.prototype).

      -
    2. -
    3. Require schema validation of JSON input.

      -
    4. -
    5. Avoid using unsafe recursive merge functions.

      -
    6. -
    7. Consider using objects without prototypes (for example, Object.create(null)), breaking the prototype chain and preventing pollution.

      -
    8. -
    9. As a best practice use Map instead of Object.

      -
    10. -
    -

    For more information on this vulnerability type:

    -

    Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018

    -

    Remediation

    -

    Upgrade dompurify to version 2.5.4, 3.1.3 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Regular Expression Denial of Service (ReDoS)

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: /argo-cd ui/yarn.lock -
    • -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - path-to-regexp -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, react-router@4.3.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - react-router@4.3.1 - - path-to-regexp@1.8.0 - - - -
    • -
    • - Introduced through: - argo-cd-ui@1.0.0 - - react-router-dom@4.3.1 - - react-router@4.3.1 - - path-to-regexp@1.8.0 - - - -
    • -
    • - Introduced through: - argo-cd-ui@1.0.0 - - argo-ui@1.0.0 - - react-router-dom@4.3.1 - - react-router@4.3.1 - - path-to-regexp@1.8.0 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) when including multiple regular expression parameters in a single segment, which will produce the regular expression /^\/([^\/]+?)-([^\/]+?)\/?$/, if two parameters within a single segment are separated by a character other than a / or .. Poor performance will block the event loop and can lead to a DoS.

    -

    Note: - While the 8.0.0 release has completely eliminated the vulnerable functionality, prior versions that have received the patch to mitigate backtracking may still be vulnerable if custom regular expressions are used. So it is strongly recommended for regular expression input to be controlled to avoid malicious performance degradation in those versions. This behavior is enforced as of version 7.1.0 via the strict option, which returns an error if a dangerous regular expression is detected.

    -

    Workaround

    -

    This vulnerability can be avoided by using a custom regular expression for parameters after the first in a segment, which excludes - and /.

    -

    PoC

    -
    /a${'-a'.repeat(8_000)}/a
    -        
    -

    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 path-to-regexp to version 0.1.10, 1.9.0, 3.3.0, 6.3.0, 8.0.0 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Denial of Service (DoS)

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • -
    • - Package Manager: golang -
    • -
    • - Vulnerable module: - - github.com/rs/cors -
    • - -
    • Introduced through: - - - github.com/argoproj/argo-cd/v2@0.0.0, github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - - github.com/rs/cors@1.9.0 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    Affected versions of this package are vulnerable to Denial of Service (DoS) through the processing of malicious preflight requests that include a Access-Control-Request-Headers header with excessive commas. An attacker can induce excessive memory consumption and potentially crash the server by sending specially crafted requests.

    -

    PoC

    -
    
    -        func BenchmarkPreflightAdversarialACRH(b *testing.B) {
    -            resps := makeFakeResponses(b.N)
    -            req, _ := http.NewRequest(http.MethodOptions, dummyEndpoint, nil)
    -            req.Header.Add(headerOrigin, dummyOrigin)
    -            req.Header.Add(headerACRM, http.MethodGet)
    -            req.Header[headerACRH] = adversarialACRH
    -            handler := Default().Handler(testHandler)
    -        
    -            b.ReportAllocs()
    -            b.ResetTimer()
    -            for i := 0; i < b.N; i++ {
    -                handler.ServeHTTP(resps[i], req)
    -            }
    -        }
    -        
    -        var adversarialACRH []string
    -        
    -        func init() { // populates adversarialACRH
    -            n := int(math.Floor(math.Sqrt(http.DefaultMaxHeaderBytes)))
    -            commas := strings.Repeat(",", n)
    -            res := make([]string, n)
    -            for i := range res {
    -                res[i] = commas
    -            }
    -            adversarialACRH = res
    -        }
    -        
    -

    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/rs/cors to version 1.11.0 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • -
    • - Package Manager: golang -
    • -
    • - Vulnerable module: - - github.com/Azure/azure-sdk-for-go/sdk/azidentity -
    • - -
    • 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 - - github.com/Azure/azure-sdk-for-go/sdk/azidentity@1.1.0 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    github.com/Azure/azure-sdk-for-go/sdk/azidentity is a module that provides Microsoft Entra ID (formerly Azure Active Directory) token authentication support across the Azure SDK. It includes a set of TokenCredential implementations, which can be used with Azure SDK clients supporting token authentication.

    -

    Affected versions of this package are vulnerable to Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') in the authentication process. An attacker can elevate privileges by exploiting race conditions during the token validation steps. This is only exploitable if the application is configured to use multiple threads or processes for handling authentication requests.

    -

    Notes:

    -
      -
    1. An attacker who successfully exploited the vulnerability could elevate privileges and read any file on the file system with SYSTEM access permissions;

      -
    2. -
    3. An attacker who successfully exploits this vulnerability can only obtain read access to the system files by exploiting this vulnerability. The attacker cannot perform write or delete operations on the files;

      -
    4. -
    5. The vulnerability exists in the following credential types: DefaultAzureCredential and ManagedIdentityCredential;

      -
    6. -
    7. The vulnerability exists in the following credential types:

      -
    8. -
    -

    ManagedIdentityApplication (.NET)

    -

    ManagedIdentityApplication (Java)

    -

    ManagedIdentityApplication (Node.js)

    -

    Remediation

    -

    Upgrade github.com/Azure/azure-sdk-for-go/sdk/azidentity to version 1.6.0 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Template Injection

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: /argo-cd ui/yarn.lock -
    • -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - dompurify -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, redoc@2.0.0-rc.64 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - redoc@2.0.0-rc.64 - - dompurify@2.3.6 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    dompurify is a DOM-only XSS sanitizer for HTML, MathML and SVG.

    -

    Affected versions of this package are vulnerable to Template Injection in purify.js, due to inconsistencies in the parsing of XML and HTML tags. Executable code can be injected in HTML inside XML CDATA blocks.

    -

    PoC

    -
    <![CDATA[ ><img src onerror=alert(1)> ]]>
    -        
    -

    Remediation

    -

    Upgrade dompurify to version 2.4.9, 3.0.11 or higher.

    -

    References

    - - -
    - - - -
    -
    -
    -
    - - - diff --git a/docs/snyk/v2.12.3/public.ecr.aws_docker_library_redis_7.0.15-alpine.html b/docs/snyk/v2.12.3/public.ecr.aws_docker_library_redis_7.0.15-alpine.html deleted file mode 100644 index dbc79e2e50588..0000000000000 --- a/docs/snyk/v2.12.3/public.ecr.aws_docker_library_redis_7.0.15-alpine.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:24:12 am (UTC+00:00)

    -
    -
    - Scanned the following paths: -
      -
    • public.ecr.aws/docker/library/redis:7.0.15-alpine/docker/library/redis (apk)
    • -
    • public.ecr.aws/docker/library/redis:7.0.15-alpine/tianon/gosu//usr/local/bin/gosu (gomodules)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    - -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.12.3/redis_7.0.15-alpine.html b/docs/snyk/v2.12.3/redis_7.0.15-alpine.html deleted file mode 100644 index e3cc28e76700f..0000000000000 --- a/docs/snyk/v2.12.3/redis_7.0.15-alpine.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:24:31 am (UTC+00:00)

    -
    -
    - Scanned the following paths: -
      -
    • redis:7.0.15-alpine (apk)
    • -
    • redis:7.0.15-alpine/tianon/gosu//usr/local/bin/gosu (gomodules)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    - -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.13.0-rc2/argocd-iac-install.html b/docs/snyk/v2.13.0-rc2/argocd-iac-install.html deleted file mode 100644 index 1fb9ff7afdb66..0000000000000 --- a/docs/snyk/v2.13.0-rc2/argocd-iac-install.html +++ /dev/null @@ -1,2891 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:23:23 am (UTC+00:00)

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

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-42 -
    • - -
    • Introduced through: - [DocId: 48] - - spec - - template - - spec - - initContainers[secret-init] - - imagePullPolicy - -
    • - -
    • - Line number: 23345 -
    • -
    - -
    - -

    Impact

    -

    The container may run with outdated or unauthorized image

    - -

    Remediation

    -

    Set `imagePullPolicy` attribute to `Always`

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

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

    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: 45] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 22882 -
    • -
    - -
    - -

    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 - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 23151 -
    • -
    - -
    - -

    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[dex] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 23105 -
    • -
    - -
    - -

    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-notifications-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 23211 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 23316 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 23340 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 23644 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 23397 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 23729 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 24119 -
    • -
    - -
    - -

    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: 46] - - spec - - template - - spec - - containers[dex] - - ports - -
    • - -
    • - Line number: 23131 -
    • -
    - -
    - -

    Impact

    -

    Increases the attack surface of the application and the container.

    - -

    Remediation

    -

    Reduce `ports` count to 2

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

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

    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: 46] - - spec - - template - - spec - - containers[dex] - - livenessProbe - -
    • - -
    • - Line number: 23105 -
    • -
    - -
    - -

    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: 48] - - spec - - template - - spec - - containers[redis] - - livenessProbe - -
    • - -
    • - Line number: 23316 -
    • -
    - -
    - -

    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: 45] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 22882 -
    • -
    - -
    - -

    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[dex] - - resources - - limits - - memory - -
    • - -
    • - Line number: 23105 -
    • -
    - -
    - -

    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 - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 23151 -
    • -
    - -
    - -

    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-notifications-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 23211 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - memory - -
    • - -
    • - Line number: 23316 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - resources - - limits - - memory - -
    • - -
    • - Line number: 23340 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 23644 -
    • -
    - -
    - -

    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: 49] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 23397 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 23729 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 24119 -
    • -
    - -
    - -

    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'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-applicationset-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 23029 -
    • -
    - -
    - -

    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: 23159 -
    • -
    - -
    - -

    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[dex] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 23134 -
    • -
    - -
    - -

    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-notifications-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 23250 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - containers[redis] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 23333 -
    • -
    - -
    - -

    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: 48] - - input - - spec - - template - - spec - - initContainers[secret-init] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 23347 -
    • -
    - -
    - -

    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] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 23651 -
    • -
    - -
    - -

    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] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 23617 -
    • -
    - -
    - -

    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: 50] - - input - - spec - - template - - spec - - containers[argocd-server] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 24020 -
    • -
    - -
    - -

    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: 51] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 24320 -
    • -
    - -
    - -

    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.13.0-rc2/public.ecr.aws_docker_library_redis_7.0.15-alpine.html b/docs/snyk/v2.13.0-rc2/public.ecr.aws_docker_library_redis_7.0.15-alpine.html deleted file mode 100644 index 26e6ff3618a3f..0000000000000 --- a/docs/snyk/v2.13.0-rc2/public.ecr.aws_docker_library_redis_7.0.15-alpine.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:21:40 am (UTC+00:00)

    -
    -
    - Scanned the following paths: -
      -
    • public.ecr.aws/docker/library/redis:7.0.15-alpine/docker/library/redis (apk)
    • -
    • public.ecr.aws/docker/library/redis:7.0.15-alpine/tianon/gosu//usr/local/bin/gosu (gomodules)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    - -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.13.0-rc2/quay.io_argoproj_argocd_v2.13.0-rc2.html b/docs/snyk/v2.13.0-rc2/quay.io_argoproj_argocd_v2.13.0-rc2.html deleted file mode 100644 index 043d857a863c8..0000000000000 --- a/docs/snyk/v2.13.0-rc2/quay.io_argoproj_argocd_v2.13.0-rc2.html +++ /dev/null @@ -1,2012 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:21:56 am (UTC+00:00)

    -
    -
    - Scanned the following paths: -
      -
    • quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd/Dockerfile (deb)
    • -
    • quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argo-cd/v2//usr/local/bin/argocd (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.13.0-rc2//usr/local/bin/kustomize (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.13.0-rc2/helm/v3//usr/local/bin/helm (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.13.0-rc2/git-lfs/git-lfs//usr/bin/git-lfs (gomodules)
    • -
    -
    - -
    -
    11 known vulnerabilities
    -
    65 vulnerable dependency paths
    -
    2355 dependencies
    -
    -
    -
    -
    - -
    -
    -
    -

    CVE-2024-41996

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - openssl/libssl3t64 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 and openssl/libssl3t64@3.0.13-0ubuntu3.4 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - coreutils@9.4-3ubuntu6 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - cyrus-sasl2/libsasl2-modules@2.1.28+dfsg1-5ubuntu3.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - libfido2/libfido2-1@1.14.0-1build3 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - openssh/openssh-client@1:9.6p1-3ubuntu13.5 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - ca-certificates@20240203 - - openssl@3.0.13-0ubuntu3.4 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - libssh/libssh-4@0.10.6-2build2 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - openldap/libldap2@2.6.7+dfsg-1~exp1ubuntu8 - - cyrus-sasl2/libsasl2-2@2.1.28+dfsg1-5ubuntu3.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - openssl@3.0.13-0ubuntu3.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - ca-certificates@20240203 - - openssl@3.0.13-0ubuntu3.4 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    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:24.04 relevant fixed versions and status.

    -

    Validating the order of the public keys in the Diffie-Hellman Key Agreement Protocol, when an approved safe prime is used, allows remote attackers (from the client side) to trigger unnecessarily expensive server-side DHE modular-exponentiation calculations. The client may cause asymmetric resource consumption. The basic attack scenario is that the client must claim that it can only communicate with DHE, and the server must be configured to allow DHE and validate the order of the public key.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:24.04 openssl.

    -

    References

    - - -
    - - - -
    -
    -

    Information Exposure

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - libgcrypt20 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 and libgcrypt20@1.10.3-2build1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - libgcrypt20@1.10.3-2build1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/dirmngr@2.4.4-2ubuntu17 - - libgcrypt20@1.10.3-2build1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/gpg@2.4.4-2ubuntu17 - - libgcrypt20@1.10.3-2build1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/gpg-agent@2.4.4-2ubuntu17 - - libgcrypt20@1.10.3-2build1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - apt@2.7.14build2 - - apt/libapt-pkg6.0t64@2.7.14build2 - - libgcrypt20@1.10.3-2build1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - apt@2.7.14build2 - - gnupg2/gpgv@2.4.4-2ubuntu17 - - libgcrypt20@1.10.3-2build1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/gpg@2.4.4-2ubuntu17 - - gnupg2/gpgconf@2.4.4-2ubuntu17 - - libgcrypt20@1.10.3-2build1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - apt@2.7.14build2 - - adduser@3.137ubuntu1 - - shadow/passwd@1:4.13+dfsg1-4ubuntu3 - - pam/libpam-modules@1.5.3-5ubuntu5.1 - - systemd/libsystemd0@255.4-1ubuntu8.4 - - libgcrypt20@1.10.3-2build1 - - - -
    • -
    - -
    - -
    - -

    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:24.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:24.04 libgcrypt20.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-26462

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2, git@1:2.43.0-1ubuntu7.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - openssh/openssh-client@1:9.6p1-3ubuntu13.5 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - libssh/libssh-4@0.10.6-2build2 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - krb5/krb5-locales@1.20.1-6ubuntu2.1 - - - -
    • -
    - -
    - -
    - -

    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:24.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:24.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    Release of Invalid Pointer or Reference

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - patch -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 and patch@2.7.6-7build3 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - patch@2.7.6-7build3 - - - -
    • -
    - -
    - -
    - -

    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:24.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:24.04 patch.

    -

    References

    - - -
    - - - -
    -
    -

    Double Free

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - patch -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 and patch@2.7.6-7build3 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - patch@2.7.6-7build3 - - - -
    • -
    - -
    - -
    - -

    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:24.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:24.04 patch.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-26458

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2, git@1:2.43.0-1ubuntu7.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - openssh/openssh-client@1:9.6p1-3ubuntu13.5 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - libssh/libssh-4@0.10.6-2build2 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - krb5/krb5-locales@1.20.1-6ubuntu2.1 - - - -
    • -
    - -
    - -
    - -

    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:24.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:24.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-26461

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2, git@1:2.43.0-1ubuntu7.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - openssh/openssh-client@1:9.6p1-3ubuntu13.5 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.4 - - libssh/libssh-4@0.10.6-2build2 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - krb5/krb5-locales@1.20.1-6ubuntu2.1 - - - -
    • -
    - -
    - -
    - -

    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:24.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:24.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Write

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - gnupg2/gpgv -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 and gnupg2/gpgv@2.4.4-2ubuntu17 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/gpgv@2.4.4-2ubuntu17 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - apt@2.7.14build2 - - gnupg2/gpgv@2.4.4-2ubuntu17 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/dirmngr@2.4.4-2ubuntu17 - - gnupg2/gpgconf@2.4.4-2ubuntu17 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/gpg-agent@2.4.4-2ubuntu17 - - gnupg2/gpgconf@2.4.4-2ubuntu17 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/gpg@2.4.4-2ubuntu17 - - gnupg2/gpgconf@2.4.4-2ubuntu17 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/dirmngr@2.4.4-2ubuntu17 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/gpg@2.4.4-2ubuntu17 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - gnupg2/gpg-agent@2.4.4-2ubuntu17 - - - -
    • -
    - -
    - -
    - -

    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:24.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:24.04 gnupg2.

    -

    References

    - - -
    - - - -
    -
    -

    Allocation of Resources Without Limits or Throttling

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - glibc/libc-bin -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 and glibc/libc-bin@2.39-0ubuntu8.3 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - glibc/libc-bin@2.39-0ubuntu8.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - glibc/libc6@2.39-0ubuntu8.3 - - - -
    • -
    - -
    - -
    - -

    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:24.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:24.04 glibc.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - git/git-man -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2, git@1:2.43.0-1ubuntu7.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - git/git-man@1:2.43.0-1ubuntu7.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git@1:2.43.0-1ubuntu7.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - git-lfs@3.4.1-1ubuntu0.1 - - git@1:2.43.0-1ubuntu7.1 - - - -
    • -
    - -
    - -
    - -

    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:24.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:24.04 git.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.13.0-rc2/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - coreutils -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 and coreutils@9.4-3ubuntu6 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.13.0-rc2 - - coreutils@9.4-3ubuntu6 - - - -
    • -
    - -
    - -
    - -

    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:24.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:24.04 coreutils.

    -

    References

    - - -
    - - - -
    -
    -
    -
    - - - diff --git a/docs/snyk/v2.13.0-rc2/redis_7.0.15-alpine.html b/docs/snyk/v2.13.0-rc2/redis_7.0.15-alpine.html deleted file mode 100644 index 9ce4786034705..0000000000000 --- a/docs/snyk/v2.13.0-rc2/redis_7.0.15-alpine.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    September 22nd 2024, 12:22:00 am (UTC+00:00)

    -
    -
    - Scanned the following paths: -
      -
    • redis:7.0.15-alpine (apk)
    • -
    • redis:7.0.15-alpine/tianon/gosu//usr/local/bin/gosu (gomodules)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    - -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.11.8/argocd-iac-namespace-install.html b/docs/snyk/v2.6.15/argocd-iac-install.html similarity index 90% rename from docs/snyk/v2.11.8/argocd-iac-namespace-install.html rename to docs/snyk/v2.6.15/argocd-iac-install.html index 1e254b4038b83..6867e68c4bd18 100644 --- a/docs/snyk/v2.11.8/argocd-iac-namespace-install.html +++ b/docs/snyk/v2.6.15/argocd-iac-install.html @@ -456,17 +456,17 @@

    Snyk test report

    -

    September 22nd 2024, 12:28:10 am (UTC+00:00)

    +

    October 29th 2023, 12:30:07 am (UTC+00:00)

    Scanned the following path:
      -
    • /argo-cd/manifests/namespace-install.yaml (Kubernetes)
    • +
    • /argo-cd/manifests/install.yaml (Kubernetes)
    -
    43 total issues
    +
    41 total issues
    @@ -475,15 +475,15 @@

    Snyk test report

    - - + +
    Project manifests/namespace-install.yaml
    Path /argo-cd/manifests/namespace-install.yaml
    Project manifests/install.yaml
    Path /argo-cd/manifests/install.yaml
    Project Type Kubernetes
    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -498,7 +498,7 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 7] + [DocId: 10] rules[0] @@ -507,17 +507,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 77 + Line number: 15180

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -529,7 +529,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -544,7 +544,7 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 8] + [DocId: 11] rules[4] @@ -553,17 +553,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 162 + Line number: 15257

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -575,7 +575,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -590,7 +590,7 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 9] + [DocId: 12] rules[0] @@ -599,17 +599,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 190 + Line number: 15285

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -621,7 +621,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -636,53 +636,7 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 10] - - rules[1] - - resources - -
  • - -
  • - Line number: 220 -
  • - - -
    - -

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 10] + [DocId: 13] rules[3] @@ -691,17 +645,17 @@

      Role or ClusterRole with dangerous permissions

    • - Line number: 238 + Line number: 15329

    Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -713,7 +667,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -728,26 +682,26 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 11] + [DocId: 13] - rules[0] + rules[1] resources
  • - Line number: 256 + Line number: 15311

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -759,7 +713,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -774,7 +728,7 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 12] + [DocId: 14] rules[0] @@ -783,17 +737,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 278 + Line number: 15345

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -820,7 +774,7 @@

    Container could be running with outdated image

  • Introduced through: - [DocId: 39] + [DocId: 46] spec @@ -828,14 +782,14 @@

    Container could be running with outdated image

    spec - initContainers[secret-init] + initContainers[copyutil] imagePullPolicy
  • - Line number: 1112 + Line number: 16361
  • @@ -857,7 +811,7 @@

    Remediation

    -

    Container could be running with outdated image

    +

    Container has no CPU limit

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

    Container could be running with outdated image

    • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 40] + [DocId: 42] + + input spec @@ -880,31 +836,35 @@

      Container could be running with outdated image

      spec - initContainers[copyutil] + containers[argocd-applicationset-controller] - imagePullPolicy + resources + + limits + + cpu
    • - Line number: 1399 + Line number: 15812

    Impact

    -

    The container may run with outdated or unauthorized image

    +

    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

    -

    Set `imagePullPolicy` attribute to `Always`

    +

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


    @@ -924,7 +884,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 36] + [DocId: 43] input @@ -934,7 +894,7 @@

    Container has no CPU limit

    spec - containers[argocd-applicationset-controller] + initContainers[copyutil] resources @@ -945,7 +905,7 @@

    Container has no CPU limit

  • - Line number: 673 + Line number: 15985
  • @@ -982,7 +942,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 37] + [DocId: 43] input @@ -992,7 +952,7 @@

    Container has no CPU limit

    spec - initContainers[copyutil] + containers[dex] resources @@ -1003,7 +963,7 @@

    Container has no CPU limit

  • - Line number: 924 + Line number: 15951
  • @@ -1040,7 +1000,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 37] + [DocId: 44] input @@ -1050,7 +1010,7 @@

    Container has no CPU limit

    spec - containers[dex] + containers[argocd-notifications-controller] resources @@ -1061,7 +1021,7 @@

    Container has no CPU limit

  • - Line number: 890 + Line number: 16041
  • @@ -1098,7 +1058,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 38] + [DocId: 45] input @@ -1108,7 +1068,7 @@

    Container has no CPU limit

    spec - containers[argocd-notifications-controller] + containers[redis] resources @@ -1119,7 +1079,7 @@

    Container has no CPU limit

  • - Line number: 984 + Line number: 16115
  • @@ -1156,7 +1116,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 39] + [DocId: 46] input @@ -1166,7 +1126,7 @@

    Container has no CPU limit

    spec - containers[redis] + initContainers[copyutil] resources @@ -1177,7 +1137,7 @@

    Container has no CPU limit

  • - Line number: 1083 + Line number: 16361
  • @@ -1214,7 +1174,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 39] + [DocId: 46] input @@ -1224,7 +1184,7 @@

    Container has no CPU limit

    spec - initContainers[secret-init] + containers[argocd-repo-server] resources @@ -1235,7 +1195,7 @@

    Container has no CPU limit

  • - Line number: 1107 + Line number: 16171
  • @@ -1272,7 +1232,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 40] + [DocId: 47] input @@ -1282,7 +1242,7 @@

    Container has no CPU limit

    spec - initContainers[copyutil] + containers[argocd-server] resources @@ -1293,7 +1253,7 @@

    Container has no CPU limit

  • - Line number: 1399 + Line number: 16446
  • @@ -1330,7 +1290,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 40] + [DocId: 48] input @@ -1340,7 +1300,7 @@

    Container has no CPU limit

    spec - containers[argocd-repo-server] + containers[argocd-application-controller] resources @@ -1351,7 +1311,7 @@

    Container has no CPU limit

  • - Line number: 1164 + Line number: 16750
  • @@ -1373,7 +1333,7 @@

    Remediation

    -

    Container has no CPU limit

    +

    Container is running with multiple open ports

    @@ -1384,13 +1344,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-36
    • Introduced through: - [DocId: 41] - - input + [DocId: 43] spec @@ -1398,40 +1356,36 @@

      Container has no CPU limit

      spec - containers[argocd-server] - - resources - - limits + containers[dex] - cpu + ports
    • - Line number: 1484 + Line number: 15965

    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.

    +

    Increases the attack surface of the application and the container.

    Remediation

    -

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

    +

    Reduce `ports` count to 2


    -

    Container has no CPU limit

    +

    Container is running with writable root filesystem

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

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-8
    • Introduced through: - [DocId: 42] - - input + [DocId: 45] spec @@ -1456,40 +1408,38 @@

      Container has no CPU limit

      spec - containers[argocd-application-controller] - - resources + containers[redis] - limits + securityContext - cpu + readOnlyRootFilesystem
    • - Line number: 1835 + Line number: 16125

    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.

    +

    Compromised process could abuse writable root filesystem to elevate privileges

    Remediation

    -

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

    +

    Set `spec.{containers, initContainers}.securityContext.readOnlyRootFilesystem` to `true`


    -

    Container is running with multiple open ports

    +

    Container is running without liveness probe

    @@ -1500,11 +1450,11 @@

    Container is running with multiple open ports

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

      Container is running with multiple open ports

      spec - containers[dex] + containers[argocd-applicationset-controller] - ports + livenessProbe
    • - Line number: 904 + Line number: 15812

    Impact

    -

    Increases the attack surface of the application and the container.

    +

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

    Remediation

    -

    Reduce `ports` count to 2

    +

    Add `livenessProbe` attribute


    @@ -1556,7 +1506,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 36] + [DocId: 43] spec @@ -1564,14 +1514,14 @@

    Container is running without liveness probe

    spec - containers[argocd-applicationset-controller] + initContainers[copyutil] livenessProbe
  • - Line number: 673 + Line number: 15985
  • @@ -1608,7 +1558,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 37] + [DocId: 43] spec @@ -1623,7 +1573,7 @@

    Container is running without liveness probe

  • - Line number: 890 + Line number: 15951
  • @@ -1660,7 +1610,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 39] + [DocId: 45] spec @@ -1675,7 +1625,7 @@

    Container is running without liveness probe

  • - Line number: 1083 + Line number: 16115
  • @@ -1697,7 +1647,7 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container is running without liveness probe

    @@ -1708,13 +1658,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 36] - - input + [DocId: 46] spec @@ -1722,35 +1670,31 @@

      Container is running without memory limit

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

    Impact

    -

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

    +

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

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Add `livenessProbe` attribute


    @@ -1770,7 +1714,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 37] + [DocId: 42] input @@ -1780,7 +1724,7 @@

    Container is running without memory limit

    spec - containers[dex] + containers[argocd-applicationset-controller] resources @@ -1791,7 +1735,7 @@

    Container is running without memory limit

  • - Line number: 890 + Line number: 15812
  • @@ -1828,7 +1772,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 37] + [DocId: 43] input @@ -1838,7 +1782,7 @@

    Container is running without memory limit

    spec - initContainers[copyutil] + containers[dex] resources @@ -1849,7 +1793,7 @@

    Container is running without memory limit

  • - Line number: 924 + Line number: 15951
  • @@ -1886,7 +1830,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 38] + [DocId: 43] input @@ -1896,7 +1840,7 @@

    Container is running without memory limit

    spec - containers[argocd-notifications-controller] + initContainers[copyutil] resources @@ -1907,7 +1851,7 @@

    Container is running without memory limit

  • - Line number: 984 + Line number: 15985
  • @@ -1944,7 +1888,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 39] + [DocId: 44] input @@ -1954,7 +1898,7 @@

    Container is running without memory limit

    spec - containers[redis] + containers[argocd-notifications-controller] resources @@ -1965,7 +1909,7 @@

    Container is running without memory limit

  • - Line number: 1083 + Line number: 16041
  • @@ -2002,7 +1946,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 39] + [DocId: 45] input @@ -2012,7 +1956,7 @@

    Container is running without memory limit

    spec - initContainers[secret-init] + containers[redis] resources @@ -2023,7 +1967,7 @@

    Container is running without memory limit

  • - Line number: 1107 + Line number: 16115
  • @@ -2060,7 +2004,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 40] + [DocId: 46] input @@ -2081,7 +2025,7 @@

    Container is running without memory limit

  • - Line number: 1399 + Line number: 16361
  • @@ -2118,7 +2062,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 40] + [DocId: 46] input @@ -2139,7 +2083,7 @@

    Container is running without memory limit

  • - Line number: 1164 + Line number: 16171
  • @@ -2176,7 +2120,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 41] + [DocId: 47] input @@ -2197,7 +2141,7 @@

    Container is running without memory limit

  • - Line number: 1484 + Line number: 16446
  • @@ -2234,7 +2178,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 42] + [DocId: 48] input @@ -2255,7 +2199,7 @@

    Container is running without memory limit

  • - Line number: 1835 + Line number: 16750
  • @@ -2292,7 +2236,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 36] + [DocId: 42] input @@ -2311,7 +2255,7 @@

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

  • - Line number: 814 + Line number: 15888
  • @@ -2348,7 +2292,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 37] + [DocId: 43] input @@ -2367,7 +2311,7 @@

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

  • - Line number: 932 + Line number: 15993
  • @@ -2404,7 +2348,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 37] + [DocId: 43] input @@ -2423,7 +2367,7 @@

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

  • - Line number: 907 + Line number: 15968
  • @@ -2460,7 +2404,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 38] + [DocId: 44] input @@ -2479,7 +2423,7 @@

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

  • - Line number: 1017 + Line number: 16049
  • @@ -2516,7 +2460,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 39] + [DocId: 45] input @@ -2535,63 +2479,7 @@

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

  • - Line number: 1100 -
  • - - -
    - -

    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[secret-init] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 1114 + Line number: 16125
    @@ -2628,7 +2516,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 40] + [DocId: 46] input @@ -2647,7 +2535,7 @@

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

  • - Line number: 1406 + Line number: 16368
  • @@ -2684,7 +2572,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 40] + [DocId: 46] input @@ -2703,7 +2591,7 @@

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

  • - Line number: 1372 + Line number: 16334
  • @@ -2740,7 +2628,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 41] + [DocId: 47] input @@ -2759,7 +2647,7 @@

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

  • - Line number: 1745 + Line number: 16660
  • @@ -2796,7 +2684,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 42] + [DocId: 48] input @@ -2815,7 +2703,7 @@

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

  • - Line number: 2036 + Line number: 16886
  • diff --git a/docs/snyk/v2.10.16/argocd-iac-namespace-install.html b/docs/snyk/v2.6.15/argocd-iac-namespace-install.html similarity index 91% rename from docs/snyk/v2.10.16/argocd-iac-namespace-install.html rename to docs/snyk/v2.6.15/argocd-iac-namespace-install.html index 1a15c2d0c5416..a0dbfd5315336 100644 --- a/docs/snyk/v2.10.16/argocd-iac-namespace-install.html +++ b/docs/snyk/v2.6.15/argocd-iac-namespace-install.html @@ -456,7 +456,7 @@

    Snyk test report

    -

    September 22nd 2024, 12:30:27 am (UTC+00:00)

    +

    October 29th 2023, 12:30:19 am (UTC+00:00)

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

    Snyk test report

    -
    43 total issues
    +
    41 total issues

    @@ -483,7 +483,7 @@

    Snyk test report

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -514,10 +514,10 @@

    Role or ClusterRole with dangerous permissions


    Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -529,7 +529,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -553,17 +553,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 162 + Line number: 154

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -575,7 +575,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -599,17 +599,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 190 + Line number: 182

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -621,7 +621,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -638,24 +638,24 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: [DocId: 10] - rules[1] + rules[3] resources
  • - Line number: 220 + Line number: 226

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -667,7 +667,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -684,24 +684,24 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: [DocId: 10] - rules[3] + rules[1] resources
  • - Line number: 238 + Line number: 208

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -713,7 +713,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -737,63 +737,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 256 + Line number: 242

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -828,14 +782,14 @@

    Container could be running with outdated image

    spec - initContainers[secret-init] + initContainers[copyutil] imagePullPolicy
  • - Line number: 1112 + Line number: 1165
  • @@ -857,7 +811,7 @@

    Remediation

    -

    Container could be running with outdated image

    +

    Container has no CPU limit

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

    Container could be running with outdated image

    • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 40] + [DocId: 35] + + input spec @@ -880,31 +836,35 @@

      Container could be running with outdated image

      spec - initContainers[copyutil] + containers[argocd-applicationset-controller] - imagePullPolicy + resources + + limits + + cpu
    • - Line number: 1393 + Line number: 616

    Impact

    -

    The container may run with outdated or unauthorized image

    +

    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

    -

    Set `imagePullPolicy` attribute to `Always`

    +

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


    @@ -934,7 +894,7 @@

    Container has no CPU limit

    spec - containers[argocd-applicationset-controller] + initContainers[copyutil] resources @@ -945,7 +905,7 @@

    Container has no CPU limit

  • - Line number: 673 + Line number: 789
  • @@ -982,7 +942,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -992,7 +952,7 @@

    Container has no CPU limit

    spec - initContainers[copyutil] + containers[dex] resources @@ -1003,7 +963,7 @@

    Container has no CPU limit

  • - Line number: 924 + Line number: 755
  • @@ -1050,7 +1010,7 @@

    Container has no CPU limit

    spec - containers[dex] + containers[argocd-notifications-controller] resources @@ -1061,7 +1021,7 @@

    Container has no CPU limit

  • - Line number: 890 + Line number: 845
  • @@ -1108,7 +1068,7 @@

    Container has no CPU limit

    spec - containers[argocd-notifications-controller] + containers[redis] resources @@ -1119,7 +1079,7 @@

    Container has no CPU limit

  • - Line number: 984 + Line number: 919
  • @@ -1166,7 +1126,7 @@

    Container has no CPU limit

    spec - containers[redis] + initContainers[copyutil] resources @@ -1177,7 +1137,7 @@

    Container has no CPU limit

  • - Line number: 1083 + Line number: 1165
  • @@ -1224,7 +1184,7 @@

    Container has no CPU limit

    spec - initContainers[secret-init] + containers[argocd-repo-server] resources @@ -1235,7 +1195,7 @@

    Container has no CPU limit

  • - Line number: 1107 + Line number: 975
  • @@ -1282,7 +1242,7 @@

    Container has no CPU limit

    spec - initContainers[copyutil] + containers[argocd-server] resources @@ -1293,7 +1253,7 @@

    Container has no CPU limit

  • - Line number: 1393 + Line number: 1250
  • @@ -1330,7 +1290,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 40] + [DocId: 41] input @@ -1340,7 +1300,7 @@

    Container has no CPU limit

    spec - containers[argocd-repo-server] + containers[argocd-application-controller] resources @@ -1351,7 +1311,7 @@

    Container has no CPU limit

  • - Line number: 1164 + Line number: 1554
  • @@ -1373,7 +1333,7 @@

    Remediation

    -

    Container has no CPU limit

    +

    Container is running with multiple open ports

    @@ -1384,13 +1344,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-36
    • Introduced through: - [DocId: 41] - - input + [DocId: 36] spec @@ -1398,40 +1356,36 @@

      Container has no CPU limit

      spec - containers[argocd-server] - - resources - - limits + containers[dex] - cpu + ports
    • - Line number: 1478 + Line number: 769

    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.

    +

    Increases the attack surface of the application and the container.

    Remediation

    -

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

    +

    Reduce `ports` count to 2


    -

    Container has no CPU limit

    +

    Container is running with writable root filesystem

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

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-8
    • Introduced through: - [DocId: 42] - - input + [DocId: 38] spec @@ -1456,40 +1408,38 @@

      Container has no CPU limit

      spec - containers[argocd-application-controller] - - resources + containers[redis] - limits + securityContext - cpu + readOnlyRootFilesystem
    • - Line number: 1829 + Line number: 929

    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.

    +

    Compromised process could abuse writable root filesystem to elevate privileges

    Remediation

    -

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

    +

    Set `spec.{containers, initContainers}.securityContext.readOnlyRootFilesystem` to `true`


    -

    Container is running with multiple open ports

    +

    Container is running without liveness probe

    @@ -1500,11 +1450,11 @@

    Container is running with multiple open ports

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

      Container is running with multiple open ports

      spec - containers[dex] + containers[argocd-applicationset-controller] - ports + livenessProbe
    • - Line number: 904 + Line number: 616

    Impact

    -

    Increases the attack surface of the application and the container.

    +

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

    Remediation

    -

    Reduce `ports` count to 2

    +

    Add `livenessProbe` attribute


    @@ -1564,14 +1514,14 @@

    Container is running without liveness probe

    spec - containers[argocd-applicationset-controller] + initContainers[copyutil] livenessProbe
  • - Line number: 673 + Line number: 789
  • @@ -1608,7 +1558,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 37] + [DocId: 36] spec @@ -1623,7 +1573,7 @@

    Container is running without liveness probe

  • - Line number: 890 + Line number: 755
  • @@ -1660,7 +1610,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 39] + [DocId: 38] spec @@ -1675,7 +1625,7 @@

    Container is running without liveness probe

  • - Line number: 1083 + Line number: 919
  • @@ -1697,7 +1647,7 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container is running without liveness probe

    @@ -1708,13 +1658,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 36] - - input + [DocId: 39] spec @@ -1722,35 +1670,31 @@

      Container is running without memory limit

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

    Impact

    -

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

    +

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

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Add `livenessProbe` attribute


    @@ -1770,7 +1714,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 37] + [DocId: 35] input @@ -1780,7 +1724,7 @@

    Container is running without memory limit

    spec - containers[dex] + containers[argocd-applicationset-controller] resources @@ -1791,7 +1735,7 @@

    Container is running without memory limit

  • - Line number: 890 + Line number: 616
  • @@ -1828,7 +1772,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -1838,7 +1782,7 @@

    Container is running without memory limit

    spec - initContainers[copyutil] + containers[dex] resources @@ -1849,7 +1793,7 @@

    Container is running without memory limit

  • - Line number: 924 + Line number: 755
  • @@ -1886,7 +1830,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 38] + [DocId: 36] input @@ -1896,7 +1840,7 @@

    Container is running without memory limit

    spec - containers[argocd-notifications-controller] + initContainers[copyutil] resources @@ -1907,7 +1851,7 @@

    Container is running without memory limit

  • - Line number: 984 + Line number: 789
  • @@ -1944,7 +1888,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 39] + [DocId: 37] input @@ -1954,7 +1898,7 @@

    Container is running without memory limit

    spec - containers[redis] + containers[argocd-notifications-controller] resources @@ -1965,7 +1909,7 @@

    Container is running without memory limit

  • - Line number: 1083 + Line number: 845
  • @@ -2002,7 +1946,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -2012,7 +1956,7 @@

    Container is running without memory limit

    spec - initContainers[secret-init] + containers[redis] resources @@ -2023,7 +1967,7 @@

    Container is running without memory limit

  • - Line number: 1107 + Line number: 919
  • @@ -2060,7 +2004,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 40] + [DocId: 39] input @@ -2081,7 +2025,7 @@

    Container is running without memory limit

  • - Line number: 1393 + Line number: 1165
  • @@ -2118,7 +2062,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 40] + [DocId: 39] input @@ -2139,7 +2083,7 @@

    Container is running without memory limit

  • - Line number: 1164 + Line number: 975
  • @@ -2176,7 +2120,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 41] + [DocId: 40] input @@ -2197,7 +2141,7 @@

    Container is running without memory limit

  • - Line number: 1478 + Line number: 1250
  • @@ -2234,7 +2178,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 42] + [DocId: 41] input @@ -2255,7 +2199,7 @@

    Container is running without memory limit

  • - Line number: 1829 + Line number: 1554
  • @@ -2292,7 +2236,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 36] + [DocId: 35] input @@ -2311,7 +2255,7 @@

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

  • - Line number: 814 + Line number: 692
  • @@ -2348,7 +2292,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -2367,7 +2311,7 @@

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

  • - Line number: 932 + Line number: 797
  • @@ -2404,7 +2348,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -2423,7 +2367,7 @@

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

  • - Line number: 907 + Line number: 772
  • @@ -2460,7 +2404,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 38] + [DocId: 37] input @@ -2479,7 +2423,7 @@

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

  • - Line number: 1017 + Line number: 853
  • @@ -2516,7 +2460,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -2535,7 +2479,7 @@

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

  • - Line number: 1100 + Line number: 929
  • @@ -2582,62 +2526,6 @@

    Container's or Pod's UID could clash with hos spec - initContainers[secret-init] - - securityContext - - runAsUser - - - -
  • - Line number: 1114 -
  • - - -
    - -

    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 - initContainers[copyutil] securityContext @@ -2647,7 +2535,7 @@

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

    • - Line number: 1400 + Line number: 1172
    @@ -2684,7 +2572,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 40] + [DocId: 39] input @@ -2703,7 +2591,7 @@

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

  • - Line number: 1366 + Line number: 1138
  • @@ -2740,7 +2628,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 41] + [DocId: 40] input @@ -2759,7 +2647,7 @@

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

  • - Line number: 1739 + Line number: 1464
  • @@ -2796,7 +2684,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 42] + [DocId: 41] input @@ -2815,7 +2703,7 @@

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

  • - Line number: 2030 + Line number: 1690
  • diff --git a/docs/snyk/v2.6.15/argocd-test.html b/docs/snyk/v2.6.15/argocd-test.html new file mode 100644 index 0000000000000..cbf674fc20222 --- /dev/null +++ b/docs/snyk/v2.6.15/argocd-test.html @@ -0,0 +1,3792 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:27:33 am (UTC+00:00)

    +
    +
    + Scanned the following paths: +
      +
    • /argo-cd/argoproj/argo-cd/v2 (gomodules)
    • /argo-cd (yarn)
    • +
    +
    + +
    +
    9 known vulnerabilities
    +
    157 vulnerable dependency paths
    +
    1727 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    Regular Expression Denial of Service (ReDoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: npm +
    • +
    • + Vulnerable module: + + semver +
    • + +
    • 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 + + semver@7.3.7 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    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.

    +

    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 semver to version 5.7.2, 6.3.1, 7.5.2 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/grpc +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and google.golang.org/grpc@1.51.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + 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@1.51.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health@1.51.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/reflection@1.51.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + 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@1.51.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.51.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health/grpc_health_v1@1.51.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + 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.51.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + 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.51.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + 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@1.3.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig@1.11.1 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 + + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/reflection@1.51.0 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.51.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health@1.51.0 + + google.golang.org/grpc/health/grpc_health_v1@1.51.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + 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/ctxlogrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + 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/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 + + google.golang.org/grpc@1.51.0 + + + +
    • +
    • + 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/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.51.0 + + + +
    • +
    • + 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/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.51.0 + + + +
    • +
    + +
    + +
    + +

    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 +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, k8s.io/apimachinery/pkg/util/net@0.24.2 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/soheilhy/cmux@0.1.5 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/apimachinery/pkg/watch@0.24.2 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc@1.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/discovery@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/tools/cache@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/transport/spdy@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/pkg/kubeclientmetrics@#a4dd357b057e + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/testing@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/dynamic@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/plugin/pkg/client/auth/gcp@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/tools/record@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + 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@0.11.0 + + + +
    • +
    • + 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@1.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 + + google.golang.org/grpc@1.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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@1.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.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.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health/grpc_health_v1@1.51.0 + + google.golang.org/grpc@1.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 + + google.golang.org/grpc@1.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + + google.golang.org/grpc@1.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/listers/core/v1@0.24.2 + + k8s.io/client-go/tools/cache@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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/auth@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#f754726f03da + + k8s.io/client-go/tools/cache@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/kubernetes/fake@0.24.2 + + k8s.io/client-go/testing@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/api/rbac/v1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/pkg/apis/clientauthentication/v1beta1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/apimachinery/pkg/api/errors@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync/common@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/apimachinery/pkg/api/equality@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + 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 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/pkg/kubeclientmetrics@#a4dd357b057e + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/testing@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@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/transport@0.24.2 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/plugin/pkg/client/auth/gcp@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@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/transport@0.24.2 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/reflection@1.51.0 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.51.0 + + google.golang.org/grpc@1.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health@1.51.0 + + google.golang.org/grpc/health/grpc_health_v1@1.51.0 + + google.golang.org/grpc@1.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/cache@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/utils/kube@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#f754726f03da + + k8s.io/client-go/listers/core/v1@0.24.2 + + k8s.io/client-go/tools/cache@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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/listers/core/v1@0.24.2 + + k8s.io/client-go/tools/cache@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#f754726f03da + + k8s.io/client-go/tools/clientcmd@0.24.2 + + k8s.io/client-go/tools/auth@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/transport/spdy@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/kubectl/pkg/util/resource@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/health@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync/resource@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/dynamic@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync/ignore@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/utils/testing@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/apimachinery/pkg/util/managedfields@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/tools/cache@0.24.2 + + k8s.io/client-go/tools/pager@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@0.11.0 + + + +
    • +
    • + 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/scheme@0.11.0 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/util/retry@0.24.2 + + k8s.io/apimachinery/pkg/api/errors@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/tools/portforward@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1@0.24.2 + + k8s.io/apimachinery/pkg/api/equality@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.11.0 + + k8s.io/apimachinery/pkg/api/equality@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/apimachinery/pkg/api/validation@0.24.2 + + k8s.io/apimachinery/pkg/apis/meta/v1/validation@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@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/transport@0.24.2 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/kubernetes/fake@0.24.2 + + k8s.io/client-go/testing@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@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/health@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/utils/kube@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync/common@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/utils/kube@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + 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 + + golang.org/x/net/http2@0.11.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/internal/testing/controlplane@0.11.0 + + k8s.io/client-go/tools/clientcmd@0.24.2 + + k8s.io/client-go/tools/auth@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/tools/record@0.24.2 + + k8s.io/client-go/tools/reference@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync/hook@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/sync/resource@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/apimachinery/pkg/watch@0.24.2 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/listers/core/v1@0.24.2 + + k8s.io/client-go/tools/cache@0.24.2 + + k8s.io/client-go/tools/pager@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@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/tools/pager@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#f754726f03da + + k8s.io/client-go/tools/cache@0.24.2 + + k8s.io/client-go/tools/pager@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/utils/kube/scheme@#b4dd8b8c3976 + + k8s.io/apimachinery/pkg/util/managedfields@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@0.11.0 + + + +
    • +
    • + 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 + + 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 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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/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.51.0 + + google.golang.org/grpc/internal/transport@1.51.0 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/kubernetes@0.24.2 + + k8s.io/client-go/kubernetes/typed/storage/v1beta1@0.24.2 + + k8s.io/client-go/applyconfigurations/storage/v1beta1@0.24.2 + + k8s.io/client-go/applyconfigurations/meta/v1@0.24.2 + + k8s.io/client-go/discovery@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/discovery@0.24.2 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#f754726f03da + + k8s.io/client-go/listers/core/v1@0.24.2 + + k8s.io/client-go/tools/cache@0.24.2 + + k8s.io/client-go/tools/pager@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@0.11.0 + + + +
    • +
    • + 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/listers/core/v1@0.24.2 + + k8s.io/client-go/tools/cache@0.24.2 + + k8s.io/client-go/tools/pager@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/diff@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/utils/kube/scheme@#b4dd8b8c3976 + + k8s.io/apimachinery/pkg/util/managedfields@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync/hook@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/sync/common@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/utils/kube@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/sync/common@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/utils/kube@#b4dd8b8c3976 + + 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@0.11.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@0.11.0 + + + +
    • +
    • + 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@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + k8s.io/client-go/kubernetes@0.24.2 + + k8s.io/client-go/kubernetes/typed/storage/v1beta1@0.24.2 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#f754726f03da + + 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@0.11.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.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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync/ignore@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/sync/hook@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/sync/common@#b4dd8b8c3976 + + github.com/argoproj/gitops-engine/pkg/utils/kube@#b4dd8b8c3976 + + 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@0.11.0 + + + +
    • +
    • + 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/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 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/cache@#b4dd8b8c3976 + + k8s.io/kubectl/pkg/util/openapi@0.24.2 + + k8s.io/client-go/discovery@0.24.2 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/sync@#b4dd8b8c3976 + + k8s.io/kubectl/pkg/util/openapi@0.24.2 + + k8s.io/client-go/discovery@0.24.2 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/gitops-engine/pkg/utils/kube@#b4dd8b8c3976 + + k8s.io/kubectl/pkg/util/openapi@0.24.2 + + k8s.io/client-go/discovery@0.24.2 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + 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 + + 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@0.11.0 + + + +
    • +
    • + 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/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.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/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + 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/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 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/restmapper@0.24.2 + + k8s.io/client-go/discovery@0.24.2 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.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

    + + +
    + + + +
    +
    +

    Directory Traversal

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/cyphar/filepath-securejoin +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/cyphar/filepath-securejoin@0.2.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/cyphar/filepath-securejoin@0.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

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + 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/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/api@#f754726f03da + + 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/controller@#f754726f03da + + 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 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-cleanhttp +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/hashicorp/go-retryablehttp@0.7.0 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.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/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/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/api@#f754726f03da + + 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/controller@#f754726f03da + + 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 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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/v2.11.8/ghcr.io_dexidp_dex_v2.38.0.html b/docs/snyk/v2.6.15/ghcr.io_dexidp_dex_v2.37.0.html similarity index 56% rename from docs/snyk/v2.11.8/ghcr.io_dexidp_dex_v2.38.0.html rename to docs/snyk/v2.6.15/ghcr.io_dexidp_dex_v2.37.0.html index 3c41c7b540880..5cac66bfdc642 100644 --- a/docs/snyk/v2.11.8/ghcr.io_dexidp_dex_v2.38.0.html +++ b/docs/snyk/v2.6.15/ghcr.io_dexidp_dex_v2.37.0.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,22 +456,19 @@

    Snyk test report

    -

    September 22nd 2024, 12:26:16 am (UTC+00:00)

    +

    October 29th 2023, 12:27:42 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)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (apk)
    • ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 (gomodules)
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (gomodules)
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (gomodules)
    -
    18 known vulnerabilities
    -
    85 vulnerable dependency paths
    -
    829 dependencies
    +
    28 known vulnerabilities
    +
    79 vulnerable dependency paths
    +
    786 dependencies

    @@ -479,32 +476,29 @@

    Snyk test report

    -
    -

    Allocation of Resources Without Limits or Throttling

    +
    +

    Out-of-bounds Write

    -
    - high severity +
    + critical severity

    • - Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate -
    • -
    • - Package Manager: golang + Package Manager: alpine:3.18
    • Vulnerable module: - golang.org/x/net/http2 + busybox/busybox
    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* and golang.org/x/net/http2@v0.19.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 and busybox/busybox@1.36.1-r0
    @@ -517,18 +511,51 @@

    Detailed paths

    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* + docker-image|ghcr.io/dexidp/dex@v2.37.0 - golang.org/x/net/http2@v0.19.0 + busybox/busybox@1.36.1-r0
    • Introduced through: - github.com/dexidp/dex@* + 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 - golang.org/x/net/http2@v0.20.0 + busybox/ssl_client@1.36.1-r0 @@ -539,49 +566,47 @@

      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 Allocation of Resources Without Limits or Throttling when reading header data from CONTINUATION frames. As part of the HPACK flow, all incoming HEADERS and CONTINUATION frames are read even if their payloads exceed MaxHeaderBytes and will be discarded. An attacker can send excessive data over a connection to render it unresponsive.

      +

      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 golang.org/x/net/http2 to version 0.23.0 or higher.

      +

      Upgrade Alpine:3.18 busybox to version 1.36.1-r1 or higher.

      References


    -
    -

    Out-of-bounds Write

    +
    +

    Denial of Service (DoS)

    -
    - medium severity +
    + high severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • Vulnerable module: - openssl/libcrypto3 + google.golang.org/grpc
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/grpc@v1.46.2
    @@ -594,75 +619,104 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - openssl/libcrypto3@3.1.4-r2 + google.golang.org/grpc@v1.46.2
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - apk-tools/apk-tools@2.14.0-r5 + github.com/dexidp/dex@* - openssl/libcrypto3@3.1.4-r2 + google.golang.org/grpc@v1.56.1
    • -
    • - 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 - - +
    + +

    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 +
    + +
    + +
      +
    • + 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

    + +
    -

    CVE-2024-0727

    +

    Improper Authentication

    @@ -738,7 +768,7 @@

    CVE-2024-0727

    • - Package Manager: alpine:3.19 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -748,7 +778,7 @@

      CVE-2024-0727

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1
    @@ -761,75 +791,75 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - busybox/ssl_client@1.36.1-r15 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - busybox/ssl_client@1.36.1-r15 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1 @@ -841,47 +871,46 @@

      Detailed paths


      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.

      +

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine:3.18. + 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.19 openssl to version 3.1.4-r5 or higher.

      +

      Upgrade Alpine:3.18 openssl to version 3.1.1-r2 or higher.

      References


    -

    Infinite loop

    +

    Inefficient Regular Expression Complexity

    @@ -892,20 +921,17 @@

    Infinite loop

    • - Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate -
    • -
    • - Package Manager: golang + Package Manager: alpine:3.18
    • Vulnerable module: - google.golang.org/protobuf/internal/encoding/json + openssl/libcrypto3
    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/internal/encoding/json@v1.31.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1
    @@ -918,18 +944,75 @@

    Detailed paths

    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* + docker-image|ghcr.io/dexidp/dex@v2.37.0 - google.golang.org/protobuf/internal/encoding/json@v1.31.0 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - github.com/dexidp/dex@* + 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 - google.golang.org/protobuf/internal/encoding/json@v1.32.0 + openssl/libssl3@3.1.1-r1 @@ -940,28 +1023,57 @@

      Detailed paths


      -

      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.

      +

      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 google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

      +

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

      References


    -

    Stack-based Buffer Overflow

    +

    Excessive Iteration

    @@ -972,20 +1084,17 @@

    Stack-based Buffer Overflow

    • - Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate -
    • -
    • - Package Manager: golang + Package Manager: alpine:3.18
    • Vulnerable module: - google.golang.org/protobuf/encoding/protojson + openssl/libcrypto3
    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1
    @@ -998,9 +1107,75 @@

    Detailed paths

    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* + 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 - google.golang.org/protobuf/encoding/protojson@v1.31.0 + openssl/libssl3@3.1.1-r1 @@ -1011,25 +1186,56 @@

      Detailed paths


      -

      Overview

      -

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

      +

      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 google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

      +

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

      References


    -

    Infinite loop

    +

    Cross-site Scripting (XSS)

    @@ -1039,21 +1245,18 @@

    Infinite loop


      -
    • - 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 + golang.org/x/net/html
    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + github.com/dexidp/dex@* and golang.org/x/net/html@v0.11.0
    @@ -1064,20 +1267,11 @@

    Infinite loop

    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 + golang.org/x/net/html@v0.11.0 @@ -1089,27 +1283,82 @@

      Detailed paths


      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.

      +

      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 google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

      +

      Upgrade golang.org/x/net/html to version 0.13.0 or higher.

      References


    -

    Insertion of Sensitive Information into Log File

    +

    MPL-2.0 license

    @@ -1119,21 +1368,18 @@

    Insertion of Sensitive Information into Log File


      -
    • - Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate -
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/hashicorp/go-retryablehttp + github.com/hashicorp/vault/sdk/helper/certutil
    • Introduced through: - github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-retryablehttp@v0.7.1 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/sdk/helper/certutil@v0.5.0
    @@ -1148,36 +1394,100 @@

    Detailed paths

    Introduced through: github.com/hairyhenderson/gomplate/v3@* - github.com/hashicorp/go-retryablehttp@v0.7.1 + 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 + + + +
  • + + +
    +
    -

    Overview

    -

    Affected versions of this package are vulnerable to Insertion of Sensitive Information into Log File due to not sanitizing urls when writing them to the log file. This could lead to an attacker writing sensitive HTTP basic auth credentials to the log file.

    -

    Remediation

    -

    Upgrade github.com/hashicorp/go-retryablehttp to version 0.7.7 or higher.

    -

    References

    - +

    MPL-2.0 license


    -

    Improper Handling of Highly Compressed Data (Data Amplification)

    +

    MPL-2.0 license

    @@ -1187,21 +1497,18 @@

    Improper Handling of Highly Compressed Data (Data Amplif
      -
    • - Manifest file: ghcr.io/dexidp/dex:v2.38.0/dexidp/dex /usr/local/bin/dex -
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/go-jose/go-jose/v3 + github.com/hashicorp/vault/api
    • Introduced through: - github.com/dexidp/dex@* and github.com/go-jose/go-jose/v3@v3.0.1 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/api@v1.6.0
    @@ -1214,9 +1521,9 @@

    Detailed paths

    • Introduced through: - github.com/dexidp/dex@* + github.com/hairyhenderson/gomplate/v3@* - github.com/go-jose/go-jose/v3@v3.0.1 + github.com/hashicorp/vault/api@v1.6.0 @@ -1227,26 +1534,17 @@

      Detailed paths


      -

      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

      - +

      MPL-2.0 license


    -

    Out-of-bounds Write

    +

    MPL-2.0 license

    @@ -1257,17 +1555,17 @@

    Out-of-bounds Write

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - busybox/busybox + github.com/hashicorp/serf/coordinate
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and busybox/busybox@1.36.1-r15 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/serf/coordinate@v0.9.7
    @@ -1280,51 +1578,9 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - busybox/busybox@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 - - busybox/busybox-binsh@1.36.1-r15 - - busybox/busybox@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - busybox/busybox-binsh@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 - - busybox/busybox-binsh@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/ssl_client@1.36.1-r15 + github.com/hashicorp/serf/coordinate@v0.9.7 @@ -1335,26 +1591,17 @@

      Detailed paths


      -

      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.19 relevant fixed versions and status.

      -

      A heap-buffer-overflow was discovered in BusyBox v.1.36.1 in the next_token function at awk.c:1159.

      -

      Remediation

      -

      Upgrade Alpine:3.19 busybox to version 1.36.1-r16 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Use After Free

    +

    MPL-2.0 license

    @@ -1365,17 +1612,17 @@

    Use After Free

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - busybox/busybox + github.com/hashicorp/hcl/v2
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and busybox/busybox@1.36.1-r15 + github.com/dexidp/dex@* and github.com/hashicorp/hcl/v2@v2.13.0
    @@ -1388,51 +1635,72 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/dexidp/dex@* - busybox/busybox@1.36.1-r15 + github.com/hashicorp/hcl/v2@v2.13.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/dexidp/dex@* - alpine-baselayout/alpine-baselayout@3.4.3-r2 + github.com/hashicorp/hcl/v2/ext/customdecode@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* - busybox/busybox-binsh@1.36.1-r15 + github.com/hashicorp/hcl/v2/ext/tryfunc@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* - busybox/busybox@1.36.1-r15 + github.com/hashicorp/hcl/v2/gohcl@v2.13.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/dexidp/dex@* - busybox/busybox-binsh@1.36.1-r15 + github.com/hashicorp/hcl/v2/hclparse@v2.13.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/dexidp/dex@* - alpine-baselayout/alpine-baselayout@3.4.3-r2 + github.com/hashicorp/hcl/v2/hclsyntax@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* - busybox/busybox-binsh@1.36.1-r15 + github.com/hashicorp/hcl/v2/hclwrite@v2.13.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/dexidp/dex@* - busybox/ssl_client@1.36.1-r15 + github.com/hashicorp/hcl/v2/json@v2.13.0 @@ -1443,26 +1711,17 @@

      Detailed paths


      -

      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.19 relevant fixed versions and status.

      -

      A use-after-free vulnerability was discovered in BusyBox v.1.36.1 via a crafted awk pattern in the awk.c copyvar function.

      -

      Remediation

      -

      Upgrade Alpine:3.19 busybox to version 1.36.1-r19 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Use After Free

    +

    MPL-2.0 license

    @@ -1473,17 +1732,17 @@

    Use After Free

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - busybox/busybox + github.com/hashicorp/hcl
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and busybox/busybox@1.36.1-r15 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/hcl@v1.0.0
    @@ -1496,51 +1755,45 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/busybox@1.36.1-r15 + github.com/hashicorp/hcl@v1.0.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 - - busybox/busybox-binsh@1.36.1-r15 + github.com/hairyhenderson/gomplate/v3@* - busybox/busybox@1.36.1-r15 + github.com/hashicorp/hcl/hcl/parser@v1.0.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/busybox-binsh@1.36.1-r15 + github.com/hashicorp/hcl/hcl/strconv@v1.0.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 + github.com/hairyhenderson/gomplate/v3@* - busybox/busybox-binsh@1.36.1-r15 + github.com/hashicorp/hcl/hcl/token@v1.0.0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/ssl_client@1.36.1-r15 + github.com/hashicorp/hcl/json/parser@v1.0.0 @@ -1551,26 +1804,17 @@

      Detailed paths


      -

      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.19 relevant fixed versions and status.

      -

      A use-after-free vulnerability in BusyBox v.1.36.1 allows attackers to cause a denial of service via a crafted awk pattern in the awk.c evaluate function.

      -

      Remediation

      -

      Upgrade Alpine:3.19 busybox to version 1.36.1-r19 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Use After Free

    +

    MPL-2.0 license

    @@ -1581,17 +1825,17 @@

    Use After Free

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - busybox/busybox + github.com/hashicorp/golang-lru/simplelru
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and busybox/busybox@1.36.1-r15 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/golang-lru/simplelru@v0.5.4
    @@ -1604,51 +1848,9 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - busybox/busybox@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 - - busybox/busybox-binsh@1.36.1-r15 - - busybox/busybox@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - busybox/busybox-binsh@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 - - alpine-baselayout/alpine-baselayout@3.4.3-r2 - - busybox/busybox-binsh@1.36.1-r15 - - - -
    • -
    • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/ssl_client@1.36.1-r15 + github.com/hashicorp/golang-lru/simplelru@v0.5.4 @@ -1659,47 +1861,38 @@

      Detailed paths


      -

      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.19 relevant fixed versions and status.

      -

      A use-after-free vulnerability was discovered in xasprintf function in xfuncs_printf.c:344 in BusyBox v.1.36.1.

      -

      Remediation

      -

      Upgrade Alpine:3.19 busybox to version 1.36.1-r17 or higher.

      -

      References

      - +

      MPL-2.0 license


    -
    -

    CVE-2023-6237

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - openssl/libcrypto3 + github.com/hashicorp/go-version
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-version@v1.5.0
    @@ -1712,75 +1905,246 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - openssl/libcrypto3@3.1.4-r2 + github.com/hashicorp/go-version@v1.5.0
    • -
    • - 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 - - +
    + +

    MPL-2.0 license

    -
  • -
  • - Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 +
    + + + +
  • +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@* - openssl/libssl3@3.1.4-r2 + github.com/hashicorp/go-sockaddr@v1.0.2
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - apk-tools/apk-tools@2.14.0-r5 + github.com/hashicorp/go-sockaddr/template@v1.0.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@* - openssl/libssl3@3.1.4-r2 + github.com/hashicorp/go-secure-stdlib/strutil@v0.1.2
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + github.com/hairyhenderson/gomplate/v3@* - busybox/ssl_client@1.36.1-r15 + github.com/hashicorp/go-secure-stdlib/parseutil@v0.1.5 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@* - openssl/libssl3@3.1.4-r2 + github.com/hashicorp/go-secure-stdlib/mlock@v0.1.2 @@ -1791,71 +2155,332 @@

      Detailed paths


      -

      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: Checking excessively long invalid RSA public keys may take - a long time.

      -

      Impact summary: Applications that use the function EVP_PKEY_public_check() - to check RSA public keys may experience long delays. Where the key that - is being checked has been obtained from an untrusted source this may lead - to a Denial of Service.

      -

      When function EVP_PKEY_public_check() is called on RSA public keys, - a computation is done to confirm that the RSA modulus, n, is composite. - For valid RSA keys, n is a product of two or more large primes and this - computation completes quickly. However, if n is an overly large prime, - then this computation would take a long time.

      -

      An application that calls EVP_PKEY_public_check() and supplies an RSA key - obtained from an untrusted source could be vulnerable to a Denial of Service - attack.

      -

      The function EVP_PKEY_public_check() is not called from other OpenSSL - functions however it is called from the OpenSSL pkey command line - application. For that reason that application is also vulnerable if used - with the '-pubin' and '-check' options on untrusted data.

      -

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

      -

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

      -

      Remediation

      -

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

      -

      References

      - +

      MPL-2.0 license

      + +
      + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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


    -
    -

    CVE-2024-2511

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - openssl/libcrypto3 + github.com/hashicorp/errwrap
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/errwrap@v1.1.0
    @@ -1868,75 +2493,9 @@

    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 + github.com/hairyhenderson/gomplate/v3@* - openssl/libssl3@3.1.4-r2 + github.com/hashicorp/errwrap@v1.1.0 @@ -1947,67 +2506,38 @@

      Detailed paths


      -

      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: Some non-default TLS server configurations can cause unbounded - memory growth when processing TLSv1.3 sessions

      -

      Impact summary: An attacker may exploit certain server configurations to trigger - unbounded memory growth that would lead to a Denial of Service

      -

      This problem can occur in TLSv1.3 if the non-default SSL_OP_NO_TICKET option is - being used (but not if early_data support is also configured and the default - anti-replay protection is in use). In this case, under certain conditions, the - session cache can get into an incorrect state and it will fail to flush properly - as it fills. The session cache will continue to grow in an unbounded manner. A - malicious client could deliberately create the scenario for this failure to - force a Denial of Service. It may also happen by accident in normal operation.

      -

      This issue only affects TLS servers supporting TLSv1.3. It does not affect TLS - clients.

      -

      The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue. OpenSSL - 1.0.2 is also not affected by this issue.

      -

      Remediation

      -

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

      -

      References

      - +

      MPL-2.0 license


    -
    -

    CVE-2024-4603

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - openssl/libcrypto3 + github.com/hashicorp/consul/api
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/consul/api@v1.13.0
    @@ -2020,75 +2550,9 @@

    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 + github.com/hairyhenderson/gomplate/v3@* - openssl/libssl3@3.1.4-r2 + github.com/hashicorp/consul/api@v1.13.0 @@ -2099,75 +2563,38 @@

      Detailed paths


      -

      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: Checking excessively long DSA keys or parameters may be very - slow.

      -

      Impact summary: Applications that use the functions EVP_PKEY_param_check() - or EVP_PKEY_public_check() to check a DSA public key or DSA 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 functions EVP_PKEY_param_check() or EVP_PKEY_public_check() perform - various checks on DSA parameters. Some of those computations take a long time - if the modulus (p parameter) is too large.

      -

      Trying to use a very large modulus is slow and OpenSSL will not allow using - public keys with a modulus which is over 10,000 bits in length for signature - verification. However the key and parameter check functions do not limit - the modulus size when performing the checks.

      -

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

      -

      These functions are not called by OpenSSL itself on untrusted DSA keys so - only applications that directly call these functions may be vulnerable.

      -

      Also vulnerable are the OpenSSL pkey 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 affected by this issue.

      -

      Remediation

      -

      Upgrade Alpine:3.19 openssl to version 3.1.5-r0 or higher.

      -

      References

      - +

      MPL-2.0 license


    -
    -

    CVE-2024-5535

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - openssl/libcrypto3 + github.com/gosimple/slug
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/hairyhenderson/gomplate/v3@* and github.com/gosimple/slug@v1.12.0
    @@ -2180,75 +2607,9 @@

    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 + github.com/hairyhenderson/gomplate/v3@* - openssl/libssl3@3.1.4-r2 + github.com/gosimple/slug@v1.12.0 @@ -2259,110 +2620,38 @@

      Detailed paths


      -

      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: Calling the OpenSSL API function SSL_select_next_proto with an - empty supported client protocols buffer may cause a crash or memory contents to - be sent to the peer.

      -

      Impact summary: A buffer overread can have a range of potential consequences - such as unexpected application beahviour or a crash. In particular this issue - could result in up to 255 bytes of arbitrary private data from memory being sent - to the peer leading to a loss of confidentiality. However, only applications - that directly call the SSL_select_next_proto function with a 0 length list of - supported client protocols are affected by this issue. This would normally never - be a valid scenario and is typically not under attacker control but may occur by - accident in the case of a configuration or programming error in the calling - application.

      -

      The OpenSSL API function SSL_select_next_proto is typically used by TLS - applications that support ALPN (Application Layer Protocol Negotiation) or NPN - (Next Protocol Negotiation). NPN is older, was never standardised and - is deprecated in favour of ALPN. We believe that ALPN is significantly more - widely deployed than NPN. The SSL_select_next_proto function accepts a list of - protocols from the server and a list of protocols from the client and returns - the first protocol that appears in the server list that also appears in the - client list. In the case of no overlap between the two lists it returns the - first item in the client list. In either case it will signal whether an overlap - between the two lists was found. In the case where SSL_select_next_proto is - called with a zero length client list it fails to notice this condition and - returns the memory immediately following the client list pointer (and reports - that there was no overlap in the lists).

      -

      This function is typically called from a server side application callback for - ALPN or a client side application callback for NPN. In the case of ALPN the list - of protocols supplied by the client is guaranteed by libssl to never be zero in - length. The list of server protocols comes from the application and should never - normally be expected to be of zero length. In this case if the - SSL_select_next_proto function has been called as expected (with the list - supplied by the client passed in the client/client_len parameters), then the - application will not be vulnerable to this issue. If the application has - accidentally been configured with a zero length server list, and has - accidentally passed that zero length server list in the client/client_len - parameters, and has additionally failed to correctly handle a "no overlap" - response (which would normally result in a handshake failure in ALPN) then it - will be vulnerable to this problem.

      -

      In the case of NPN, the protocol permits the client to opportunistically select - a protocol when there is no overlap. OpenSSL returns the first client protocol - in the no overlap case in support of this. The list of client protocols comes - from the application and should never normally be expected to be of zero length. - However if the SSL_select_next_proto function is accidentally called with a - client_len of 0 then an invalid memory pointer will be returned instead. If the - application uses this output as the opportunistic protocol then the loss of - confidentiality will occur.

      -

      This issue has been assessed as Low severity because applications are most - likely to be vulnerable if they are using NPN instead of ALPN - but NPN is not - widely used. It also requires an application configuration or programming error. - Finally, this issue would not typically be under attacker control making active - exploitation unlikely.

      -

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

      -

      Due to the low severity of this issue we are not issuing new releases of - OpenSSL at this time. The fix will be included in the next releases when they - become available.

      -

      Remediation

      -

      Upgrade Alpine:3.19 openssl to version 3.1.6-r0 or higher.

      -

      References

      - +

      MPL-2.0 license


    -
    -

    CVE-2024-4741

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.19 + Package Manager: golang
    • - Vulnerable module: + Module: - openssl/libcrypto3 + github.com/go-sql-driver/mysql
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + github.com/dexidp/dex@* and github.com/go-sql-driver/mysql@v1.7.1
    @@ -2375,75 +2664,9 @@

    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 + github.com/dexidp/dex@* - openssl/libssl3@3.1.4-r2 + github.com/go-sql-driver/mysql@v1.7.1 @@ -2454,20 +2677,17 @@

      Detailed paths


      -

      NVD Description

      -

      This vulnerability has not been analyzed by NVD yet.

      -

      Remediation

      -

      Upgrade Alpine:3.19 openssl to version 3.1.6-r0 or higher.

      +

      MPL-2.0 license


    -

    CVE-2024-6119

    +

    CVE-2023-5363

    @@ -2478,7 +2698,7 @@

    CVE-2024-6119

    • - Package Manager: alpine:3.19 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -2488,7 +2708,7 @@

      CVE-2024-6119

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1
    @@ -2501,75 +2721,75 @@

    Detailed paths

    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - busybox/ssl_client@1.36.1-r15 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1 - openssl/libcrypto3@3.1.4-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - apk-tools/apk-tools@2.14.0-r5 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.38.0 + docker-image|ghcr.io/dexidp/dex@v2.37.0 - busybox/ssl_client@1.36.1-r15 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.1.4-r2 + openssl/libssl3@3.1.1-r1 @@ -2582,41 +2802,55 @@

      Detailed paths

      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: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

      -

      Impact summary: Abnormal termination of an application can a cause a denial of - service.

      -

      Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

      -

      Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

      -

      TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

      -

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

      + 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.19 openssl to version 3.1.7-r0 or higher.

      +

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

      References


    diff --git a/docs/snyk/master/ghcr.io_dexidp_dex_v2.41.1.html b/docs/snyk/v2.6.15/haproxy_2.6.14-alpine.html similarity index 70% rename from docs/snyk/master/ghcr.io_dexidp_dex_v2.41.1.html rename to docs/snyk/v2.6.15/haproxy_2.6.14-alpine.html index 55eb2fcbe954b..605a7d8b7d5bd 100644 --- a/docs/snyk/master/ghcr.io_dexidp_dex_v2.41.1.html +++ b/docs/snyk/v2.6.15/haproxy_2.6.14-alpine.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,55 +456,58 @@

    Snyk test report

    -

    September 22nd 2024, 12:19:01 am (UTC+00:00)

    +

    October 29th 2023, 12:27:48 am (UTC+00:00)

    - Scanned the following paths: + Scanned the following path:
      -
    • ghcr.io/dexidp/dex:v2.41.1/dexidp/dex (apk)
    • -
    • ghcr.io/dexidp/dex:v2.41.1/hairyhenderson/gomplate/v4//usr/local/bin/gomplate (gomodules)
    • -
    • ghcr.io/dexidp/dex:v2.41.1/dexidp/dex//usr/local/bin/docker-entrypoint (gomodules)
    • -
    • ghcr.io/dexidp/dex:v2.41.1/dexidp/dex//usr/local/bin/dex (gomodules)
    • +
    • haproxy:2.6.14-alpine (apk)
    -
    2 known vulnerabilities
    -
    8 vulnerable dependency paths
    -
    969 dependencies
    +
    1 known vulnerabilities
    +
    9 vulnerable dependency paths
    +
    18 dependencies
    - +
    + + + + + + + +
    Project docker-image|haproxy
    Path haproxy:2.6.14-alpine
    Package Manager apk
    +
    -
    -

    Insertion of Sensitive Information into Log File

    +
    +

    CVE-2023-5363

    -
    - medium severity +
    + low severity

    • - Manifest file: ghcr.io/dexidp/dex:v2.41.1/hairyhenderson/gomplate/v4 /usr/local/bin/gomplate -
    • -
    • - Package Manager: golang + Package Manager: alpine:3.18
    • Vulnerable module: - google.golang.org/grpc/metadata + openssl/libcrypto3
    • Introduced through: - github.com/hairyhenderson/gomplate/v4@* and google.golang.org/grpc/metadata@v1.64.0 + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0
    @@ -517,140 +520,97 @@

    Detailed paths

    • Introduced through: - github.com/hairyhenderson/gomplate/v4@* + docker-image|haproxy@2.6.14-alpine - google.golang.org/grpc/metadata@v1.64.0 + openssl/libcrypto3@3.1.2-r0
    • -
    - -
    - -
    - -

    Overview

    -

    google.golang.org/grpc/metadata is a package that defines the structure of the metadata supported by the gRPC library

    -

    Affected versions of this package are vulnerable to Insertion of Sensitive Information into Log File in the form of gRPC metadata. If the metadata contains sensitive information an attacker can expose it.

    -

    Remediation

    -

    Upgrade google.golang.org/grpc/metadata to version 1.64.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-6119

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: alpine:3.20 -
    • -
    • - Vulnerable module: - - openssl/libcrypto3 -
    • - -
    • Introduced through: - - docker-image|ghcr.io/dexidp/dex@v2.41.1 and openssl/libcrypto3@3.3.1-r3 - -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - openssl/libcrypto3@3.3.1-r3 + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.1-r3 + openssl/libcrypto3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - busybox/ssl_client@1.36.1-r29 + busybox/ssl_client@1.36.1-r2 - openssl/libcrypto3@3.3.1-r3 + openssl/libcrypto3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - apk-tools/apk-tools@2.14.4-r0 + .haproxy-rundeps@20230809.001942 - openssl/libssl3@3.3.1-r3 + openssl/libssl3@3.1.2-r0 - openssl/libcrypto3@3.3.1-r3 + openssl/libcrypto3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + 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.3.1-r3 + openssl/libssl3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.1-r3 + openssl/libssl3@3.1.2-r0
    • Introduced through: - docker-image|ghcr.io/dexidp/dex@v2.41.1 + docker-image|haproxy@2.6.14-alpine - busybox/ssl_client@1.36.1-r29 + busybox/ssl_client@1.36.1-r2 - openssl/libssl3@3.3.1-r3 + openssl/libssl3@3.1.2-r0 @@ -663,41 +623,55 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      Issue summary: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

      -

      Impact summary: Abnormal termination of an application can a cause a denial of - service.

      -

      Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

      -

      Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

      -

      TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

      -

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

      + 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.20 openssl to version 3.3.2-r0 or higher.

      +

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

      References


    diff --git a/docs/snyk/v2.10.16/quay.io_argoproj_argocd_v2.10.16.html b/docs/snyk/v2.6.15/quay.io_argoproj_argocd_v2.6.15.html similarity index 59% rename from docs/snyk/v2.10.16/quay.io_argoproj_argocd_v2.10.16.html rename to docs/snyk/v2.6.15/quay.io_argoproj_argocd_v2.6.15.html index 8ba1cd553e6c1..759d3b81c634b 100644 --- a/docs/snyk/v2.10.16/quay.io_argoproj_argocd_v2.10.16.html +++ b/docs/snyk/v2.6.15/quay.io_argoproj_argocd_v2.6.15.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,23 +456,19 @@

    Snyk test report

    -

    September 22nd 2024, 12:28:55 am (UTC+00:00)

    +

    October 29th 2023, 12:28:36 am (UTC+00:00)

    Scanned the following paths:
      -
    • quay.io/argoproj/argocd:v2.10.16/argoproj/argocd/Dockerfile (deb)
    • -
    • quay.io/argoproj/argocd:v2.10.16/argoproj/argo-cd/v2//usr/local/bin/argocd (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.10.16//usr/local/bin/kustomize (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.10.16/helm/v3//usr/local/bin/helm (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.10.16/git-lfs/git-lfs//usr/bin/git-lfs (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.6.15/argoproj/argocd (deb)
    • quay.io/argoproj/argocd:v2.6.15/argoproj/argo-cd/v2 (gomodules)
    • quay.io/argoproj/argocd:v2.6.15/kustomize/kustomize/v4 (gomodules)
    • quay.io/argoproj/argocd:v2.6.15/helm/v3 (gomodules)
    • quay.io/argoproj/argocd:v2.6.15/git-lfs/git-lfs (gomodules)
    -
    35 known vulnerabilities
    -
    241 vulnerable dependency paths
    -
    2278 dependencies
    +
    48 known vulnerabilities
    +
    168 vulnerable dependency paths
    +
    2063 dependencies
    @@ -481,7 +477,670 @@

    Snyk test report

    -

    Allocation of Resources Without Limits or Throttling

    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + gopkg.in/yaml.v3 +
    • + +
    • Introduced through: + + sigs.k8s.io/kustomize/kustomize/v4@* and gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + sigs.k8s.io/kustomize/kustomize/v4@* + + gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    gopkg.in/yaml.v3 is a YAML support package for the Go language.

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) via the Unmarshal function, which causes the program to crash when attempting to deserialize invalid input.

    +

    PoC

    +
    package main
    +        
    +        import (
    +            "gopkg.in/yaml.v3"
    +        )
    +        
    +        func main() {
    +            var t interface{}
    +            yaml.Unmarshal([]byte("0: [:!00 \xef"), &t)
    +        }
    +        
    +

    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 gopkg.in/yaml.v3 to version 3.0.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    NULL Pointer Dereference

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + gopkg.in/yaml.v3 +
    • + +
    • Introduced through: + + sigs.k8s.io/kustomize/kustomize/v4@* and gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + sigs.k8s.io/kustomize/kustomize/v4@* + + gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    gopkg.in/yaml.v3 is a YAML support package for the Go language.

    +

    Affected versions of this package are vulnerable to NULL Pointer Dereference when parsing #\n-\n-\n0 via the parserc.go parser.

    +

    PoC

    +
    package main
    +        
    +        import (
    +            "gopkg.in/yaml.v3"
    +        )
    +        
    +        func main() {
    +            var t interface{}
    +            yaml.Unmarshal([]byte("#\n-\n-\n0"), &t)
    +        }
    +        
    +

    Remediation

    +

    Upgrade gopkg.in/yaml.v3 to version 3.0.1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/grpc +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/grpc@v1.51.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/grpc@v1.51.0 + + + +
    • +
    + +
    + +
    + +

    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 +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2/hpack +
    • + +
    • Introduced through: + + sigs.k8s.io/kustomize/kustomize/v4@* and golang.org/x/net/http2/hpack@v0.0.0-20220127200216-cd36cc0744dd + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + sigs.k8s.io/kustomize/kustomize/v4@* + + golang.org/x/net/http2/hpack@v0.0.0-20220127200216-cd36cc0744dd + + + +
    • +
    • + Introduced through: + helm.sh/helm/v3@* + + golang.org/x/net/http2/hpack@v0.0.0-20220722155237-a158d28d115b + + + +
    • +
    + +
    + +
    + +

    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 golang.org/x/net/http2/hpack to version 0.7.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and golang.org/x/net/http2@v0.11.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + golang.org/x/net/http2@v0.11.0 + + + +
    • +
    • + Introduced through: + helm.sh/helm/v3@* + + golang.org/x/net/http2@v0.0.0-20220722155237-a158d28d115b + + + +
    • +
    + +
    + +
    + +

    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

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + helm.sh/helm/v3@* and golang.org/x/net/http2@v0.0.0-20220722155237-a158d28d115b + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + helm.sh/helm/v3@* + + golang.org/x/net/http2@v0.0.0-20220722155237-a158d28d115b + + + +
    • +
    + +
    + +
    + +

    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 as an HTTP/2 connection can hang during closing if a shutdown was preempted by a fatal error.

    +

    Remediation

    +

    Upgrade golang.org/x/net/http2 to version 0.0.0-20220906165146-f3363e06e74c, 1.18.6, 1.19.1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + helm.sh/helm/v3@* and golang.org/x/net/http2@v0.0.0-20220722155237-a158d28d115b + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + helm.sh/helm/v3@* + + golang.org/x/net/http2@v0.0.0-20220722155237-a158d28d115b + + + +
    • +
    + +
    + +
    + +

    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 golang.org/x/net/http2 to version 0.7.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + glibc/libc-bin +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.6.15 and glibc/libc-bin@2.35-0ubuntu3.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.6.15 + + glibc/libc-bin@2.35-0ubuntu3.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.6.15 + + glibc/libc6@2.35-0ubuntu3.1 + + + +
    • +
    + +
    + +
    + +

    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.

    +

    A buffer overflow was discovered in the GNU C Library's dynamic loader ld.so while processing the GLIBC_TUNABLES environment variable. This issue could allow a local attacker to use maliciously crafted GLIBC_TUNABLES environment variables when launching binaries with SUID permission to execute code with elevated privileges.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 glibc to version 2.35-0ubuntu3.4 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Directory Traversal

    @@ -491,21 +1150,18 @@

    Allocation of Resources Without Limits or Throttling

      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argo-cd/v2 /usr/local/bin/argocd -
    • Package Manager: golang
    • Vulnerable module: - golang.org/x/net/http2 + github.com/cyphar/filepath-securejoin
    • Introduced through: - github.com/argoproj/argo-cd/v2@* and golang.org/x/net/http2@v0.19.0 + github.com/argoproj/argo-cd/v2@* and github.com/cyphar/filepath-securejoin@v0.2.3
    @@ -520,7 +1176,7 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@* - golang.org/x/net/http2@v0.19.0 + github.com/cyphar/filepath-securejoin@v0.2.3 @@ -529,7 +1185,7 @@

    Detailed paths

    Introduced through: helm.sh/helm/v3@* - golang.org/x/net/http2@v0.17.0 + github.com/cyphar/filepath-securejoin@v0.2.3 @@ -541,52 +1197,68 @@

    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 Allocation of Resources Without Limits or Throttling when reading header data from CONTINUATION frames. As part of the HPACK flow, all incoming HEADERS and CONTINUATION frames are read even if their payloads exceed MaxHeaderBytes and will be discarded. An attacker can send excessive data over a connection to render it unresponsive.

    +

    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 golang.org/x/net/http2 to version 0.23.0 or higher.

    +

    Upgrade github.com/cyphar/filepath-securejoin to version 0.2.4 or higher.

    References


    -
    -

    CVE-2024-41996

    +
    +

    Out-of-bounds Write

    -
    - medium severity +
    + high severity

      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - openssl/libssl3 + curl/libcurl3-gnutls
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and openssl/libssl3@3.0.2-0ubuntu1.16 + docker-image|quay.io/argoproj/argocd@v2.6.15, git@1:2.34.1-1ubuntu1.10 and others
    @@ -598,113 +1270,92 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 + git@1:2.34.1-1ubuntu1.10 - openssl/libssl3@3.0.2-0ubuntu1.16 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - libfido2/libfido2-1@1.10.0-1 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - +
  • - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - ca-certificates@20230311ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.16 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - +
    + +

    NVD Description

    +

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

    +

    This flaw makes curl overflow a heap based buffer in the SOCKS5 proxy + handshake.

    +

    When curl is asked to pass along the host name to the SOCKS5 proxy to allow + that to resolve the address instead of it getting done by curl itself, the + maximum length that host name can be is 255 bytes.

    +

    If the host name is detected to be longer, curl switches to local name + resolving and instead passes on the resolved address only. Due to this bug, + the local variable that means "let the host resolve the name" could get the + wrong value during a slow SOCKS5 handshake, and contrary to the intention, + copy the too long host name to the target buffer instead of copying just the + resolved address there.

    +

    The target buffer being a heap based buffer, and the host name coming from the + URL that curl has been told to operate with.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 curl to version 7.81.0-1ubuntu1.14 or higher.

    +

    References

    + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - +
    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssl@3.0.2-0ubuntu1.16 - - +
  • +
    +

    CVE-2020-22916

    +
    - +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + xz-utils/liblzma5 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.6.15 and xz-utils/liblzma5@5.2.5-2ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - ca-certificates@20230311ubuntu0.22.04.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 - openssl@3.0.2-0ubuntu1.16 + xz-utils/liblzma5@5.2.5-2ubuntu1 @@ -716,28 +1367,32 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Ubuntu. +

      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.

      -

      Validating the order of the public keys in the Diffie-Hellman Key Agreement Protocol, when an approved safe prime is used, allows remote attackers (from the client side) to trigger unnecessarily expensive server-side DHE modular-exponentiation calculations. The client may cause asymmetric resource consumption. The basic attack scenario is that the client must claim that it can only communicate with DHE, and the server must be configured to allow DHE and validate the order of the public key.

      +

      ** DISPUTED ** 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 openssl.

      +

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

      References


    -

    CVE-2024-6119

    +

    Out-of-bounds Write

    @@ -747,22 +1402,19 @@

    CVE-2024-6119


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - openssl/libssl3 + perl/perl-modules-5.34
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and openssl/libssl3@3.0.2-0ubuntu1.16 + docker-image|quay.io/argoproj/argocd@v2.6.15, git@1:2.34.1-1ubuntu1.10 and others
    @@ -774,113 +1426,61 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + git@1:2.34.1-1ubuntu1.10 - libfido2/libfido2-1@1.10.0-1 + perl@5.34.0-3ubuntu1.2 - openssl/libssl3@3.0.2-0ubuntu1.16 + perl/perl-modules-5.34@5.34.0-3ubuntu1.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 + docker-image|quay.io/argoproj/argocd@v2.6.15 - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + git@1:2.34.1-1ubuntu1.10 - ca-certificates@20230311ubuntu0.22.04.1 + perl@5.34.0-3ubuntu1.2 - openssl@3.0.2-0ubuntu1.16 + perl/libperl5.34@5.34.0-3ubuntu1.2 - openssl/libssl3@3.0.2-0ubuntu1.16 + perl/perl-modules-5.34@5.34.0-3ubuntu1.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 + docker-image|quay.io/argoproj/argocd@v2.6.15 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 + git@1:2.34.1-1ubuntu1.10 - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + perl@5.34.0-3ubuntu1.2 - openssl/libssl3@3.0.2-0ubuntu1.16 + perl/libperl5.34@5.34.0-3ubuntu1.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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 + docker-image|quay.io/argoproj/argocd@v2.6.15 - 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.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + git@1:2.34.1-1ubuntu1.10 - openssl@3.0.2-0ubuntu1.16 + perl@5.34.0-3ubuntu1.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - ca-certificates@20230311ubuntu0.22.04.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 - openssl@3.0.2-0ubuntu1.16 + perl/perl-base@5.34.0-3ubuntu1.2 @@ -892,48 +1492,27 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Ubuntu. +

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

      -

      Issue summary: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

      -

      Impact summary: Abnormal termination of an application can a cause a denial of - service.

      -

      Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

      -

      Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

      -

      TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

      -

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

      +

      In Perl 5.34.0, function S_find_uninit_var in sv.c has a stack-based crash that can lead to remote code execution or local privilege escalation.

      Remediation

      -

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.18 or higher.

      +

      There is no fixed version for Ubuntu:22.04 perl.

      References


    -

    Information Exposure

    +

    CVE-2023-5363

    @@ -943,21 +1522,18 @@

    Information Exposure


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - libgcrypt20 + openssl/libssl3
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and libgcrypt20@1.9.4-3ubuntu3 + docker-image|quay.io/argoproj/argocd@v2.6.15 and openssl/libssl3@3.0.2-0ubuntu1.10
    @@ -970,150 +1546,113 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - libgcrypt20@1.9.4-3ubuntu3 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - gnupg2/dirmngr@2.2.27-3ubuntu2.1 + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - libgcrypt20@1.9.4-3ubuntu3 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - gnupg2/gpg@2.2.27-3ubuntu2.1 + libfido2/libfido2-1@1.10.0-1 - libgcrypt20@1.9.4-3ubuntu3 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - apt@2.4.12 + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - apt/libapt-pkg6.0@2.4.12 - - libgcrypt20@1.9.4-3ubuntu3 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - apt@2.4.12 + ca-certificates@20230311ubuntu0.22.04.1 - gnupg2/gpgv@2.2.27-3ubuntu2.1 + openssl@3.0.2-0ubuntu1.10 - libgcrypt20@1.9.4-3ubuntu3 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 - libgcrypt20@1.9.4-3ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + git@1:2.34.1-1ubuntu1.10 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 - gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 - libgcrypt20@1.9.4-3ubuntu3 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + adduser@3.118ubuntu5 - libgcrypt20@1.9.4-3ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + shadow/passwd@1:4.8.1-2ubuntu2.1 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + pam/libpam-modules@1.4.0-11ubuntu2.3 - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + libnsl/libnsl2@1.3.0-2build2 - libgcrypt20@1.9.4-3ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 - libgcrypt20@1.9.4-3ubuntu3 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 - libgcrypt20@1.9.4-3ubuntu3 + openssl@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - apt@2.4.12 + docker-image|quay.io/argoproj/argocd@v2.6.15 - apt/libapt-pkg6.0@2.4.12 - - systemd/libsystemd0@249.11-0ubuntu3.12 + ca-certificates@20230311ubuntu0.22.04.1 - libgcrypt20@1.9.4-3ubuntu3 + openssl@3.0.2-0ubuntu1.10 @@ -1125,28 +1664,62 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream libgcrypt20 package and not the libgcrypt20 package as distributed by Ubuntu. +

      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.

      -

      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.

      +

      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

      -

      There is no fixed version for Ubuntu:22.04 libgcrypt20.

      +

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.12 or higher.

      References


    -

    CVE-2024-26462

    +

    Out-of-bounds Read

    @@ -1156,21 +1729,18 @@

    CVE-2024-26462


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - krb5/libk5crypto3 + libx11/libx11-data
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + docker-image|quay.io/argoproj/argocd@v2.6.15 and libx11/libx11-data@2:1.7.5-1ubuntu0.2
    @@ -1183,159 +1753,62 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - krb5/libk5crypto3@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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.10.16 - - 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.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libkrb5-3@1.19.2-2ubuntu0.3 + libx11/libx11-data@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - adduser@3.118ubuntu5 + docker-image|quay.io/argoproj/argocd@v2.6.15 - 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.10.16 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + libx11/libx11-data@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - git@1:2.34.1-1ubuntu1.11 + libxext/libxext6@2:1.3.4-1build1 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 + docker-image|quay.io/argoproj/argocd@v2.6.15 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 + libxmu/libxmuu1@2:1.1.3-3 - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + xauth@1:1.1-1build2 - krb5/libkrb5support0@1.19.2-2ubuntu0.3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 @@ -1347,27 +1820,27 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. +

      Note: Versions mentioned in the description apply only to the upstream libx11 package and not the libx11 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.

      +

      A vulnerability was found in libX11 due to a boundary condition within the _XkbReadKeySyms() function. This flaw allows a local user to trigger an out-of-bounds read error and read the contents of memory on the system.

      Remediation

      -

      There is no fixed version for Ubuntu:22.04 krb5.

      +

      Upgrade Ubuntu:22.04 libx11 to version 2:1.7.5-1ubuntu0.3 or higher.

      References


    -

    CVE-2024-37371

    +

    Loop with Unreachable Exit Condition ('Infinite Loop')

    @@ -1377,21 +1850,18 @@

    CVE-2024-37371


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - krb5/libk5crypto3 + libx11/libx11-data
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + docker-image|quay.io/argoproj/argocd@v2.6.15 and libx11/libx11-data@2:1.7.5-1ubuntu0.2
    @@ -1404,159 +1874,183 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libk5crypto3@1.19.2-2ubuntu0.3 + libx11/libx11-data@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.2 - - pam/libpam-modules@1.4.0-11ubuntu2.4 + docker-image|quay.io/argoproj/argocd@v2.6.15 - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 - krb5/libk5crypto3@1.19.2-2ubuntu0.3 + libx11/libx11-data@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.2 - - pam/libpam-modules@1.4.0-11ubuntu2.4 + docker-image|quay.io/argoproj/argocd@v2.6.15 - 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 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libkrb5-3@1.19.2-2ubuntu0.3 + libxext/libxext6@2:1.3.4-1build1 + + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.2 + docker-image|quay.io/argoproj/argocd@v2.6.15 - pam/libpam-modules@1.4.0-11ubuntu2.4 - - libnsl/libnsl2@1.3.0-2build2 + libxmu/libxmuu1@2:1.1.3-3 - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + xauth@1:1.1-1build2 - krb5/libkrb5-3@1.19.2-2ubuntu0.3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    A vulnerability was found in libX11 due to an infinite loop within the PutSubImage() function. This flaw allows a local user to consume all available system resources and cause a denial of service condition.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 libx11 to version 2:1.7.5-1ubuntu0.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Integer Overflow or Wraparound

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + libx11/libx11-data +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.6.15 and libx11/libx11-data@2:1.7.5-1ubuntu0.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + libx11/libx11-data@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - openssh/openssh-client@1:8.9p1-3ubuntu0.10 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + libx11/libx11-data@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 + docker-image|quay.io/argoproj/argocd@v2.6.15 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 + docker-image|quay.io/argoproj/argocd@v2.6.15 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 + libxext/libxext6@2:1.3.4-1build1 - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.2 + docker-image|quay.io/argoproj/argocd@v2.6.15 - pam/libpam-modules@1.4.0-11ubuntu2.4 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + libxmu/libxmuu1@2:1.1.3-3 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libkrb5support0@1.19.2-2ubuntu0.3 + xauth@1:1.1-1build2 + + libx11/libx11-6@2:1.7.5-1ubuntu0.2 @@ -1568,27 +2062,27 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. +

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

      -

      In MIT Kerberos 5 (aka krb5) before 1.21.3, an attacker can cause invalid memory reads during GSS message token handling by sending message tokens with invalid length fields.

      +

      A vulnerability was found in libX11 due to an integer overflow within the XCreateImage() function. This flaw allows a local user to trigger an integer overflow and execute arbitrary code with elevated privileges.

      Remediation

      -

      Upgrade Ubuntu:22.04 krb5 to version 1.19.2-2ubuntu0.4 or higher.

      +

      Upgrade Ubuntu:22.04 libx11 to version 2:1.7.5-1ubuntu0.3 or higher.

      References


    -

    CVE-2024-37370

    +

    Access of Uninitialized Pointer

    @@ -1598,9 +2092,6 @@

    CVE-2024-37370


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • @@ -1612,7 +2103,7 @@

      CVE-2024-37370

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + docker-image|quay.io/argoproj/argocd@v2.6.15 and krb5/libk5crypto3@1.19.2-2ubuntu0.2
    @@ -1625,230 +2116,159 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libk5crypto3@1.19.2-2ubuntu0.3 + krb5/libk5crypto3@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-2ubuntu2.2 + shadow/passwd@1:4.8.1-2ubuntu2.1 - pam/libpam-modules@1.4.0-11ubuntu2.4 + pam/libpam-modules@1.4.0-11ubuntu2.3 libnsl/libnsl2@1.3.0-2build2 libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - krb5/libk5crypto3@1.19.2-2ubuntu0.3 + krb5/libk5crypto3@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-2ubuntu2.2 + shadow/passwd@1:4.8.1-2ubuntu2.1 - pam/libpam-modules@1.4.0-11ubuntu2.4 + pam/libpam-modules@1.4.0-11ubuntu2.3 libnsl/libnsl2@1.3.0-2build2 libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - krb5/libkrb5-3@1.19.2-2ubuntu0.3 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 - krb5/libk5crypto3@1.19.2-2ubuntu0.3 + krb5/libk5crypto3@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libkrb5-3@1.19.2-2ubuntu0.3 + krb5/libkrb5-3@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-2ubuntu2.2 + shadow/passwd@1:4.8.1-2ubuntu2.1 - pam/libpam-modules@1.4.0-11ubuntu2.4 + pam/libpam-modules@1.4.0-11ubuntu2.3 libnsl/libnsl2@1.3.0-2build2 libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - krb5/libkrb5-3@1.19.2-2ubuntu0.3 + krb5/libkrb5-3@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - openssh/openssh-client@1:8.9p1-3ubuntu0.10 + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - git@1:2.34.1-1ubuntu1.11 + git@1:2.34.1-1ubuntu1.10 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - git@1:2.34.1-1ubuntu1.11 + git@1:2.34.1-1ubuntu1.10 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 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.10.16 - - 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.

    -

    In MIT Kerberos 5 (aka krb5) before 1.21.3, an attacker can modify the plaintext Extra Count field of a confidential GSS krb5 wrap token, causing the unwrapped token to appear truncated to the application.

    -

    Remediation

    -

    Upgrade Ubuntu:22.04 krb5 to version 1.19.2-2ubuntu0.4 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Denial of Service (DoS)

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argo-cd/v2 /usr/local/bin/argocd -
    • -
    • - Package Manager: golang -
    • -
    • - Vulnerable module: - - github.com/rs/cors -
    • - -
    • Introduced through: - - github.com/argoproj/argo-cd/v2@* and github.com/rs/cors@v1.9.0 - -
    • -
    - -
    - - -

    Detailed paths

    + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + -
      +
    • Introduced through: - github.com/argoproj/argo-cd/v2@* + docker-image|quay.io/argoproj/argocd@v2.6.15 - github.com/rs/cors@v1.9.0 + krb5/libkrb5support0@1.19.2-2ubuntu0.2 @@ -1859,67 +2279,32 @@

      Detailed paths


      -

      Overview

      -

      Affected versions of this package are vulnerable to Denial of Service (DoS) through the processing of malicious preflight requests that include a Access-Control-Request-Headers header with excessive commas. An attacker can induce excessive memory consumption and potentially crash the server by sending specially crafted requests.

      -

      PoC

      -
      
      -        func BenchmarkPreflightAdversarialACRH(b *testing.B) {
      -            resps := makeFakeResponses(b.N)
      -            req, _ := http.NewRequest(http.MethodOptions, dummyEndpoint, nil)
      -            req.Header.Add(headerOrigin, dummyOrigin)
      -            req.Header.Add(headerACRM, http.MethodGet)
      -            req.Header[headerACRH] = adversarialACRH
      -            handler := Default().Handler(testHandler)
      -        
      -            b.ReportAllocs()
      -            b.ResetTimer()
      -            for i := 0; i < b.N; i++ {
      -                handler.ServeHTTP(resps[i], req)
      -            }
      -        }
      -        
      -        var adversarialACRH []string
      -        
      -        func init() { // populates adversarialACRH
      -            n := int(math.Floor(math.Sqrt(http.DefaultMaxHeaderBytes)))
      -            commas := strings.Repeat(",", n)
      -            res := make([]string, n)
      -            for i := range res {
      -                res[i] = commas
      -            }
      -            adversarialACRH = res
      -        }
      -        
      -

      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

        -
      • -
      +

      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.

      +

      lib/kadm5/kadm_rpc_xdr.c in MIT Kerberos 5 (aka krb5) before 1.20.2 and 1.21.x before 1.21.1 frees an uninitialized pointer. A remote authenticated user can trigger a kadmind crash. This occurs because _xdr_kadm5_principal_ent_rec does not validate the relationship between n_key_data and the key_data array count.

      Remediation

      -

      Upgrade github.com/rs/cors to version 1.11.0 or higher.

      +

      There is no fixed version for Ubuntu:22.04 krb5.

      References


    -

    Insertion of Sensitive Information into Log File

    +

    Improper Input Validation

    @@ -1929,21 +2314,18 @@

    Insertion of Sensitive Information into Log File


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argo-cd/v2 /usr/local/bin/argocd -
    • Package Manager: golang
    • Vulnerable module: - github.com/hashicorp/go-retryablehttp + golang.org/x/text/language
    • Introduced through: - github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-retryablehttp@v0.7.4 + sigs.k8s.io/kustomize/kustomize/v4@* and golang.org/x/text/language@v0.3.7
    @@ -1956,9 +2338,18 @@

    Detailed paths

    • Introduced through: - github.com/argoproj/argo-cd/v2@* + sigs.k8s.io/kustomize/kustomize/v4@* + + golang.org/x/text/language@v0.3.7 + + + +
    • +
    • + Introduced through: + helm.sh/helm/v3@* - github.com/hashicorp/go-retryablehttp@v0.7.4 + golang.org/x/text/language@v0.3.7 @@ -1970,24 +2361,27 @@

      Detailed paths


      Overview

      -

      Affected versions of this package are vulnerable to Insertion of Sensitive Information into Log File due to not sanitizing urls when writing them to the log file. This could lead to an attacker writing sensitive HTTP basic auth credentials to the log file.

      +

      Affected versions of this package are vulnerable to Improper Input Validation due to the parser being, by design, exposed to untrusted user input, which can be leveraged to force a program to consume significant time parsing Accept-Language headers.

      Remediation

      -

      Upgrade github.com/hashicorp/go-retryablehttp to version 0.7.7 or higher.

      +

      Upgrade golang.org/x/text/language to version 0.3.8 or higher.

      References


    -

    CVE-2023-4039

    +

    Incorrect Privilege Assignment

    @@ -1998,20 +2392,17 @@

    CVE-2023-4039

    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • Vulnerable module: - gcc-12/libstdc++6 + golang.org/x/sys/unix
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + helm.sh/helm/v3@* and golang.org/x/sys/unix@v0.0.0-20220722155257-8c9f86f7a55f
    @@ -2024,51 +2415,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - apt@2.4.12 - - gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - apt@2.4.12 - - apt/libapt-pkg6.0@2.4.12 - - gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - gcc-12/gcc-12-base@12.3.0-1ubuntu1~22.04 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + helm.sh/helm/v3@* - gcc-12/libgcc-s1@12.3.0-1ubuntu1~22.04 + golang.org/x/sys/unix@v0.0.0-20220722155257-8c9f86f7a55f @@ -2079,40 +2428,25 @@

      Detailed paths


      -

      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.

      -

      DISPUTEDA failure in the -fstack-protector feature in GCC-based toolchains - that target AArch64 allows an attacker to exploit an existing buffer - overflow in dynamically-sized local variables in your application - without this being detected. This stack-protector failure only applies - to C99-style dynamically-sized local variables or those created using - alloca(). The stack-protector operates as intended for statically-sized - local variables.

      -

      The default behavior when the stack-protector - detects an overflow is to terminate your application, resulting in - controlled loss of availability. An attacker who can exploit a buffer - overflow without triggering the stack-protector might be able to change - program flow control to cause an uncontrolled loss of availability or to - go further and affect confidentiality or integrity. NOTE: The GCC project argues that this is a missed hardening bug and not a vulnerability by itself.

      +

      Overview

      +

      Affected versions of this package are vulnerable to Incorrect Privilege Assignment such that when called with a non-zero flags parameter, the Faccessat function can incorrectly report that a file is accessible.

      Remediation

      -

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

      +

      Upgrade golang.org/x/sys/unix to version 0.1.0 or higher.

      References


    -

    Integer Overflow or Wraparound

    +

    Denial of Service (DoS)

    @@ -2123,21 +2457,18 @@

    Integer Overflow or Wraparound

    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + 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.0.0-20220722155237-a158d28d115b - docker-image|quay.io/argoproj/argocd@v2.10.16, git@1:2.34.1-1ubuntu1.11 and others
    @@ -2149,11 +2480,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 + helm.sh/helm/v3@* - expat/libexpat1@2.4.7-1ubuntu0.3 + golang.org/x/net/http2@v0.0.0-20220722155237-a158d28d115b @@ -2164,28 +2493,40 @@

      Detailed paths


      -

      NVD Description

      -

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

      -

      An issue was discovered in libexpat before 2.6.3. dtdCopy in xmlparse.c can have an integer overflow for nDefaultAtts on 32-bit platforms (where UINT_MAX equals SIZE_MAX).

      +

      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 Ubuntu:22.04 expat to version 2.4.7-1ubuntu0.4 or higher.

      +

      Upgrade golang.org/x/net/http2 to version 0.4.0 or higher.

      References


    -

    XML External Entity (XXE) Injection

    +

    Improper Verification of Cryptographic Signature

    @@ -2196,21 +2537,18 @@

    XML External Entity (XXE) Injection

    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • Vulnerable module: - expat/libexpat1 + golang.org/x/crypto/openpgp/clearsign
    • Introduced through: + helm.sh/helm/v3@* and golang.org/x/crypto/openpgp/clearsign@v0.0.0-20220525230936-793ad666bf5e - docker-image|quay.io/argoproj/argocd@v2.10.16, git@1:2.34.1-1ubuntu1.11 and others
    @@ -2222,11 +2560,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 + helm.sh/helm/v3@* - expat/libexpat1@2.4.7-1ubuntu0.3 + golang.org/x/crypto/openpgp/clearsign@v0.0.0-20220525230936-793ad666bf5e @@ -2237,28 +2573,26 @@

      Detailed paths


      -

      NVD Description

      -

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

      -

      An issue was discovered in libexpat before 2.6.3. xmlparse.c does not reject a negative length for XML_ParseBuffer.

      +

      Overview

      +

      Affected versions of this package are vulnerable to Improper Verification of Cryptographic Signature via the crypto/openpgp/clearsign/clearsign.go component. An attacker can spoof the 'Hash' Armor Header, leading a victim to believe the signature was generated using a different message digest algorithm than what was actually used. Moreover, the attacker can prepend arbitrary text to cleartext messages without invalidating the signatures.

      Remediation

      -

      Upgrade Ubuntu:22.04 expat to version 2.4.7-1ubuntu0.4 or higher.

      +

      Upgrade golang.org/x/crypto/openpgp/clearsign to version 0.1.0 or higher.

      References


    -

    Integer Overflow or Wraparound

    +

    Memory Leak

    @@ -2268,22 +2602,19 @@

    Integer Overflow or Wraparound


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - expat/libexpat1 + glibc/libc-bin
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.6.15 and glibc/libc-bin@2.35-0ubuntu3.1 - docker-image|quay.io/argoproj/argocd@v2.10.16, git@1:2.34.1-1ubuntu1.11 and others
    @@ -2295,11 +2626,18 @@

    Detailed paths

    -

    Out-of-bounds Read

    +

    MPL-2.0 license

    @@ -2342,21 +2686,18 @@

    Out-of-bounds Read

    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • - Vulnerable module: + Module: - curl/libcurl3-gnutls + github.com/r3labs/diff
    • Introduced through: + github.com/argoproj/argo-cd/v2@* and github.com/r3labs/diff@v1.1.0 - docker-image|quay.io/argoproj/argocd@v2.10.16, git@1:2.34.1-1ubuntu1.11 and others
    @@ -2368,11 +2709,9 @@

    Detailed paths

    -

    CVE-2024-8096

    +

    MPL-2.0 license

    @@ -2424,21 +2743,18 @@

    CVE-2024-8096

    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • - Vulnerable module: + Module: - curl/libcurl3-gnutls + github.com/hashicorp/go-version
    • Introduced through: + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-version@v1.2.1 - docker-image|quay.io/argoproj/argocd@v2.10.16, git@1:2.34.1-1ubuntu1.11 and others
    @@ -2450,11 +2766,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 + github.com/argoproj/argo-cd/v2@* - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 + github.com/hashicorp/go-version@v1.2.1 @@ -2465,53 +2779,38 @@

      Detailed paths


      -

      NVD Description

      -

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

      -

      When curl is told to use the Certificate Status Request TLS extension, often referred to as OCSP stapling, to verify that the server certificate is valid, it might fail to detect some OCSP problems and instead wrongly consider the response as fine. If the returned status reports another error than 'revoked' (like for example 'unauthorized') it is not treated as a bad certficate.

      -

      Remediation

      -

      Upgrade Ubuntu:22.04 curl to version 7.81.0-1ubuntu1.18 or higher.

      -

      References

      - +

      MPL-2.0 license


    -
    -

    CVE-2023-7008

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • - Vulnerable module: + Module: - systemd/libsystemd0 + github.com/hashicorp/go-retryablehttp
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and systemd/libsystemd0@249.11-0ubuntu3.12 + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-retryablehttp@v0.7.0
    @@ -2523,111 +2822,10 @@

    Detailed paths

    -
    -

    Arbitrary Code Injection

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • - Vulnerable module: + Module: - shadow/passwd + github.com/hashicorp/go-cleanhttp
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and shadow/passwd@1:4.8.1-2ubuntu2.2 + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-cleanhttp@v0.5.2
    @@ -2702,40 +2880,9 @@

    Detailed paths

    -
    -

    Uncontrolled Recursion

    +
    +

    MPL-2.0 license

    -
    - low severity +
    + medium severity

    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • - Vulnerable module: + Module: - pcre3/libpcre3 + github.com/gosimple/slug
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + github.com/argoproj/argo-cd/v2@* and github.com/gosimple/slug@v1.13.1
    @@ -2806,20 +2937,9 @@

    Detailed paths

    -
    -

    Release of Invalid Pointer or Reference

    +
    +

    Denial of Service (DoS)

    -
    - low severity +
    + medium severity

    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • Vulnerable module: - patch + github.com/docker/distribution/registry/api/v2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and patch@2.7.6-7build2 + helm.sh/helm/v3@* and github.com/docker/distribution/registry/api/v2@v2.8.1+incompatible
    @@ -2894,9 +2994,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + helm.sh/helm/v3@* - patch@2.7.6-7build2 + github.com/docker/distribution/registry/api/v2@v2.8.1+incompatible @@ -2907,27 +3007,26 @@

      Detailed paths


      -

      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.

      +

      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

      -

      There is no fixed version for Ubuntu:22.04 patch.

      +

      Upgrade github.com/docker/distribution/registry/api/v2 to version 2.8.2-beta.1 or higher.

      References


    -

    Double Free

    +

    CVE-2022-46908

    @@ -2937,22 +3036,19 @@

    Double Free


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - patch + sqlite3/libsqlite3-0
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and patch@2.7.6-7build2 + docker-image|quay.io/argoproj/argocd@v2.6.15, gnupg2/gpg@2.2.27-3ubuntu2.1 and others
    @@ -2964,9 +3060,11 @@

    Detailed paths

    -

    CVE-2024-2511

    +

    Arbitrary Code Injection

    @@ -3012,21 +3108,18 @@

    CVE-2024-2511


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - openssl/libssl3 + shadow/passwd
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and openssl/libssl3@3.0.2-0ubuntu1.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 and shadow/passwd@1:4.8.1-2ubuntu2.1
    @@ -3039,113 +3132,40 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - libfido2/libfido2-1@1.10.0-1 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - ca-certificates@20230311ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.16 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - - openssl/libssl3@3.0.2-0ubuntu1.16 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 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 - - openssl/libssl3@3.0.2-0ubuntu1.16 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 + + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - openssl@3.0.2-0ubuntu1.16 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ca-certificates@20230311ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.16 + shadow/login@1:4.8.1-2ubuntu2.1 @@ -3157,46 +3177,29 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Ubuntu. +

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

      -

      Issue summary: Some non-default TLS server configurations can cause unbounded - memory growth when processing TLSv1.3 sessions

      -

      Impact summary: An attacker may exploit certain server configurations to trigger - unbounded memory growth that would lead to a Denial of Service

      -

      This problem can occur in TLSv1.3 if the non-default SSL_OP_NO_TICKET option is - being used (but not if early_data support is also configured and the default - anti-replay protection is in use). In this case, under certain conditions, the - session cache can get into an incorrect state and it will fail to flush properly - as it fills. The session cache will continue to grow in an unbounded manner. A - malicious client could deliberately create the scenario for this failure to - force a Denial of Service. It may also happen by accident in normal operation.

      -

      This issue only affects TLS servers supporting TLSv1.3. It does not affect TLS - clients.

      -

      The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue. OpenSSL - 1.0.2 is also not affected by this issue.

      +

      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

      -

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.17 or higher.

      +

      There is no fixed version for Ubuntu:22.04 shadow.

      References


    -

    CVE-2024-4603

    +

    Out-of-bounds Write

    @@ -3206,21 +3209,18 @@

    CVE-2024-4603


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - openssl/libssl3 + procps/libprocps8
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and openssl/libssl3@3.0.2-0ubuntu1.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 and procps/libprocps8@2:3.3.17-6ubuntu2
    @@ -3230,116 +3230,32 @@

    CVE-2024-4603

    Detailed paths

    -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - libfido2/libfido2-1@1.10.0-1 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - ca-certificates@20230311ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.16 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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.6.15 - openssl/libssl3@3.0.2-0ubuntu1.16 + procps/libprocps8@2:3.3.17-6ubuntu2
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 + + procps@2:3.3.17-6ubuntu2 - openssl@3.0.2-0ubuntu1.16 + procps/libprocps8@2:3.3.17-6ubuntu2
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ca-certificates@20230311ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.16 + procps@2:3.3.17-6ubuntu2 @@ -3351,54 +3267,27 @@

        Detailed paths


        NVD Description

        -

        Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Ubuntu. +

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

        -

        Issue summary: Checking excessively long DSA keys or parameters may be very - slow.

        -

        Impact summary: Applications that use the functions EVP_PKEY_param_check() - or EVP_PKEY_public_check() to check a DSA public key or DSA 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 functions EVP_PKEY_param_check() or EVP_PKEY_public_check() perform - various checks on DSA parameters. Some of those computations take a long time - if the modulus (p parameter) is too large.

        -

        Trying to use a very large modulus is slow and OpenSSL will not allow using - public keys with a modulus which is over 10,000 bits in length for signature - verification. However the key and parameter check functions do not limit - the modulus size when performing the checks.

        -

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

        -

        These functions are not called by OpenSSL itself on untrusted DSA keys so - only applications that directly call these functions may be vulnerable.

        -

        Also vulnerable are the OpenSSL pkey 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 affected by this issue.

        +

        Under some circumstances, this weakness allows a user who has access to run the “ps” utility on a machine, the ability to write almost unlimited amounts of unfiltered data into the process heap.

        Remediation

        -

        Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.17 or higher.

        +

        There is no fixed version for Ubuntu:22.04 procps.

        References


    -

    CVE-2024-4741

    +

    Uncontrolled Recursion

    @@ -3408,21 +3297,18 @@

    CVE-2024-4741


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - openssl/libssl3 + pcre3/libpcre3
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and openssl/libssl3@3.0.2-0ubuntu1.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1
    @@ -3435,113 +3321,20 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - libfido2/libfido2-1@1.10.0-1 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - ca-certificates@20230311ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.16 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - openssl@3.0.2-0ubuntu1.16 + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ca-certificates@20230311ubuntu0.22.04.1 + grep@3.7-1build1 - openssl@3.0.2-0ubuntu1.16 + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 @@ -3553,23 +3346,32 @@

      Detailed paths


      NVD Description

      -

      This vulnerability has not been analyzed by NVD yet.

      +

      Note: Versions mentioned in the description apply only to the upstream pcre3 package and not the pcre3 package as distributed by Ubuntu:22.04. + 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

      -

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.17 or higher.

      +

      There is no fixed version for Ubuntu:22.04 pcre3.

      References


    -

    CVE-2024-5535

    +

    Release of Invalid Pointer or Reference

    @@ -3579,21 +3381,18 @@

    CVE-2024-5535


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - openssl/libssl3 + patch
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and openssl/libssl3@3.0.2-0ubuntu1.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 and patch@2.7.6-7build2
    @@ -3606,113 +3405,76 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - openssl/libssl3@3.0.2-0ubuntu1.16 + patch@2.7.6-7build2
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - libfido2/libfido2-1@1.10.0-1 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - +
  • - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream patch package and not the patch package as distributed by Ubuntu:22.04. + 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

    + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - ca-certificates@20230311ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.16 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - +
    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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 - - openssl/libssl3@3.0.2-0ubuntu1.16 - - +
  • +
    +

    Double Free

    +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssl@3.0.2-0ubuntu1.16 - - +
    + low severity +
    -
  • +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + patch +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.6.15 and patch@2.7.6-7build2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - ca-certificates@20230311ubuntu0.22.04.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 - openssl@3.0.2-0ubuntu1.16 + patch@2.7.6-7build2 @@ -3724,89 +3486,31 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Ubuntu. +

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

      -

      Issue summary: Calling the OpenSSL API function SSL_select_next_proto with an - empty supported client protocols buffer may cause a crash or memory contents to - be sent to the peer.

      -

      Impact summary: A buffer overread can have a range of potential consequences - such as unexpected application beahviour or a crash. In particular this issue - could result in up to 255 bytes of arbitrary private data from memory being sent - to the peer leading to a loss of confidentiality. However, only applications - that directly call the SSL_select_next_proto function with a 0 length list of - supported client protocols are affected by this issue. This would normally never - be a valid scenario and is typically not under attacker control but may occur by - accident in the case of a configuration or programming error in the calling - application.

      -

      The OpenSSL API function SSL_select_next_proto is typically used by TLS - applications that support ALPN (Application Layer Protocol Negotiation) or NPN - (Next Protocol Negotiation). NPN is older, was never standardised and - is deprecated in favour of ALPN. We believe that ALPN is significantly more - widely deployed than NPN. The SSL_select_next_proto function accepts a list of - protocols from the server and a list of protocols from the client and returns - the first protocol that appears in the server list that also appears in the - client list. In the case of no overlap between the two lists it returns the - first item in the client list. In either case it will signal whether an overlap - between the two lists was found. In the case where SSL_select_next_proto is - called with a zero length client list it fails to notice this condition and - returns the memory immediately following the client list pointer (and reports - that there was no overlap in the lists).

      -

      This function is typically called from a server side application callback for - ALPN or a client side application callback for NPN. In the case of ALPN the list - of protocols supplied by the client is guaranteed by libssl to never be zero in - length. The list of server protocols comes from the application and should never - normally be expected to be of zero length. In this case if the - SSL_select_next_proto function has been called as expected (with the list - supplied by the client passed in the client/client_len parameters), then the - application will not be vulnerable to this issue. If the application has - accidentally been configured with a zero length server list, and has - accidentally passed that zero length server list in the client/client_len - parameters, and has additionally failed to correctly handle a "no overlap" - response (which would normally result in a handshake failure in ALPN) then it - will be vulnerable to this problem.

      -

      In the case of NPN, the protocol permits the client to opportunistically select - a protocol when there is no overlap. OpenSSL returns the first client protocol - in the no overlap case in support of this. The list of client protocols comes - from the application and should never normally be expected to be of zero length. - However if the SSL_select_next_proto function is accidentally called with a - client_len of 0 then an invalid memory pointer will be returned instead. If the - application uses this output as the opportunistic protocol then the loss of - confidentiality will occur.

      -

      This issue has been assessed as Low severity because applications are most - likely to be vulnerable if they are using NPN instead of ALPN - but NPN is not - widely used. It also requires an application configuration or programming error. - Finally, this issue would not typically be under attacker control making active - exploitation unlikely.

      -

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

      -

      Due to the low severity of this issue we are not issuing new releases of - OpenSSL at this time. The fix will be included in the next releases when they - become available.

      +

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

      Remediation

      -

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.17 or higher.

      +

      There is no fixed version for Ubuntu:22.04 patch.

      References


    -

    CVE-2023-50495

    +

    Improper Authentication

    @@ -3816,21 +3520,18 @@

    CVE-2023-50495


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - ncurses/libtinfo6 + openssl/libssl3
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and ncurses/libtinfo6@6.3-2ubuntu0.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 and openssl/libssl3@3.0.2-0ubuntu1.10
    @@ -3843,200 +3544,113 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - ncurses/libtinfo6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - bash@5.1-6ubuntu1.1 - - ncurses/libtinfo6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - ncurses/libncursesw6@6.3-2ubuntu0.1 - - ncurses/libtinfo6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - less@590-1ubuntu0.22.04.3 - - ncurses/libtinfo6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - libedit/libedit2@3.1-20210910-1build1 - - ncurses/libtinfo6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/libncurses6@6.3-2ubuntu0.1 - - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/ncurses-bin@6.3-2ubuntu0.1 + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - procps@2:3.3.17-6ubuntu2.1 + libfido2/libfido2-1@1.10.0-1 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - util-linux@2.37.2-4ubuntu3.4 + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - gnupg2/gpg@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 - gnupg2/gpgconf@2.2.27-3ubuntu2.1 + ca-certificates@20230311ubuntu0.22.04.1 - readline/libreadline8@8.1.2-1 + openssl@3.0.2-0ubuntu1.10 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + git@1:2.34.1-1ubuntu1.10 - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 - pinentry/pinentry-curses@1.1.1-1build2 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/libncursesw6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + adduser@3.118ubuntu5 - procps@2:3.3.17-6ubuntu2.1 + shadow/passwd@1:4.8.1-2ubuntu2.1 - ncurses/libncursesw6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + pam/libpam-modules@1.4.0-11ubuntu2.3 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + libnsl/libnsl2@1.3.0-2build2 - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - pinentry/pinentry-curses@1.1.1-1build2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - ncurses/libncursesw6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 - ncurses/libncurses6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - procps@2:3.3.17-6ubuntu2.1 - - ncurses/libncurses6@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/ncurses-base@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + ca-certificates@20230311ubuntu0.22.04.1 - ncurses/ncurses-bin@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10 @@ -4048,29 +3662,47 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. +

      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.

      -

      NCurse v6.4-20230418 was discovered to contain a segmentation fault via the component _nc_wrap_entry().

      +

      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

      -

      There is no fixed version for Ubuntu:22.04 ncurses.

      +

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.12 or higher.

      References


    -

    CVE-2023-45918

    +

    Inefficient Regular Expression Complexity

    @@ -4080,21 +3712,18 @@

    CVE-2023-45918


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - ncurses/libtinfo6 + openssl/libssl3
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and ncurses/libtinfo6@6.3-2ubuntu0.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 and openssl/libssl3@3.0.2-0ubuntu1.10
    @@ -4107,200 +3736,315 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - bash@5.1-6ubuntu1.1 + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/libncursesw6@6.3-2ubuntu0.1 + libfido2/libfido2-1@1.10.0-1 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - less@590-1ubuntu0.22.04.3 + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 + + ca-certificates@20230311ubuntu0.22.04.1 - libedit/libedit2@3.1-20210910-1build1 + openssl@3.0.2-0ubuntu1.10 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/libncurses6@6.3-2ubuntu0.1 + git@1:2.34.1-1ubuntu1.10 - ncurses/libtinfo6@6.3-2ubuntu0.1 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 + + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 - ncurses/ncurses-bin@6.3-2ubuntu0.1 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - ncurses/libtinfo6@6.3-2ubuntu0.1 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 + + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - procps@2:3.3.17-6ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - util-linux@2.37.2-4ubuntu3.4 + ca-certificates@20230311ubuntu0.22.04.1 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10
    • +
    + +
    + +
    + +

    NVD Description

    +

    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: 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 Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.12 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Excessive Iteration

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + openssl/libssl3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.6.15 and openssl/libssl3@3.0.2-0ubuntu1.10 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - 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 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 - pinentry/pinentry-curses@1.1.1-1build2 + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 + + libfido2/libfido2-1@1.10.0-1 - ncurses/libncursesw6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - procps@2:3.3.17-6ubuntu2.1 + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - ncurses/libncursesw6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + ca-certificates@20230311ubuntu0.22.04.1 - pinentry/pinentry-curses@1.1.1-1build2 + openssl@3.0.2-0ubuntu1.10 - ncurses/libncursesw6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/libncurses6@6.3-2ubuntu0.1 + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 + + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - procps@2:3.3.17-6ubuntu2.1 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 - ncurses/libncurses6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/ncurses-base@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - ncurses/ncurses-bin@6.3-2ubuntu0.1 + ca-certificates@20230311ubuntu0.22.04.1 + + openssl@3.0.2-0ubuntu1.10 @@ -4312,27 +4056,56 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. +

      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.

      -

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

      +

      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

      -

      There is no fixed version for Ubuntu:22.04 ncurses.

      +

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.12 or higher.

      References


    -

    Resource Exhaustion

    +

    CVE-2023-28531

    @@ -4342,21 +4115,18 @@

    Resource Exhaustion


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - libzstd/libzstd1 + openssh/openssh-client
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and libzstd/libzstd1@1.4.8+dfsg-3build1 + docker-image|quay.io/argoproj/argocd@v2.6.15 and openssh/openssh-client@1:8.9p1-3ubuntu0.3
    @@ -4369,9 +4139,9 @@

    Detailed paths

    -

    Integer Overflow or Wraparound

    +

    NULL Pointer Dereference

    @@ -4419,22 +4184,19 @@

    Integer Overflow or Wraparound


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - krb5/libk5crypto3 + openldap/libldap-2.5-0
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + docker-image|quay.io/argoproj/argocd@v2.6.15, gnupg2/dirmngr@2.2.27-3ubuntu2.1 and others
    @@ -4446,159 +4208,33 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - krb5/libk5crypto3@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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.10.16 - - 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.10.16 - - krb5/libkrb5-3@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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.10.16 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.2 + docker-image|quay.io/argoproj/argocd@v2.6.15 - pam/libpam-modules@1.4.0-11ubuntu2.4 - - libnsl/libnsl2@1.3.0-2build2 + git@1:2.34.1-1ubuntu1.10 - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libkrb5support0@1.19.2-2ubuntu0.3 + openldap/libldap-common@2.5.16+dfsg-0ubuntu0.22.04.1 @@ -4610,30 +4246,34 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. +

      Note: Versions mentioned in the description apply only to the upstream openldap package and not the openldap package as distributed by Ubuntu:22.04. 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.

      +

      A vulnerability was found in openldap. This security flaw causes a null pointer dereference in ber_memalloc_x() function.

      Remediation

      -

      There is no fixed version for Ubuntu:22.04 krb5.

      +

      There is no fixed version for Ubuntu:22.04 openldap.

      References


    -

    CVE-2024-26461

    +

    Resource Exhaustion

    @@ -4643,21 +4283,18 @@

    CVE-2024-26461


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - krb5/libk5crypto3 + libzstd/libzstd1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + docker-image|quay.io/argoproj/argocd@v2.6.15 and libzstd/libzstd1@1.4.8+dfsg-3build1
    @@ -4670,159 +4307,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - krb5/libk5crypto3@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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.10.16 - - 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.10.16 - - krb5/libkrb5-3@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - 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.10.16 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 - - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.16 - - 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.10.16 - - 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.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - krb5/libkrb5support0@1.19.2-2ubuntu0.3 + libzstd/libzstd1@1.4.8+dfsg-3build1 @@ -4834,27 +4321,30 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. +

      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.

      -

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

      +

      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 krb5.

      +

      There is no fixed version for Ubuntu:22.04 libzstd.

      References


    -

    CVE-2024-26458

    +

    Integer Overflow or Wraparound

    @@ -4864,9 +4354,6 @@

    CVE-2024-26458


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • @@ -4878,7 +4365,7 @@

      CVE-2024-26458

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + docker-image|quay.io/argoproj/argocd@v2.6.15 and krb5/libk5crypto3@1.19.2-2ubuntu0.2
    @@ -4891,159 +4378,159 @@

    Detailed paths

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

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. +

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu:22.04. 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.

      +

      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


    @@ -5085,9 +4574,6 @@

    Out-of-bounds Write


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • @@ -5099,7 +4585,7 @@

      Out-of-bounds Write

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and gnupg2/gpgv@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.6.15 and gnupg2/gpgv@2.2.27-3ubuntu2.1
    @@ -5112,7 +4598,7 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gpgv@2.2.27-3ubuntu2.1 @@ -5121,9 +4607,9 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - apt@2.4.12 + apt@2.4.10 gnupg2/gpgv@2.2.27-3ubuntu2.1 @@ -5132,7 +4618,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5143,7 +4629,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/dirmngr@2.2.27-3ubuntu2.1 @@ -5154,7 +4640,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gpg@2.2.27-3ubuntu2.1 @@ -5165,7 +4651,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5178,7 +4664,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5191,7 +4677,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/dirmngr@2.2.27-3ubuntu2.1 @@ -5200,7 +4686,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5211,7 +4697,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5224,7 +4710,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 @@ -5233,7 +4719,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5244,7 +4730,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 @@ -5253,7 +4739,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5264,7 +4750,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gpg@2.2.27-3ubuntu2.1 @@ -5273,7 +4759,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5284,7 +4770,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5297,7 +4783,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5310,7 +4796,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gpg-agent@2.2.27-3ubuntu2.1 @@ -5319,7 +4805,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5330,7 +4816,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5343,7 +4829,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5356,7 +4842,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 @@ -5365,7 +4851,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5376,7 +4862,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 @@ -5385,7 +4871,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5396,7 +4882,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gpgsm@2.2.27-3ubuntu2.1 @@ -5405,7 +4891,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5416,7 +4902,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -5430,20 +4916,20 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream gnupg2 package and not the gnupg2 package as distributed by Ubuntu. +

      Note: Versions mentioned in the description apply only to the upstream gnupg2 package and not the gnupg2 package as distributed by Ubuntu:22.04. 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


      @@ -5464,9 +4950,6 @@

      Allocation of Resources Without Limits or Throttling

        -
      • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
      • Package Manager: ubuntu:22.04
      • @@ -5478,7 +4961,7 @@

        Allocation of Resources Without Limits or Throttling

        Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and glibc/libc-bin@2.35-0ubuntu3.8 + docker-image|quay.io/argoproj/argocd@v2.6.15 and glibc/libc-bin@2.35-0ubuntu3.1
      @@ -5491,18 +4974,18 @@

      Detailed paths

      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - glibc/libc-bin@2.35-0ubuntu3.8 + glibc/libc-bin@2.35-0ubuntu3.1
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - glibc/libc6@2.35-0ubuntu3.8 + glibc/libc6@2.35-0ubuntu3.1 @@ -5514,17 +4997,17 @@

        Detailed paths


        NVD Description

        -

        Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu. +

        Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu:22.04. 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


        @@ -5545,9 +5028,6 @@

        Improper Input Validation


          -
        • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
        • Package Manager: ubuntu:22.04
        • @@ -5560,7 +5040,7 @@

          Improper Input Validation

        • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16, git@1:2.34.1-1ubuntu1.11 and others + docker-image|quay.io/argoproj/argocd@v2.6.15, git@1:2.34.1-1ubuntu1.10 and others
        @@ -5572,31 +5052,31 @@

        Detailed paths

        • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - git@1:2.34.1-1ubuntu1.11 + git@1:2.34.1-1ubuntu1.10 - git/git-man@1:2.34.1-1ubuntu1.11 + git/git-man@1:2.34.1-1ubuntu1.10
        • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 - git@1:2.34.1-1ubuntu1.11 + git@1:2.34.1-1ubuntu1.10
        • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 + docker-image|quay.io/argoproj/argocd@v2.6.15 git-lfs@3.0.2-1ubuntu0.2 - git@1:2.34.1-1ubuntu1.11 + git@1:2.34.1-1ubuntu1.10 @@ -5608,15 +5088,15 @@

          Detailed paths


          NVD Description

          -

          Note: Versions mentioned in the description apply only to the upstream git package and not the git package as distributed by Ubuntu. +

          Note: Versions mentioned in the description apply only to the upstream git package and not the git package as distributed by Ubuntu:22.04. 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

          @@ -5638,9 +5118,6 @@

          Uncontrolled Recursion


            -
          • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile -
          • Package Manager: ubuntu:22.04
          • @@ -5652,7 +5129,7 @@

            Uncontrolled Recursion

          • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + docker-image|quay.io/argoproj/argocd@v2.6.15 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04
          @@ -5665,7 +5142,7 @@

          Detailed paths

    -

    Improper Input Validation

    +

    CVE-2023-38546

    @@ -5754,8 +5230,89 @@

    Improper Input Validation

    • - Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd Dockerfile + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + curl/libcurl3-gnutls +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.6.15, git@1:2.34.1-1ubuntu1.10 and others
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.6.15 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    This flaw allows an attacker to insert cookies at will into a running program + using libcurl, if the specific series of conditions are met.

    +

    libcurl performs transfers. In its API, an application creates "easy handles" + that are the individual handles for single transfers.

    +

    libcurl provides a function call that duplicates en easy handle called + curl_easy_duphandle.

    +

    If a transfer has cookies enabled when the handle is duplicated, the + cookie-enable state is also cloned - but without cloning the actual + cookies. If the source handle did not read any cookies from a specific file on + disk, the cloned version of the handle would instead store the file name as + none (using the four ASCII letters, no quotes).

    +

    Subsequent use of the cloned handle that does not explicitly set a source to + load cookies from would then inadvertently load cookies from a file named + none - if such a file exists and is readable in the current directory of the + program using libcurl. And if using the correct file format of course.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 curl to version 7.81.0-1ubuntu1.14 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Input Validation

    +
    + +
    + low severity +
    + +
    + +
    • Package Manager: ubuntu:22.04
    • @@ -5767,7 +5324,7 @@

      Improper Input Validation

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.10.16 and coreutils@8.32-4.1ubuntu1.2 + docker-image|quay.io/argoproj/argocd@v2.6.15 and coreutils@8.32-4.1ubuntu1
    @@ -5780,9 +5337,9 @@

    Detailed paths

    +
    +

    Out-of-bounds Write

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + bash +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.6.15 and bash@5.1-6ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.6.15 + + 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:22.04. + 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

    +

    There is no fixed version for Ubuntu:22.04 bash.

    +

    References

    + + +
    + + + +
    diff --git a/docs/snyk/master/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html b/docs/snyk/v2.6.15/redis_7.0.11-alpine.html similarity index 64% rename from docs/snyk/master/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html rename to docs/snyk/v2.6.15/redis_7.0.11-alpine.html index d9db5c2fc73c8..ef98cc541da29 100644 --- a/docs/snyk/master/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html +++ b/docs/snyk/v2.6.15/redis_7.0.11-alpine.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,18 +456,18 @@

    Snyk test report

    -

    September 22nd 2024, 12:19:15 am (UTC+00:00)

    +

    October 29th 2023, 12:28:42 am (UTC+00:00)

    Scanned the following path:
      -
    • public.ecr.aws/docker/library/haproxy:2.6.17-alpine/docker/library/haproxy (apk)
    • +
    • redis:7.0.11-alpine (apk)
    5 known vulnerabilities
    -
    42 vulnerable dependency paths
    +
    41 vulnerable dependency paths
    18 dependencies
    @@ -476,8 +476,8 @@

    Snyk test report

    - - + + @@ -485,19 +485,19 @@

    Snyk test report

    -
    -

    Use After Free

    +
    +

    Out-of-bounds Write

    -
    - medium severity +
    + critical severity

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -507,7 +507,7 @@

      Use After Free

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and busybox/busybox@1.36.1-r28 + docker-image|redis@7.0.11-alpine and busybox/busybox@1.36.1-r0
    @@ -520,62 +520,51 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/busybox@1.36.1-r28 + busybox/busybox@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 + alpine-baselayout/alpine-baselayout@3.4.3-r1 - busybox/busybox-binsh@1.36.1-r28 - - busybox/busybox@1.36.1-r28 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + busybox/busybox-binsh@1.36.1-r0 - busybox/busybox-binsh@1.36.1-r28 + busybox/busybox@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 - - busybox/busybox-binsh@1.36.1-r28 + busybox/busybox-binsh@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + alpine-baselayout/alpine-baselayout@3.4.3-r1 - busybox/busybox-binsh@1.36.1-r28 + busybox/busybox-binsh@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 @@ -588,24 +577,24 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      A use-after-free vulnerability in BusyBox v.1.36.1 allows attackers to cause a denial of service via a crafted awk pattern in the awk.c evaluate function.

      + 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.20 busybox to version 1.36.1-r29 or higher.

      +

      Upgrade Alpine:3.18 busybox to version 1.36.1-r1 or higher.

      References


    -

    Use After Free

    +

    Improper Authentication

    @@ -616,17 +605,17 @@

    Use After Free

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: - busybox/busybox + openssl/libcrypto3
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and busybox/busybox@1.36.1-r28 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -639,62 +628,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine - busybox/busybox@1.36.1-r28 + .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|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 + .redis-rundeps@20230614.215749 - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1 - busybox/busybox@1.36.1-r28 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 + .redis-rundeps@20230614.215749 - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + apk-tools/apk-tools@2.14.0-r2 - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 - busybox/ssl_client@1.36.1-r28 + openssl/libssl3@3.1.1-r1 @@ -706,36 +730,57 @@

      Detailed paths


      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.20 relevant fixed versions and status.

      -

      A use-after-free vulnerability was discovered in BusyBox v.1.36.1 via a crafted awk pattern in the awk.c copyvar function.

      +

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine:3.18. + 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.20 busybox to version 1.36.1-r29 or higher.

      +

      Upgrade Alpine:3.18 openssl to version 3.1.1-r2 or higher.

      References


    -
    -

    CVE-2024-4741

    +
    +

    Inefficient Regular Expression Complexity

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -745,7 +790,7 @@

      CVE-2024-4741

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and openssl/libcrypto3@3.3.0-r2 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -758,108 +803,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - openssl/libcrypto3@3.3.0-r2 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - .haproxy-rundeps@20240524.005458 + docker-image|redis@7.0.11-alpine - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + .redis-rundeps@20230614.215749 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 @@ -871,30 +905,67 @@

      Detailed paths


      NVD Description

      -

      This vulnerability has not been analyzed by NVD yet.

      +

      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.20 openssl to version 3.3.0-r3 or higher.

      +

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

      +

      References

      +
    -
    -

    CVE-2024-5535

    +
    +

    Excessive Iteration

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -904,7 +975,7 @@

      CVE-2024-5535

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and openssl/libcrypto3@3.3.0-r2 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -917,108 +988,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 - - openssl/libcrypto3@3.3.0-r2 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + .redis-rundeps@20230614.215749 - .haproxy-rundeps@20240524.005458 + openssl/libssl3@3.1.1-r1 - openssl/libssl3@3.3.0-r2 - - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 @@ -1031,87 +1091,54 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      Issue summary: Calling the OpenSSL API function SSL_select_next_proto with an - empty supported client protocols buffer may cause a crash or memory contents to - be sent to the peer.

      -

      Impact summary: A buffer overread can have a range of potential consequences - such as unexpected application beahviour or a crash. In particular this issue - could result in up to 255 bytes of arbitrary private data from memory being sent - to the peer leading to a loss of confidentiality. However, only applications - that directly call the SSL_select_next_proto function with a 0 length list of - supported client protocols are affected by this issue. This would normally never - be a valid scenario and is typically not under attacker control but may occur by - accident in the case of a configuration or programming error in the calling - application.

      -

      The OpenSSL API function SSL_select_next_proto is typically used by TLS - applications that support ALPN (Application Layer Protocol Negotiation) or NPN - (Next Protocol Negotiation). NPN is older, was never standardised and - is deprecated in favour of ALPN. We believe that ALPN is significantly more - widely deployed than NPN. The SSL_select_next_proto function accepts a list of - protocols from the server and a list of protocols from the client and returns - the first protocol that appears in the server list that also appears in the - client list. In the case of no overlap between the two lists it returns the - first item in the client list. In either case it will signal whether an overlap - between the two lists was found. In the case where SSL_select_next_proto is - called with a zero length client list it fails to notice this condition and - returns the memory immediately following the client list pointer (and reports - that there was no overlap in the lists).

      -

      This function is typically called from a server side application callback for - ALPN or a client side application callback for NPN. In the case of ALPN the list - of protocols supplied by the client is guaranteed by libssl to never be zero in - length. The list of server protocols comes from the application and should never - normally be expected to be of zero length. In this case if the - SSL_select_next_proto function has been called as expected (with the list - supplied by the client passed in the client/client_len parameters), then the - application will not be vulnerable to this issue. If the application has - accidentally been configured with a zero length server list, and has - accidentally passed that zero length server list in the client/client_len - parameters, and has additionally failed to correctly handle a "no overlap" - response (which would normally result in a handshake failure in ALPN) then it - will be vulnerable to this problem.

      -

      In the case of NPN, the protocol permits the client to opportunistically select - a protocol when there is no overlap. OpenSSL returns the first client protocol - in the no overlap case in support of this. The list of client protocols comes - from the application and should never normally be expected to be of zero length. - However if the SSL_select_next_proto function is accidentally called with a - client_len of 0 then an invalid memory pointer will be returned instead. If the - application uses this output as the opportunistic protocol then the loss of - confidentiality will occur.

      -

      This issue has been assessed as Low severity because applications are most - likely to be vulnerable if they are using NPN instead of ALPN - but NPN is not - widely used. It also requires an application configuration or programming error. - Finally, this issue would not typically be under attacker control making active - exploitation unlikely.

      -

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

      -

      Due to the low severity of this issue we are not issuing new releases of - OpenSSL at this time. The fix will be included in the next releases when they - become available.

      + 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.20 openssl to version 3.3.1-r1 or higher.

      +

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

      References


    -

    CVE-2024-6119

    +

    CVE-2023-5363

    @@ -1122,7 +1149,7 @@

    CVE-2024-6119

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -1132,7 +1159,7 @@

      CVE-2024-6119

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and openssl/libcrypto3@3.3.0-r2 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -1145,108 +1172,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - openssl/libcrypto3@3.3.0-r2 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - .haproxy-rundeps@20240524.005458 + docker-image|redis@7.0.11-alpine - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + .redis-rundeps@20230614.215749 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 @@ -1259,41 +1275,55 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      Issue summary: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

      -

      Impact summary: Abnormal termination of an application can a cause a denial of - service.

      -

      Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

      -

      Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

      -

      TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

      -

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

      + 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.20 openssl to version 3.3.2-r0 or higher.

      +

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

      References


    diff --git a/docs/snyk/v2.12.3/argocd-iac-namespace-install.html b/docs/snyk/v2.7.14/argocd-iac-install.html similarity index 90% rename from docs/snyk/v2.12.3/argocd-iac-namespace-install.html rename to docs/snyk/v2.7.14/argocd-iac-install.html index aab9b5b3686cb..602c76a57c103 100644 --- a/docs/snyk/v2.12.3/argocd-iac-namespace-install.html +++ b/docs/snyk/v2.7.14/argocd-iac-install.html @@ -456,17 +456,17 @@

    Snyk test report

    -

    September 22nd 2024, 12:25:59 am (UTC+00:00)

    +

    October 29th 2023, 12:27:04 am (UTC+00:00)

    Scanned the following path:
      -
    • /argo-cd/manifests/namespace-install.yaml (Kubernetes)
    • +
    • /argo-cd/manifests/install.yaml (Kubernetes)
    -
    43 total issues
    +
    41 total issues
    @@ -475,15 +475,15 @@

    Snyk test report

    Project docker-image|public.ecr.aws/docker/library/haproxy
    Path public.ecr.aws/docker/library/haproxy:2.6.17-alpine/docker/library/haproxy
    Project docker-image|redis
    Path redis:7.0.11-alpine
    Package Manager apk
    - - + +
    Project manifests/namespace-install.yaml
    Path /argo-cd/manifests/namespace-install.yaml
    Project manifests/install.yaml
    Path /argo-cd/manifests/install.yaml
    Project Type Kubernetes
    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -498,7 +498,7 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 7] + [DocId: 10] rules[0] @@ -507,17 +507,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 77 + Line number: 16324

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -529,7 +529,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -544,7 +544,7 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 8] + [DocId: 11] rules[4] @@ -553,17 +553,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 164 + Line number: 16401

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -575,7 +575,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -590,7 +590,7 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 9] + [DocId: 12] rules[0] @@ -599,17 +599,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 192 + Line number: 16429

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -621,7 +621,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -636,53 +636,7 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 10] - - rules[1] - - resources - -
  • - -
  • - Line number: 222 -
  • - - -
    - -

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 10] + [DocId: 13] rules[3] @@ -691,17 +645,17 @@

      Role or ClusterRole with dangerous permissions

    • - Line number: 240 + Line number: 16477

    Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -713,7 +667,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -728,26 +682,26 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 11] + [DocId: 13] - rules[0] + rules[1] resources
  • - Line number: 258 + Line number: 16459

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -759,7 +713,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -774,7 +728,7 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: - [DocId: 12] + [DocId: 14] rules[0] @@ -783,17 +737,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 280 + Line number: 16493

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -820,7 +774,7 @@

    Container could be running with outdated image

  • Introduced through: - [DocId: 39] + [DocId: 46] spec @@ -828,14 +782,14 @@

    Container could be running with outdated image

    spec - initContainers[secret-init] + initContainers[copyutil] imagePullPolicy
  • - Line number: 1114 + Line number: 17530
  • @@ -857,7 +811,7 @@

    Remediation

    -

    Container could be running with outdated image

    +

    Container has no CPU limit

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

    Container could be running with outdated image

    • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 40] + [DocId: 42] + + input spec @@ -880,31 +836,35 @@

      Container could be running with outdated image

      spec - initContainers[copyutil] + containers[argocd-applicationset-controller] - imagePullPolicy + resources + + limits + + cpu
    • - Line number: 1413 + Line number: 16980

    Impact

    -

    The container may run with outdated or unauthorized image

    +

    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

    -

    Set `imagePullPolicy` attribute to `Always`

    +

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


    @@ -924,7 +884,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 36] + [DocId: 43] input @@ -934,7 +894,7 @@

    Container has no CPU limit

    spec - containers[argocd-applicationset-controller] + initContainers[copyutil] resources @@ -945,7 +905,7 @@

    Container has no CPU limit

  • - Line number: 675 + Line number: 17152
  • @@ -982,7 +942,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 37] + [DocId: 43] input @@ -992,7 +952,7 @@

    Container has no CPU limit

    spec - initContainers[copyutil] + containers[dex] resources @@ -1003,7 +963,7 @@

    Container has no CPU limit

  • - Line number: 926 + Line number: 17118
  • @@ -1040,7 +1000,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 37] + [DocId: 44] input @@ -1050,7 +1010,7 @@

    Container has no CPU limit

    spec - containers[dex] + containers[argocd-notifications-controller] resources @@ -1061,7 +1021,7 @@

    Container has no CPU limit

  • - Line number: 892 + Line number: 17212
  • @@ -1098,7 +1058,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 38] + [DocId: 45] input @@ -1108,7 +1068,7 @@

    Container has no CPU limit

    spec - containers[argocd-notifications-controller] + containers[redis] resources @@ -1119,7 +1079,7 @@

    Container has no CPU limit

  • - Line number: 986 + Line number: 17286
  • @@ -1156,7 +1116,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 39] + [DocId: 46] input @@ -1166,7 +1126,7 @@

    Container has no CPU limit

    spec - containers[redis] + initContainers[copyutil] resources @@ -1177,7 +1137,7 @@

    Container has no CPU limit

  • - Line number: 1085 + Line number: 17530
  • @@ -1214,7 +1174,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 39] + [DocId: 46] input @@ -1224,7 +1184,7 @@

    Container has no CPU limit

    spec - initContainers[secret-init] + containers[argocd-repo-server] resources @@ -1235,7 +1195,7 @@

    Container has no CPU limit

  • - Line number: 1109 + Line number: 17342
  • @@ -1272,7 +1232,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 40] + [DocId: 47] input @@ -1282,7 +1242,7 @@

    Container has no CPU limit

    spec - initContainers[copyutil] + containers[argocd-server] resources @@ -1293,7 +1253,7 @@

    Container has no CPU limit

  • - Line number: 1413 + Line number: 17615
  • @@ -1330,7 +1290,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 40] + [DocId: 48] input @@ -1340,7 +1300,7 @@

    Container has no CPU limit

    spec - containers[argocd-repo-server] + containers[argocd-application-controller] resources @@ -1351,7 +1311,7 @@

    Container has no CPU limit

  • - Line number: 1166 + Line number: 17919
  • @@ -1373,7 +1333,7 @@

    Remediation

    -

    Container has no CPU limit

    +

    Container is running with multiple open ports

    @@ -1384,13 +1344,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-36
    • Introduced through: - [DocId: 41] - - input + [DocId: 43] spec @@ -1398,40 +1356,36 @@

      Container has no CPU limit

      spec - containers[argocd-server] - - resources - - limits + containers[dex] - cpu + ports
    • - Line number: 1498 + Line number: 17132

    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.

    +

    Increases the attack surface of the application and the container.

    Remediation

    -

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

    +

    Reduce `ports` count to 2


    -

    Container has no CPU limit

    +

    Container is running with writable root filesystem

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

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-8
    • Introduced through: - [DocId: 42] - - input + [DocId: 45] spec @@ -1456,40 +1408,38 @@

      Container has no CPU limit

      spec - containers[argocd-application-controller] - - resources + containers[redis] - limits + securityContext - cpu + readOnlyRootFilesystem
    • - Line number: 1849 + Line number: 17296

    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.

    +

    Compromised process could abuse writable root filesystem to elevate privileges

    Remediation

    -

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

    +

    Set `spec.{containers, initContainers}.securityContext.readOnlyRootFilesystem` to `true`


    -

    Container is running with multiple open ports

    +

    Container is running without liveness probe

    @@ -1500,11 +1450,11 @@

    Container is running with multiple open ports

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

      Container is running with multiple open ports

      spec - containers[dex] + containers[argocd-applicationset-controller] - ports + livenessProbe
    • - Line number: 906 + Line number: 16980

    Impact

    -

    Increases the attack surface of the application and the container.

    +

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

    Remediation

    -

    Reduce `ports` count to 2

    +

    Add `livenessProbe` attribute


    @@ -1556,7 +1506,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 36] + [DocId: 43] spec @@ -1564,14 +1514,14 @@

    Container is running without liveness probe

    spec - containers[argocd-applicationset-controller] + initContainers[copyutil] livenessProbe
  • - Line number: 675 + Line number: 17152
  • @@ -1608,7 +1558,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 37] + [DocId: 43] spec @@ -1623,7 +1573,7 @@

    Container is running without liveness probe

  • - Line number: 892 + Line number: 17118
  • @@ -1660,7 +1610,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 39] + [DocId: 45] spec @@ -1675,7 +1625,7 @@

    Container is running without liveness probe

  • - Line number: 1085 + Line number: 17286
  • @@ -1697,7 +1647,7 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container is running without liveness probe

    @@ -1708,13 +1658,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 36] - - input + [DocId: 46] spec @@ -1722,35 +1670,31 @@

      Container is running without memory limit

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

    Impact

    -

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

    +

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

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Add `livenessProbe` attribute


    @@ -1770,7 +1714,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 37] + [DocId: 42] input @@ -1780,7 +1724,7 @@

    Container is running without memory limit

    spec - containers[dex] + containers[argocd-applicationset-controller] resources @@ -1791,7 +1735,7 @@

    Container is running without memory limit

  • - Line number: 892 + Line number: 16980
  • @@ -1828,7 +1772,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 37] + [DocId: 43] input @@ -1838,7 +1782,7 @@

    Container is running without memory limit

    spec - initContainers[copyutil] + containers[dex] resources @@ -1849,7 +1793,7 @@

    Container is running without memory limit

  • - Line number: 926 + Line number: 17118
  • @@ -1886,7 +1830,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 38] + [DocId: 43] input @@ -1896,7 +1840,7 @@

    Container is running without memory limit

    spec - containers[argocd-notifications-controller] + initContainers[copyutil] resources @@ -1907,7 +1851,7 @@

    Container is running without memory limit

  • - Line number: 986 + Line number: 17152
  • @@ -1944,7 +1888,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 39] + [DocId: 44] input @@ -1954,7 +1898,7 @@

    Container is running without memory limit

    spec - containers[redis] + containers[argocd-notifications-controller] resources @@ -1965,7 +1909,7 @@

    Container is running without memory limit

  • - Line number: 1085 + Line number: 17212
  • @@ -2002,7 +1946,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 39] + [DocId: 45] input @@ -2012,7 +1956,7 @@

    Container is running without memory limit

    spec - initContainers[secret-init] + containers[redis] resources @@ -2023,7 +1967,7 @@

    Container is running without memory limit

  • - Line number: 1109 + Line number: 17286
  • @@ -2060,7 +2004,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 40] + [DocId: 46] input @@ -2081,7 +2025,7 @@

    Container is running without memory limit

  • - Line number: 1413 + Line number: 17530
  • @@ -2118,7 +2062,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 40] + [DocId: 46] input @@ -2139,7 +2083,7 @@

    Container is running without memory limit

  • - Line number: 1166 + Line number: 17342
  • @@ -2176,7 +2120,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 41] + [DocId: 47] input @@ -2197,7 +2141,7 @@

    Container is running without memory limit

  • - Line number: 1498 + Line number: 17615
  • @@ -2234,7 +2178,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 42] + [DocId: 48] input @@ -2255,7 +2199,7 @@

    Container is running without memory limit

  • - Line number: 1849 + Line number: 17919
  • @@ -2292,7 +2236,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 36] + [DocId: 42] input @@ -2311,7 +2255,7 @@

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

  • - Line number: 816 + Line number: 17055
  • @@ -2348,7 +2292,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 37] + [DocId: 43] input @@ -2367,7 +2311,7 @@

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

  • - Line number: 934 + Line number: 17160
  • @@ -2404,7 +2348,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 37] + [DocId: 43] input @@ -2423,7 +2367,7 @@

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

  • - Line number: 909 + Line number: 17135
  • @@ -2460,7 +2404,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 38] + [DocId: 44] input @@ -2479,7 +2423,7 @@

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

  • - Line number: 1019 + Line number: 17220
  • @@ -2516,7 +2460,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 39] + [DocId: 45] input @@ -2535,63 +2479,7 @@

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

  • - Line number: 1102 -
  • - - -
    - -

    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[secret-init] - - securityContext - - runAsUser - -
    • - -
    • - Line number: 1116 + Line number: 17296
    @@ -2628,7 +2516,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 40] + [DocId: 46] input @@ -2647,7 +2535,7 @@

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

  • - Line number: 1420 + Line number: 17537
  • @@ -2684,7 +2572,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 40] + [DocId: 46] input @@ -2703,7 +2591,7 @@

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

  • - Line number: 1386 + Line number: 17503
  • @@ -2740,7 +2628,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 41] + [DocId: 47] input @@ -2759,7 +2647,7 @@

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

  • - Line number: 1759 + Line number: 17829
  • @@ -2796,7 +2684,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 42] + [DocId: 48] input @@ -2815,7 +2703,7 @@

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

  • - Line number: 2050 + Line number: 18061
  • diff --git a/docs/snyk/v2.13.0-rc2/argocd-iac-namespace-install.html b/docs/snyk/v2.7.14/argocd-iac-namespace-install.html similarity index 91% rename from docs/snyk/v2.13.0-rc2/argocd-iac-namespace-install.html rename to docs/snyk/v2.7.14/argocd-iac-namespace-install.html index ecec28af1a8cd..937ce3343905e 100644 --- a/docs/snyk/v2.13.0-rc2/argocd-iac-namespace-install.html +++ b/docs/snyk/v2.7.14/argocd-iac-namespace-install.html @@ -456,7 +456,7 @@

    Snyk test report

    -

    September 22nd 2024, 12:23:32 am (UTC+00:00)

    +

    October 29th 2023, 12:27:17 am (UTC+00:00)

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

    Snyk test report

    -
    43 total issues
    +
    41 total issues

    @@ -483,7 +483,7 @@

    Snyk test report

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -514,10 +514,10 @@

    Role or ClusterRole with dangerous permissions


    Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -529,7 +529,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -553,17 +553,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 164 + Line number: 154

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -575,7 +575,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -599,17 +599,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 192 + Line number: 182

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -621,7 +621,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -638,24 +638,24 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: [DocId: 10] - rules[1] + rules[3] resources
  • - Line number: 222 + Line number: 230

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -667,7 +667,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -684,24 +684,24 @@

    Role or ClusterRole with dangerous permissions

  • Introduced through: [DocId: 10] - rules[3] + rules[1] resources
  • - Line number: 240 + Line number: 212

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -713,7 +713,7 @@

    Remediation

    -

    Role or ClusterRole with dangerous permissions

    +

    Role with dangerous permissions

    @@ -737,63 +737,17 @@

    Role or ClusterRole with dangerous permissions

  • - Line number: 258 + Line number: 246

  • Impact

    -

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

    +

    Using this role grants dangerous permissions

    Remediation

    -

    Consider removing these permissions

    - - -
    -
    - - - -
    -
    -

    Role or ClusterRole with dangerous permissions

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

    Impact

    -

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

    - -

    Remediation

    -

    Consider removing these permissions

    +

    Consider removing this permissions


    @@ -828,14 +782,14 @@

    Container could be running with outdated image

    spec - initContainers[secret-init] + initContainers[copyutil] imagePullPolicy
  • - Line number: 1138 + Line number: 1190
  • @@ -857,7 +811,7 @@

    Remediation

    -

    Container could be running with outdated image

    +

    Container has no CPU limit

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

    Container could be running with outdated image

    • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 40] + [DocId: 35] + + input spec @@ -880,31 +836,35 @@

      Container could be running with outdated image

      spec - initContainers[copyutil] + containers[argocd-applicationset-controller] - imagePullPolicy + resources + + limits + + cpu
    • - Line number: 1437 + Line number: 640

    Impact

    -

    The container may run with outdated or unauthorized image

    +

    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

    -

    Set `imagePullPolicy` attribute to `Always`

    +

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


    @@ -934,7 +894,7 @@

    Container has no CPU limit

    spec - containers[argocd-applicationset-controller] + initContainers[copyutil] resources @@ -945,7 +905,7 @@

    Container has no CPU limit

  • - Line number: 675 + Line number: 812
  • @@ -982,7 +942,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -992,7 +952,7 @@

    Container has no CPU limit

    spec - initContainers[copyutil] + containers[dex] resources @@ -1003,7 +963,7 @@

    Container has no CPU limit

  • - Line number: 944 + Line number: 778
  • @@ -1050,7 +1010,7 @@

    Container has no CPU limit

    spec - containers[dex] + containers[argocd-notifications-controller] resources @@ -1061,7 +1021,7 @@

    Container has no CPU limit

  • - Line number: 898 + Line number: 872
  • @@ -1108,7 +1068,7 @@

    Container has no CPU limit

    spec - containers[argocd-notifications-controller] + containers[redis] resources @@ -1119,7 +1079,7 @@

    Container has no CPU limit

  • - Line number: 1004 + Line number: 946
  • @@ -1166,7 +1126,7 @@

    Container has no CPU limit

    spec - containers[redis] + initContainers[copyutil] resources @@ -1177,7 +1137,7 @@

    Container has no CPU limit

  • - Line number: 1109 + Line number: 1190
  • @@ -1224,7 +1184,7 @@

    Container has no CPU limit

    spec - initContainers[secret-init] + containers[argocd-repo-server] resources @@ -1235,7 +1195,7 @@

    Container has no CPU limit

  • - Line number: 1133 + Line number: 1002
  • @@ -1282,7 +1242,7 @@

    Container has no CPU limit

    spec - initContainers[copyutil] + containers[argocd-server] resources @@ -1293,7 +1253,7 @@

    Container has no CPU limit

  • - Line number: 1437 + Line number: 1275
  • @@ -1330,7 +1290,7 @@

    Container has no CPU limit

  • Introduced through: - [DocId: 40] + [DocId: 41] input @@ -1340,7 +1300,7 @@

    Container has no CPU limit

    spec - containers[argocd-repo-server] + containers[argocd-application-controller] resources @@ -1351,7 +1311,7 @@

    Container has no CPU limit

  • - Line number: 1190 + Line number: 1579
  • @@ -1373,7 +1333,7 @@

    Remediation

    -

    Container has no CPU limit

    +

    Container is running with multiple open ports

    @@ -1384,13 +1344,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-36
    • Introduced through: - [DocId: 41] - - input + [DocId: 36] spec @@ -1398,40 +1356,36 @@

      Container has no CPU limit

      spec - containers[argocd-server] - - resources - - limits + containers[dex] - cpu + ports
    • - Line number: 1522 + Line number: 792

    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.

    +

    Increases the attack surface of the application and the container.

    Remediation

    -

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

    +

    Reduce `ports` count to 2


    -

    Container has no CPU limit

    +

    Container is running with writable root filesystem

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

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-8
    • Introduced through: - [DocId: 42] - - input + [DocId: 38] spec @@ -1456,40 +1408,38 @@

      Container has no CPU limit

      spec - containers[argocd-application-controller] - - resources + containers[redis] - limits + securityContext - cpu + readOnlyRootFilesystem
    • - Line number: 1912 + Line number: 956

    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.

    +

    Compromised process could abuse writable root filesystem to elevate privileges

    Remediation

    -

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

    +

    Set `spec.{containers, initContainers}.securityContext.readOnlyRootFilesystem` to `true`


    -

    Container is running with multiple open ports

    +

    Container is running without liveness probe

    @@ -1500,11 +1450,11 @@

    Container is running with multiple open ports

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

      Container is running with multiple open ports

      spec - containers[dex] + containers[argocd-applicationset-controller] - ports + livenessProbe
    • - Line number: 924 + Line number: 640

    Impact

    -

    Increases the attack surface of the application and the container.

    +

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

    Remediation

    -

    Reduce `ports` count to 2

    +

    Add `livenessProbe` attribute


    @@ -1564,14 +1514,14 @@

    Container is running without liveness probe

    spec - containers[argocd-applicationset-controller] + initContainers[copyutil] livenessProbe
  • - Line number: 675 + Line number: 812
  • @@ -1608,7 +1558,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 37] + [DocId: 36] spec @@ -1623,7 +1573,7 @@

    Container is running without liveness probe

  • - Line number: 898 + Line number: 778
  • @@ -1660,7 +1610,7 @@

    Container is running without liveness probe

  • Introduced through: - [DocId: 39] + [DocId: 38] spec @@ -1675,7 +1625,7 @@

    Container is running without liveness probe

  • - Line number: 1109 + Line number: 946
  • @@ -1697,7 +1647,7 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container is running without liveness probe

    @@ -1708,13 +1658,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 36] - - input + [DocId: 39] spec @@ -1722,35 +1670,31 @@

      Container is running without memory limit

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

    Impact

    -

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

    +

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

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Add `livenessProbe` attribute


    @@ -1770,7 +1714,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 37] + [DocId: 35] input @@ -1780,7 +1724,7 @@

    Container is running without memory limit

    spec - containers[dex] + containers[argocd-applicationset-controller] resources @@ -1791,7 +1735,7 @@

    Container is running without memory limit

  • - Line number: 898 + Line number: 640
  • @@ -1828,7 +1772,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -1838,7 +1782,7 @@

    Container is running without memory limit

    spec - initContainers[copyutil] + containers[dex] resources @@ -1849,7 +1793,7 @@

    Container is running without memory limit

  • - Line number: 944 + Line number: 778
  • @@ -1886,7 +1830,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 38] + [DocId: 36] input @@ -1896,7 +1840,7 @@

    Container is running without memory limit

    spec - containers[argocd-notifications-controller] + initContainers[copyutil] resources @@ -1907,7 +1851,7 @@

    Container is running without memory limit

  • - Line number: 1004 + Line number: 812
  • @@ -1944,7 +1888,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 39] + [DocId: 37] input @@ -1954,7 +1898,7 @@

    Container is running without memory limit

    spec - containers[redis] + containers[argocd-notifications-controller] resources @@ -1965,7 +1909,7 @@

    Container is running without memory limit

  • - Line number: 1109 + Line number: 872
  • @@ -2002,7 +1946,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -2012,7 +1956,7 @@

    Container is running without memory limit

    spec - initContainers[secret-init] + containers[redis] resources @@ -2023,7 +1967,7 @@

    Container is running without memory limit

  • - Line number: 1133 + Line number: 946
  • @@ -2060,7 +2004,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 40] + [DocId: 39] input @@ -2081,7 +2025,7 @@

    Container is running without memory limit

  • - Line number: 1437 + Line number: 1190
  • @@ -2118,7 +2062,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 40] + [DocId: 39] input @@ -2139,7 +2083,7 @@

    Container is running without memory limit

  • - Line number: 1190 + Line number: 1002
  • @@ -2176,7 +2120,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 41] + [DocId: 40] input @@ -2197,7 +2141,7 @@

    Container is running without memory limit

  • - Line number: 1522 + Line number: 1275
  • @@ -2234,7 +2178,7 @@

    Container is running without memory limit

  • Introduced through: - [DocId: 42] + [DocId: 41] input @@ -2255,7 +2199,7 @@

    Container is running without memory limit

  • - Line number: 1912 + Line number: 1579
  • @@ -2292,7 +2236,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 36] + [DocId: 35] input @@ -2311,7 +2255,7 @@

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

  • - Line number: 822 + Line number: 715
  • @@ -2348,7 +2292,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -2367,7 +2311,7 @@

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

  • - Line number: 952 + Line number: 820
  • @@ -2404,7 +2348,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -2423,7 +2367,7 @@

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

  • - Line number: 927 + Line number: 795
  • @@ -2460,7 +2404,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 38] + [DocId: 37] input @@ -2479,7 +2423,7 @@

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

  • - Line number: 1043 + Line number: 880
  • @@ -2516,7 +2460,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -2535,7 +2479,7 @@

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

  • - Line number: 1126 + Line number: 956
  • @@ -2582,62 +2526,6 @@

    Container's or Pod's UID could clash with hos spec - initContainers[secret-init] - - securityContext - - runAsUser - - - -
  • - Line number: 1140 -
  • - - -
    - -

    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 - initContainers[copyutil] securityContext @@ -2647,7 +2535,7 @@

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

    • - Line number: 1444 + Line number: 1197
    @@ -2684,7 +2572,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 40] + [DocId: 39] input @@ -2703,7 +2591,7 @@

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

  • - Line number: 1410 + Line number: 1163
  • @@ -2740,7 +2628,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 41] + [DocId: 40] input @@ -2759,7 +2647,7 @@

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

  • - Line number: 1813 + Line number: 1489
  • @@ -2796,7 +2684,7 @@

    Container's or Pod's UID could clash with hos
  • Introduced through: - [DocId: 42] + [DocId: 41] input @@ -2815,7 +2703,7 @@

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

  • - Line number: 2113 + Line number: 1721
  • diff --git a/docs/snyk/v2.11.8/argocd-test.html b/docs/snyk/v2.7.14/argocd-test.html similarity index 80% rename from docs/snyk/v2.11.8/argocd-test.html rename to docs/snyk/v2.7.14/argocd-test.html index ed53a51bfbfd9..342599913dab0 100644 --- a/docs/snyk/v2.11.8/argocd-test.html +++ b/docs/snyk/v2.7.14/argocd-test.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,20 +456,19 @@

    Snyk test report

    -

    September 22nd 2024, 12:26:11 am (UTC+00:00)

    +

    October 29th 2023, 12:24:41 am (UTC+00:00)

    Scanned the following paths:
      -
    • /argo-cd/argoproj/argo-cd/v2/go.mod (gomodules)
    • -
    • /argo-cd/ui/yarn.lock (yarn)
    • +
    • /argo-cd/argoproj/argo-cd/v2 (gomodules)
    • /argo-cd (yarn)
    -
    7 known vulnerabilities
    -
    158 vulnerable dependency paths
    -
    2041 dependencies
    +
    9 known vulnerabilities
    +
    161 vulnerable dependency paths
    +
    1748 dependencies

    @@ -478,7 +477,7 @@

    Snyk test report

    -

    Allocation of Resources Without Limits or Throttling

    +

    Regular Expression Denial of Service (ReDoS)

    @@ -489,21 +488,170 @@

    Allocation of Resources Without Limits or Throttling

  • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod + Package Manager: npm +
  • +
  • + Vulnerable module: + + semver +
  • + +
  • Introduced through: + + + argo-cd-ui@1.0.0, superagent@8.0.9 and others
  • + + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + argo-cd-ui@1.0.0 + + superagent@8.0.9 + + semver@7.3.8 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    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.

    +

    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 semver to version 5.7.2, 6.3.1, 7.5.2 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
    • Package Manager: golang
    • Vulnerable module: - golang.org/x/net/http2 + google.golang.org/grpc
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and google.golang.org/grpc@1.51.0 - github.com/argoproj/argo-cd/v2@0.0.0, k8s.io/apimachinery/pkg/util/net@0.26.11 and others
    @@ -517,9 +665,7 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -528,9 +674,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/soheilhy/cmux@0.1.5 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -539,9 +685,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/health@1.51.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -550,9 +696,9 @@

    Detailed paths

    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/reflection@1.51.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -561,11 +707,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -574,11 +718,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport@0.26.11 - - k8s.io/apimachinery/pkg/util/net@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.3.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -587,11 +729,9 @@

    Detailed paths

    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 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -600,11 +740,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.26.11 + google.golang.org/grpc/health/grpc_health_v1@1.51.0 - k8s.io/client-go/rest@0.26.11 - - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -613,11 +751,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport/spdy@0.26.11 - - k8s.io/client-go/rest@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -626,11 +762,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/pkg/kubeclientmetrics@#d56162821bd1 + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a - k8s.io/client-go/rest@0.26.11 - - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -639,11 +773,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/testing@0.26.11 - - k8s.io/client-go/rest@0.26.11 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.31.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -652,11 +784,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/dynamic@0.26.11 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 - k8s.io/client-go/rest@0.26.11 - - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -665,11 +795,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 - k8s.io/client-go/rest@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -678,11 +808,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/azure@0.26.11 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 - k8s.io/client-go/rest@0.26.11 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig@1.11.1 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -691,11 +821,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/gcp@0.26.11 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 - k8s.io/client-go/rest@0.26.11 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -704,11 +834,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/oidc@0.26.11 + google.golang.org/grpc/reflection@1.51.0 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.51.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -717,11 +847,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/record@0.26.11 + google.golang.org/grpc/health@1.51.0 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/health/grpc_health_v1@1.51.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -730,13 +860,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -745,13 +875,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 - k8s.io/client-go/transport@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -760,13 +890,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 - google.golang.org/grpc@1.59.0 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 - google.golang.org/grpc/internal/transport@1.59.0 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.51.0 @@ -775,28 +907,94 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 - google.golang.org/grpc@1.59.0 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 - google.golang.org/grpc/internal/transport@1.59.0 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 - golang.org/x/net/http2@0.19.0 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 + + google.golang.org/grpc@1.51.0 + + +
    + +
    + +

    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 +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, k8s.io/apimachinery/pkg/util/net@0.24.2 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • 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 + k8s.io/apimachinery/pkg/util/net@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -805,13 +1003,9 @@

      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.59.0 - - google.golang.org/grpc/internal/transport@1.59.0 + github.com/soheilhy/cmux@0.1.5 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -820,13 +1014,20 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + k8s.io/client-go/rest@0.24.2 - google.golang.org/grpc@1.59.0 + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/internal/transport@1.59.0 + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -835,13 +1036,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -850,13 +1049,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.46.1 - - google.golang.org/grpc@1.59.0 + google.golang.org/grpc@1.51.0 - google.golang.org/grpc/internal/transport@1.59.0 + google.golang.org/grpc/internal/transport@1.51.0 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -865,13 +1062,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/health/grpc_health_v1@1.59.0 + k8s.io/client-go/discovery@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/client-go/rest@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -880,13 +1075,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/openapi@0.26.11 - - k8s.io/client-go/discovery@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -895,13 +1088,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/clientcmd@0.26.11 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/client-go/tools/auth@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/rest@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -910,13 +1101,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#f48567108f01 - - k8s.io/client-go/tools/cache@0.26.11 + github.com/argoproj/pkg/kubeclientmetrics@#a4dd357b057e - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -925,13 +1114,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery/fake@0.26.11 + k8s.io/client-go/testing@0.24.2 - k8s.io/client-go/testing@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/rest@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -940,13 +1127,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/fake@0.26.11 - - k8s.io/client-go/testing@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -955,13 +1140,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/client-go/plugin/pkg/client/auth/azure@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/rest@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -970,13 +1153,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/apps/v1@0.26.11 - - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/client-go/plugin/pkg/client/auth/gcp@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -985,13 +1166,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers@0.26.11 + k8s.io/client-go/plugin/pkg/client/auth/oidc@0.24.2 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/rest@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1000,13 +1179,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/listers/core/v1@0.26.11 - - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/client-go/tools/record@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1015,13 +1192,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/remotecommand@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport/spdy@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1030,13 +1207,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + k8s.io/client-go/rest@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/transport@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1045,15 +1222,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/pkg/apis/clientauthentication/v1beta1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1062,15 +1237,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1079,15 +1252,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/api/rbac/v1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.3.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1096,15 +1267,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/api/core/v1@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1113,15 +1282,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/errors@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + google.golang.org/grpc/health/grpc_health_v1@1.51.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1130,15 +1297,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#18ba62e1f1fb + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.31.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1147,15 +1312,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/equality@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1164,15 +1327,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport/spdy@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/client-go/transport@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1181,15 +1342,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/pkg/kubeclientmetrics@#d56162821bd1 - - k8s.io/client-go/rest@0.26.11 + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a - k8s.io/client-go/transport@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1198,15 +1357,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/testing@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1215,15 +1372,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/azure@0.26.11 - - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/listers/core/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1232,15 +1387,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/gcp@0.26.11 + k8s.io/client-go/informers@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1249,15 +1402,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/oidc@0.26.11 - - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/tools/clientcmd@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/client-go/tools/auth@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1266,15 +1417,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + github.com/argoproj/notifications-engine/pkg/controller@#f754726f03da - google.golang.org/grpc/health/grpc_health_v1@1.59.0 + k8s.io/client-go/tools/cache@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/client-go/rest@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1283,15 +1432,13 @@

      Detailed paths

      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 + k8s.io/client-go/discovery/fake@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/client-go/testing@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1300,15 +1447,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/health@1.59.0 + k8s.io/client-go/kubernetes/fake@0.24.2 - google.golang.org/grpc/health/grpc_health_v1@1.59.0 + k8s.io/client-go/testing@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/client-go/rest@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1317,15 +1462,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/cache@#18ba62e1f1fb - - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/client-go/tools/remotecommand@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1334,15 +1477,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync@#18ba62e1f1fb + k8s.io/api/core/v1@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1351,15 +1494,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@#18ba62e1f1fb + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1368,15 +1511,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#f48567108f01 + k8s.io/api/rbac/v1@0.24.2 - k8s.io/client-go/listers/core/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1385,15 +1528,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#f48567108f01 + k8s.io/client-go/pkg/apis/clientauthentication/v1beta1@0.24.2 - k8s.io/client-go/tools/clientcmd@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/tools/auth@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1402,15 +1545,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/event@0.14.7 + k8s.io/apimachinery/pkg/api/errors@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1419,15 +1562,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/core/v1@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/common@#ad9a694fe4bc - k8s.io/client-go/listers/core/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1436,15 +1579,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/cache@0.14.7 + k8s.io/apimachinery/pkg/api/equality@0.24.2 - sigs.k8s.io/controller-runtime/pkg/cache/internal@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1453,15 +1596,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/term@0.26.11 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/client-go/tools/remotecommand@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/transport/spdy@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1470,15 +1613,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/metrics@0.14.7 + github.com/argoproj/pkg/kubeclientmetrics@#a4dd357b057e - k8s.io/client-go/tools/leaderelection@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/tools/leaderelection/resourcelock@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1487,15 +1630,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + k8s.io/client-go/testing@0.24.2 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + k8s.io/client-go/rest@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/transport@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1504,15 +1647,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#f48567108f01 + k8s.io/client-go/plugin/pkg/client/auth/azure@0.24.2 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + k8s.io/client-go/rest@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/transport@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1521,17 +1664,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/Azure/kubelogin/pkg/token@0.0.20 + k8s.io/client-go/plugin/pkg/client/auth/gcp@0.24.2 - k8s.io/client-go/pkg/apis/clientauthentication/v1beta1@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1540,17 +1681,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/dynamic@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/client-go/plugin/pkg/client/auth/oidc@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1559,17 +1698,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/ignore@#18ba62e1f1fb + google.golang.org/grpc/reflection@1.51.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.51.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1578,17 +1715,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#18ba62e1f1fb - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + google.golang.org/grpc/health@1.51.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + google.golang.org/grpc/health/grpc_health_v1@1.51.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1597,17 +1732,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/testing@#18ba62e1f1fb + github.com/argoproj/gitops-engine/pkg/cache@#ad9a694fe4bc - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1616,17 +1749,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/strategicpatch@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync@#ad9a694fe4bc - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1635,17 +1766,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime@0.14.7 + github.com/argoproj/gitops-engine/pkg/utils/kube@#ad9a694fe4bc - sigs.k8s.io/controller-runtime/pkg/scheme@0.14.7 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1654,17 +1783,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/listers/core/v1@0.26.11 - - k8s.io/api/core/v1@0.26.11 + github.com/argoproj/notifications-engine/pkg/api@#f754726f03da - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/listers/core/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1673,17 +1800,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/resource@0.26.11 + k8s.io/client-go/informers/core/v1@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/listers/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1692,17 +1817,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/health@#18ba62e1f1fb - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + github.com/argoproj/notifications-engine/pkg/cmd@#f754726f03da - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/tools/clientcmd@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/tools/auth@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1711,17 +1834,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/util/retry@0.26.11 + k8s.io/kubectl/pkg/util/term@0.24.2 - k8s.io/apimachinery/pkg/api/errors@0.26.11 + k8s.io/client-go/tools/remotecommand@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1730,17 +1851,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/managedfields@0.26.11 + k8s.io/kubectl/pkg/util/resource@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1749,17 +1870,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.26.11 + github.com/argoproj/gitops-engine/pkg/health@#ad9a694fe4bc - k8s.io/client-go/tools/pager@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1768,17 +1889,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/portforward@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/resource@#ad9a694fe4bc - k8s.io/api/core/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1787,17 +1908,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/apimachinery/pkg/api/equality@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1806,17 +1927,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/validation@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/ignore@#ad9a694fe4bc - k8s.io/apimachinery/pkg/apis/meta/v1/validation@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1825,17 +1946,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery/fake@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#ad9a694fe4bc - k8s.io/client-go/testing@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1844,17 +1965,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/fake@0.26.11 + github.com/argoproj/gitops-engine/pkg/utils/testing@#ad9a694fe4bc - k8s.io/client-go/testing@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1863,17 +1984,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/remotecommand@0.26.11 + k8s.io/apimachinery/pkg/util/managedfields@0.24.2 - k8s.io/client-go/transport/spdy@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1882,17 +2003,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/health@#18ba62e1f1fb + k8s.io/client-go/tools/cache@0.24.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@#18ba62e1f1fb + k8s.io/client-go/tools/pager@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1901,17 +2022,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#18ba62e1f1fb + sigs.k8s.io/controller-runtime@0.11.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@#18ba62e1f1fb + sigs.k8s.io/controller-runtime/pkg/scheme@0.11.0 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1920,17 +2041,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.14.7 + k8s.io/client-go/util/retry@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.14.7 + k8s.io/apimachinery/pkg/api/errors@0.24.2 - k8s.io/client-go/restmapper@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1939,17 +2060,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + k8s.io/client-go/tools/portforward@0.24.2 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + k8s.io/api/core/v1@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1958,17 +2079,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/envtest@0.14.7 + k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1@0.24.2 - sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane@0.14.7 + k8s.io/apimachinery/pkg/api/equality@0.24.2 - k8s.io/client-go/tools/clientcmd@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/tools/auth@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1977,17 +2098,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/handler@0.14.7 + k8s.io/apimachinery/pkg/api/validation@0.24.2 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1/validation@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -1996,17 +2117,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#f48567108f01 + k8s.io/client-go/discovery/fake@0.24.2 - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + k8s.io/client-go/testing@0.24.2 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + k8s.io/client-go/rest@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/transport@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2015,17 +2136,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#f48567108f01 + k8s.io/client-go/kubernetes/fake@0.24.2 - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + k8s.io/client-go/testing@0.24.2 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + k8s.io/client-go/rest@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/transport@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2034,19 +2155,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/diff@#18ba62e1f1fb - - k8s.io/apimachinery/pkg/util/strategicpatch@0.26.11 + k8s.io/client-go/tools/remotecommand@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2055,19 +2174,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/runtime/serializer@0.26.11 + github.com/argoproj/gitops-engine/pkg/health@#ad9a694fe4bc - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.26.11 + github.com/argoproj/gitops-engine/pkg/utils/kube@#ad9a694fe4bc - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2076,19 +2193,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/core/v1@0.26.11 - - k8s.io/client-go/listers/core/v1@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/common@#ad9a694fe4bc - k8s.io/api/core/v1@0.26.11 + github.com/argoproj/gitops-engine/pkg/utils/kube@#ad9a694fe4bc - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2097,19 +2212,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 + sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.11.0 - k8s.io/api/storage/v1beta1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2118,19 +2231,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/record@0.26.11 - - k8s.io/client-go/tools/reference@0.26.11 + sigs.k8s.io/controller-runtime@0.11.0 - k8s.io/api/core/v1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/manager@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/recorder@0.11.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/tools/record@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2139,19 +2250,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@#18ba62e1f1fb + sigs.k8s.io/controller-runtime/pkg/envtest@0.11.0 - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#18ba62e1f1fb + sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane@0.11.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#18ba62e1f1fb + k8s.io/client-go/tools/clientcmd@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/tools/auth@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2160,19 +2269,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#f48567108f01 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/client-go/tools/pager@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2181,19 +2290,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/apps/v1@0.26.11 + k8s.io/client-go/tools/record@0.24.2 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/client-go/tools/reference@0.24.2 - k8s.io/client-go/tools/pager@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2202,19 +2311,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/hook@#ad9a694fe4bc - k8s.io/client-go/tools/cache@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/resource@#ad9a694fe4bc - k8s.io/client-go/tools/pager@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2223,19 +2332,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/apimachinery/pkg/runtime/serializer@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2244,19 +2353,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/term@0.26.11 + k8s.io/client-go/listers/core/v1@0.24.2 - k8s.io/client-go/tools/remotecommand@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/client-go/transport/spdy@0.26.11 + k8s.io/client-go/tools/pager@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2265,19 +2374,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/metrics@0.14.7 + k8s.io/client-go/informers@0.24.2 - k8s.io/client-go/tools/leaderelection@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/client-go/tools/leaderelection/resourcelock@0.26.11 + k8s.io/client-go/tools/pager@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2286,19 +2395,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/controller@#f754726f03da - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/tools/cache@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/client-go/tools/pager@0.24.2 - google.golang.org/api/option@0.132.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2307,19 +2416,19 @@

      Detailed paths

      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/argoproj/gitops-engine/pkg/utils/kube/scheme@#ad9a694fe4bc - github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 + k8s.io/apimachinery/pkg/util/managedfields@0.24.2 - github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2328,19 +2437,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#f48567108f01 + k8s.io/kubectl/pkg/util/term@0.24.2 - k8s.io/client-go/listers/core/v1@0.26.11 + k8s.io/client-go/tools/remotecommand@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2349,19 +2458,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 - k8s.io/client-go/kubernetes/typed/storage/v1beta1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 - k8s.io/client-go/applyconfigurations/storage/v1beta1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 - k8s.io/client-go/applyconfigurations/meta/v1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - k8s.io/client-go/discovery@0.26.11 + google.golang.org/grpc@1.51.0 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/internal/transport@1.51.0 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2370,21 +2479,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/clientcmd@0.26.11 - - k8s.io/client-go/tools/clientcmd/api/latest@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.26.11 + sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2393,21 +2500,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.26.11 + sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 + sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - k8s.io/api/storage/v1beta1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2416,21 +2521,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/event@0.14.7 - - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/client-go/kubernetes@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/client-go/kubernetes/typed/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/client-go/applyconfigurations/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/applyconfigurations/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2439,21 +2542,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/cache@0.14.7 + sigs.k8s.io/controller-runtime/pkg/builder@0.11.0 - sigs.k8s.io/controller-runtime/pkg/cache/internal@0.14.7 + sigs.k8s.io/controller-runtime/pkg/webhook/admission@0.11.0 - k8s.io/client-go/tools/cache@0.26.11 + sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics@0.11.0 - k8s.io/client-go/tools/pager@0.26.11 + sigs.k8s.io/controller-runtime/pkg/metrics@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2462,21 +2563,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + k8s.io/client-go/discovery@0.24.2 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + k8s.io/client-go/kubernetes/scheme@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/api/storage/v1beta1@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/api/core/v1@0.24.2 - google.golang.org/api/option@0.132.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2485,21 +2586,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/diff@#18ba62e1f1fb + k8s.io/client-go/tools/clientcmd@0.24.2 - k8s.io/kubectl/pkg/cmd/util@0.26.11 + k8s.io/client-go/tools/clientcmd/api/latest@0.24.2 - k8s.io/kubectl/pkg/validation@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - k8s.io/cli-runtime/pkg/resource@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/client-go/restmapper@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2508,21 +2609,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@#18ba62e1f1fb + github.com/argoproj/notifications-engine/pkg/api@#f754726f03da - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#18ba62e1f1fb + k8s.io/client-go/listers/core/v1@0.24.2 - github.com/argoproj/gitops-engine/pkg/sync/common@#18ba62e1f1fb + k8s.io/client-go/tools/cache@0.24.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@#18ba62e1f1fb + k8s.io/client-go/tools/pager@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2531,21 +2632,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#18ba62e1f1fb + k8s.io/client-go/informers/core/v1@0.24.2 - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#18ba62e1f1fb + k8s.io/client-go/listers/core/v1@0.24.2 - github.com/argoproj/gitops-engine/pkg/sync/common@#18ba62e1f1fb + k8s.io/client-go/tools/cache@0.24.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@#18ba62e1f1fb + k8s.io/client-go/tools/pager@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2554,21 +2655,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/source@0.14.7 + github.com/argoproj/gitops-engine/pkg/diff@#ad9a694fe4bc - sigs.k8s.io/controller-runtime/pkg/source/internal@0.14.7 + github.com/argoproj/gitops-engine/pkg/utils/kube/scheme@#ad9a694fe4bc - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + k8s.io/apimachinery/pkg/util/managedfields@0.24.2 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2577,21 +2678,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/builder@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/hook@#ad9a694fe4bc - sigs.k8s.io/controller-runtime/pkg/webhook/admission@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#ad9a694fe4bc - sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/common@#ad9a694fe4bc - sigs.k8s.io/controller-runtime/pkg/metrics@0.14.7 + github.com/argoproj/gitops-engine/pkg/utils/kube@#ad9a694fe4bc - k8s.io/client-go/tools/leaderelection@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/client-go/tools/leaderelection/resourcelock@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2600,21 +2701,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#f48567108f01 + github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#ad9a694fe4bc - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#ad9a694fe4bc - google.golang.org/api/chat/v1@0.132.0 + github.com/argoproj/gitops-engine/pkg/sync/common@#ad9a694fe4bc - google.golang.org/api/transport/http@0.132.0 + github.com/argoproj/gitops-engine/pkg/utils/kube@#ad9a694fe4bc - google.golang.org/api/option@0.132.0 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/client-go/discovery@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2623,23 +2724,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/builder@0.14.7 - - sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.14.7 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - k8s.io/apimachinery/pkg/runtime/serializer@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.26.11 + sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2648,23 +2747,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/envtest@0.14.7 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.14.7 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/runtime/serializer@0.26.11 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2673,23 +2772,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#f48567108f01 + k8s.io/client-go/kubernetes@0.24.2 - k8s.io/client-go/tools/clientcmd@0.26.11 + k8s.io/client-go/kubernetes/typed/storage/v1beta1@0.24.2 - k8s.io/client-go/tools/clientcmd/api/latest@0.26.11 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2698,23 +2797,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + sigs.k8s.io/controller-runtime/pkg/builder@0.11.0 - k8s.io/client-go/discovery@0.26.11 + sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.11.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer@0.24.2 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2723,23 +2822,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes@0.26.11 + sigs.k8s.io/controller-runtime/pkg/envtest@0.11.0 - k8s.io/client-go/kubernetes/typed/storage/v1beta1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.11.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer@0.24.2 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2748,23 +2847,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube/scheme@#18ba62e1f1fb + github.com/argoproj/notifications-engine/pkg/cmd@#f754726f03da - k8s.io/kubernetes/pkg/apis/storage/install@1.26.11 + k8s.io/client-go/tools/clientcmd@0.24.2 - k8s.io/kubernetes/pkg/apis/storage/v1alpha1@1.26.11 + k8s.io/client-go/tools/clientcmd/api/latest@0.24.2 - k8s.io/api/storage/v1alpha1@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2773,23 +2872,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/ignore@#ad9a694fe4bc - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/hook@#ad9a694fe4bc - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#ad9a694fe4bc - k8s.io/client-go/dynamic@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/common@#ad9a694fe4bc - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + github.com/argoproj/gitops-engine/pkg/utils/kube@#ad9a694fe4bc - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2798,23 +2897,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/handler@0.14.7 + sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/client-go/dynamic@0.26.11 + sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2823,23 +2922,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#f48567108f01 + sigs.k8s.io/controller-runtime/pkg/handler@0.11.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.11.0 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - google.golang.org/api/chat/v1@0.132.0 + sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - google.golang.org/api/transport/http@0.132.0 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - google.golang.org/api/option@0.132.0 + k8s.io/client-go/restmapper@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/client-go/discovery@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2848,23 +2947,25 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/ignore@#18ba62e1f1fb + github.com/argoproj/gitops-engine/pkg/cache@#ad9a694fe4bc - github.com/argoproj/gitops-engine/pkg/sync/hook@#18ba62e1f1fb + k8s.io/kubectl/pkg/util/openapi@0.24.2 - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#18ba62e1f1fb + k8s.io/client-go/discovery@0.24.2 - github.com/argoproj/gitops-engine/pkg/sync/common@#18ba62e1f1fb + k8s.io/client-go/kubernetes/scheme@0.24.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@#18ba62e1f1fb + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - golang.org/x/net/http2@0.19.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 @@ -2873,23 +2974,25 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync@#ad9a694fe4bc + + k8s.io/kubectl/pkg/util/openapi@0.24.2 - sigs.k8s.io/controller-runtime/pkg/source@0.14.7 + k8s.io/client-go/discovery@0.24.2 - sigs.k8s.io/controller-runtime/pkg/source/internal@0.14.7 + k8s.io/client-go/kubernetes/scheme@0.24.2 - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + k8s.io/api/storage/v1beta1@0.24.2 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + k8s.io/api/core/v1@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2898,23 +3001,25 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime@0.14.7 + github.com/argoproj/gitops-engine/pkg/utils/kube@#ad9a694fe4bc - sigs.k8s.io/controller-runtime/pkg/manager@0.14.7 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - sigs.k8s.io/controller-runtime/pkg/webhook@0.14.7 + k8s.io/client-go/discovery@0.24.2 - sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics@0.14.7 + k8s.io/client-go/kubernetes/scheme@0.24.2 - sigs.k8s.io/controller-runtime/pkg/metrics@0.14.7 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/client-go/tools/leaderelection@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/client-go/tools/leaderelection/resourcelock@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - golang.org/x/net/http2@0.19.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 @@ -2923,23 +3028,27 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#f48567108f01 + sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.11.0 + + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + k8s.io/client-go/restmapper@0.24.2 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + k8s.io/client-go/discovery@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/kubernetes/scheme@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/api/storage/v1beta1@0.24.2 - google.golang.org/api/option@0.132.0 + k8s.io/api/core/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - golang.org/x/net/http2@0.19.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 @@ -2948,25 +3057,27 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/cache@#18ba62e1f1fb + sigs.k8s.io/controller-runtime/pkg/source@0.11.0 + + sigs.k8s.io/controller-runtime/pkg/source/internal@0.11.0 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - k8s.io/client-go/discovery@0.26.11 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/api/storage/v1beta1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - k8s.io/api/core/v1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -2975,25 +3086,60 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync@#18ba62e1f1fb + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - k8s.io/client-go/discovery@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - golang.org/x/net/http2@0.19.0 + k8s.io/apimachinery/pkg/watch@0.24.2 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 + + + +
    • +
    • + 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 + + k8s.io/client-go/kubernetes/scheme@0.24.2 + + k8s.io/api/storage/v1beta1@0.24.2 + + k8s.io/api/core/v1@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@0.11.0 @@ -3002,25 +3148,31 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@#18ba62e1f1fb + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/client-go/discovery@0.26.11 + sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/api/core/v1@0.24.2 - golang.org/x/net/http2@0.19.0 + 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@0.11.0 @@ -3029,27 +3181,33 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.14.7 + 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.14.7 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/client-go/restmapper@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/client-go/kubernetes/scheme@0.26.11 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.11.0 @@ -3058,27 +3216,33 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/source@0.14.7 + sigs.k8s.io/controller-runtime/pkg/handler@0.11.0 - sigs.k8s.io/controller-runtime/pkg/source/internal@0.14.7 + sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.11.0 - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/api/core/v1@0.24.2 - golang.org/x/net/http2@0.19.0 + 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@0.11.0 @@ -3087,29 +3251,37 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller@0.14.7 + 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/source@0.14.7 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - sigs.k8s.io/controller-runtime/pkg/source/internal@0.14.7 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - golang.org/x/net/http2@0.19.0 + k8s.io/apimachinery/pkg/watch@0.24.2 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.11.0 @@ -3121,27 +3293,36 @@

      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 Allocation of Resources Without Limits or Throttling when reading header data from CONTINUATION frames. As part of the HPACK flow, all incoming HEADERS and CONTINUATION frames are read even if their payloads exceed MaxHeaderBytes and will be discarded. An attacker can send excessive data over a connection to render it unresponsive.

      +

      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.23.0 or higher.

      +

      Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

      References


    -

    Prototype Pollution

    +

    Directory Traversal

    @@ -3152,21 +3333,18 @@

    Prototype Pollution

    • - Manifest file: /argo-cd ui/yarn.lock -
    • -
    • - Package Manager: npm + Package Manager: golang
    • Vulnerable module: - dompurify + github.com/cyphar/filepath-securejoin
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/cyphar/filepath-securejoin@0.2.3 - argo-cd-ui@1.0.0, redoc@2.0.0-rc.64 and others
    @@ -3178,11 +3356,9 @@

    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 - dompurify@2.3.6 + github.com/cyphar/filepath-securejoin@0.2.3 @@ -3194,108 +3370,46 @@

      Detailed paths


      Overview

      -

      dompurify is a DOM-only XSS sanitizer for HTML, MathML and SVG.

      -

      Affected versions of this package are vulnerable to Prototype Pollution due to improper user input sanitization through the depth-checking mechanism, an attacker can exploit this vulnerability by using special nesting techniques to create a malicious HTML file.

      +

      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

      -

      Prototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as __proto__, constructor and prototype. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values. Properties on the Object.prototype are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.

      -

      There are two main ways in which the pollution of prototypes occurs:

      +

      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:

        -
      • Unsafe Object recursive merge

        -
      • -
      • Property definition by path

        -
      • +
      • Information Disclosure: Allows the attacker to gain information about the folder structure or read the contents of sensitive files on the system.
      -

      Unsafe Object recursive merge

      -

      The logic of a vulnerable recursive merge function follows the following high-level model:

      -
      merge (target, source)
      -        
      -          foreach property of source
      -        
      -            if property exists and is an object on both the target and the source
      -        
      -              merge(target[property], source[property])
      -        
      -            else
      -        
      -              target[property] = source[property]
      +        

      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
               
      -
      - -

      When the source object contains a property named __proto__ defined with Object.defineProperty() , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of Object and the source of Object as defined by the attacker. Properties are then copied on the Object prototype.

      -

      Clone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: merge({},source).

      -

      lodash and Hoek are examples of libraries susceptible to recursive merge attacks.

      -

      Property definition by path

      -

      There are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: theFunction(object, path, value)

      -

      If the attacker can control the value of “path”, they can set this value to __proto__.myValue. myValue is then assigned to the prototype of the class of the object.

      -

      Types of attacks

      -

      There are a few methods by which Prototype Pollution can be manipulated:

      - - - - - - - - - - - - - - - - - - - - - - - -
      TypeOriginShort description
      Denial of service (DoS)ClientThis is the most likely attack.
      DoS occurs when Object holds generic functions that are implicitly called for various operations (for example, toString and valueOf).
      The attacker pollutes Object.prototype.someattr and alters its state to an unexpected value such as Int or Object. In this case, the code fails and is likely to cause a denial of service.
      For example: if an attacker pollutes Object.prototype.toString by defining it as an integer, if the codebase at any point was reliant on someobject.toString() it would fail.
      Remote Code ExecutionClientRemote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.
      For example: eval(someobject.someattr). In this case, if the attacker pollutes Object.prototype.someattr they are likely to be able to leverage this in order to execute code.
      Property InjectionClientThe attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.
      For example: if a codebase checks privileges for someuser.isAdmin, then when the attacker pollutes Object.prototype.isAdmin and sets it to equal true, they can then achieve admin privileges.
      -

      Affected environments

      -

      The following environments are susceptible to a Prototype Pollution attack:

      +

      Note %2e is the URL encoded version of . (dot).

        -
      • Application server

        -
      • -
      • Web server

        -
      • -
      • Web browser

        -
      • +
      • Writing arbitrary files: Allows the attacker to create or replace existing files. This type of vulnerability is also known as Zip-Slip.
      -

      How to prevent

      -
        -
      1. Freeze the prototype— use Object.freeze (Object.prototype).

        -
      2. -
      3. Require schema validation of JSON input.

        -
      4. -
      5. Avoid using unsafe recursive merge functions.

        -
      6. -
      7. Consider using objects without prototypes (for example, Object.create(null)), breaking the prototype chain and preventing pollution.

        -
      8. -
      9. As a best practice use Map instead of Object.

        -
      10. -
      -

      For more information on this vulnerability type:

      -

      Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018

      +

      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 dompurify to version 2.5.4, 3.1.3 or higher.

      +

      Upgrade github.com/cyphar/filepath-securejoin to version 0.2.4 or higher.

      References


    -

    Regular Expression Denial of Service (ReDoS)

    +

    MPL-2.0 license

    @@ -3306,21 +3420,18 @@

    Regular Expression Denial of Service (ReDoS)

    • - Manifest file: /argo-cd ui/yarn.lock -
    • -
    • - Package Manager: npm + Package Manager: golang
    • - Vulnerable module: + Module: - path-to-regexp + github.com/r3labs/diff
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/r3labs/diff@1.1.0 - argo-cd-ui@1.0.0, react-router@4.3.1 and others
    @@ -3332,39 +3443,9 @@

    Detailed paths

    • Introduced through: - argo-cd-ui@1.0.0 - - react-router@4.3.1 - - path-to-regexp@1.8.0 - - - -
    • -
    • - Introduced through: - argo-cd-ui@1.0.0 - - react-router-dom@4.3.1 - - react-router@4.3.1 - - path-to-regexp@1.8.0 - - - -
    • -
    • - Introduced through: - argo-cd-ui@1.0.0 - - argo-ui@1.0.0 - - react-router-dom@4.3.1 - - react-router@4.3.1 + github.com/argoproj/argo-cd/v2@0.0.0 - path-to-regexp@1.8.0 + github.com/r3labs/diff@1.1.0 @@ -3375,96 +3456,17 @@

      Detailed paths


      -

      Overview

      -

      Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) when including multiple regular expression parameters in a single segment, which will produce the regular expression /^\/([^\/]+?)-([^\/]+?)\/?$/, if two parameters within a single segment are separated by a character other than a / or .. Poor performance will block the event loop and can lead to a DoS.

      -

      Note: - While the 8.0.0 release has completely eliminated the vulnerable functionality, prior versions that have received the patch to mitigate backtracking may still be vulnerable if custom regular expressions are used. So it is strongly recommended for regular expression input to be controlled to avoid malicious performance degradation in those versions. This behavior is enforced as of version 7.1.0 via the strict option, which returns an error if a dangerous regular expression is detected.

      -

      Workaround

      -

      This vulnerability can be avoided by using a custom regular expression for parameters after the first in a segment, which excludes - and /.

      -

      PoC

      -
      /a${'-a'.repeat(8_000)}/a
      -        
      -

      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 path-to-regexp to version 0.1.10, 1.9.0, 3.3.0, 6.3.0, 8.0.0 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Denial of Service (DoS)

    +

    MPL-2.0 license

    @@ -3474,22 +3476,19 @@

    Denial of Service (DoS)


      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/rs/cors + github.com/hashicorp/go-version
    • Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0, github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 and others + github.com/argoproj/argo-cd/v2@0.0.0, code.gitea.io/sdk/gitea@0.15.1 and others
    @@ -3503,9 +3502,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + code.gitea.io/sdk/gitea@0.15.1 - github.com/rs/cors@1.9.0 + github.com/hashicorp/go-version@1.2.1 @@ -3516,67 +3515,17 @@

    Detailed paths


    -

    Overview

    -

    Affected versions of this package are vulnerable to Denial of Service (DoS) through the processing of malicious preflight requests that include a Access-Control-Request-Headers header with excessive commas. An attacker can induce excessive memory consumption and potentially crash the server by sending specially crafted requests.

    -

    PoC

    -
    
    -        func BenchmarkPreflightAdversarialACRH(b *testing.B) {
    -            resps := makeFakeResponses(b.N)
    -            req, _ := http.NewRequest(http.MethodOptions, dummyEndpoint, nil)
    -            req.Header.Add(headerOrigin, dummyOrigin)
    -            req.Header.Add(headerACRM, http.MethodGet)
    -            req.Header[headerACRH] = adversarialACRH
    -            handler := Default().Handler(testHandler)
    -        
    -            b.ReportAllocs()
    -            b.ResetTimer()
    -            for i := 0; i < b.N; i++ {
    -                handler.ServeHTTP(resps[i], req)
    -            }
    -        }
    -        
    -        var adversarialACRH []string
    -        
    -        func init() { // populates adversarialACRH
    -            n := int(math.Floor(math.Sqrt(http.DefaultMaxHeaderBytes)))
    -            commas := strings.Repeat(",", n)
    -            res := make([]string, n)
    -            for i := range res {
    -                res[i] = commas
    -            }
    -            adversarialACRH = res
    -        }
    -        
    -

    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/rs/cors to version 1.11.0 or higher.

    -

    References

    - +

    MPL-2.0 license


    -

    Insertion of Sensitive Information into Log File

    +

    MPL-2.0 license

    @@ -3586,21 +3535,18 @@

    Insertion of Sensitive Information into Log File


      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • Package Manager: golang
    • - Vulnerable module: + 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 + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/hashicorp/go-retryablehttp@0.7.0
    @@ -3615,7 +3561,7 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/hashicorp/go-retryablehttp@0.7.0 @@ -3624,9 +3570,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + github.com/xanzy/go-gitlab@0.60.0 - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/hashicorp/go-retryablehttp@0.7.0 @@ -3635,9 +3581,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/xanzy/go-gitlab@0.91.1 + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.0 @@ -3646,11 +3594,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.0 @@ -3659,11 +3609,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/cmd@#f754726f03da + + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/hashicorp/go-retryablehttp@0.7.0 @@ -3672,11 +3624,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/api@#f754726f03da + + 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.4 + github.com/hashicorp/go-retryablehttp@0.7.0 @@ -3685,28 +3641,85 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/controller@#f754726f03da - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.0 + + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-cleanhttp +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/hashicorp/go-retryablehttp@0.7.0 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#f48567108f01 + github.com/hashicorp/go-retryablehttp@0.7.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + 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@#f48567108f01 + github.com/xanzy/go-gitlab@0.60.0 - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3715,13 +3728,26 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + 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@#f48567108f01 + 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.4 + github.com/hashicorp/go-retryablehttp@0.7.0 + + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3730,13 +3756,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + 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.4 + github.com/hashicorp/go-retryablehttp@0.7.0 + + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3745,15 +3773,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/cmd@#f754726f03da - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 - - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + 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.4 + github.com/hashicorp/go-retryablehttp@0.7.0 + + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3762,85 +3790,36 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/api@#f754726f03da - github.com/argoproj/notifications-engine/pkg/subscriptions@#f48567108f01 + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da - github.com/argoproj/notifications-engine/pkg/services@#f48567108f01 + 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.4 + github.com/hashicorp/go-retryablehttp@0.7.0 + + github.com/hashicorp/go-cleanhttp@0.5.2
    • -
    - -
    - -
    - -

    Overview

    -

    Affected versions of this package are vulnerable to Insertion of Sensitive Information into Log File due to not sanitizing urls when writing them to the log file. This could lead to an attacker writing sensitive HTTP basic auth credentials to the log file.

    -

    Remediation

    -

    Upgrade github.com/hashicorp/go-retryablehttp to version 0.7.7 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • -
    • - Package Manager: golang -
    • -
    • - Vulnerable module: - - github.com/Azure/azure-sdk-for-go/sdk/azidentity -
    • - -
    • 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 + github.com/argoproj/notifications-engine/pkg/controller@#f754726f03da + + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da + + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/Azure/azure-sdk-for-go/sdk/azidentity@1.1.0 + 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 @@ -3851,46 +3830,17 @@

      Detailed paths


      -

      Overview

      -

      github.com/Azure/azure-sdk-for-go/sdk/azidentity is a module that provides Microsoft Entra ID (formerly Azure Active Directory) token authentication support across the Azure SDK. It includes a set of TokenCredential implementations, which can be used with Azure SDK clients supporting token authentication.

      -

      Affected versions of this package are vulnerable to Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') in the authentication process. An attacker can elevate privileges by exploiting race conditions during the token validation steps. This is only exploitable if the application is configured to use multiple threads or processes for handling authentication requests.

      -

      Notes:

      -
        -
      1. An attacker who successfully exploited the vulnerability could elevate privileges and read any file on the file system with SYSTEM access permissions;

        -
      2. -
      3. An attacker who successfully exploits this vulnerability can only obtain read access to the system files by exploiting this vulnerability. The attacker cannot perform write or delete operations on the files;

        -
      4. -
      5. The vulnerability exists in the following credential types: DefaultAzureCredential and ManagedIdentityCredential;

        -
      6. -
      7. The vulnerability exists in the following credential types:

        -
      8. -
      -

      ManagedIdentityApplication (.NET)

      -

      ManagedIdentityApplication (Java)

      -

      ManagedIdentityApplication (Node.js)

      -

      Remediation

      -

      Upgrade github.com/Azure/azure-sdk-for-go/sdk/azidentity to version 1.6.0 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Template Injection

    +

    MPL-2.0 license

    @@ -3901,21 +3851,18 @@

    Template Injection

    • - Manifest file: /argo-cd ui/yarn.lock -
    • -
    • - Package Manager: npm + Package Manager: golang
    • - Vulnerable module: + Module: - dompurify + 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, redoc@2.0.0-rc.64 and others
    @@ -3927,11 +3874,9 @@

    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 - dompurify@2.3.6 + github.com/gosimple/slug@1.13.1 @@ -3942,25 +3887,12 @@

      Detailed paths


      -

      Overview

      -

      dompurify is a DOM-only XSS sanitizer for HTML, MathML and SVG.

      -

      Affected versions of this package are vulnerable to Template Injection in purify.js, due to inconsistencies in the parsing of XML and HTML tags. Executable code can be injected in HTML inside XML CDATA blocks.

      -

      PoC

      -
      <![CDATA[ ><img src onerror=alert(1)> ]]>
      -        
      -

      Remediation

      -

      Upgrade dompurify to version 2.4.9, 3.0.11 or higher.

      -

      References

      - +

      MPL-2.0 license


    diff --git a/docs/snyk/v2.7.14/ghcr.io_dexidp_dex_v2.37.0.html b/docs/snyk/v2.7.14/ghcr.io_dexidp_dex_v2.37.0.html new file mode 100644 index 0000000000000..57ebb7d952e52 --- /dev/null +++ b/docs/snyk/v2.7.14/ghcr.io_dexidp_dex_v2.37.0.html @@ -0,0 +1,2862 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:24:54 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 (gomodules)
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (gomodules)
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (gomodules)
    • +
    +
    + +
    +
    28 known vulnerabilities
    +
    79 vulnerable dependency paths
    +
    786 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    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

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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

    + + +
    + + + +
    +
    +

    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:3.18. + 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

    + + +
    + + + +
    +
    +

    Cross-site Scripting (XSS)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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

    + +
    + + + +
    +
    +

    CVE-2023-5363

    +
    + +
    + 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

    +

    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

    + + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.13.0-rc2/argocd-test.html b/docs/snyk/v2.7.14/haproxy_2.6.14-alpine.html similarity index 52% rename from docs/snyk/v2.13.0-rc2/argocd-test.html rename to docs/snyk/v2.7.14/haproxy_2.6.14-alpine.html index 339b8e739fba1..953bbbe0d1e05 100644 --- a/docs/snyk/v2.13.0-rc2/argocd-test.html +++ b/docs/snyk/v2.7.14/haproxy_2.6.14-alpine.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,54 +456,59 @@

    Snyk test report

    -

    September 22nd 2024, 12:21:26 am (UTC+00:00)

    +

    October 29th 2023, 12:24:59 am (UTC+00:00)

    - Scanned the following paths: + Scanned the following path:
      -
    • /argo-cd/argoproj/argo-cd/v2/go.mod (gomodules)
    • -
    • /argo-cd/ui/yarn.lock (yarn)
    • +
    • haproxy:2.6.14-alpine (apk)
    -
    2 known vulnerabilities
    -
    4 vulnerable dependency paths
    -
    2132 dependencies
    +
    1 known vulnerabilities
    +
    9 vulnerable dependency paths
    +
    18 dependencies
    - +
    + + + + + + + +
    Project docker-image|haproxy
    Path haproxy:2.6.14-alpine
    Package Manager apk
    +
    -
    -

    Regular Expression Denial of Service (ReDoS)

    +
    +

    CVE-2023-5363

    -
    - medium severity +
    + low severity

    • - Manifest file: /argo-cd ui/yarn.lock -
    • -
    • - Package Manager: npm + Package Manager: alpine:3.18
    • Vulnerable module: - path-to-regexp + openssl/libcrypto3
    • Introduced through: + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 - argo-cd-ui@1.0.0, react-router@4.3.1 and others
    @@ -515,180 +520,97 @@

    Detailed paths

    • Introduced through: - argo-cd-ui@1.0.0 + docker-image|haproxy@2.6.14-alpine - react-router@4.3.1 - - path-to-regexp@1.8.0 + openssl/libcrypto3@3.1.2-r0
    • Introduced through: - argo-cd-ui@1.0.0 - - react-router-dom@4.3.1 + docker-image|haproxy@2.6.14-alpine - react-router@4.3.1 + .haproxy-rundeps@20230809.001942 - path-to-regexp@1.8.0 + openssl/libcrypto3@3.1.2-r0
    • Introduced through: - argo-cd-ui@1.0.0 + docker-image|haproxy@2.6.14-alpine - argo-ui@1.0.0 + apk-tools/apk-tools@2.14.0-r2 - react-router-dom@4.3.1 + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine - react-router@4.3.1 + busybox/ssl_client@1.36.1-r2 - path-to-regexp@1.8.0 + openssl/libcrypto3@3.1.2-r0
    • -
    - -
    - -
    - -

    Overview

    -

    Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) when including multiple regular expression parameters in a single segment, which will produce the regular expression /^\/([^\/]+?)-([^\/]+?)\/?$/, if two parameters within a single segment are separated by a character other than a / or .. Poor performance will block the event loop and can lead to a DoS.

    -

    Note: - While the 8.0.0 release has completely eliminated the vulnerable functionality, prior versions that have received the patch to mitigate backtracking may still be vulnerable if custom regular expressions are used. So it is strongly recommended for regular expression input to be controlled to avoid malicious performance degradation in those versions. This behavior is enforced as of version 7.1.0 via the strict option, which returns an error if a dangerous regular expression is detected.

    -

    Workaround

    -

    This vulnerability can be avoided by using a custom regular expression for parameters after the first in a segment, which excludes - and /.

    -

    PoC

    -
    /a${'-a'.repeat(8_000)}/a
    -        
    -

    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 path-to-regexp to version 0.1.10, 1.9.0, 3.3.0, 6.3.0, 8.0.0 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • -
    • - Package Manager: golang -
    • -
    • - Vulnerable module: - - github.com/Azure/azure-sdk-for-go/sdk/azidentity -
    • - -
    • Introduced through: - - - github.com/argoproj/argo-cd/v2@0.0.0, github.com/Azure/kubelogin/pkg/token@0.0.20 and others -
    • -
    +
  • + 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 + + -

    Detailed paths

    +
  • +
  • + 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: - github.com/argoproj/argo-cd/v2@0.0.0 + docker-image|haproxy@2.6.14-alpine - github.com/Azure/kubelogin/pkg/token@0.0.20 + busybox/ssl_client@1.36.1-r2 - github.com/Azure/azure-sdk-for-go/sdk/azidentity@1.1.0 + openssl/libssl3@3.1.2-r0 @@ -699,41 +621,57 @@

      Detailed paths


      -

      Overview

      -

      github.com/Azure/azure-sdk-for-go/sdk/azidentity is a module that provides Microsoft Entra ID (formerly Azure Active Directory) token authentication support across the Azure SDK. It includes a set of TokenCredential implementations, which can be used with Azure SDK clients supporting token authentication.

      -

      Affected versions of this package are vulnerable to Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') in the authentication process. An attacker can elevate privileges by exploiting race conditions during the token validation steps. This is only exploitable if the application is configured to use multiple threads or processes for handling authentication requests.

      -

      Notes:

      -
        -
      1. An attacker who successfully exploited the vulnerability could elevate privileges and read any file on the file system with SYSTEM access permissions;

        -
      2. -
      3. An attacker who successfully exploits this vulnerability can only obtain read access to the system files by exploiting this vulnerability. The attacker cannot perform write or delete operations on the files;

        -
      4. -
      5. The vulnerability exists in the following credential types: DefaultAzureCredential and ManagedIdentityCredential;

        -
      6. -
      7. The vulnerability exists in the following credential types:

        -
      8. -
      -

      ManagedIdentityApplication (.NET)

      -

      ManagedIdentityApplication (Java)

      -

      ManagedIdentityApplication (Node.js)

      +

      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 github.com/Azure/azure-sdk-for-go/sdk/azidentity to version 1.6.0 or higher.

      +

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

      References


  • diff --git a/docs/snyk/v2.11.8/quay.io_argoproj_argocd_v2.11.8.html b/docs/snyk/v2.7.14/quay.io_argoproj_argocd_v2.7.14.html similarity index 67% rename from docs/snyk/v2.11.8/quay.io_argoproj_argocd_v2.11.8.html rename to docs/snyk/v2.7.14/quay.io_argoproj_argocd_v2.7.14.html index 0a77724bd2238..5b4ea7a6ff4d0 100644 --- a/docs/snyk/v2.11.8/quay.io_argoproj_argocd_v2.11.8.html +++ b/docs/snyk/v2.7.14/quay.io_argoproj_argocd_v2.7.14.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,23 +456,19 @@

    Snyk test report

    -

    September 22nd 2024, 12:26:39 am (UTC+00:00)

    +

    October 29th 2023, 12:25:22 am (UTC+00:00)

    Scanned the following paths:
      -
    • quay.io/argoproj/argocd:v2.11.8/argoproj/argocd/Dockerfile (deb)
    • -
    • quay.io/argoproj/argocd:v2.11.8/argoproj/argo-cd/v2//usr/local/bin/argocd (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.11.8//usr/local/bin/kustomize (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.11.8/helm/v3//usr/local/bin/helm (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.11.8/git-lfs/git-lfs//usr/bin/git-lfs (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.7.14/argoproj/argocd (deb)
    • quay.io/argoproj/argocd:v2.7.14/argoproj/argo-cd/v2 (gomodules)
    • quay.io/argoproj/argocd:v2.7.14/kustomize/kustomize/v5 (gomodules)
    • quay.io/argoproj/argocd:v2.7.14/helm/v3 (gomodules)
    • quay.io/argoproj/argocd:v2.7.14/git-lfs/git-lfs (gomodules)
    -
    27 known vulnerabilities
    -
    173 vulnerable dependency paths
    -
    2280 dependencies
    +
    41 known vulnerabilities
    +
    159 vulnerable dependency paths
    +
    2065 dependencies
    @@ -481,7 +477,163 @@

    Snyk test report

    -

    Allocation of Resources Without Limits or Throttling

    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/grpc +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/grpc@v1.51.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/grpc@v1.51.0 + + + +
    • +
    + +
    + +
    + +

    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 +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2/hpack +
    • + +
    • Introduced through: + + helm.sh/helm/v3@* and golang.org/x/net/http2/hpack@v0.5.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + helm.sh/helm/v3@* + + golang.org/x/net/http2/hpack@v0.5.0 + + + +
    • +
    + +
    + +
    + +

    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 golang.org/x/net/http2/hpack to version 0.7.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    @@ -492,8 +644,91 @@

    Allocation of Resources Without Limits or Throttling

  • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argo-cd/v2 /usr/local/bin/argocd + Package Manager: golang +
  • +
  • + Vulnerable module: + + golang.org/x/net/http2 +
  • + +
  • Introduced through: + + github.com/argoproj/argo-cd/v2@* and golang.org/x/net/http2@v0.11.0 +
  • + + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + golang.org/x/net/http2@v0.11.0 + + + +
    • +
    • + Introduced through: + helm.sh/helm/v3@* + + golang.org/x/net/http2@v0.5.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 +
    + +
    + +
    • Package Manager: golang
    • @@ -505,7 +740,7 @@

      Allocation of Resources Without Limits or Throttling

      Introduced through: - github.com/argoproj/argo-cd/v2@* and golang.org/x/net/http2@v0.19.0 + helm.sh/helm/v3@* and golang.org/x/net/http2@v0.5.0
    @@ -513,23 +748,496 @@

    Allocation of Resources Without Limits or Throttling

    -

    Detailed paths

    +

    Detailed paths

    + +
      +
    • + Introduced through: + helm.sh/helm/v3@* + + golang.org/x/net/http2@v0.5.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) 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 golang.org/x/net/http2 to version 0.7.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + glibc/libc-bin +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.7.14 and glibc/libc-bin@2.35-0ubuntu3.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + glibc/libc-bin@2.35-0ubuntu3.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + glibc/libc6@2.35-0ubuntu3.1 + + + +
    • +
    + +
    + +
    + +

    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.

    +

    A buffer overflow was discovered in the GNU C Library's dynamic loader ld.so while processing the GLIBC_TUNABLES environment variable. This issue could allow a local attacker to use maliciously crafted GLIBC_TUNABLES environment variables when launching binaries with SUID permission to execute code with elevated privileges.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 glibc to version 2.35-0ubuntu3.4 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Directory Traversal

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/cyphar/filepath-securejoin +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/cyphar/filepath-securejoin@v0.2.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/cyphar/filepath-securejoin@v0.2.3 + + + +
    • +
    • + 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

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + curl/libcurl3-gnutls +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.7.14, git@1:2.34.1-1ubuntu1.10 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    This flaw makes curl overflow a heap based buffer in the SOCKS5 proxy + handshake.

    +

    When curl is asked to pass along the host name to the SOCKS5 proxy to allow + that to resolve the address instead of it getting done by curl itself, the + maximum length that host name can be is 255 bytes.

    +

    If the host name is detected to be longer, curl switches to local name + resolving and instead passes on the resolved address only. Due to this bug, + the local variable that means "let the host resolve the name" could get the + wrong value during a slow SOCKS5 handshake, and contrary to the intention, + copy the too long host name to the target buffer instead of copying just the + resolved address there.

    +

    The target buffer being a heap based buffer, and the host name coming from the + URL that curl has been told to operate with.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 curl to version 7.81.0-1ubuntu1.14 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2020-22916

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + xz-utils/liblzma5 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.7.14 and xz-utils/liblzma5@5.2.5-2ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + 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.

    +

    ** DISPUTED ** 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

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + perl/perl-modules-5.34 +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.7.14, git@1:2.34.1-1ubuntu1.10 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + git@1:2.34.1-1ubuntu1.10 + + perl@5.34.0-3ubuntu1.2 + + perl/perl-modules-5.34@5.34.0-3ubuntu1.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + git@1:2.34.1-1ubuntu1.10 + + perl@5.34.0-3ubuntu1.2 + + perl/libperl5.34@5.34.0-3ubuntu1.2 + + perl/perl-modules-5.34@5.34.0-3ubuntu1.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + git@1:2.34.1-1ubuntu1.10 + + perl@5.34.0-3ubuntu1.2 + + perl/libperl5.34@5.34.0-3ubuntu1.2 + + -
        +
      • Introduced through: - github.com/argoproj/argo-cd/v2@* + docker-image|quay.io/argoproj/argocd@v2.7.14 - golang.org/x/net/http2@v0.19.0 + git@1:2.34.1-1ubuntu1.10 + + perl@5.34.0-3ubuntu1.2
      • Introduced through: - helm.sh/helm/v3@* + docker-image|quay.io/argoproj/argocd@v2.7.14 - golang.org/x/net/http2@v0.17.0 + perl/perl-base@5.34.0-3ubuntu1.2 @@ -540,28 +1248,28 @@

        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 Allocation of Resources Without Limits or Throttling when reading header data from CONTINUATION frames. As part of the HPACK flow, all incoming HEADERS and CONTINUATION frames are read even if their payloads exceed MaxHeaderBytes and will be discarded. An attacker can send excessive data over a connection to render it unresponsive.

        +

        NVD Description

        +

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

        +

        In Perl 5.34.0, function S_find_uninit_var in sv.c has a stack-based crash that can lead to remote code execution or local privilege escalation.

        Remediation

        -

        Upgrade golang.org/x/net/http2 to version 0.23.0 or higher.

        +

        There is no fixed version for Ubuntu:22.04 perl.

        References


    -

    CVE-2024-41996

    +

    CVE-2023-5363

    @@ -571,9 +1279,6 @@

    CVE-2024-41996


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • @@ -585,7 +1290,7 @@

      CVE-2024-41996

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and openssl/libssl3@3.0.2-0ubuntu1.18 + docker-image|quay.io/argoproj/argocd@v2.7.14 and openssl/libssl3@3.0.2-0ubuntu1.10
    @@ -598,113 +1303,113 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - openssl/libssl3@3.0.2-0ubuntu1.18 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - openssl/libssl3@3.0.2-0ubuntu1.18 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 libfido2/libfido2-1@1.10.0-1 - openssl/libssl3@3.0.2-0ubuntu1.18 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - openssh/openssh-client@1:8.9p1-3ubuntu0.10 + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - openssl/libssl3@3.0.2-0ubuntu1.18 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 ca-certificates@20230311ubuntu0.22.04.1 - openssl@3.0.2-0ubuntu1.18 + openssl@3.0.2-0ubuntu1.10 - openssl/libssl3@3.0.2-0ubuntu1.18 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - git@1:2.34.1-1ubuntu1.11 + git@1:2.34.1-1ubuntu1.10 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.17 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 - openssl/libssl3@3.0.2-0ubuntu1.18 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-2ubuntu2.2 + shadow/passwd@1:4.8.1-2ubuntu2.1 - pam/libpam-modules@1.4.0-11ubuntu2.4 + pam/libpam-modules@1.4.0-11ubuntu2.3 libnsl/libnsl2@1.3.0-2build2 libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.4 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - krb5/libkrb5-3@1.19.2-2ubuntu0.4 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 - openssl/libssl3@3.0.2-0ubuntu1.18 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - openssl@3.0.2-0ubuntu1.18 + openssl@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 ca-certificates@20230311ubuntu0.22.04.1 - openssl@3.0.2-0ubuntu1.18 + openssl@3.0.2-0ubuntu1.10 @@ -718,26 +1423,60 @@

      Detailed paths

      NVD Description

      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.

      -

      Validating the order of the public keys in the Diffie-Hellman Key Agreement Protocol, when an approved safe prime is used, allows remote attackers (from the client side) to trigger unnecessarily expensive server-side DHE modular-exponentiation calculations. The client may cause asymmetric resource consumption. The basic attack scenario is that the client must claim that it can only communicate with DHE, and the server must be configured to allow DHE and validate the order of the public key.

      +

      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

      -

      There is no fixed version for Ubuntu:22.04 openssl.

      +

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.12 or higher.

      References


    -

    Information Exposure

    +

    Out-of-bounds Read

    @@ -747,21 +1486,18 @@

    Information Exposure


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - libgcrypt20 + libx11/libx11-data
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and libgcrypt20@1.9.4-3ubuntu3 + docker-image|quay.io/argoproj/argocd@v2.7.14 and libx11/libx11-data@2:1.7.5-1ubuntu0.2
    @@ -774,150 +1510,304 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-data@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/dirmngr@2.2.27-3ubuntu2.1 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-data@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - gnupg2/gpg@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.7.14 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - apt@2.4.13 + libxext/libxext6@2:1.3.4-1build1 - apt/libapt-pkg6.0@2.4.13 - - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - apt@2.4.13 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gpgv@2.2.27-3ubuntu2.1 + libxmu/libxmuu1@2:1.1.3-3 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - gnupg2/gpg@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gpgconf@2.2.27-3ubuntu2.1 + xauth@1:1.1-1build2 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    A vulnerability was found in libX11 due to a boundary condition within the _XkbReadKeySyms() function. This flaw allows a local user to trigger an out-of-bounds read error and read the contents of memory on the system.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 libx11 to version 2:1.7.5-1ubuntu0.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Loop with Unreachable Exit Condition ('Infinite Loop')

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + libx11/libx11-data +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.7.14 and libx11/libx11-data@2:1.7.5-1ubuntu0.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + libx11/libx11-data@2:1.7.5-1ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-data@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + libxext/libxext6@2:1.3.4-1build1 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + libxmu/libxmuu1@2:1.1.3-3 - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + xauth@1:1.1-1build2 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    A vulnerability was found in libX11 due to an infinite loop within the PutSubImage() function. This flaw allows a local user to consume all available system resources and cause a denial of service condition.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 libx11 to version 2:1.7.5-1ubuntu0.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Integer Overflow or Wraparound

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + libx11/libx11-data +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.7.14 and libx11/libx11-data@2:1.7.5-1ubuntu0.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + libx11/libx11-data@2:1.7.5-1ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-data@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gpgsm@2.2.27-3ubuntu2.1 + libxext/libxext6@2:1.3.4-1build1 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - apt@2.4.13 + libxmu/libxmuu1@2:1.1.3-3 - apt/libapt-pkg6.0@2.4.13 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 - systemd/libsystemd0@249.11-0ubuntu3.12 + xauth@1:1.1-1build2 - libgcrypt20@1.9.4-3ubuntu3 + libx11/libx11-6@2:1.7.5-1ubuntu0.2 @@ -929,28 +1819,27 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream libgcrypt20 package and not the libgcrypt20 package as distributed by Ubuntu. +

      Note: Versions mentioned in the description apply only to the upstream libx11 package and not the libx11 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.

      +

      A vulnerability was found in libX11 due to an integer overflow within the XCreateImage() function. This flaw allows a local user to trigger an integer overflow and execute arbitrary code with elevated privileges.

      Remediation

      -

      There is no fixed version for Ubuntu:22.04 libgcrypt20.

      +

      Upgrade Ubuntu:22.04 libx11 to version 2:1.7.5-1ubuntu0.3 or higher.

      References


    -

    CVE-2024-26462

    +

    Access of Uninitialized Pointer

    @@ -960,9 +1849,6 @@

    CVE-2024-26462


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • @@ -974,7 +1860,7 @@

      CVE-2024-26462

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and krb5/libk5crypto3@1.19.2-2ubuntu0.4 + docker-image|quay.io/argoproj/argocd@v2.7.14 and krb5/libk5crypto3@1.19.2-2ubuntu0.2
    @@ -987,159 +1873,159 @@

    Detailed paths

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

      Detailed paths

      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.

      +

      lib/kadm5/kadm_rpc_xdr.c in MIT Kerberos 5 (aka krb5) before 1.20.2 and 1.21.x before 1.21.1 frees an uninitialized pointer. A remote authenticated user can trigger a kadmind crash. This occurs because _xdr_kadm5_principal_ent_rec does not validate the relationship between n_key_data and the key_data array count.

      Remediation

      There is no fixed version for Ubuntu:22.04 krb5.

      References

      + +
      + + + +
    +
    +

    Memory Leak

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + glibc/libc-bin +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.7.14 and glibc/libc-bin@2.35-0ubuntu3.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + glibc/libc-bin@2.35-0ubuntu3.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + glibc/libc6@2.35-0ubuntu3.1 + + + +
    • +
    + +
    + +
    + +

    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.

    +

    A flaw was found in the GNU C Library. A recent fix for CVE-2023-4806 introduced the potential for a memory leak, which may result in an application crash.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 glibc.

    +

    References

    +
    -

    Denial of Service (DoS)

    +

    MPL-2.0 license

    @@ -1181,21 +2154,18 @@

    Denial of Service (DoS)


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argo-cd/v2 /usr/local/bin/argocd -
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/rs/cors + github.com/r3labs/diff
    • Introduced through: - github.com/argoproj/argo-cd/v2@* and github.com/rs/cors@v1.9.0 + github.com/argoproj/argo-cd/v2@* and github.com/r3labs/diff@v1.1.0
    @@ -1210,7 +2180,7 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@* - github.com/rs/cors@v1.9.0 + github.com/r3labs/diff@v1.1.0 @@ -1221,67 +2191,17 @@

    Detailed paths


    -

    Overview

    -

    Affected versions of this package are vulnerable to Denial of Service (DoS) through the processing of malicious preflight requests that include a Access-Control-Request-Headers header with excessive commas. An attacker can induce excessive memory consumption and potentially crash the server by sending specially crafted requests.

    -

    PoC

    -
    
    -        func BenchmarkPreflightAdversarialACRH(b *testing.B) {
    -            resps := makeFakeResponses(b.N)
    -            req, _ := http.NewRequest(http.MethodOptions, dummyEndpoint, nil)
    -            req.Header.Add(headerOrigin, dummyOrigin)
    -            req.Header.Add(headerACRM, http.MethodGet)
    -            req.Header[headerACRH] = adversarialACRH
    -            handler := Default().Handler(testHandler)
    -        
    -            b.ReportAllocs()
    -            b.ResetTimer()
    -            for i := 0; i < b.N; i++ {
    -                handler.ServeHTTP(resps[i], req)
    -            }
    -        }
    -        
    -        var adversarialACRH []string
    -        
    -        func init() { // populates adversarialACRH
    -            n := int(math.Floor(math.Sqrt(http.DefaultMaxHeaderBytes)))
    -            commas := strings.Repeat(",", n)
    -            res := make([]string, n)
    -            for i := range res {
    -                res[i] = commas
    -            }
    -            adversarialACRH = res
    -        }
    -        
    -

    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/rs/cors to version 1.11.0 or higher.

    -

    References

    - +

    MPL-2.0 license


    -

    Insertion of Sensitive Information into Log File

    +

    MPL-2.0 license

    @@ -1291,21 +2211,18 @@

    Insertion of Sensitive Information into Log File


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argo-cd/v2 /usr/local/bin/argocd -
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/hashicorp/go-retryablehttp + github.com/hashicorp/go-version
    • Introduced through: - github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-retryablehttp@v0.7.4 + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-version@v1.2.1
    @@ -1320,7 +2237,7 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@* - github.com/hashicorp/go-retryablehttp@v0.7.4 + github.com/hashicorp/go-version@v1.2.1 @@ -1331,25 +2248,17 @@

    Detailed paths


    -

    Overview

    -

    Affected versions of this package are vulnerable to Insertion of Sensitive Information into Log File due to not sanitizing urls when writing them to the log file. This could lead to an attacker writing sensitive HTTP basic auth credentials to the log file.

    -

    Remediation

    -

    Upgrade github.com/hashicorp/go-retryablehttp to version 0.7.7 or higher.

    -

    References

    - +

    MPL-2.0 license


    -

    CVE-2023-4039

    +

    MPL-2.0 license

    @@ -1360,20 +2269,17 @@

    CVE-2023-4039

    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • - Vulnerable module: + Module: - gcc-12/libstdc++6 + github.com/hashicorp/go-retryablehttp
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-retryablehttp@v0.7.0
    @@ -1386,51 +2292,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - apt@2.4.13 - - gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - apt@2.4.13 - - apt/libapt-pkg6.0@2.4.13 - - gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - gcc-12/gcc-12-base@12.3.0-1ubuntu1~22.04 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + github.com/argoproj/argo-cd/v2@* - gcc-12/libgcc-s1@12.3.0-1ubuntu1~22.04 + github.com/hashicorp/go-retryablehttp@v0.7.0 @@ -1441,40 +2305,17 @@

      Detailed paths


      -

      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.

      -

      DISPUTEDA failure in the -fstack-protector feature in GCC-based toolchains - that target AArch64 allows an attacker to exploit an existing buffer - overflow in dynamically-sized local variables in your application - without this being detected. This stack-protector failure only applies - to C99-style dynamically-sized local variables or those created using - alloca(). The stack-protector operates as intended for statically-sized - local variables.

      -

      The default behavior when the stack-protector - detects an overflow is to terminate your application, resulting in - controlled loss of availability. An attacker who can exploit a buffer - overflow without triggering the stack-protector might be able to change - program flow control to cause an uncontrolled loss of availability or to - go further and affect confidentiality or integrity. NOTE: The GCC project argues that this is a missed hardening bug and not a vulnerability by itself.

      -

      Remediation

      -

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

      -

      References

      - +

      MPL-2.0 license


    -

    Integer Overflow or Wraparound

    +

    MPL-2.0 license

    @@ -1485,21 +2326,18 @@

    Integer Overflow or Wraparound

    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • - Vulnerable module: + Module: - expat/libexpat1 + 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@v2.11.8, git@1:2.34.1-1ubuntu1.11 and others
    @@ -1511,11 +2349,9 @@

    Detailed paths

    -

    XML External Entity (XXE) Injection

    +

    MPL-2.0 license

    @@ -1558,21 +2383,18 @@

    XML External Entity (XXE) Injection

    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • - Vulnerable module: + Module: - expat/libexpat1 + 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@v2.11.8, git@1:2.34.1-1ubuntu1.11 and others
    @@ -1584,11 +2406,9 @@

    Detailed paths

    -

    Integer Overflow or Wraparound

    +

    Denial of Service (DoS)

    @@ -1631,21 +2440,18 @@

    Integer Overflow or Wraparound

    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:22.04 + Package Manager: golang
    • Vulnerable module: - expat/libexpat1 + 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@v2.11.8, git@1:2.34.1-1ubuntu1.11 and others
    @@ -1657,11 +2463,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - git@1:2.34.1-1ubuntu1.11 + helm.sh/helm/v3@* - expat/libexpat1@2.4.7-1ubuntu0.3 + github.com/docker/distribution/registry/api/v2@v2.8.1+incompatible @@ -1672,53 +2476,48 @@

      Detailed paths


      -

      NVD Description

      -

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

      -

      An issue was discovered in libexpat before 2.6.3. nextScaffoldPart in xmlparse.c can have an integer overflow for m_groupSize on 32-bit platforms (where UINT_MAX equals SIZE_MAX).

      +

      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:22.04 expat to version 2.4.7-1ubuntu0.4 or higher.

      +

      Upgrade github.com/docker/distribution/registry/api/v2 to version 2.8.2-beta.1 or higher.

      References


    -
    -

    CVE-2024-8096

    +
    +

    CVE-2022-46908

    -
    - medium severity +
    + low severity

      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - curl/libcurl3-gnutls + sqlite3/libsqlite3-0
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8, git@1:2.34.1-1ubuntu1.11 and others + docker-image|quay.io/argoproj/argocd@v2.7.14, gnupg2/gpg@2.2.27-3ubuntu2.1 and others
    @@ -1730,11 +2529,11 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - git@1:2.34.1-1ubuntu1.11 + gnupg2/gpg@2.2.27-3ubuntu2.1 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.17 + sqlite3/libsqlite3-0@3.37.2-2ubuntu0.1 @@ -1746,28 +2545,29 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream curl package and not the curl package as distributed by Ubuntu. +

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

      -

      When curl is told to use the Certificate Status Request TLS extension, often referred to as OCSP stapling, to verify that the server certificate is valid, it might fail to detect some OCSP problems and instead wrongly consider the response as fine. If the returned status reports another error than 'revoked' (like for example 'unauthorized') it is not treated as a bad certficate.

      +

      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

      -

      Upgrade Ubuntu:22.04 curl to version 7.81.0-1ubuntu1.18 or higher.

      +

      There is no fixed version for Ubuntu:22.04 sqlite3.

      References


    -

    CVE-2023-7008

    +

    Arbitrary Code Injection

    @@ -1777,21 +2577,18 @@

    CVE-2023-7008


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - systemd/libsystemd0 + shadow/passwd
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and systemd/libsystemd0@249.11-0ubuntu3.12 + docker-image|quay.io/argoproj/argocd@v2.7.14 and shadow/passwd@1:4.8.1-2ubuntu2.1
    @@ -1804,110 +2601,40 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - systemd/libsystemd0@249.11-0ubuntu3.12 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - apt@2.4.13 - - systemd/libsystemd0@249.11-0ubuntu3.12 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - procps/libprocps8@2:3.3.17-6ubuntu2.1 - - systemd/libsystemd0@249.11-0ubuntu3.12 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - util-linux@2.37.2-4ubuntu3.4 - - systemd/libsystemd0@249.11-0ubuntu3.12 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - util-linux/bsdutils@1:2.37.2-4ubuntu3.4 - - systemd/libsystemd0@249.11-0ubuntu3.12 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - apt@2.4.13 - - apt/libapt-pkg6.0@2.4.13 - - systemd/libsystemd0@249.11-0ubuntu3.12 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - systemd/libudev1@249.11-0ubuntu3.12 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - libfido2/libfido2-1@1.10.0-1 + adduser@3.118ubuntu5 - systemd/libudev1@249.11-0ubuntu3.12 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - util-linux@2.37.2-4ubuntu3.4 + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - systemd/libudev1@249.11-0ubuntu3.12 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - apt@2.4.13 + docker-image|quay.io/argoproj/argocd@v2.7.14 - apt/libapt-pkg6.0@2.4.13 - - systemd/libudev1@249.11-0ubuntu3.12 + shadow/login@1:4.8.1-2ubuntu2.1 @@ -1919,33 +2646,29 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream systemd package and not the systemd package as distributed by Ubuntu. +

      Note: Versions mentioned in the description apply only to the upstream shadow package and not the shadow package as distributed by Ubuntu:22.04. 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.

      +

      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 systemd.

      +

      There is no fixed version for Ubuntu:22.04 shadow.

      References


    -

    Arbitrary Code Injection

    +

    Out-of-bounds Write

    @@ -1955,21 +2678,18 @@

    Arbitrary Code Injection


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - shadow/passwd + procps/libprocps8
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and shadow/passwd@1:4.8.1-2ubuntu2.2 + docker-image|quay.io/argoproj/argocd@v2.7.14 and procps/libprocps8@2:3.3.17-6ubuntu2
    @@ -1982,40 +2702,29 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - shadow/passwd@1:4.8.1-2ubuntu2.2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - adduser@3.118ubuntu5 + docker-image|quay.io/argoproj/argocd@v2.7.14 - shadow/passwd@1:4.8.1-2ubuntu2.2 + procps/libprocps8@2:3.3.17-6ubuntu2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - openssh/openssh-client@1:8.9p1-3ubuntu0.10 + procps@2:3.3.17-6ubuntu2 - shadow/passwd@1:4.8.1-2ubuntu2.2 + procps/libprocps8@2:3.3.17-6ubuntu2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - shadow/login@1:4.8.1-2ubuntu2.2 + procps@2:3.3.17-6ubuntu2 @@ -2027,24 +2736,22 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream shadow package and not the shadow package as distributed by Ubuntu. +

      Note: Versions mentioned in the description apply only to the upstream procps package and not the procps 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.

      +

      Under some circumstances, this weakness allows a user who has access to run the “ps” utility on a machine, the ability to write almost unlimited amounts of unfiltered data into the process heap.

      Remediation

      -

      There is no fixed version for Ubuntu:22.04 shadow.

      +

      There is no fixed version for Ubuntu:22.04 procps.

      References


    @@ -2059,9 +2766,6 @@

    Uncontrolled Recursion


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • @@ -2073,7 +2777,7 @@

      Uncontrolled Recursion

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + docker-image|quay.io/argoproj/argocd@v2.7.14 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1
    @@ -2086,7 +2790,7 @@

    Detailed paths

    -

    CVE-2023-50495

    +

    Improper Authentication

    @@ -2292,21 +2989,18 @@

    CVE-2023-50495


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - ncurses/libtinfo6 + openssl/libssl3
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and ncurses/libtinfo6@6.3-2ubuntu0.1 + docker-image|quay.io/argoproj/argocd@v2.7.14 and openssl/libssl3@3.0.2-0ubuntu1.10
    @@ -2316,203 +3010,116 @@

    CVE-2023-50495

    Detailed paths

    -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - ncurses/libtinfo6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - bash@5.1-6ubuntu1.1 - - ncurses/libtinfo6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - ncurses/libncursesw6@6.3-2ubuntu0.1 - - ncurses/libtinfo6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - less@590-1ubuntu0.22.04.3 - - ncurses/libtinfo6@6.3-2ubuntu0.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - libedit/libedit2@3.1-20210910-1build1 - - ncurses/libtinfo6@6.3-2ubuntu0.1 - - - -
    • +
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - ncurses/libncurses6@6.3-2ubuntu0.1 + docker-image|quay.io/argoproj/argocd@v2.7.14 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - ncurses/ncurses-bin@6.3-2ubuntu0.1 + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - procps@2:3.3.17-6ubuntu2.1 + libfido2/libfido2-1@1.10.0-1 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - util-linux@2.37.2-4ubuntu3.4 + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 + ca-certificates@20230311ubuntu0.22.04.1 - readline/libreadline8@8.1.2-1 + openssl@3.0.2-0ubuntu1.10 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + git@1:2.34.1-1ubuntu1.10 - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 - pinentry/pinentry-curses@1.1.1-1build2 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - ncurses/libncursesw6@6.3-2ubuntu0.1 - - - -
      • -
      • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + adduser@3.118ubuntu5 - procps@2:3.3.17-6ubuntu2.1 + shadow/passwd@1:4.8.1-2ubuntu2.1 - ncurses/libncursesw6@6.3-2ubuntu0.1 - - - -
      • -
      • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + pam/libpam-modules@1.4.0-11ubuntu2.3 - gnupg2/gnupg@2.2.27-3ubuntu2.1 + libnsl/libnsl2@1.3.0-2build2 - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - pinentry/pinentry-curses@1.1.1-1build2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - ncurses/libncursesw6@6.3-2ubuntu0.1 - - - -
      • -
      • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 - ncurses/libncurses6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - procps@2:3.3.17-6ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.7.14 - ncurses/libncurses6@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - ncurses/ncurses-base@6.3-2ubuntu0.1 - - - -
      • -
      • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + ca-certificates@20230311ubuntu0.22.04.1 - ncurses/ncurses-bin@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10 @@ -2524,29 +3131,47 @@

        Detailed paths


        NVD Description

        -

        Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. +

        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.

        -

        NCurse v6.4-20230418 was discovered to contain a segmentation fault via the component _nc_wrap_entry().

        +

        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

        -

        There is no fixed version for Ubuntu:22.04 ncurses.

        +

        Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.12 or higher.

        References


    -

    CVE-2023-45918

    +

    Inefficient Regular Expression Complexity

    @@ -2556,21 +3181,18 @@

    CVE-2023-45918


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - ncurses/libtinfo6 + openssl/libssl3
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and ncurses/libtinfo6@6.3-2ubuntu0.1 + docker-image|quay.io/argoproj/argocd@v2.7.14 and openssl/libssl3@3.0.2-0ubuntu1.10
    @@ -2583,200 +3205,315 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - bash@5.1-6ubuntu1.1 + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - ncurses/libncursesw6@6.3-2ubuntu0.1 + libfido2/libfido2-1@1.10.0-1 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - less@590-1ubuntu0.22.04.3 + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 + + ca-certificates@20230311ubuntu0.22.04.1 - libedit/libedit2@3.1-20210910-1build1 + openssl@3.0.2-0ubuntu1.10 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 + + git@1:2.34.1-1ubuntu1.10 - ncurses/libncurses6@6.3-2ubuntu0.1 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 - ncurses/libtinfo6@6.3-2ubuntu0.1 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 + + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - ncurses/ncurses-bin@6.3-2ubuntu0.1 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - procps@2:3.3.17-6ubuntu2.1 - - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - util-linux@2.37.2-4ubuntu3.4 + ca-certificates@20230311ubuntu0.22.04.1 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10
    • +
    + +
    + +
    + +

    NVD Description

    +

    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: 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 Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.12 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Excessive Iteration

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + openssl/libssl3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.7.14 and openssl/libssl3@3.0.2-0ubuntu1.10 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - readline/libreadline8@8.1.2-1 + docker-image|quay.io/argoproj/argocd@v2.7.14 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - pinentry/pinentry-curses@1.1.1-1build2 + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 - ncurses/libtinfo6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 + + libfido2/libfido2-1@1.10.0-1 - ncurses/libncursesw6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - procps@2:3.3.17-6ubuntu2.1 + openssh/openssh-client@1:8.9p1-3ubuntu0.3 - ncurses/libncursesw6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.7.14 - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + ca-certificates@20230311ubuntu0.22.04.1 - pinentry/pinentry-curses@1.1.1-1build2 + openssl@3.0.2-0ubuntu1.10 - ncurses/libncursesw6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 - ncurses/libncurses6@6.3-2ubuntu0.1 + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 - procps@2:3.3.17-6ubuntu2.1 + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - ncurses/libncurses6@6.3-2ubuntu0.1 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 + + openssl/libssl3@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - ncurses/ncurses-base@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 + + ca-certificates@20230311ubuntu0.22.04.1 - ncurses/ncurses-bin@6.3-2ubuntu0.1 + openssl@3.0.2-0ubuntu1.10 @@ -2788,27 +3525,56 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. +

      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.

      -

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

      +

      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

      -

      There is no fixed version for Ubuntu:22.04 ncurses.

      +

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.12 or higher.

      References


    -

    Resource Exhaustion

    +

    CVE-2023-28531

    @@ -2818,21 +3584,18 @@

    Resource Exhaustion


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - libzstd/libzstd1 + openssh/openssh-client
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and libzstd/libzstd1@1.4.8+dfsg-3build1 + docker-image|quay.io/argoproj/argocd@v2.7.14 and openssh/openssh-client@1:8.9p1-3ubuntu0.3
    @@ -2845,9 +3608,9 @@

    Detailed paths

    -

    Integer Overflow or Wraparound

    +

    NULL Pointer Dereference

    @@ -2895,22 +3653,19 @@

    Integer Overflow or Wraparound


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - krb5/libk5crypto3 + openldap/libldap-2.5-0
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and krb5/libk5crypto3@1.19.2-2ubuntu0.4 + docker-image|quay.io/argoproj/argocd@v2.7.14, gnupg2/dirmngr@2.2.27-3ubuntu2.1 and others
    @@ -2922,159 +3677,33 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - krb5/libk5crypto3@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - 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.4 - - krb5/libk5crypto3@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - 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.4 - - krb5/libkrb5-3@1.19.2-2ubuntu0.4 - - krb5/libk5crypto3@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - krb5/libkrb5-3@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - 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.4 - - krb5/libkrb5-3@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.17 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.17 - - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.4 + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.2 + docker-image|quay.io/argoproj/argocd@v2.7.14 - pam/libpam-modules@1.4.0-11ubuntu2.4 - - libnsl/libnsl2@1.3.0-2build2 + git@1:2.34.1-1ubuntu1.10 - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.4 + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - krb5/libkrb5support0@1.19.2-2ubuntu0.4 + openldap/libldap-common@2.5.16+dfsg-0ubuntu0.22.04.1 @@ -3086,30 +3715,34 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. +

      Note: Versions mentioned in the description apply only to the upstream openldap package and not the openldap package as distributed by Ubuntu:22.04. 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.

      +

      A vulnerability was found in openldap. This security flaw causes a null pointer dereference in ber_memalloc_x() function.

      Remediation

      -

      There is no fixed version for Ubuntu:22.04 krb5.

      +

      There is no fixed version for Ubuntu:22.04 openldap.

      References


    -

    CVE-2024-26461

    +

    Resource Exhaustion

    @@ -3119,21 +3752,18 @@

    CVE-2024-26461


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - krb5/libk5crypto3 + libzstd/libzstd1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and krb5/libk5crypto3@1.19.2-2ubuntu0.4 + docker-image|quay.io/argoproj/argocd@v2.7.14 and libzstd/libzstd1@1.4.8+dfsg-3build1
    @@ -3146,159 +3776,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - krb5/libk5crypto3@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - 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.4 - - krb5/libk5crypto3@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - 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.4 - - krb5/libkrb5-3@1.19.2-2ubuntu0.4 - - krb5/libk5crypto3@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - krb5/libkrb5-3@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - 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.4 - - krb5/libkrb5-3@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - openssh/openssh-client@1:8.9p1-3ubuntu0.10 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.17 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - git@1:2.34.1-1ubuntu1.11 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.17 - - libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - - krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 - - 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.4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - krb5/libkrb5support0@1.19.2-2ubuntu0.4 + libzstd/libzstd1@1.4.8+dfsg-3build1 @@ -3310,27 +3790,30 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. +

      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.

      -

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

      +

      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 krb5.

      +

      There is no fixed version for Ubuntu:22.04 libzstd.

      References


    -

    CVE-2024-26458

    +

    Integer Overflow or Wraparound

    @@ -3340,9 +3823,6 @@

    CVE-2024-26458


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • @@ -3354,7 +3834,7 @@

      CVE-2024-26458

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and krb5/libk5crypto3@1.19.2-2ubuntu0.4 + docker-image|quay.io/argoproj/argocd@v2.7.14 and krb5/libk5crypto3@1.19.2-2ubuntu0.2
    @@ -3367,159 +3847,159 @@

    Detailed paths

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

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. +

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu:22.04. 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.

      +

      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


    @@ -3561,9 +4043,6 @@

    Out-of-bounds Write


      -
    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
    • Package Manager: ubuntu:22.04
    • @@ -3575,7 +4054,7 @@

      Out-of-bounds Write

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and gnupg2/gpgv@2.2.27-3ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.7.14 and gnupg2/gpgv@2.2.27-3ubuntu2.1
    @@ -3588,7 +4067,7 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gpgv@2.2.27-3ubuntu2.1 @@ -3597,9 +4076,9 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - apt@2.4.13 + apt@2.4.10 gnupg2/gpgv@2.2.27-3ubuntu2.1 @@ -3608,7 +4087,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3619,7 +4098,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/dirmngr@2.2.27-3ubuntu2.1 @@ -3630,7 +4109,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gpg@2.2.27-3ubuntu2.1 @@ -3641,7 +4120,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3654,7 +4133,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3667,7 +4146,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/dirmngr@2.2.27-3ubuntu2.1 @@ -3676,7 +4155,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3687,7 +4166,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3700,7 +4179,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 @@ -3709,7 +4188,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3720,7 +4199,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 @@ -3729,7 +4208,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3740,7 +4219,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gpg@2.2.27-3ubuntu2.1 @@ -3749,7 +4228,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3760,7 +4239,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3773,7 +4252,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3786,7 +4265,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gpg-agent@2.2.27-3ubuntu2.1 @@ -3795,7 +4274,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3806,7 +4285,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3819,7 +4298,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3832,7 +4311,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 @@ -3841,7 +4320,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3852,7 +4331,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 @@ -3861,7 +4340,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3872,7 +4351,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gpgsm@2.2.27-3ubuntu2.1 @@ -3881,7 +4360,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3892,7 +4371,7 @@

      Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -3906,20 +4385,20 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply only to the upstream gnupg2 package and not the gnupg2 package as distributed by Ubuntu. +

      Note: Versions mentioned in the description apply only to the upstream gnupg2 package and not the gnupg2 package as distributed by Ubuntu:22.04. 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


      @@ -3940,9 +4419,6 @@

      Allocation of Resources Without Limits or Throttling

        -
      • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
      • Package Manager: ubuntu:22.04
      • @@ -3954,7 +4430,7 @@

        Allocation of Resources Without Limits or Throttling

        Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and glibc/libc-bin@2.35-0ubuntu3.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 and glibc/libc-bin@2.35-0ubuntu3.1
      @@ -3967,18 +4443,18 @@

      Detailed paths

      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - glibc/libc-bin@2.35-0ubuntu3.8 + glibc/libc-bin@2.35-0ubuntu3.1
      • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - glibc/libc6@2.35-0ubuntu3.8 + glibc/libc6@2.35-0ubuntu3.1 @@ -3990,17 +4466,17 @@

        Detailed paths


        NVD Description

        -

        Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu. +

        Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu:22.04. 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


        @@ -4021,9 +4497,6 @@

        Improper Input Validation


          -
        • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
        • Package Manager: ubuntu:22.04
        • @@ -4036,7 +4509,7 @@

          Improper Input Validation

        • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8, git@1:2.34.1-1ubuntu1.11 and others + docker-image|quay.io/argoproj/argocd@v2.7.14, git@1:2.34.1-1ubuntu1.10 and others
        @@ -4048,31 +4521,31 @@

        Detailed paths

        • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - git@1:2.34.1-1ubuntu1.11 + git@1:2.34.1-1ubuntu1.10 - git/git-man@1:2.34.1-1ubuntu1.11 + git/git-man@1:2.34.1-1ubuntu1.10
        • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 - git@1:2.34.1-1ubuntu1.11 + git@1:2.34.1-1ubuntu1.10
        • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 + docker-image|quay.io/argoproj/argocd@v2.7.14 git-lfs@3.0.2-1ubuntu0.2 - git@1:2.34.1-1ubuntu1.11 + git@1:2.34.1-1ubuntu1.10 @@ -4084,15 +4557,15 @@

          Detailed paths


          NVD Description

          -

          Note: Versions mentioned in the description apply only to the upstream git package and not the git package as distributed by Ubuntu. +

          Note: Versions mentioned in the description apply only to the upstream git package and not the git package as distributed by Ubuntu:22.04. 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

          @@ -4114,9 +4587,6 @@

          Uncontrolled Recursion


            -
          • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile -
          • Package Manager: ubuntu:22.04
          • @@ -4128,7 +4598,7 @@

            Uncontrolled Recursion

          • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + docker-image|quay.io/argoproj/argocd@v2.7.14 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04
          @@ -4141,7 +4611,7 @@

          Detailed paths

    -

    Improper Input Validation

    +

    CVE-2023-38546

    @@ -4230,8 +4699,89 @@

    Improper Input Validation

    • - Manifest file: quay.io/argoproj/argocd:v2.11.8/argoproj/argocd Dockerfile + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + curl/libcurl3-gnutls +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.7.14, git@1:2.34.1-1ubuntu1.10 and others
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.13 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    This flaw allows an attacker to insert cookies at will into a running program + using libcurl, if the specific series of conditions are met.

    +

    libcurl performs transfers. In its API, an application creates "easy handles" + that are the individual handles for single transfers.

    +

    libcurl provides a function call that duplicates en easy handle called + curl_easy_duphandle.

    +

    If a transfer has cookies enabled when the handle is duplicated, the + cookie-enable state is also cloned - but without cloning the actual + cookies. If the source handle did not read any cookies from a specific file on + disk, the cloned version of the handle would instead store the file name as + none (using the four ASCII letters, no quotes).

    +

    Subsequent use of the cloned handle that does not explicitly set a source to + load cookies from would then inadvertently load cookies from a file named + none - if such a file exists and is readable in the current directory of the + program using libcurl. And if using the correct file format of course.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 curl to version 7.81.0-1ubuntu1.14 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Input Validation

    +
    + +
    + low severity +
    + +
    + +
    • Package Manager: ubuntu:22.04
    • @@ -4243,7 +4793,7 @@

      Improper Input Validation

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.11.8 and coreutils@8.32-4.1ubuntu1.2 + docker-image|quay.io/argoproj/argocd@v2.7.14 and coreutils@8.32-4.1ubuntu1
    @@ -4256,9 +4806,9 @@

    Detailed paths

    +
    +

    Out-of-bounds Write

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + bash +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.7.14 and bash@5.1-6ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.14 + + 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:22.04. + 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

    +

    There is no fixed version for Ubuntu:22.04 bash.

    +

    References

    + + +
    + + + +
    diff --git a/docs/snyk/v2.12.3/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html b/docs/snyk/v2.7.14/redis_7.0.11-alpine.html similarity index 64% rename from docs/snyk/v2.12.3/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html rename to docs/snyk/v2.7.14/redis_7.0.11-alpine.html index bed01faa336f7..bb89e05940bc5 100644 --- a/docs/snyk/v2.12.3/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html +++ b/docs/snyk/v2.7.14/redis_7.0.11-alpine.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,18 +456,18 @@

    Snyk test report

    -

    September 22nd 2024, 12:24:09 am (UTC+00:00)

    +

    October 29th 2023, 12:25:30 am (UTC+00:00)

    Scanned the following path:
      -
    • public.ecr.aws/docker/library/haproxy:2.6.17-alpine/docker/library/haproxy (apk)
    • +
    • redis:7.0.11-alpine (apk)
    5 known vulnerabilities
    -
    42 vulnerable dependency paths
    +
    41 vulnerable dependency paths
    18 dependencies
    @@ -476,8 +476,8 @@

    Snyk test report

    - - + + @@ -485,19 +485,19 @@

    Snyk test report

    -
    -

    Use After Free

    +
    +

    Out-of-bounds Write

    -
    - medium severity +
    + critical severity

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -507,7 +507,7 @@

      Use After Free

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and busybox/busybox@1.36.1-r28 + docker-image|redis@7.0.11-alpine and busybox/busybox@1.36.1-r0
    @@ -520,62 +520,51 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/busybox@1.36.1-r28 + busybox/busybox@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 + alpine-baselayout/alpine-baselayout@3.4.3-r1 - busybox/busybox-binsh@1.36.1-r28 - - busybox/busybox@1.36.1-r28 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + busybox/busybox-binsh@1.36.1-r0 - busybox/busybox-binsh@1.36.1-r28 + busybox/busybox@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 - - busybox/busybox-binsh@1.36.1-r28 + busybox/busybox-binsh@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + alpine-baselayout/alpine-baselayout@3.4.3-r1 - busybox/busybox-binsh@1.36.1-r28 + busybox/busybox-binsh@1.36.1-r0
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 @@ -588,24 +577,24 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      A use-after-free vulnerability in BusyBox v.1.36.1 allows attackers to cause a denial of service via a crafted awk pattern in the awk.c evaluate function.

      + 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.20 busybox to version 1.36.1-r29 or higher.

      +

      Upgrade Alpine:3.18 busybox to version 1.36.1-r1 or higher.

      References


    -

    Use After Free

    +

    Improper Authentication

    @@ -616,17 +605,17 @@

    Use After Free

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: - busybox/busybox + openssl/libcrypto3
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and busybox/busybox@1.36.1-r28 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -639,62 +628,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine - busybox/busybox@1.36.1-r28 + .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|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 + .redis-rundeps@20230614.215749 - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1 - busybox/busybox@1.36.1-r28 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - alpine-baselayout/alpine-baselayout@3.6.5-r0 + .redis-rundeps@20230614.215749 - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + apk-tools/apk-tools@2.14.0-r2 - busybox/busybox-binsh@1.36.1-r28 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 - busybox/ssl_client@1.36.1-r28 + openssl/libssl3@3.1.1-r1 @@ -706,36 +730,57 @@

      Detailed paths


      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.20 relevant fixed versions and status.

      -

      A use-after-free vulnerability was discovered in BusyBox v.1.36.1 via a crafted awk pattern in the awk.c copyvar function.

      +

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine:3.18. + 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.20 busybox to version 1.36.1-r29 or higher.

      +

      Upgrade Alpine:3.18 openssl to version 3.1.1-r2 or higher.

      References


    -
    -

    CVE-2024-4741

    +
    +

    Inefficient Regular Expression Complexity

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -745,7 +790,7 @@

      CVE-2024-4741

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and openssl/libcrypto3@3.3.0-r2 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -758,108 +803,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - openssl/libcrypto3@3.3.0-r2 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - .haproxy-rundeps@20240524.005458 + docker-image|redis@7.0.11-alpine - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + .redis-rundeps@20230614.215749 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 @@ -871,30 +905,67 @@

      Detailed paths


      NVD Description

      -

      This vulnerability has not been analyzed by NVD yet.

      +

      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.20 openssl to version 3.3.0-r3 or higher.

      +

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

      +

      References

      +
    -
    -

    CVE-2024-5535

    +
    +

    Excessive Iteration

    -
    - low severity +
    + medium severity

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -904,7 +975,7 @@

      CVE-2024-5535

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and openssl/libcrypto3@3.3.0-r2 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -917,108 +988,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 - - openssl/libcrypto3@3.3.0-r2 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + .redis-rundeps@20230614.215749 - .haproxy-rundeps@20240524.005458 + openssl/libssl3@3.1.1-r1 - openssl/libssl3@3.3.0-r2 - - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 @@ -1031,87 +1091,54 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      Issue summary: Calling the OpenSSL API function SSL_select_next_proto with an - empty supported client protocols buffer may cause a crash or memory contents to - be sent to the peer.

      -

      Impact summary: A buffer overread can have a range of potential consequences - such as unexpected application beahviour or a crash. In particular this issue - could result in up to 255 bytes of arbitrary private data from memory being sent - to the peer leading to a loss of confidentiality. However, only applications - that directly call the SSL_select_next_proto function with a 0 length list of - supported client protocols are affected by this issue. This would normally never - be a valid scenario and is typically not under attacker control but may occur by - accident in the case of a configuration or programming error in the calling - application.

      -

      The OpenSSL API function SSL_select_next_proto is typically used by TLS - applications that support ALPN (Application Layer Protocol Negotiation) or NPN - (Next Protocol Negotiation). NPN is older, was never standardised and - is deprecated in favour of ALPN. We believe that ALPN is significantly more - widely deployed than NPN. The SSL_select_next_proto function accepts a list of - protocols from the server and a list of protocols from the client and returns - the first protocol that appears in the server list that also appears in the - client list. In the case of no overlap between the two lists it returns the - first item in the client list. In either case it will signal whether an overlap - between the two lists was found. In the case where SSL_select_next_proto is - called with a zero length client list it fails to notice this condition and - returns the memory immediately following the client list pointer (and reports - that there was no overlap in the lists).

      -

      This function is typically called from a server side application callback for - ALPN or a client side application callback for NPN. In the case of ALPN the list - of protocols supplied by the client is guaranteed by libssl to never be zero in - length. The list of server protocols comes from the application and should never - normally be expected to be of zero length. In this case if the - SSL_select_next_proto function has been called as expected (with the list - supplied by the client passed in the client/client_len parameters), then the - application will not be vulnerable to this issue. If the application has - accidentally been configured with a zero length server list, and has - accidentally passed that zero length server list in the client/client_len - parameters, and has additionally failed to correctly handle a "no overlap" - response (which would normally result in a handshake failure in ALPN) then it - will be vulnerable to this problem.

      -

      In the case of NPN, the protocol permits the client to opportunistically select - a protocol when there is no overlap. OpenSSL returns the first client protocol - in the no overlap case in support of this. The list of client protocols comes - from the application and should never normally be expected to be of zero length. - However if the SSL_select_next_proto function is accidentally called with a - client_len of 0 then an invalid memory pointer will be returned instead. If the - application uses this output as the opportunistic protocol then the loss of - confidentiality will occur.

      -

      This issue has been assessed as Low severity because applications are most - likely to be vulnerable if they are using NPN instead of ALPN - but NPN is not - widely used. It also requires an application configuration or programming error. - Finally, this issue would not typically be under attacker control making active - exploitation unlikely.

      -

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

      -

      Due to the low severity of this issue we are not issuing new releases of - OpenSSL at this time. The fix will be included in the next releases when they - become available.

      + 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.20 openssl to version 3.3.1-r1 or higher.

      +

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

      References


    -

    CVE-2024-6119

    +

    CVE-2023-5363

    @@ -1122,7 +1149,7 @@

    CVE-2024-6119

    • - Package Manager: alpine:3.20 + Package Manager: alpine:3.18
    • Vulnerable module: @@ -1132,7 +1159,7 @@

      CVE-2024-6119

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine and openssl/libcrypto3@3.3.0-r2 + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1
    @@ -1145,108 +1172,97 @@

    Detailed paths

    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - openssl/libcrypto3@3.3.0-r2 - - - -
    • -
    • - Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine - - .haproxy-rundeps@20240524.005458 + docker-image|redis@7.0.11-alpine - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + .redis-rundeps@20230614.215749 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + apk-tools/apk-tools@2.14.0-r2 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - ca-certificates/ca-certificates@20240226-r0 + busybox/ssl_client@1.36.1-r0 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 - openssl/libcrypto3@3.3.0-r2 + openssl/libcrypto3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - .haproxy-rundeps@20240524.005458 + .redis-rundeps@20230614.215749 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - apk-tools/apk-tools@2.14.4-r0 + apk-tools/apk-tools@2.14.0-r2 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1
    • Introduced through: - docker-image|public.ecr.aws/docker/library/haproxy@2.6.17-alpine + docker-image|redis@7.0.11-alpine - busybox/ssl_client@1.36.1-r28 + busybox/ssl_client@1.36.1-r0 - openssl/libssl3@3.3.0-r2 + openssl/libssl3@3.1.1-r1 @@ -1259,41 +1275,55 @@

      Detailed paths

      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.20 relevant fixed versions and status.

      -

      Issue summary: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

      -

      Impact summary: Abnormal termination of an application can a cause a denial of - service.

      -

      Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

      -

      Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

      -

      TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

      -

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

      + 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.20 openssl to version 3.3.2-r0 or higher.

      +

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

      References


    diff --git a/docs/snyk/v2.8.5/argocd-iac-install.html b/docs/snyk/v2.8.5/argocd-iac-install.html new file mode 100644 index 0000000000000..3d4dd5fd52b45 --- /dev/null +++ b/docs/snyk/v2.8.5/argocd-iac-install.html @@ -0,0 +1,2679 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:24:06 am (UTC+00:00)

    +
    +
    + Scanned the following path: +
      +
    • /argo-cd/manifests/install.yaml (Kubernetes)
    • +
    +
    + +
    +
    40 total issues
    +
    +
    +
    +
    + +
    +
    Project docker-image|public.ecr.aws/docker/library/haproxy
    Path public.ecr.aws/docker/library/haproxy:2.6.17-alpine/docker/library/haproxy
    Project docker-image|redis
    Path redis:7.0.11-alpine
    Package Manager apk
    + + + + + +
    Project manifests/install.yaml
    Path /argo-cd/manifests/install.yaml
    Project Type Kubernetes
    +
    +
    +
    +

    Role with dangerous permissions

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

    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[4] + + resources + +
    • + +
    • + Line number: 18543 +
    • +
    + +
    + +

    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[0] + + resources + +
    • + +
    • + Line number: 18571 +
    • +
    + +
    + +

    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[3] + + resources + +
    • + +
    • + Line number: 18619 +
    • +
    + +
    + +

    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[1] + + resources + +
    • + +
    • + Line number: 18601 +
    • +
    + +
    + +

    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: 14] + + rules[0] + + resources + +
    • + +
    • + Line number: 18635 +
    • +
    + +
    + +

    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: 19761 +
    • +
    + +
    + +

    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: 19118 +
    • +
    + +
    + +

    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: 19351 +
    • +
    + +
    + +

    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: 19317 +
    • +
    + +
    + +

    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: 19411 +
    • +
    + +
    + +

    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: 19504 +
    • +
    + +
    + +

    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: 19761 +
    • +
    + +
    + +

    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: 19561 +
    • +
    + +
    + +

    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: 19846 +
    • +
    + +
    + +

    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: 20162 +
    • +
    + +
    + +

    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: 19331 +
    • +
    + +
    + +

    Impact

    +

    Increases the attack surface of the application and the container.

    + +

    Remediation

    +

    Reduce `ports` count to 2

    + + +
    +
    + + + +
    +
    +

    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: 19118 +
    • +
    + +
    + +

    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: 19351 +
    • +
    + +
    + +

    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: 19317 +
    • +
    + +
    + +

    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: 19504 +
    • +
    + +
    + +

    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: 19761 +
    • +
    + +
    + +

    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: 19118 +
    • +
    + +
    + +

    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: 19317 +
    • +
    + +
    + +

    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: 19351 +
    • +
    + +
    + +

    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: 19411 +
    • +
    + +
    + +

    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: 19504 +
    • +
    + +
    + +

    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: 19761 +
    • +
    + +
    + +

    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: 19561 +
    • +
    + +
    + +

    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: 19846 +
    • +
    + +
    + +

    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: 20162 +
    • +
    + +
    + +

    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's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 41] + + input + + spec + + template + + spec + + containers[argocd-applicationset-controller] + + securityContext + + 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] + + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 19359 +
    • +
    + +
    + +

    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] + + input + + spec + + template + + spec + + containers[dex] + + securityContext + + 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] + + input + + spec + + template + + spec + + containers[argocd-notifications-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 19438 +
    • +
    + +
    + +

    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[redis] + + securityContext + + 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] + + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 19768 +
    • +
    + +
    + +

    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-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: 20072 +
    • +
    + +
    + +

    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: 20310 +
    • +
    + +
    + +

    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.8.5/argocd-iac-namespace-install.html b/docs/snyk/v2.8.5/argocd-iac-namespace-install.html new file mode 100644 index 0000000000000..aae75827ee40d --- /dev/null +++ b/docs/snyk/v2.8.5/argocd-iac-namespace-install.html @@ -0,0 +1,2679 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:24:17 am (UTC+00:00)

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

    Role with dangerous permissions

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

    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[4] + + resources + +
    • + +
    • + Line number: 154 +
    • +
    + +
    + +

    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: 182 +
    • +
    + +
    + +

    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: 230 +
    • +
    + +
    + +

    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: 212 +
    • +
    + +
    + +

    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: 246 +
    • +
    + +
    + +

    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: 38] + + spec + + template + + spec + + initContainers[copyutil] + + imagePullPolicy + +
    • + +
    • + Line number: 1267 +
    • +
    + +
    + +

    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: 34] + + input + + spec + + template + + spec + + containers[argocd-applicationset-controller] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 624 +
    • +
    + +
    + +

    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: 35] + + input + + spec + + template + + spec + + initContainers[copyutil] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 857 +
    • +
    + +
    + +

    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: 35] + + input + + spec + + template + + spec + + containers[dex] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 823 +
    • +
    + +
    + +

    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[argocd-notifications-controller] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 917 +
    • +
    + +
    + +

    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[redis] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 1010 +
    • +
    + +
    + +

    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 + + initContainers[copyutil] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 1267 +
    • +
    + +
    + +

    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[argocd-repo-server] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 1067 +
    • +
    + +
    + +

    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-server] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 1352 +
    • +
    + +
    + +

    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-application-controller] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 1668 +
    • +
    + +
    + +

    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: 35] + + spec + + template + + spec + + containers[dex] + + ports + +
    • + +
    • + Line number: 837 +
    • +
    + +
    + +

    Impact

    +

    Increases the attack surface of the application and the container.

    + +

    Remediation

    +

    Reduce `ports` count to 2

    + + +
    +
    + + + +
    +
    +

    Container is running without liveness probe

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

    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: 35] + + spec + + template + + spec + + initContainers[copyutil] + + livenessProbe + +
    • + +
    • + Line number: 857 +
    • +
    + +
    + +

    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: 35] + + spec + + template + + spec + + containers[dex] + + livenessProbe + +
    • + +
    • + Line number: 823 +
    • +
    + +
    + +

    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: 37] + + spec + + template + + spec + + containers[redis] + + livenessProbe + +
    • + +
    • + Line number: 1010 +
    • +
    + +
    + +

    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 + + initContainers[copyutil] + + livenessProbe + +
    • + +
    • + Line number: 1267 +
    • +
    + +
    + +

    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: 34] + + input + + spec + + template + + spec + + containers[argocd-applicationset-controller] + + resources + + limits + + memory + +
    • + +
    • + Line number: 624 +
    • +
    + +
    + +

    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: 35] + + input + + spec + + template + + spec + + containers[dex] + + resources + + limits + + memory + +
    • + +
    • + Line number: 823 +
    • +
    + +
    + +

    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: 35] + + input + + spec + + template + + spec + + initContainers[copyutil] + + resources + + limits + + memory + +
    • + +
    • + Line number: 857 +
    • +
    + +
    + +

    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[argocd-notifications-controller] + + resources + + limits + + memory + +
    • + +
    • + Line number: 917 +
    • +
    + +
    + +

    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[redis] + + resources + + limits + + memory + +
    • + +
    • + Line number: 1010 +
    • +
    + +
    + +

    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 + + initContainers[copyutil] + + resources + + limits + + memory + +
    • + +
    • + Line number: 1267 +
    • +
    + +
    + +

    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[argocd-repo-server] + + resources + + limits + + memory + +
    • + +
    • + Line number: 1067 +
    • +
    + +
    + +

    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-server] + + resources + + limits + + memory + +
    • + +
    • + Line number: 1352 +
    • +
    + +
    + +

    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-application-controller] + + resources + + limits + + memory + +
    • + +
    • + Line number: 1668 +
    • +
    + +
    + +

    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's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 34] + + input + + spec + + template + + spec + + containers[argocd-applicationset-controller] + + securityContext + + 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] + + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 865 +
    • +
    + +
    + +

    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] + + input + + spec + + template + + spec + + containers[dex] + + securityContext + + 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] + + input + + spec + + template + + spec + + containers[argocd-notifications-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 944 +
    • +
    + +
    + +

    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[redis] + + securityContext + + 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] + + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1274 +
    • +
    + +
    + +

    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[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: 1578 +
    • +
    + +
    + +

    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: 1816 +
    • +
    + +
    + +

    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.8.5/argocd-test.html b/docs/snyk/v2.8.5/argocd-test.html new file mode 100644 index 0000000000000..3a5f08a08b860 --- /dev/null +++ b/docs/snyk/v2.8.5/argocd-test.html @@ -0,0 +1,1031 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:21:29 am (UTC+00:00)

    +
    +
    + Scanned the following paths: +
      +
    • /argo-cd/argoproj/argo-cd/v2 (gomodules)
    • /argo-cd (yarn)
    • +
    +
    + +
    +
    6 known vulnerabilities
    +
    19 vulnerable dependency paths
    +
    1853 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    LGPL-3.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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/xanzy/go-gitlab@0.86.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@#3446d4ae8520 + + 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@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 + + 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@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 + + 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@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 + + 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@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 + + 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 +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-cleanhttp +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/hashicorp/go-retryablehttp@0.7.4 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + 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.86.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.86.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/argoproj/notifications-engine/pkg/services@#3446d4ae8520 + + 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@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 + + 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@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 + + 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@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 + + 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@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 + + 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 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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/v2.8.5/ghcr.io_dexidp_dex_v2.37.0.html b/docs/snyk/v2.8.5/ghcr.io_dexidp_dex_v2.37.0.html new file mode 100644 index 0000000000000..74f7da7894829 --- /dev/null +++ b/docs/snyk/v2.8.5/ghcr.io_dexidp_dex_v2.37.0.html @@ -0,0 +1,2862 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:21:38 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 (gomodules)
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (gomodules)
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (gomodules)
    • +
    +
    + +
    +
    28 known vulnerabilities
    +
    79 vulnerable dependency paths
    +
    786 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    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

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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

    + + +
    + + + +
    +
    +

    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:3.18. + 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

    + + +
    + + + +
    +
    +

    Cross-site Scripting (XSS)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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

    + +
    + + + +
    +
    +

    CVE-2023-5363

    +
    + +
    + 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

    +

    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

    + + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/master/public.ecr.aws_docker_library_redis_7.0.15-alpine.html b/docs/snyk/v2.8.5/haproxy_2.6.14-alpine.html similarity index 51% rename from docs/snyk/master/public.ecr.aws_docker_library_redis_7.0.15-alpine.html rename to docs/snyk/v2.8.5/haproxy_2.6.14-alpine.html index ccf5d62549670..020d8275f0dad 100644 --- a/docs/snyk/master/public.ecr.aws_docker_library_redis_7.0.15-alpine.html +++ b/docs/snyk/v2.8.5/haproxy_2.6.14-alpine.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,27 +456,226 @@

    Snyk test report

    -

    September 22nd 2024, 12:19:21 am (UTC+00:00)

    +

    October 29th 2023, 12:21:43 am (UTC+00:00)

    - Scanned the following paths: + Scanned the following path:
      -
    • public.ecr.aws/docker/library/redis:7.0.15-alpine/docker/library/redis (apk)
    • -
    • public.ecr.aws/docker/library/redis:7.0.15-alpine/tianon/gosu//usr/local/bin/gosu (gomodules)
    • +
    • haproxy:2.6.14-alpine (apk)
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    +
    1 known vulnerabilities
    +
    9 vulnerable dependency paths
    18 dependencies
    - +
    + + + + + + + +
    Project docker-image|haproxy
    Path haproxy:2.6.14-alpine
    Package Manager apk
    +
    - No known vulnerabilities detected. +
    +
    +

    CVE-2023-5363

    +
    + +
    + 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

    +

    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

    + + +
    + + + +
    +
    diff --git a/docs/snyk/v2.12.3/quay.io_argoproj_argocd_v2.12.3.html b/docs/snyk/v2.8.5/quay.io_argoproj_argocd_v2.8.5.html similarity index 57% rename from docs/snyk/v2.12.3/quay.io_argoproj_argocd_v2.12.3.html rename to docs/snyk/v2.8.5/quay.io_argoproj_argocd_v2.8.5.html index 0bd0879c74d78..eb2bb47c67fc8 100644 --- a/docs/snyk/v2.12.3/quay.io_argoproj_argocd_v2.12.3.html +++ b/docs/snyk/v2.8.5/quay.io_argoproj_argocd_v2.8.5.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,23 +456,19 @@

    Snyk test report

    -

    September 22nd 2024, 12:24:27 am (UTC+00:00)

    +

    October 29th 2023, 12:22:15 am (UTC+00:00)

    Scanned the following paths:
      -
    • quay.io/argoproj/argocd:v2.12.3/argoproj/argocd/Dockerfile (deb)
    • -
    • quay.io/argoproj/argocd:v2.12.3/argoproj/argo-cd/v2//usr/local/bin/argocd (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.12.3//usr/local/bin/kustomize (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.12.3/helm/v3//usr/local/bin/helm (gomodules)
    • -
    • quay.io/argoproj/argocd:v2.12.3/git-lfs/git-lfs//usr/bin/git-lfs (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.8.5/argoproj/argocd (deb)
    • quay.io/argoproj/argocd:v2.8.5/argoproj/argo-cd/v2 (gomodules)
    • quay.io/argoproj/argocd:v2.8.5/kustomize/kustomize/v5 (gomodules)
    • quay.io/argoproj/argocd:v2.8.5/helm/v3 (gomodules)
    • quay.io/argoproj/argocd:v2.8.5/git-lfs/git-lfs (gomodules)
    -
    17 known vulnerabilities
    -
    81 vulnerable dependency paths
    -
    2292 dependencies
    +
    29 known vulnerabilities
    +
    97 vulnerable dependency paths
    +
    2117 dependencies
    @@ -480,32 +476,29 @@

    Snyk test report

    -
    -

    CVE-2024-41996

    +
    +

    Denial of Service (DoS)

    -
    - medium severity +
    + high severity

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: golang
    • Vulnerable module: - openssl/libssl3t64 + golang.org/x/net/http2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 and openssl/libssl3t64@3.0.13-0ubuntu3.3 + helm.sh/helm/v3@* and golang.org/x/net/http2@v0.8.0
    @@ -518,135 +511,86 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + helm.sh/helm/v3@* - openssl/libssl3t64@3.0.13-0ubuntu3.3 + golang.org/x/net/http2@v0.8.0
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - coreutils@9.4-3ubuntu6 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - cyrus-sasl2/libsasl2-modules@2.1.28+dfsg1-5ubuntu3.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
  • - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - libfido2/libfido2-1@1.14.0-1build3 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
    + +

    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

    + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - openssh/openssh-client@1:9.6p1-3ubuntu13.5 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - ca-certificates@20240203 - - openssl@3.0.13-0ubuntu3.3 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 - - libssh/libssh-4@0.10.6-2build2 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
  • +
    +

    Directory Traversal

    +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
    + high severity +
    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 - - openldap/libldap2@2.6.7+dfsg-1~exp1ubuntu8 - - cyrus-sasl2/libsasl2-2@2.1.28+dfsg1-5ubuntu3.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - openssl@3.0.13-0ubuntu3.3 - - +
      +
    • + 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: - docker-image|quay.io/argoproj/argocd@v2.12.3 + helm.sh/helm/v3@* - ca-certificates@20240203 - - openssl@3.0.13-0ubuntu3.3 + github.com/cyphar/filepath-securejoin@v0.2.3 @@ -657,29 +601,47 @@

      Detailed paths


      -

      NVD Description

      -

      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:24.04 relevant fixed versions and status.

      -

      Validating the order of the public keys in the Diffie-Hellman Key Agreement Protocol, when an approved safe prime is used, allows remote attackers (from the client side) to trigger unnecessarily expensive server-side DHE modular-exponentiation calculations. The client may cause asymmetric resource consumption. The basic attack scenario is that the client must claim that it can only communicate with DHE, and the server must be configured to allow DHE and validate the order of the public key.

      +

      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

      -

      There is no fixed version for Ubuntu:24.04 openssl.

      +

      Upgrade github.com/cyphar/filepath-securejoin to version 0.2.4 or higher.

      References


  • -

    CVE-2024-6119

    +

    CVE-2020-22916

    @@ -690,20 +652,17 @@

    CVE-2024-6119

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - openssl/libssl3t64 + xz-utils/liblzma5
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 and openssl/libssl3t64@3.0.13-0ubuntu3.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 and xz-utils/liblzma5@5.2.5-2ubuntu1
    @@ -716,135 +675,134 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - openssl/libssl3t64@3.0.13-0ubuntu3.3 + xz-utils/liblzma5@5.2.5-2ubuntu1
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - coreutils@9.4-3ubuntu6 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - cyrus-sasl2/libsasl2-modules@2.1.28+dfsg1-5ubuntu3.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
  • - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - libfido2/libfido2-1@1.14.0-1build3 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
    + +

    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.

    +

    ** DISPUTED ** 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

    + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - openssh/openssh-client@1:9.6p1-3ubuntu13.5 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - +
    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - ca-certificates@20240203 - - openssl@3.0.13-0ubuntu3.3 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 - - + -
  • +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + perl/perl-modules-5.34 +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.8.5, git@1:2.34.1-1ubuntu1.10 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + git@1:2.34.1-1ubuntu1.10 - libssh/libssh-4@0.10.6-2build2 + perl@5.34.0-3ubuntu1.2 - openssl/libssl3t64@3.0.13-0ubuntu3.3 + perl/perl-modules-5.34@5.34.0-3ubuntu1.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 + git@1:2.34.1-1ubuntu1.10 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + perl@5.34.0-3ubuntu1.2 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + perl/libperl5.34@5.34.0-3ubuntu1.2 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 + perl/perl-modules-5.34@5.34.0-3ubuntu1.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + git@1:2.34.1-1ubuntu1.10 - openldap/libldap2@2.6.7+dfsg-1~exp1ubuntu8 + perl@5.34.0-3ubuntu1.2 - cyrus-sasl2/libsasl2-2@2.1.28+dfsg1-5ubuntu3.1 - - openssl/libssl3t64@3.0.13-0ubuntu3.3 + perl/libperl5.34@5.34.0-3ubuntu1.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 + + git@1:2.34.1-1ubuntu1.10 - openssl@3.0.13-0ubuntu3.3 + perl@5.34.0-3ubuntu1.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - ca-certificates@20240203 - - openssl@3.0.13-0ubuntu3.3 + perl/perl-base@5.34.0-3ubuntu1.2 @@ -856,48 +814,27 @@

      Detailed paths


      NVD Description

      -

      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:24.04 relevant fixed versions and status.

      -

      Issue summary: Applications performing certificate name checks (e.g., TLS - clients checking server certificates) may attempt to read an invalid memory - address resulting in abnormal termination of the application process.

      -

      Impact summary: Abnormal termination of an application can a cause a denial of - service.

      -

      Applications performing certificate name checks (e.g., TLS clients checking - server certificates) may attempt to read an invalid memory address when - comparing the expected name with an otherName subject alternative name of an - X.509 certificate. This may result in an exception that terminates the - application program.

      -

      Note that basic certificate chain validation (signatures, dates, ...) is not - affected, the denial of service can occur only when the application also - specifies an expected DNS name, Email address or IP address.

      -

      TLS servers rarely solicit client certificates, and even when they do, they - generally don't perform a name check against a reference identifier (expected - identity), but rather extract the presented identity after checking the - certificate chain. So TLS servers are generally not affected and the severity - of the issue is Moderate.

      -

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

      +

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

      +

      In Perl 5.34.0, function S_find_uninit_var in sv.c has a stack-based crash that can lead to remote code execution or local privilege escalation.

      Remediation

      -

      Upgrade Ubuntu:24.04 openssl to version 3.0.13-0ubuntu3.4 or higher.

      +

      There is no fixed version for Ubuntu:22.04 perl.

      References


    -

    Information Exposure

    +

    Access of Uninitialized Pointer

    @@ -908,20 +845,17 @@

    Information Exposure

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - libgcrypt20 + krb5/libk5crypto3
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 and libgcrypt20@1.10.3-2build1 + docker-image|quay.io/argoproj/argocd@v2.8.5 and krb5/libk5crypto3@1.19.2-2ubuntu0.2
    @@ -934,100 +868,159 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - libgcrypt20@1.10.3-2build1 + krb5/libk5crypto3@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 - gnupg2/dirmngr@2.4.4-2ubuntu17 + pam/libpam-modules@1.4.0-11ubuntu2.3 - libgcrypt20@1.10.3-2build1 + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + krb5/libk5crypto3@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - gnupg2/gpg@2.4.4-2ubuntu17 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 - libgcrypt20@1.10.3-2build1 + krb5/libkrb5-3@1.19.2-2ubuntu0.2 + + krb5/libk5crypto3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + krb5/libkrb5-3@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - gnupg2/gpg-agent@2.4.4-2ubuntu17 + adduser@3.118ubuntu5 - libgcrypt20@1.10.3-2build1 + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + krb5/libkrb5-3@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - apt@2.7.14build2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - apt/libapt-pkg6.0t64@2.7.14build2 + openssh/openssh-client@1:8.9p1-3ubuntu0.4 - libgcrypt20@1.10.3-2build1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - apt@2.7.14build2 + git@1:2.34.1-1ubuntu1.10 - gnupg2/gpgv@2.4.4-2ubuntu17 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 - libgcrypt20@1.10.3-2build1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 + + git@1:2.34.1-1ubuntu1.10 - gnupg2/gpg@2.4.4-2ubuntu17 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 - gnupg2/gpgconf@2.4.4-2ubuntu17 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 - libgcrypt20@1.10.3-2build1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - apt@2.7.14build2 + adduser@3.118ubuntu5 - adduser@3.137ubuntu1 + shadow/passwd@1:4.8.1-2ubuntu2.1 - shadow/passwd@1:4.13+dfsg1-4ubuntu3 + pam/libpam-modules@1.4.0-11ubuntu2.3 - pam/libpam-modules@1.5.3-5ubuntu5.1 + libnsl/libnsl2@1.3.0-2build2 - systemd/libsystemd0@255.4-1ubuntu8.4 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - libgcrypt20@1.10.3-2build1 + krb5/libkrb5support0@1.19.2-2ubuntu0.2 @@ -1039,28 +1032,31 @@

      Detailed paths


      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:24.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.

      +

      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.

      +

      lib/kadm5/kadm_rpc_xdr.c in MIT Kerberos 5 (aka krb5) before 1.20.2 and 1.21.x before 1.21.1 frees an uninitialized pointer. A remote authenticated user can trigger a kadmind crash. This occurs because _xdr_kadm5_principal_ent_rec does not validate the relationship between n_key_data and the key_data array count.

      Remediation

      -

      There is no fixed version for Ubuntu:24.04 libgcrypt20.

      +

      There is no fixed version for Ubuntu:22.04 krb5.

      References


    -

    CVE-2024-26462

    +

    LGPL-3.0 license

    @@ -1071,21 +1067,75 @@

    CVE-2024-26462

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile + Package Manager: golang
    • - Package Manager: ubuntu:24.04 + Module: + + gopkg.in/retry.v1
    • -
    • - Vulnerable module: - krb5/libk5crypto3 +
    • 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

    + +
    + + + +
    +
    +

    Memory Leak

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + glibc/libc-bin
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 and glibc/libc-bin@2.35-0ubuntu3.4 - docker-image|quay.io/argoproj/argocd@v2.12.3, git@1:2.43.0-1ubuntu7.1 and others
    @@ -1097,146 +1147,627 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + glibc/libc-bin@2.35-0ubuntu3.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + glibc/libc6@2.35-0ubuntu3.4
    • +
    + +
    + +
    + +

    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.

    +

    A flaw was found in the GNU C Library. A recent fix for CVE-2023-4806 introduced the potential for a memory leak, which may result in an application crash.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 glibc.

    +

    References

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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: - docker-image|quay.io/argoproj/argocd@v2.12.3 + github.com/argoproj/argo-cd/v2@* - git@1:2.43.0-1ubuntu7.1 + github.com/r3labs/diff@v1.1.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@* - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + github.com/hashicorp/go-version@v1.2.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@* - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + github.com/hashicorp/go-retryablehttp@v0.7.4 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@* - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + github.com/hashicorp/go-multierror@v1.1.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@* - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + github.com/hashicorp/go-cleanhttp@v0.5.2
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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@v2.12.3 + github.com/argoproj/argo-cd/v2@* - git@1:2.43.0-1ubuntu7.1 + github.com/gosimple/slug@v1.13.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    CVE-2022-46908

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + sqlite3/libsqlite3-0 +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.8.5, gnupg2/gpg@2.2.27-3ubuntu2.1 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gpg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + sqlite3/libsqlite3-0@3.37.2-2ubuntu0.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    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

    + + +
    + + + +
    +
    +

    Arbitrary Code Injection

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + shadow/passwd +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.5 and shadow/passwd@1:4.8.1-2ubuntu2.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 + adduser@3.118ubuntu5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + shadow/passwd@1:4.8.1-2ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + openssh/openssh-client@1:8.9p1-3ubuntu0.4 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + shadow/passwd@1:4.8.1-2ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + shadow/login@1:4.8.1-2ubuntu2.1 -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 - - +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream shadow package and not the shadow package as distributed by Ubuntu:22.04. + 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

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + procps/libprocps8 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.5 and procps/libprocps8@2:3.3.17-6ubuntu2 -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - +
    • +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - openssh/openssh-client@1:9.6p1-3ubuntu13.5 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - +
    -
  • + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + procps/libprocps8@2:3.3.17-6ubuntu2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + procps@2:3.3.17-6ubuntu2 - libssh/libssh-4@0.10.6-2build2 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + procps/libprocps8@2:3.3.17-6ubuntu2
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/krb5-locales@1.20.1-6ubuntu2.1 + procps@2:3.3.17-6ubuntu2 @@ -1248,51 +1779,48 @@

      Detailed paths


      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:24.04 relevant fixed versions and status.

      -

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

      +

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

      +

      Under some circumstances, this weakness allows a user who has access to run the “ps” utility on a machine, the ability to write almost unlimited amounts of unfiltered data into the process heap.

      Remediation

      -

      There is no fixed version for Ubuntu:24.04 krb5.

      +

      There is no fixed version for Ubuntu:22.04 procps.

      References


    -
    -

    Denial of Service (DoS)

    +
    +

    Uncontrolled Recursion

    -
    - medium severity +
    + low severity

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argo-cd/v2 /usr/local/bin/argocd -
    • -
    • - Package Manager: golang + Package Manager: ubuntu:22.04
    • Vulnerable module: - github.com/rs/cors + pcre3/libpcre3
    • Introduced through: - github.com/argoproj/argo-cd/v2@* and github.com/rs/cors@v1.9.0 + docker-image|quay.io/argoproj/argocd@v2.8.5 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1
    @@ -1305,9 +1833,20 @@

    Detailed paths

    • Introduced through: - github.com/argoproj/argo-cd/v2@* + docker-image|quay.io/argoproj/argocd@v2.8.5 + + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - github.com/rs/cors@v1.9.0 + grep@3.7-1build1 + + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 @@ -1318,92 +1857,55 @@

      Detailed paths


      -

      Overview

      -

      Affected versions of this package are vulnerable to Denial of Service (DoS) through the processing of malicious preflight requests that include a Access-Control-Request-Headers header with excessive commas. An attacker can induce excessive memory consumption and potentially crash the server by sending specially crafted requests.

      -

      PoC

      -
      
      -        func BenchmarkPreflightAdversarialACRH(b *testing.B) {
      -            resps := makeFakeResponses(b.N)
      -            req, _ := http.NewRequest(http.MethodOptions, dummyEndpoint, nil)
      -            req.Header.Add(headerOrigin, dummyOrigin)
      -            req.Header.Add(headerACRM, http.MethodGet)
      -            req.Header[headerACRH] = adversarialACRH
      -            handler := Default().Handler(testHandler)
      -        
      -            b.ReportAllocs()
      -            b.ResetTimer()
      -            for i := 0; i < b.N; i++ {
      -                handler.ServeHTTP(resps[i], req)
      -            }
      -        }
      -        
      -        var adversarialACRH []string
      -        
      -        func init() { // populates adversarialACRH
      -            n := int(math.Floor(math.Sqrt(http.DefaultMaxHeaderBytes)))
      -            commas := strings.Repeat(",", n)
      -            res := make([]string, n)
      -            for i := range res {
      -                res[i] = commas
      -            }
      -            adversarialACRH = res
      -        }
      -        
      -

      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

        -
      • -
      +

      NVD Description

      +

      Note: Versions mentioned in the description apply only to the upstream pcre3 package and not the pcre3 package as distributed by Ubuntu:22.04. + 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

      -

      Upgrade github.com/rs/cors to version 1.11.0 or higher.

      +

      There is no fixed version for Ubuntu:22.04 pcre3.

      References


    -
    -

    Integer Overflow or Wraparound

    +
    +

    Release of Invalid Pointer or Reference

    -
    - medium severity +
    + low severity

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - expat/libexpat1 + patch
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 and patch@2.7.6-7build2 - docker-image|quay.io/argoproj/argocd@v2.12.3, git@1:2.43.0-1ubuntu7.1 and others
    @@ -1415,11 +1917,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 - - expat/libexpat1@2.6.1-2build1 + patch@2.7.6-7build2 @@ -1431,52 +1931,48 @@

      Detailed paths


      NVD Description

      -

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

      -

      An issue was discovered in libexpat before 2.6.3. dtdCopy in xmlparse.c can have an integer overflow for nDefaultAtts on 32-bit platforms (where UINT_MAX equals SIZE_MAX).

      +

      Note: Versions mentioned in the description apply only to the upstream patch package and not the patch package as distributed by Ubuntu:22.04. + 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

      -

      Upgrade Ubuntu:24.04 expat to version 2.6.1-2ubuntu0.1 or higher.

      +

      There is no fixed version for Ubuntu:22.04 patch.

      References


    -
    -

    XML External Entity (XXE) Injection

    +
    +

    Double Free

    -
    - medium severity +
    + low severity

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - expat/libexpat1 + patch
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 and patch@2.7.6-7build2 - docker-image|quay.io/argoproj/argocd@v2.12.3, git@1:2.43.0-1ubuntu7.1 and others
    @@ -1488,11 +1984,9 @@

    Detailed paths

    -
    -

    Integer Overflow or Wraparound

    +
    +

    CVE-2023-28531

    -
    - medium severity +
    + low severity

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - expat/libexpat1 + openssh/openssh-client
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 and openssh/openssh-client@1:8.9p1-3ubuntu0.4 - docker-image|quay.io/argoproj/argocd@v2.12.3, git@1:2.43.0-1ubuntu7.1 and others
    @@ -1561,11 +2056,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - expat/libexpat1@2.6.1-2build1 + openssh/openssh-client@1:8.9p1-3ubuntu0.4 @@ -1577,52 +2070,50 @@

      Detailed paths


      NVD Description

      -

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

      -

      An issue was discovered in libexpat before 2.6.3. nextScaffoldPart in xmlparse.c can have an integer overflow for m_groupSize on 32-bit platforms (where UINT_MAX equals SIZE_MAX).

      +

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

      +

      ssh-add in OpenSSH before 9.3 adds smartcard keys to ssh-agent without the intended per-hop destination constraints. The earliest affected version is 8.9.

      Remediation

      -

      Upgrade Ubuntu:24.04 expat to version 2.6.1-2ubuntu0.1 or higher.

      +

      There is no fixed version for Ubuntu:22.04 openssh.

      References


    -
    -

    CVE-2024-8096

    +
    +

    NULL Pointer Dereference

    -
    - medium severity +
    + low severity

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - curl/libcurl3t64-gnutls + openldap/libldap-2.5-0
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3, git@1:2.43.0-1ubuntu7.1 and others + docker-image|quay.io/argoproj/argocd@v2.8.5, gnupg2/dirmngr@2.2.27-3ubuntu2.1 and others
    @@ -1634,11 +2125,33 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 + + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + openldap/libldap-common@2.5.16+dfsg-0ubuntu0.22.04.1 @@ -1650,28 +2163,34 @@

      Detailed paths


      NVD Description

      -

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

      -

      When curl is told to use the Certificate Status Request TLS extension, often referred to as OCSP stapling, to verify that the server certificate is valid, it might fail to detect some OCSP problems and instead wrongly consider the response as fine. If the returned status reports another error than 'revoked' (like for example 'unauthorized') it is not treated as a bad certficate.

      +

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

      +

      A vulnerability was found in openldap. This security flaw causes a null pointer dereference in ber_memalloc_x() function.

      Remediation

      -

      Upgrade Ubuntu:24.04 curl to version 8.5.0-2ubuntu10.4 or higher.

      +

      There is no fixed version for Ubuntu:22.04 openldap.

      References


    -

    Release of Invalid Pointer or Reference

    +

    Resource Exhaustion

    @@ -1682,20 +2201,17 @@

    Release of Invalid Pointer or Reference

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - patch + libzstd/libzstd1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 and patch@2.7.6-7build3 + docker-image|quay.io/argoproj/argocd@v2.8.5 and libzstd/libzstd1@1.4.8+dfsg-3build1
    @@ -1708,9 +2224,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - patch@2.7.6-7build3 + libzstd/libzstd1@1.4.8+dfsg-3build1 @@ -1722,26 +2238,30 @@

      Detailed paths


      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:24.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.

      +

      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:24.04 patch.

      +

      There is no fixed version for Ubuntu:22.04 libzstd.

      References


    -

    Double Free

    +

    Integer Overflow or Wraparound

    @@ -1752,20 +2272,17 @@

    Double Free

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - patch + krb5/libk5crypto3
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 and patch@2.7.6-7build3 + docker-image|quay.io/argoproj/argocd@v2.8.5 and krb5/libk5crypto3@1.19.2-2ubuntu0.2
    @@ -1778,9 +2295,159 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 + + krb5/libk5crypto3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + krb5/libk5crypto3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + krb5/libkrb5-3@1.19.2-2ubuntu0.2 + + krb5/libk5crypto3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + krb5/libkrb5-3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + krb5/libkrb5-3@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + openssh/openssh-client@1:8.9p1-3ubuntu0.4 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.3 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - patch@2.7.6-7build3 + krb5/libkrb5support0@1.19.2-2ubuntu0.2 @@ -1792,31 +2459,29 @@

      Detailed paths


      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:24.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.

      +

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu:22.04. + 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:24.04 patch.

      +

      There is no fixed version for Ubuntu:22.04 krb5.

      References


    -

    CVE-2024-26458

    +

    Out-of-bounds Write

    @@ -1827,21 +2492,18 @@

    CVE-2024-26458

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - krb5/libk5crypto3 + gnupg2/gpgv
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 and gnupg2/gpgv@2.2.27-3ubuntu2.1 - docker-image|quay.io/argoproj/argocd@v2.12.3, git@1:2.43.0-1ubuntu7.1 and others
    @@ -1853,354 +2515,313 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + apt@2.4.10 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpgv@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpgconf@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gpg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/gpgconf@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/gpgconf@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpgsm@2.2.27-3ubuntu2.1 - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/gpgconf@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + gnupg2/dirmngr@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - openssh/openssh-client@1:9.6p1-3ubuntu13.5 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/dirmngr@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/dirmngr@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - libssh/libssh-4@0.10.6-2build2 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/krb5-locales@1.20.1-6ubuntu2.1 + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1
    • -
    - -
    - -
    - -

    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:24.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:24.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2024-26461

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.12.3, git@1:2.43.0-1ubuntu7.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 - - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpg@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/gpg@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 - - krb5/libkrb5-3@1.20.1-6ubuntu2.1 - - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/gpg@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libk5crypto3@1.20.1-6ubuntu2.1 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - krb5/libkrb5support0@1.20.1-6ubuntu2.1 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/libkrb5-3@1.20.1-6ubuntu2.1 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - openssh/openssh-client@1:9.6p1-3ubuntu13.5 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git@1:2.43.0-1ubuntu7.1 - - curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3 + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - libssh/libssh-4@0.10.6-2build2 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1 + gnupg2/gpgsm@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - krb5/krb5-locales@1.20.1-6ubuntu2.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -2212,27 +2833,31 @@

      Detailed paths


      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:24.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.

      +

      Note: Versions mentioned in the description apply only to the upstream gnupg2 package and not the gnupg2 package as distributed by Ubuntu:22.04. + 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:24.04 krb5.

      +

      There is no fixed version for Ubuntu:22.04 gnupg2.

      References


    -

    Out-of-bounds Write

    +

    Allocation of Resources Without Limits or Throttling

    @@ -2243,20 +2868,17 @@

    Out-of-bounds Write

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - gnupg2/gpgv + glibc/libc-bin
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 and gnupg2/gpgv@2.4.4-2ubuntu17 + docker-image|quay.io/argoproj/argocd@v2.8.5 and glibc/libc-bin@2.35-0ubuntu3.4
    @@ -2269,80 +2891,109 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - gnupg2/gpgv@2.4.4-2ubuntu17 + glibc/libc-bin@2.35-0ubuntu3.4
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - apt@2.7.14build2 + docker-image|quay.io/argoproj/argocd@v2.8.5 - gnupg2/gpgv@2.4.4-2ubuntu17 + glibc/libc6@2.35-0ubuntu3.4
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - gnupg2/dirmngr@2.4.4-2ubuntu17 - - gnupg2/gpgconf@2.4.4-2ubuntu17 - - +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - gnupg2/gpg-agent@2.4.4-2ubuntu17 - - gnupg2/gpgconf@2.4.4-2ubuntu17 - - +
  • - +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + git/git-man +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.8.5, git@1:2.34.1-1ubuntu1.10 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    -

    Allocation of Resources Without Limits or Throttling

    +

    Uncontrolled Recursion

    @@ -2389,20 +3036,17 @@

    Allocation of Resources Without Limits or Throttling

  • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
  • -
  • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
  • Vulnerable module: - glibc/libc-bin + gcc-12/libstdc++6
  • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 and glibc/libc-bin@2.39-0ubuntu8.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04
  • @@ -2415,18 +3059,51 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + apt@2.4.10 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 + + apt@2.4.10 + + apt/libapt-pkg6.0@2.4.10 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 - glibc/libc-bin@2.39-0ubuntu8.3 + gcc-12/gcc-12-base@12.3.0-1ubuntu1~22.04
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - glibc/libc6@2.39-0ubuntu8.3 + gcc-12/libgcc-s1@12.3.0-1ubuntu1~22.04 @@ -2438,23 +3115,23 @@

      Detailed paths


      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:24.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.

      +

      Note: Versions mentioned in the description apply only to the upstream gcc-12 package and not the gcc-12 package as distributed by Ubuntu:22.04. + 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:24.04 glibc.

      +

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

      References


    @@ -2470,21 +3147,18 @@

    Improper Input Validation

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - git/git-man + coreutils
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.5 and coreutils@8.32-4.1ubuntu1 - docker-image|quay.io/argoproj/argocd@v2.12.3, git@1:2.43.0-1ubuntu7.1 and others
    @@ -2496,31 +3170,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 - - git/git-man@1:2.43.0-1ubuntu7.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 - - git@1:2.43.0-1ubuntu7.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 + docker-image|quay.io/argoproj/argocd@v2.8.5 - git-lfs@3.4.1-1ubuntu0.1 - - git@1:2.43.0-1ubuntu7.1 + coreutils@8.32-4.1ubuntu1 @@ -2532,27 +3184,29 @@

      Detailed paths


      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:24.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).

      +

      Note: Versions mentioned in the description apply only to the upstream coreutils package and not the coreutils package as distributed by Ubuntu:22.04. + 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:24.04 git.

      +

      There is no fixed version for Ubuntu:22.04 coreutils.

      References


    -

    Improper Input Validation

    +

    Out-of-bounds Write

    @@ -2563,20 +3217,17 @@

    Improper Input Validation

    • - Manifest file: quay.io/argoproj/argocd:v2.12.3/argoproj/argocd Dockerfile -
    • -
    • - Package Manager: ubuntu:24.04 + Package Manager: ubuntu:22.04
    • Vulnerable module: - coreutils + bash
    • Introduced through: - docker-image|quay.io/argoproj/argocd@v2.12.3 and coreutils@9.4-3ubuntu6 + docker-image|quay.io/argoproj/argocd@v2.8.5 and bash@5.1-6ubuntu1
    @@ -2589,9 +3240,9 @@

    Detailed paths

    diff --git a/docs/snyk/v2.8.5/redis_7.0.11-alpine.html b/docs/snyk/v2.8.5/redis_7.0.11-alpine.html new file mode 100644 index 0000000000000..20730eb214f1d --- /dev/null +++ b/docs/snyk/v2.8.5/redis_7.0.11-alpine.html @@ -0,0 +1,1335 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:22:23 am (UTC+00:00)

    +
    +
    + Scanned the following path: +
      +
    • redis:7.0.11-alpine (apk)
    • +
    +
    + +
    +
    5 known vulnerabilities
    +
    41 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

    + + +
    + + + +
    +
    +

    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:3.18. + 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

    + + +
    + + + +
    +
    +

    CVE-2023-5363

    +
    + +
    + 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

    +

    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

    + + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.9.0-rc3/argocd-iac-install.html b/docs/snyk/v2.9.0-rc3/argocd-iac-install.html new file mode 100644 index 0000000000000..207acd982d50e --- /dev/null +++ b/docs/snyk/v2.9.0-rc3/argocd-iac-install.html @@ -0,0 +1,2679 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:20:57 am (UTC+00:00)

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

    Role with dangerous permissions

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

    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[4] + + resources + +
    • + +
    • + Line number: 20393 +
    • +
    + +
    + +

    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[0] + + resources + +
    • + +
    • + Line number: 20421 +
    • +
    + +
    + +

    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[3] + + resources + +
    • + +
    • + Line number: 20469 +
    • +
    + +
    + +

    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[1] + + resources + +
    • + +
    • + Line number: 20451 +
    • +
    + +
    + +

    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: 14] + + rules[0] + + resources + +
    • + +
    • + Line number: 20485 +
    • +
    + +
    + +

    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: 21618 +
    • +
    + +
    + +

    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: 20969 +
    • +
    + +
    + +

    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: 21214 +
    • +
    + +
    + +

    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: 21180 +
    • +
    + +
    + +

    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: 21274 +
    • +
    + +
    + +

    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: 21361 +
    • +
    + +
    + +

    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: 21618 +
    • +
    + +
    + +

    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: 21418 +
    • +
    + +
    + +

    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: 21703 +
    • +
    + +
    + +

    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: 22019 +
    • +
    + +
    + +

    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: 21194 +
    • +
    + +
    + +

    Impact

    +

    Increases the attack surface of the application and the container.

    + +

    Remediation

    +

    Reduce `ports` count to 2

    + + +
    +
    + + + +
    +
    +

    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: 20969 +
    • +
    + +
    + +

    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: 21214 +
    • +
    + +
    + +

    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: 21180 +
    • +
    + +
    + +

    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: 21361 +
    • +
    + +
    + +

    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: 21618 +
    • +
    + +
    + +

    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: 20969 +
    • +
    + +
    + +

    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: 21180 +
    • +
    + +
    + +

    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: 21214 +
    • +
    + +
    + +

    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: 21274 +
    • +
    + +
    + +

    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: 21361 +
    • +
    + +
    + +

    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: 21618 +
    • +
    + +
    + +

    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: 21418 +
    • +
    + +
    + +

    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: 21703 +
    • +
    + +
    + +

    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: 22019 +
    • +
    + +
    + +

    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's or Pod's UID could clash with host's UID

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

    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] + + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 21222 +
    • +
    + +
    + +

    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] + + input + + spec + + template + + spec + + containers[dex] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 21197 +
    • +
    + +
    + +

    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[argocd-notifications-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 21295 +
    • +
    + +
    + +

    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[redis] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 21371 +
    • +
    + +
    + +

    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 + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 21625 +
    • +
    + +
    + +

    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-repo-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 21591 +
    • +
    + +
    + +

    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: 21929 +
    • +
    + +
    + +

    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: 22167 +
    • +
    + +
    + +

    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.9.0-rc3/argocd-iac-namespace-install.html b/docs/snyk/v2.9.0-rc3/argocd-iac-namespace-install.html new file mode 100644 index 0000000000000..9e4ae7e5224e8 --- /dev/null +++ b/docs/snyk/v2.9.0-rc3/argocd-iac-namespace-install.html @@ -0,0 +1,2679 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:21:10 am (UTC+00:00)

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

    Role with dangerous permissions

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

    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[4] + + resources + +
    • + +
    • + Line number: 154 +
    • +
    + +
    + +

    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: 182 +
    • +
    + +
    + +

    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: 230 +
    • +
    + +
    + +

    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: 212 +
    • +
    + +
    + +

    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: 246 +
    • +
    + +
    + +

    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: 38] + + spec + + template + + spec + + initContainers[copyutil] + + imagePullPolicy + +
    • + +
    • + Line number: 1274 +
    • +
    + +
    + +

    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: 34] + + input + + spec + + template + + spec + + containers[argocd-applicationset-controller] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 625 +
    • +
    + +
    + +

    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: 35] + + input + + spec + + template + + spec + + initContainers[copyutil] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 870 +
    • +
    + +
    + +

    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: 35] + + input + + spec + + template + + spec + + containers[dex] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 836 +
    • +
    + +
    + +

    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[argocd-notifications-controller] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 930 +
    • +
    + +
    + +

    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[redis] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 1017 +
    • +
    + +
    + +

    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 + + initContainers[copyutil] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 1274 +
    • +
    + +
    + +

    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[argocd-repo-server] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 1074 +
    • +
    + +
    + +

    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-server] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 1359 +
    • +
    + +
    + +

    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-application-controller] + + resources + + limits + + cpu + +
    • + +
    • + Line number: 1675 +
    • +
    + +
    + +

    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: 35] + + spec + + template + + spec + + containers[dex] + + ports + +
    • + +
    • + Line number: 850 +
    • +
    + +
    + +

    Impact

    +

    Increases the attack surface of the application and the container.

    + +

    Remediation

    +

    Reduce `ports` count to 2

    + + +
    +
    + + + +
    +
    +

    Container is running without liveness probe

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

    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: 35] + + spec + + template + + spec + + initContainers[copyutil] + + livenessProbe + +
    • + +
    • + Line number: 870 +
    • +
    + +
    + +

    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: 35] + + spec + + template + + spec + + containers[dex] + + livenessProbe + +
    • + +
    • + Line number: 836 +
    • +
    + +
    + +

    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: 37] + + spec + + template + + spec + + containers[redis] + + livenessProbe + +
    • + +
    • + Line number: 1017 +
    • +
    + +
    + +

    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 + + initContainers[copyutil] + + livenessProbe + +
    • + +
    • + Line number: 1274 +
    • +
    + +
    + +

    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: 34] + + input + + spec + + template + + spec + + containers[argocd-applicationset-controller] + + resources + + limits + + memory + +
    • + +
    • + Line number: 625 +
    • +
    + +
    + +

    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: 35] + + input + + spec + + template + + spec + + containers[dex] + + resources + + limits + + memory + +
    • + +
    • + Line number: 836 +
    • +
    + +
    + +

    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: 35] + + input + + spec + + template + + spec + + initContainers[copyutil] + + resources + + limits + + memory + +
    • + +
    • + Line number: 870 +
    • +
    + +
    + +

    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[argocd-notifications-controller] + + resources + + limits + + memory + +
    • + +
    • + Line number: 930 +
    • +
    + +
    + +

    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[redis] + + resources + + limits + + memory + +
    • + +
    • + Line number: 1017 +
    • +
    + +
    + +

    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 + + initContainers[copyutil] + + resources + + limits + + memory + +
    • + +
    • + Line number: 1274 +
    • +
    + +
    + +

    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[argocd-repo-server] + + resources + + limits + + memory + +
    • + +
    • + Line number: 1074 +
    • +
    + +
    + +

    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-server] + + resources + + limits + + memory + +
    • + +
    • + Line number: 1359 +
    • +
    + +
    + +

    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-application-controller] + + resources + + limits + + memory + +
    • + +
    • + Line number: 1675 +
    • +
    + +
    + +

    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's or Pod's UID could clash with host's UID

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

    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] + + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 878 +
    • +
    + +
    + +

    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] + + input + + spec + + template + + spec + + containers[dex] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 853 +
    • +
    + +
    + +

    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[argocd-notifications-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 951 +
    • +
    + +
    + +

    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[redis] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1027 +
    • +
    + +
    + +

    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 + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1281 +
    • +
    + +
    + +

    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[argocd-repo-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1247 +
    • +
    + +
    + +

    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: 1585 +
    • +
    + +
    + +

    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: 1823 +
    • +
    + +
    + +

    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.10.16/argocd-test.html b/docs/snyk/v2.9.0-rc3/argocd-test.html similarity index 76% rename from docs/snyk/v2.10.16/argocd-test.html rename to docs/snyk/v2.9.0-rc3/argocd-test.html index 82f63569dbd39..8a9efc79fd7df 100644 --- a/docs/snyk/v2.10.16/argocd-test.html +++ b/docs/snyk/v2.9.0-rc3/argocd-test.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,20 +456,19 @@

    Snyk test report

    -

    September 22nd 2024, 12:28:24 am (UTC+00:00)

    +

    October 29th 2023, 12:18:17 am (UTC+00:00)

    Scanned the following paths:
      -
    • /argo-cd/argoproj/argo-cd/v2/go.mod (gomodules)
    • -
    • /argo-cd/ui/yarn.lock (yarn)
    • +
    • /argo-cd/argoproj/argo-cd/v2 (gomodules)
    • /argo-cd (yarn)
    8 known vulnerabilities
    -
    164 vulnerable dependency paths
    -
    2042 dependencies
    +
    167 vulnerable dependency paths
    +
    1920 dependencies
    @@ -478,7 +477,7 @@

    Snyk test report

    -

    Allocation of Resources Without Limits or Throttling

    +

    Denial of Service (DoS)

    @@ -488,22 +487,19 @@

    Allocation of Resources Without Limits or Throttling

      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • Package Manager: golang
    • Vulnerable module: - golang.org/x/net/http2 + google.golang.org/grpc
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and google.golang.org/grpc@1.56.2 - github.com/argoproj/argo-cd/v2@0.0.0, k8s.io/apimachinery/pkg/util/net@0.26.11 and others
    @@ -517,9 +513,7 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -528,9 +522,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/soheilhy/cmux@0.1.5 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -539,9 +533,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -552,7 +546,7 @@

    Detailed paths

    github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -561,11 +555,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/watch@0.26.11 - - k8s.io/apimachinery/pkg/util/net@0.26.11 + google.golang.org/grpc/health@1.56.2 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -574,11 +566,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport@0.26.11 + google.golang.org/grpc/reflection@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -587,11 +577,9 @@

    Detailed paths

    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 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -600,11 +588,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.26.11 - - k8s.io/client-go/rest@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -613,11 +599,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport/spdy@0.26.11 - - k8s.io/client-go/rest@0.26.11 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -626,11 +610,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/pkg/kubeclientmetrics@#d56162821bd1 - - k8s.io/client-go/rest@0.26.11 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -639,11 +621,20 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/testing@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 + + google.golang.org/grpc@1.56.2 + + + + +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.26.11 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -652,11 +643,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/dynamic@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - k8s.io/client-go/rest@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -665,11 +656,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.26.11 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/client-go/rest@0.26.11 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig@1.16.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -678,11 +669,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/azure@0.26.11 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/client-go/rest@0.26.11 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -691,11 +682,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/gcp@0.26.11 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -704,11 +695,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/oidc@0.26.11 + google.golang.org/grpc/reflection@1.56.2 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.56.2 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -717,11 +708,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/record@0.26.11 + google.golang.org/grpc/health@1.56.2 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -730,13 +721,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -745,13 +736,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/client-go/transport@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/apimachinery/pkg/util/net@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -760,13 +751,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + 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 - google.golang.org/grpc@1.59.0 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - google.golang.org/grpc/internal/transport@1.59.0 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc@1.56.2 @@ -775,13 +768,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - google.golang.org/grpc@1.59.0 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - google.golang.org/grpc/internal/transport@1.59.0 + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.11.3 - golang.org/x/net/http2@0.19.0 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 + + google.golang.org/grpc@1.56.2 @@ -790,28 +785,105 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.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 - google.golang.org/grpc@1.59.0 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - google.golang.org/grpc/internal/transport@1.59.0 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@0.19.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

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, k8s.io/apimachinery/pkg/util/net@0.24.2 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - google.golang.org/grpc@1.59.0 + golang.org/x/net/http2@0.15.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/internal/transport@1.59.0 + github.com/soheilhy/cmux@0.1.5 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -820,13 +892,20 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + k8s.io/client-go/rest@0.24.2 - google.golang.org/grpc@1.59.0 + golang.org/x/net/http2@0.15.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/internal/transport@1.59.0 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -835,13 +914,24 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 + k8s.io/apimachinery/pkg/watch@0.24.2 + + k8s.io/apimachinery/pkg/util/net@0.24.2 + + golang.org/x/net/http2@0.15.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc@1.59.0 + google.golang.org/grpc@1.56.2 - google.golang.org/grpc/internal/transport@1.59.0 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -850,13 +940,24 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.46.1 + k8s.io/client-go/discovery@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.15.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc@1.59.0 + k8s.io/client-go/transport/spdy@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -865,13 +966,24 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/health/grpc_health_v1@1.59.0 + github.com/argoproj/pkg/kubeclientmetrics@#d56162821bd1 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.15.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc@1.59.0 + k8s.io/client-go/testing@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -880,13 +992,24 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/client-go/dynamic@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.15.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -895,13 +1018,24 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/clientcmd@0.26.11 + k8s.io/client-go/plugin/pkg/client/auth/azure@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.15.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/auth@0.26.11 + k8s.io/client-go/plugin/pkg/client/auth/gcp@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -910,13 +1044,24 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#84b9f7913604 + k8s.io/client-go/plugin/pkg/client/auth/oidc@0.24.2 + + k8s.io/client-go/rest@0.24.2 + + golang.org/x/net/http2@0.15.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/client-go/tools/record@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -925,13 +1070,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery/fake@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/testing@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -940,13 +1085,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/fake@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/testing@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -955,13 +1100,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/client-go/dynamic@0.26.11 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -970,13 +1115,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/apps/v1@0.26.11 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/client-go/tools/cache@0.26.11 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -985,13 +1130,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - k8s.io/client-go/tools/cache@0.26.11 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1000,13 +1145,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/listers/core/v1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - k8s.io/client-go/tools/cache@0.26.11 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1015,13 +1160,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/remotecommand@0.26.11 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - k8s.io/client-go/transport/spdy@0.26.11 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1030,13 +1175,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - google.golang.org/api/chat/v1@0.132.0 + google.golang.org/grpc@1.56.2 - google.golang.org/api/transport/http@0.132.0 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1045,15 +1190,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/pkg/apis/clientauthentication/v1beta1@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1062,15 +1205,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1079,15 +1220,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/api/rbac/v1@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1096,15 +1235,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/tools/clientcmd@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/tools/auth@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1113,15 +1250,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/errors@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + github.com/argoproj/notifications-engine/pkg/controller@#9dcecdc3eebf - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1130,15 +1265,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#b6ec82aedce5 + k8s.io/client-go/discovery/fake@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/testing@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1147,15 +1280,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/equality@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/kubernetes/fake@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/testing@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1164,15 +1295,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport/spdy@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1181,15 +1310,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/pkg/kubeclientmetrics@#d56162821bd1 - - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/informers/apps/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1198,15 +1325,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/testing@0.26.11 - - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/informers@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1215,15 +1340,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/azure@0.26.11 + k8s.io/client-go/listers/core/v1@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1232,15 +1355,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/gcp@0.26.11 - - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/tools/remotecommand@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1249,15 +1370,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/oidc@0.26.11 + k8s.io/client-go/pkg/apis/clientauthentication/v1beta1@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1266,15 +1387,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - 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.59.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1283,15 +1404,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/reflection@1.59.0 + k8s.io/api/rbac/v1@0.24.2 - google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.59.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1300,15 +1421,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/health@1.59.0 + k8s.io/api/core/v1@0.24.2 - google.golang.org/grpc/health/grpc_health_v1@1.59.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1317,15 +1438,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/cache@#b6ec82aedce5 + k8s.io/apimachinery/pkg/api/errors@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1334,15 +1455,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync@#b6ec82aedce5 + github.com/argoproj/gitops-engine/pkg/sync/common@#b0fffe419a0f - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1351,15 +1472,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@#b6ec82aedce5 + k8s.io/apimachinery/pkg/api/equality@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1368,15 +1489,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#84b9f7913604 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/client-go/listers/core/v1@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1385,15 +1506,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#84b9f7913604 + github.com/argoproj/pkg/kubeclientmetrics@#d56162821bd1 - k8s.io/client-go/tools/clientcmd@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/tools/auth@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1402,15 +1523,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/event@0.14.7 + k8s.io/client-go/testing@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1419,15 +1540,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/core/v1@0.26.11 + k8s.io/client-go/plugin/pkg/client/auth/azure@0.24.2 - k8s.io/client-go/listers/core/v1@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1436,15 +1557,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/cache@0.14.7 + k8s.io/client-go/plugin/pkg/client/auth/gcp@0.24.2 - sigs.k8s.io/controller-runtime/pkg/cache/internal@0.14.7 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1453,15 +1574,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/term@0.26.11 + k8s.io/client-go/plugin/pkg/client/auth/oidc@0.24.2 - k8s.io/client-go/tools/remotecommand@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/client-go/transport/spdy@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1470,15 +1591,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/metrics@0.14.7 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - k8s.io/client-go/tools/leaderelection@0.26.11 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/client-go/tools/leaderelection/resourcelock@0.26.11 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.26.11 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1487,15 +1608,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + google.golang.org/grpc/reflection@1.56.2 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.56.2 - google.golang.org/api/chat/v1@0.132.0 + google.golang.org/grpc@1.56.2 - google.golang.org/api/transport/http@0.132.0 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1504,15 +1625,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#84b9f7913604 + google.golang.org/grpc/health@1.56.2 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - google.golang.org/api/chat/v1@0.132.0 + google.golang.org/grpc@1.56.2 - google.golang.org/api/transport/http@0.132.0 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1521,17 +1642,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/Azure/kubelogin/pkg/token@0.0.20 - - k8s.io/client-go/pkg/apis/clientauthentication/v1beta1@0.26.11 + github.com/argoproj/gitops-engine/pkg/cache@#b0fffe419a0f - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1540,17 +1659,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/dynamic@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync@#b0fffe419a0f - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1559,17 +1676,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/ignore@#b6ec82aedce5 + github.com/argoproj/gitops-engine/pkg/utils/kube@#b0fffe419a0f - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1578,17 +1693,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#b6ec82aedce5 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + github.com/argoproj/notifications-engine/pkg/api@#9dcecdc3eebf - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/listers/core/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1597,17 +1710,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/testing@#b6ec82aedce5 + github.com/argoproj/notifications-engine/pkg/cmd@#9dcecdc3eebf - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/client-go/tools/clientcmd@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/tools/auth@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1616,17 +1727,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/strategicpatch@0.26.11 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1635,17 +1744,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime@0.14.7 + k8s.io/client-go/informers/core/v1@0.24.2 - sigs.k8s.io/controller-runtime/pkg/scheme@0.14.7 + k8s.io/client-go/listers/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1654,17 +1761,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/listers/core/v1@0.26.11 - - k8s.io/api/core/v1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/cache/internal@0.11.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1673,17 +1778,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/resource@0.26.11 + k8s.io/kubectl/pkg/util/term@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/tools/remotecommand@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1692,17 +1795,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/health@#b6ec82aedce5 + github.com/Azure/kubelogin/pkg/token@0.0.20 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/client-go/pkg/apis/clientauthentication/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1711,17 +1814,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/util/retry@0.26.11 + k8s.io/apimachinery/pkg/util/managedfields@0.24.2 - k8s.io/apimachinery/pkg/api/errors@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1730,17 +1833,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/managedfields@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/resource@#b0fffe419a0f - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1749,17 +1852,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/client-go/tools/pager@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1768,17 +1871,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/portforward@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/ignore@#b0fffe419a0f - k8s.io/api/core/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1787,17 +1890,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#b0fffe419a0f - k8s.io/apimachinery/pkg/api/equality@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1806,17 +1909,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/validation@0.26.11 + github.com/argoproj/gitops-engine/pkg/utils/testing@#b0fffe419a0f - k8s.io/apimachinery/pkg/apis/meta/v1/validation@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1825,17 +1928,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery/fake@0.26.11 + sigs.k8s.io/controller-runtime@0.11.0 - k8s.io/client-go/testing@0.26.11 + sigs.k8s.io/controller-runtime/pkg/scheme@0.11.0 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1844,17 +1947,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/fake@0.26.11 + k8s.io/client-go/listers/core/v1@0.24.2 - k8s.io/client-go/testing@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1863,17 +1966,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/remotecommand@0.26.11 + k8s.io/kubectl/pkg/util/resource@0.24.2 - k8s.io/client-go/transport/spdy@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1882,17 +1985,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/health@#b6ec82aedce5 + github.com/argoproj/gitops-engine/pkg/health@#b0fffe419a0f - github.com/argoproj/gitops-engine/pkg/utils/kube@#b6ec82aedce5 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1901,17 +2004,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#b6ec82aedce5 + k8s.io/client-go/util/retry@0.24.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@#b6ec82aedce5 + k8s.io/apimachinery/pkg/api/errors@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1920,17 +2023,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.14.7 + k8s.io/client-go/tools/cache@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.14.7 + k8s.io/client-go/tools/pager@0.24.2 - k8s.io/client-go/restmapper@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1939,17 +2042,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + k8s.io/client-go/tools/portforward@0.24.2 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + k8s.io/api/core/v1@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1958,17 +2061,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/envtest@0.14.7 + k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1@0.24.2 - sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane@0.14.7 + k8s.io/apimachinery/pkg/api/equality@0.24.2 - k8s.io/client-go/tools/clientcmd@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/tools/auth@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1977,17 +2080,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/handler@0.14.7 + k8s.io/apimachinery/pkg/api/validation@0.24.2 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1/validation@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -1996,17 +2099,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#84b9f7913604 + k8s.io/client-go/discovery/fake@0.24.2 - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + k8s.io/client-go/testing@0.24.2 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + k8s.io/client-go/rest@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/transport@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2015,17 +2118,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#84b9f7913604 + k8s.io/client-go/kubernetes/fake@0.24.2 - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + k8s.io/client-go/testing@0.24.2 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + k8s.io/client-go/rest@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/transport@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2034,19 +2137,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/diff@#b6ec82aedce5 + k8s.io/client-go/tools/remotecommand@0.24.2 - k8s.io/apimachinery/pkg/util/strategicpatch@0.26.11 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2055,19 +2156,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/runtime/serializer@0.26.11 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.26.11 + github.com/argoproj/gitops-engine/pkg/health@#b0fffe419a0f - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + github.com/argoproj/gitops-engine/pkg/utils/kube@#b0fffe419a0f - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2076,19 +2175,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/core/v1@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/common@#b0fffe419a0f - k8s.io/client-go/listers/core/v1@0.26.11 + github.com/argoproj/gitops-engine/pkg/utils/kube@#b0fffe419a0f - k8s.io/api/core/v1@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2097,19 +2194,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 - - k8s.io/api/storage/v1beta1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.11.0 - k8s.io/api/core/v1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2118,19 +2213,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/record@0.26.11 + sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - k8s.io/client-go/tools/reference@0.26.11 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - k8s.io/api/core/v1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2139,19 +2232,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@#b6ec82aedce5 - - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#b6ec82aedce5 + sigs.k8s.io/controller-runtime/pkg/envtest@0.11.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#b6ec82aedce5 + sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/tools/clientcmd@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/tools/auth@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2160,19 +2251,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#84b9f7913604 + github.com/argoproj/gitops-engine/pkg/diff@#b0fffe419a0f - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/apimachinery/pkg/util/managedfields@0.24.2 - k8s.io/client-go/tools/pager@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2181,19 +2272,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/apps/v1@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/hook@#b0fffe419a0f - k8s.io/client-go/tools/cache@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/resource@#b0fffe419a0f - k8s.io/client-go/tools/pager@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2202,19 +2293,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer@0.24.2 - k8s.io/client-go/tools/cache@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - k8s.io/client-go/tools/pager@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2223,19 +2314,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2244,19 +2335,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/term@0.26.11 + k8s.io/client-go/informers/core/v1@0.24.2 - k8s.io/client-go/tools/remotecommand@0.26.11 + k8s.io/client-go/listers/core/v1@0.24.2 - k8s.io/client-go/transport/spdy@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2265,19 +2356,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/metrics@0.14.7 + k8s.io/client-go/tools/record@0.24.2 - k8s.io/client-go/tools/leaderelection@0.26.11 + k8s.io/client-go/tools/reference@0.24.2 - k8s.io/client-go/tools/leaderelection/resourcelock@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/transport@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2286,19 +2377,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/controller@#9dcecdc3eebf - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/tools/cache@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/client-go/tools/pager@0.24.2 - google.golang.org/api/option@0.132.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2307,19 +2398,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 + k8s.io/client-go/informers/apps/v1@0.24.2 - github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 + k8s.io/client-go/tools/cache@0.24.2 - github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 + k8s.io/client-go/tools/pager@0.24.2 - github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2328,19 +2419,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#84b9f7913604 + k8s.io/client-go/informers@0.24.2 - k8s.io/client-go/listers/core/v1@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/tools/pager@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2349,19 +2440,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes@0.26.11 + github.com/argoproj/notifications-engine/pkg/api@#9dcecdc3eebf - k8s.io/client-go/kubernetes/typed/storage/v1beta1@0.26.11 + k8s.io/client-go/listers/core/v1@0.24.2 - k8s.io/client-go/applyconfigurations/storage/v1beta1@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/client-go/applyconfigurations/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2370,21 +2461,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/clientcmd@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/client-go/tools/clientcmd/api/latest@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2393,21 +2482,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.26.11 - - k8s.io/client-go/kubernetes/scheme@0.26.11 + k8s.io/kubectl/pkg/util/term@0.24.2 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/client-go/tools/remotecommand@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/transport/spdy@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/transport@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2416,21 +2503,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/event@0.14.7 - - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/client-go/dynamic@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2439,21 +2524,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/cache@0.14.7 - - sigs.k8s.io/controller-runtime/pkg/cache/internal@0.14.7 + sigs.k8s.io/controller-runtime/pkg/handler@0.11.0 - k8s.io/client-go/tools/cache@0.26.11 + sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.11.0 - k8s.io/client-go/tools/pager@0.26.11 + sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/cache/internal@0.11.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2462,21 +2545,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + k8s.io/client-go/kubernetes@0.24.2 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + k8s.io/client-go/kubernetes/typed/storage/v1beta1@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/client-go/applyconfigurations/storage/v1beta1@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/client-go/applyconfigurations/meta/v1@0.24.2 - google.golang.org/api/option@0.132.0 + k8s.io/client-go/discovery@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/client-go/rest@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2485,21 +2566,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/diff@#b6ec82aedce5 - - k8s.io/kubectl/pkg/cmd/util@0.26.11 + sigs.k8s.io/controller-runtime/pkg/builder@0.11.0 - k8s.io/kubectl/pkg/validation@0.26.11 + sigs.k8s.io/controller-runtime/pkg/webhook/admission@0.11.0 - k8s.io/cli-runtime/pkg/resource@0.26.11 + sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics@0.11.0 - k8s.io/client-go/restmapper@0.26.11 + sigs.k8s.io/controller-runtime/pkg/metrics@0.11.0 - k8s.io/client-go/discovery@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2508,21 +2587,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@#b6ec82aedce5 + k8s.io/client-go/tools/clientcmd@0.24.2 - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#b6ec82aedce5 + k8s.io/client-go/tools/clientcmd/api/latest@0.24.2 - github.com/argoproj/gitops-engine/pkg/sync/common@#b6ec82aedce5 + k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@#b6ec82aedce5 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2531,21 +2610,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#b6ec82aedce5 + k8s.io/client-go/discovery@0.24.2 - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#b6ec82aedce5 + k8s.io/client-go/kubernetes/scheme@0.24.2 - github.com/argoproj/gitops-engine/pkg/sync/common@#b6ec82aedce5 + k8s.io/api/storage/v1beta1@0.24.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@#b6ec82aedce5 + k8s.io/api/core/v1@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2554,21 +2633,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/source@0.14.7 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - sigs.k8s.io/controller-runtime/pkg/source/internal@0.14.7 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + k8s.io/client-go/dynamic@0.24.2 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2577,21 +2656,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/builder@0.14.7 + sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - sigs.k8s.io/controller-runtime/pkg/webhook/admission@0.14.7 + sigs.k8s.io/controller-runtime/pkg/cache/internal@0.11.0 - sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics@0.14.7 + k8s.io/client-go/tools/cache@0.24.2 - sigs.k8s.io/controller-runtime/pkg/metrics@0.14.7 + k8s.io/client-go/tools/pager@0.24.2 - k8s.io/client-go/tools/leaderelection@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/tools/leaderelection/resourcelock@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2600,21 +2679,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#84b9f7913604 + github.com/argoproj/gitops-engine/pkg/sync/hook@#b0fffe419a0f - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#b0fffe419a0f - google.golang.org/api/chat/v1@0.132.0 + github.com/argoproj/gitops-engine/pkg/sync/common@#b0fffe419a0f - google.golang.org/api/transport/http@0.132.0 + github.com/argoproj/gitops-engine/pkg/utils/kube@#b0fffe419a0f - google.golang.org/api/option@0.132.0 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/client-go/discovery@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2623,23 +2702,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/builder@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#b0fffe419a0f - sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#b0fffe419a0f - k8s.io/apimachinery/pkg/runtime/serializer@0.26.11 + github.com/argoproj/gitops-engine/pkg/sync/common@#b0fffe419a0f - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.26.11 + github.com/argoproj/gitops-engine/pkg/utils/kube@#b0fffe419a0f - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2648,23 +2725,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/envtest@0.14.7 - - sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.14.7 + sigs.k8s.io/controller-runtime@0.11.0 - k8s.io/apimachinery/pkg/runtime/serializer@0.26.11 + sigs.k8s.io/controller-runtime/pkg/manager@0.11.0 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.26.11 + sigs.k8s.io/controller-runtime/pkg/webhook@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/metrics@0.11.0 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2673,23 +2748,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#84b9f7913604 + sigs.k8s.io/controller-runtime/pkg/source@0.11.0 - k8s.io/client-go/tools/clientcmd@0.26.11 + sigs.k8s.io/controller-runtime/pkg/source/internal@0.11.0 - k8s.io/client-go/tools/clientcmd/api/latest@0.26.11 + sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.26.11 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/client-go/rest@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 - - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2698,23 +2771,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + sigs.k8s.io/controller-runtime/pkg/builder@0.11.0 - k8s.io/client-go/discovery@0.26.11 + sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.11.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer@0.24.2 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2723,23 +2796,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes@0.26.11 + sigs.k8s.io/controller-runtime/pkg/envtest@0.11.0 - k8s.io/client-go/kubernetes/typed/storage/v1beta1@0.26.11 + sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.11.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer@0.24.2 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2748,23 +2821,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube/scheme@#b6ec82aedce5 + github.com/argoproj/notifications-engine/pkg/cmd@#9dcecdc3eebf - k8s.io/kubernetes/pkg/apis/storage/install@1.26.11 + k8s.io/client-go/tools/clientcmd@0.24.2 - k8s.io/kubernetes/pkg/apis/storage/v1alpha1@1.26.11 + k8s.io/client-go/tools/clientcmd/api/latest@0.24.2 - k8s.io/api/storage/v1alpha1@0.26.11 + k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2773,23 +2846,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + k8s.io/client-go/discovery@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2798,23 +2871,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/handler@0.14.7 + k8s.io/client-go/kubernetes@0.24.2 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + k8s.io/client-go/kubernetes/typed/storage/v1beta1@0.24.2 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2823,23 +2896,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#84b9f7913604 + github.com/argoproj/gitops-engine/pkg/utils/kube/scheme@#b0fffe419a0f - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + k8s.io/kubernetes/pkg/apis/storage/install@1.24.2 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + k8s.io/kubernetes/pkg/apis/storage/v1alpha1@1.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/api/storage/v1alpha1@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/api/core/v1@0.24.2 - google.golang.org/api/option@0.132.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2848,23 +2921,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/ignore@#b6ec82aedce5 + sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@#b6ec82aedce5 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#b6ec82aedce5 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#b6ec82aedce5 + k8s.io/client-go/dynamic@0.24.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@#b6ec82aedce5 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2873,23 +2946,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/ignore@#b0fffe419a0f - sigs.k8s.io/controller-runtime/pkg/source@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/hook@#b0fffe419a0f - sigs.k8s.io/controller-runtime/pkg/source/internal@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#b0fffe419a0f - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + github.com/argoproj/gitops-engine/pkg/sync/common@#b0fffe419a0f - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + github.com/argoproj/gitops-engine/pkg/utils/kube@#b0fffe419a0f - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2898,23 +2971,23 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime@0.14.7 + sigs.k8s.io/controller-runtime/pkg/controller@0.11.0 - sigs.k8s.io/controller-runtime/pkg/manager@0.14.7 + sigs.k8s.io/controller-runtime/pkg/source@0.11.0 - sigs.k8s.io/controller-runtime/pkg/webhook@0.14.7 + sigs.k8s.io/controller-runtime/pkg/source/internal@0.11.0 - sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics@0.14.7 + sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - sigs.k8s.io/controller-runtime/pkg/metrics@0.14.7 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - k8s.io/client-go/tools/leaderelection@0.26.11 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/client-go/tools/leaderelection/resourcelock@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/client-go/rest@0.26.11 + k8s.io/client-go/rest@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2923,23 +2996,25 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#84b9f7913604 + github.com/argoproj/gitops-engine/pkg/cache@#b0fffe419a0f + + k8s.io/kubectl/pkg/util/openapi@0.24.2 - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + k8s.io/client-go/discovery@0.24.2 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + k8s.io/client-go/kubernetes/scheme@0.24.2 - google.golang.org/api/chat/v1@0.132.0 + k8s.io/api/storage/v1beta1@0.24.2 - google.golang.org/api/transport/http@0.132.0 + k8s.io/api/core/v1@0.24.2 - google.golang.org/api/option@0.132.0 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - google.golang.org/grpc@1.59.0 + k8s.io/apimachinery/pkg/watch@0.24.2 - google.golang.org/grpc/internal/transport@1.59.0 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2948,25 +3023,25 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/cache@#b6ec82aedce5 + github.com/argoproj/gitops-engine/pkg/sync@#b0fffe419a0f - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/client-go/kubernetes/scheme@0.26.11 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -2975,25 +3050,25 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync@#b6ec82aedce5 + github.com/argoproj/gitops-engine/pkg/utils/kube@#b0fffe419a0f - k8s.io/kubectl/pkg/util/openapi@0.26.11 + k8s.io/kubectl/pkg/util/openapi@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/client-go/kubernetes/scheme@0.26.11 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -3002,25 +3077,25 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@#b6ec82aedce5 + sigs.k8s.io/controller-runtime/pkg/handler@0.11.0 - k8s.io/kubectl/pkg/util/openapi@0.26.11 + sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.11.0 - k8s.io/client-go/discovery@0.26.11 + sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - k8s.io/client-go/kubernetes/scheme@0.26.11 + sigs.k8s.io/controller-runtime/pkg/cache/internal@0.11.0 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/client-go/tools/cache@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/client-go/tools/pager@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -3029,27 +3104,27 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.14.7 + sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.11.0 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.14.7 + sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - k8s.io/client-go/restmapper@0.26.11 + k8s.io/client-go/restmapper@0.24.2 - k8s.io/client-go/discovery@0.26.11 + k8s.io/client-go/discovery@0.24.2 - k8s.io/client-go/kubernetes/scheme@0.26.11 + k8s.io/client-go/kubernetes/scheme@0.24.2 - k8s.io/api/storage/v1beta1@0.26.11 + k8s.io/api/storage/v1beta1@0.24.2 - k8s.io/api/core/v1@0.26.11 + k8s.io/api/core/v1@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -3058,27 +3133,27 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/source@0.14.7 + sigs.k8s.io/controller-runtime/pkg/source@0.11.0 - sigs.k8s.io/controller-runtime/pkg/source/internal@0.14.7 + sigs.k8s.io/controller-runtime/pkg/source/internal@0.11.0 - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -3087,29 +3162,29 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller@0.14.7 + sigs.k8s.io/controller-runtime/pkg/controller@0.11.0 - sigs.k8s.io/controller-runtime/pkg/source@0.14.7 + sigs.k8s.io/controller-runtime/pkg/source@0.11.0 - sigs.k8s.io/controller-runtime/pkg/source/internal@0.14.7 + sigs.k8s.io/controller-runtime/pkg/source/internal@0.11.0 - sigs.k8s.io/controller-runtime/pkg/predicate@0.14.7 + sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.14.7 + sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - sigs.k8s.io/controller-runtime/pkg/client@0.14.7 + sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - k8s.io/client-go/dynamic@0.26.11 + k8s.io/client-go/dynamic@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.26.11 + k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - k8s.io/apimachinery/pkg/watch@0.26.11 + k8s.io/apimachinery/pkg/watch@0.24.2 - k8s.io/apimachinery/pkg/util/net@0.26.11 + k8s.io/apimachinery/pkg/util/net@0.24.2 - golang.org/x/net/http2@0.19.0 + golang.org/x/net/http2@0.15.0 @@ -3121,52 +3196,58 @@

      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 Allocation of Resources Without Limits or Throttling when reading header data from CONTINUATION frames. As part of the HPACK flow, all incoming HEADERS and CONTINUATION frames are read even if their payloads exceed MaxHeaderBytes and will be discarded. An attacker can send excessive data over a connection to render it unresponsive.

      +

      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.23.0 or higher.

      +

      Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

      References


    -
    -

    Prototype Pollution

    +
    +

    LGPL-3.0 license

    -
    - high severity +
    + medium severity

    • - Manifest file: /argo-cd ui/yarn.lock -
    • -
    • - Package Manager: npm + Package Manager: golang
    • - Vulnerable module: + Module: - dompurify + 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
    @@ -3178,11 +3259,11 @@

    Detailed paths

    • Introduced through: - argo-cd-ui@1.0.0 + github.com/argoproj/argo-cd/v2@0.0.0 - redoc@2.0.0-rc.64 + github.com/Azure/kubelogin/pkg/token@0.0.20 - dompurify@2.3.6 + gopkg.in/retry.v1@1.0.3 @@ -3193,109 +3274,17 @@

      Detailed paths


      -

      Overview

      -

      dompurify is a DOM-only XSS sanitizer for HTML, MathML and SVG.

      -

      Affected versions of this package are vulnerable to Prototype Pollution due to improper user input sanitization through the depth-checking mechanism, an attacker can exploit this vulnerability by using special nesting techniques to create a malicious HTML file.

      -

      Details

      -

      Prototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as __proto__, constructor and prototype. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values. Properties on the Object.prototype are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.

      -

      There are two main ways in which the pollution of prototypes occurs:

      -
        -
      • Unsafe Object recursive merge

        -
      • -
      • Property definition by path

        -
      • -
      -

      Unsafe Object recursive merge

      -

      The logic of a vulnerable recursive merge function follows the following high-level model:

      -
      merge (target, source)
      -        
      -          foreach property of source
      -        
      -            if property exists and is an object on both the target and the source
      -        
      -              merge(target[property], source[property])
      -        
      -            else
      -        
      -              target[property] = source[property]
      -        
      -
      - -

      When the source object contains a property named __proto__ defined with Object.defineProperty() , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of Object and the source of Object as defined by the attacker. Properties are then copied on the Object prototype.

      -

      Clone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: merge({},source).

      -

      lodash and Hoek are examples of libraries susceptible to recursive merge attacks.

      -

      Property definition by path

      -

      There are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: theFunction(object, path, value)

      -

      If the attacker can control the value of “path”, they can set this value to __proto__.myValue. myValue is then assigned to the prototype of the class of the object.

      -

      Types of attacks

      -

      There are a few methods by which Prototype Pollution can be manipulated:

      - - - - - - - - - - - - - - - - - - - - - - - -
      TypeOriginShort description
      Denial of service (DoS)ClientThis is the most likely attack.
      DoS occurs when Object holds generic functions that are implicitly called for various operations (for example, toString and valueOf).
      The attacker pollutes Object.prototype.someattr and alters its state to an unexpected value such as Int or Object. In this case, the code fails and is likely to cause a denial of service.
      For example: if an attacker pollutes Object.prototype.toString by defining it as an integer, if the codebase at any point was reliant on someobject.toString() it would fail.
      Remote Code ExecutionClientRemote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.
      For example: eval(someobject.someattr). In this case, if the attacker pollutes Object.prototype.someattr they are likely to be able to leverage this in order to execute code.
      Property InjectionClientThe attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.
      For example: if a codebase checks privileges for someuser.isAdmin, then when the attacker pollutes Object.prototype.isAdmin and sets it to equal true, they can then achieve admin privileges.
      -

      Affected environments

      -

      The following environments are susceptible to a Prototype Pollution attack:

      -
        -
      • Application server

        -
      • -
      • Web server

        -
      • -
      • Web browser

        -
      • -
      -

      How to prevent

      -
        -
      1. Freeze the prototype— use Object.freeze (Object.prototype).

        -
      2. -
      3. Require schema validation of JSON input.

        -
      4. -
      5. Avoid using unsafe recursive merge functions.

        -
      6. -
      7. Consider using objects without prototypes (for example, Object.create(null)), breaking the prototype chain and preventing pollution.

        -
      8. -
      9. As a best practice use Map instead of Object.

        -
      10. -
      -

      For more information on this vulnerability type:

      -

      Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018

      -

      Remediation

      -

      Upgrade dompurify to version 2.5.4, 3.1.3 or higher.

      -

      References

      - +

      LGPL-3.0 license


    -

    Regular Expression Denial of Service (ReDoS)

    +

    MPL-2.0 license

    @@ -3306,21 +3295,18 @@

    Regular Expression Denial of Service (ReDoS)

    • - Manifest file: /argo-cd ui/yarn.lock -
    • -
    • - Package Manager: npm + Package Manager: golang
    • - Vulnerable module: + Module: - path-to-regexp + github.com/r3labs/diff
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/r3labs/diff@1.1.0 - argo-cd-ui@1.0.0, react-router@4.3.1 and others
    @@ -3332,39 +3318,68 @@

    Detailed paths

    • Introduced through: - argo-cd-ui@1.0.0 - - react-router@4.3.1 + github.com/argoproj/argo-cd/v2@0.0.0 - path-to-regexp@1.8.0 + github.com/r3labs/diff@1.1.0
    • -
    • - Introduced through: - argo-cd-ui@1.0.0 - - react-router-dom@4.3.1 - - react-router@4.3.1 - - path-to-regexp@1.8.0 - - +
    - +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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: - argo-cd-ui@1.0.0 - - argo-ui@1.0.0 - - react-router-dom@4.3.1 + github.com/argoproj/argo-cd/v2@0.0.0 - react-router@4.3.1 + code.gitea.io/sdk/gitea@0.15.1 - path-to-regexp@1.8.0 + github.com/hashicorp/go-version@1.2.1 @@ -3375,96 +3390,17 @@

      Detailed paths


      -

      Overview

      -

      Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) when including multiple regular expression parameters in a single segment, which will produce the regular expression /^\/([^\/]+?)-([^\/]+?)\/?$/, if two parameters within a single segment are separated by a character other than a / or .. Poor performance will block the event loop and can lead to a DoS.

      -

      Note: - While the 8.0.0 release has completely eliminated the vulnerable functionality, prior versions that have received the patch to mitigate backtracking may still be vulnerable if custom regular expressions are used. So it is strongly recommended for regular expression input to be controlled to avoid malicious performance degradation in those versions. This behavior is enforced as of version 7.1.0 via the strict option, which returns an error if a dangerous regular expression is detected.

      -

      Workaround

      -

      This vulnerability can be avoided by using a custom regular expression for parameters after the first in a segment, which excludes - and /.

      -

      PoC

      -
      /a${'-a'.repeat(8_000)}/a
      -        
      -

      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 path-to-regexp to version 0.1.10, 1.9.0, 3.3.0, 6.3.0, 8.0.0 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Regular Expression Denial of Service (ReDoS)

    +

    MPL-2.0 license

    @@ -3474,21 +3410,18 @@

    Regular Expression Denial of Service (ReDoS)


      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/whilp/git-urls + github.com/hashicorp/go-retryablehttp
    • Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 and github.com/whilp/git-urls@1.0.2 + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/hashicorp/go-retryablehttp@0.7.4
    @@ -3503,7 +3436,7 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/whilp/git-urls@1.0.2 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -3512,9 +3445,9 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + github.com/xanzy/go-gitlab@0.91.1 - github.com/whilp/git-urls@1.0.2 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -3523,11 +3456,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/whilp/git-urls@1.0.2 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -3536,11 +3469,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf + + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/whilp/git-urls@1.0.2 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -3549,13 +3484,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/cmd@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/whilp/git-urls@1.0.2 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -3564,163 +3499,32 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/api@#9dcecdc3eebf + + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/whilp/git-urls@1.0.2 + github.com/hashicorp/go-retryablehttp@0.7.4 - - -
    - -
    - -

    Overview

    -

    github.com/whilp/git-urls is a Git URLs parser

    -

    Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) due to the usage of an insecure regular expression in scpSyntax. Exploiting this vulnerability is possible when a long input is provided inside the directory path of the git URL.

    -

    Note: - This vulnerability has existed since commit 4a18977c6eecbf4ce0ca1e486e9ba77072ba4395.

    -

    PoC

    -
    
    -        var payload = strings.Repeat("////", 19000000) //payload used, the number can be tweaked to cause 7 second delay
    -        malicious_url := "6en6ar@-:0////" + payload + "\"
    -        begin := time.Now()
    -        //u, err := giturls.ParseScp("remote_username@10.10.0.2:/remote/directory")// normal git url
    -        _, err := giturls.ParseScp(malicious_url)
    -        if err != nil {
    -        fmt.Errorf("[ - ] Error ->" + err.Error())
    -        }
    -        //fmt.Println("[ + ] Url --> " + u.Host)
    -        elapse := time.Since(begin)
    -        fmt.Printf("Function took %s", elapse)
    -        
    -

    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

    -

    There is no fixed version for github.com/whilp/git-urls.

    -

    References

    - - -
    - - - -
    -
    -

    Denial of Service (DoS)

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • -
    • - Package Manager: golang -
    • -
    • - Vulnerable module: - - github.com/rs/cors -
    • - -
    • Introduced through: - - - github.com/argoproj/argo-cd/v2@0.0.0, github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + github.com/argoproj/notifications-engine/pkg/controller@#9dcecdc3eebf + + 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/rs/cors@1.9.0 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -3731,67 +3535,17 @@

      Detailed paths


      -

      Overview

      -

      Affected versions of this package are vulnerable to Denial of Service (DoS) through the processing of malicious preflight requests that include a Access-Control-Request-Headers header with excessive commas. An attacker can induce excessive memory consumption and potentially crash the server by sending specially crafted requests.

      -

      PoC

      -
      
      -        func BenchmarkPreflightAdversarialACRH(b *testing.B) {
      -            resps := makeFakeResponses(b.N)
      -            req, _ := http.NewRequest(http.MethodOptions, dummyEndpoint, nil)
      -            req.Header.Add(headerOrigin, dummyOrigin)
      -            req.Header.Add(headerACRM, http.MethodGet)
      -            req.Header[headerACRH] = adversarialACRH
      -            handler := Default().Handler(testHandler)
      -        
      -            b.ReportAllocs()
      -            b.ResetTimer()
      -            for i := 0; i < b.N; i++ {
      -                handler.ServeHTTP(resps[i], req)
      -            }
      -        }
      -        
      -        var adversarialACRH []string
      -        
      -        func init() { // populates adversarialACRH
      -            n := int(math.Floor(math.Sqrt(http.DefaultMaxHeaderBytes)))
      -            commas := strings.Repeat(",", n)
      -            res := make([]string, n)
      -            for i := range res {
      -                res[i] = commas
      -            }
      -            adversarialACRH = res
      -        }
      -        
      -

      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/rs/cors to version 1.11.0 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Insertion of Sensitive Information into Log File

    +

    MPL-2.0 license

    @@ -3801,22 +3555,19 @@

    Insertion of Sensitive Information into Log File


      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/hashicorp/go-retryablehttp + github.com/hashicorp/go-cleanhttp
    • 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/hashicorp/go-retryablehttp@0.7.4 and others
    @@ -3831,17 +3582,8 @@

    Detailed paths

    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@#84b9f7913604 - - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3852,7 +3594,7 @@

    Detailed paths

    github.com/xanzy/go-gitlab@0.91.1 - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3861,24 +3603,11 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 - - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + 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/cmd@#84b9f7913604 - - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3887,41 +3616,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + 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/api@#84b9f7913604 - - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 - - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 - - 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@#84b9f7913604 - - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 - - github.com/hashicorp/go-retryablehttp@0.7.4 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3930,13 +3631,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + 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 @@ -3945,13 +3648,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/cmd@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + 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 @@ -3960,15 +3665,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/api@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + 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 @@ -3977,15 +3684,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/controller@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/subscriptions@#84b9f7913604 + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/services@#84b9f7913604 + 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 @@ -3996,25 +3705,17 @@

    Detailed paths


    -

    Overview

    -

    Affected versions of this package are vulnerable to Insertion of Sensitive Information into Log File due to not sanitizing urls when writing them to the log file. This could lead to an attacker writing sensitive HTTP basic auth credentials to the log file.

    -

    Remediation

    -

    Upgrade github.com/hashicorp/go-retryablehttp to version 0.7.7 or higher.

    -

    References

    - +

    MPL-2.0 license


  • -

    Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')

    +

    MPL-2.0 license

    @@ -4024,22 +3725,19 @@

    Concurrent Execution using Shared Resource with Improper
      -
    • - Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod -
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/Azure/azure-sdk-for-go/sdk/azidentity + 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/Azure/kubelogin/pkg/token@0.0.20 and others
    @@ -4053,9 +3751,7 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/Azure/kubelogin/pkg/token@0.0.20 - - github.com/Azure/azure-sdk-for-go/sdk/azidentity@1.1.0 + github.com/gosimple/slug@1.13.1 @@ -4066,116 +3762,12 @@

    Detailed paths


    -

    Overview

    -

    github.com/Azure/azure-sdk-for-go/sdk/azidentity is a module that provides Microsoft Entra ID (formerly Azure Active Directory) token authentication support across the Azure SDK. It includes a set of TokenCredential implementations, which can be used with Azure SDK clients supporting token authentication.

    -

    Affected versions of this package are vulnerable to Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') in the authentication process. An attacker can elevate privileges by exploiting race conditions during the token validation steps. This is only exploitable if the application is configured to use multiple threads or processes for handling authentication requests.

    -

    Notes:

    -
      -
    1. An attacker who successfully exploited the vulnerability could elevate privileges and read any file on the file system with SYSTEM access permissions;

      -
    2. -
    3. An attacker who successfully exploits this vulnerability can only obtain read access to the system files by exploiting this vulnerability. The attacker cannot perform write or delete operations on the files;

      -
    4. -
    5. The vulnerability exists in the following credential types: DefaultAzureCredential and ManagedIdentityCredential;

      -
    6. -
    7. The vulnerability exists in the following credential types:

      -
    8. -
    -

    ManagedIdentityApplication (.NET)

    -

    ManagedIdentityApplication (Java)

    -

    ManagedIdentityApplication (Node.js)

    -

    Remediation

    -

    Upgrade github.com/Azure/azure-sdk-for-go/sdk/azidentity to version 1.6.0 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Template Injection

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Manifest file: /argo-cd ui/yarn.lock -
    • -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - dompurify -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, redoc@2.0.0-rc.64 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - redoc@2.0.0-rc.64 - - dompurify@2.3.6 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    dompurify is a DOM-only XSS sanitizer for HTML, MathML and SVG.

    -

    Affected versions of this package are vulnerable to Template Injection in purify.js, due to inconsistencies in the parsing of XML and HTML tags. Executable code can be injected in HTML inside XML CDATA blocks.

    -

    PoC

    -
    <![CDATA[ ><img src onerror=alert(1)> ]]>
    -        
    -

    Remediation

    -

    Upgrade dompurify to version 2.4.9, 3.0.11 or higher.

    -

    References

    - +

    MPL-2.0 license


    diff --git a/docs/snyk/v2.9.0-rc3/ghcr.io_dexidp_dex_v2.37.0.html b/docs/snyk/v2.9.0-rc3/ghcr.io_dexidp_dex_v2.37.0.html new file mode 100644 index 0000000000000..99e019bd198fc --- /dev/null +++ b/docs/snyk/v2.9.0-rc3/ghcr.io_dexidp_dex_v2.37.0.html @@ -0,0 +1,2862 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:18:27 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 (gomodules)
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (gomodules)
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (gomodules)
    • +
    +
    + +
    +
    28 known vulnerabilities
    +
    79 vulnerable dependency paths
    +
    786 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    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

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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

    + + +
    + + + +
    +
    +

    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:3.18. + 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

    + + +
    + + + +
    +
    +

    Cross-site Scripting (XSS)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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

    + +
    + + + +
    +
    +

    CVE-2023-5363

    +
    + +
    + 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

    +

    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

    + + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.9.0-rc3/haproxy_2.6.14-alpine.html b/docs/snyk/v2.9.0-rc3/haproxy_2.6.14-alpine.html new file mode 100644 index 0000000000000..d4837cba79b4d --- /dev/null +++ b/docs/snyk/v2.9.0-rc3/haproxy_2.6.14-alpine.html @@ -0,0 +1,683 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:18:32 am (UTC+00:00)

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

    CVE-2023-5363

    +
    + +
    + 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

    +

    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

    + + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.9.0-rc3/quay.io_argoproj_argocd_v2.9.0-rc3.html b/docs/snyk/v2.9.0-rc3/quay.io_argoproj_argocd_v2.9.0-rc3.html new file mode 100644 index 0000000000000..c815a4833afb8 --- /dev/null +++ b/docs/snyk/v2.9.0-rc3/quay.io_argoproj_argocd_v2.9.0-rc3.html @@ -0,0 +1,3366 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:18:58 am (UTC+00:00)

    +
    +
    + Scanned the following paths: +
      +
    • quay.io/argoproj/argocd:v2.9.0-rc3/argoproj/argocd (deb)
    • quay.io/argoproj/argocd:v2.9.0-rc3/argoproj/argo-cd/v2 (gomodules)
    • quay.io/argoproj/argocd:v2.9.0-rc3 (gomodules)
    • quay.io/argoproj/argocd:v2.9.0-rc3/helm/v3 (gomodules)
    • quay.io/argoproj/argocd:v2.9.0-rc3/git-lfs/git-lfs (gomodules)
    • +
    +
    + +
    +
    30 known vulnerabilities
    +
    99 vulnerable dependency paths
    +
    2185 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + 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

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and golang.org/x/net/http2@v0.15.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + golang.org/x/net/http2@v0.15.0 + + + +
    • +
    • + 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

    + + +
    + + + +
    +
    +

    Directory Traversal

    +
    + +
    + high severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + xz-utils/liblzma5 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and xz-utils/liblzma5@5.2.5-2ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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.

    +

    ** DISPUTED ** 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

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + perl/perl-modules-5.34 +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3, git@1:2.34.1-1ubuntu1.10 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + git@1:2.34.1-1ubuntu1.10 + + perl@5.34.0-3ubuntu1.2 + + perl/perl-modules-5.34@5.34.0-3ubuntu1.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + git@1:2.34.1-1ubuntu1.10 + + perl@5.34.0-3ubuntu1.2 + + perl/libperl5.34@5.34.0-3ubuntu1.2 + + perl/perl-modules-5.34@5.34.0-3ubuntu1.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + git@1:2.34.1-1ubuntu1.10 + + perl@5.34.0-3ubuntu1.2 + + perl/libperl5.34@5.34.0-3ubuntu1.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + git@1:2.34.1-1ubuntu1.10 + + perl@5.34.0-3ubuntu1.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + perl/perl-base@5.34.0-3ubuntu1.2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    In Perl 5.34.0, function S_find_uninit_var in sv.c has a stack-based crash that can lead to remote code execution or local privilege escalation.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 perl.

    +

    References

    + + +
    + + + +
    +
    +

    Access of Uninitialized Pointer

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and krb5/libk5crypto3@1.19.2-2ubuntu0.2 + +
    • +
    + +
    + + +

    Detailed paths

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

    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.

    +

    lib/kadm5/kadm_rpc_xdr.c in MIT Kerberos 5 (aka krb5) before 1.20.2 and 1.21.x before 1.21.1 frees an uninitialized pointer. A remote authenticated user can trigger a kadmind crash. This occurs because _xdr_kadm5_principal_ent_rec does not validate the relationship between n_key_data and the key_data array count.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    LGPL-3.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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

    + +
    + + + +
    +
    +

    Memory Leak

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + glibc/libc-bin +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and glibc/libc-bin@2.35-0ubuntu3.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + glibc/libc-bin@2.35-0ubuntu3.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + glibc/libc6@2.35-0ubuntu3.4 + + + +
    • +
    + +
    + +
    + +

    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.

    +

    A flaw was found in the GNU C Library. A recent fix for CVE-2023-4806 introduced the potential for a memory leak, which may result in an application crash.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 glibc.

    +

    References

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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 +
    + +
    + +
      +
    • + 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

    + +
    + + + +
    +
    +

    CVE-2022-46908

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + sqlite3/libsqlite3-0 +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3, gnupg2/gpg@2.2.27-3ubuntu2.1 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + sqlite3/libsqlite3-0@3.37.2-2ubuntu0.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    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

    + + +
    + + + +
    +
    +

    Arbitrary Code Injection

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + shadow/passwd +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and shadow/passwd@1:4.8.1-2ubuntu2.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + openssh/openssh-client@1:8.9p1-3ubuntu0.4 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + shadow/login@1:4.8.1-2ubuntu2.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream shadow package and not the shadow package as distributed by Ubuntu:22.04. + 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

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + procps/libprocps8 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and procps/libprocps8@2:3.3.17-6ubuntu2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + procps/libprocps8@2:3.3.17-6ubuntu2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + procps@2:3.3.17-6ubuntu2 + + procps/libprocps8@2:3.3.17-6ubuntu2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + procps@2:3.3.17-6ubuntu2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    Under some circumstances, this weakness allows a user who has access to run the “ps” utility on a machine, the ability to write almost unlimited amounts of unfiltered data into the process heap.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 procps.

    +

    References

    + + +
    + + + +
    +
    +

    Uncontrolled Recursion

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + pcre3/libpcre3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + patch +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and patch@2.7.6-7build2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + patch +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and patch@2.7.6-7build2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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:22.04. + 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-28531

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

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + openssh/openssh-client@1:8.9p1-3ubuntu0.4 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    ssh-add in OpenSSH before 9.3 adds smartcard keys to ssh-agent without the intended per-hop destination constraints. The earliest affected version is 8.9.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 openssh.

    +

    References

    + + +
    + + + +
    +
    +

    NULL Pointer Dereference

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + openldap/libldap-2.5-0 +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3, gnupg2/dirmngr@2.2.27-3ubuntu2.1 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.14 + + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + openldap/libldap-common@2.5.16+dfsg-0ubuntu0.22.04.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

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

    +

    A vulnerability was found in openldap. This security flaw causes a null pointer dereference in ber_memalloc_x() function.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 openldap.

    +

    References

    + + +
    + + + +
    +
    +

    Resource Exhaustion

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

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and krb5/libk5crypto3@1.19.2-2ubuntu0.2 + +
    • +
    + +
    + + +

    Detailed paths

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

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnupg2/gpgv +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and gnupg2/gpgv@2.2.27-3ubuntu2.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + apt@2.4.10 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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.0-rc3 + + 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.0-rc3 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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.0-rc3 + + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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.0-rc3 + + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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.0-rc3 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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.0-rc3 + + 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.0-rc3 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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.0-rc3 + + 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.0-rc3 + + 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.0-rc3 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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.0-rc3 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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.0-rc3 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + glibc/libc-bin +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and glibc/libc-bin@2.35-0ubuntu3.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + glibc/libc-bin@2.35-0ubuntu3.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + glibc/libc6@2.35-0ubuntu3.4 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + git/git-man +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3, git@1:2.34.1-1ubuntu1.10 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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.0-rc3 + + git@1:2.34.1-1ubuntu1.10 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gcc-12/libstdc++6 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + apt@2.4.10 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + apt@2.4.10 + + apt/libapt-pkg6.0@2.4.10 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + gcc-12/gcc-12-base@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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:22.04. + 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 +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + coreutils +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and coreutils@8.32-4.1ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + coreutils@8.32-4.1ubuntu1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream coreutils package and not the coreutils package as distributed by Ubuntu:22.04. + 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

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + bash +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 and bash@5.1-6ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.0-rc3 + + 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:22.04. + 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

    +

    There is no fixed version for Ubuntu:22.04 bash.

    +

    References

    + + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.9.0-rc3/redis_7.0.11-alpine.html b/docs/snyk/v2.9.0-rc3/redis_7.0.11-alpine.html new file mode 100644 index 0000000000000..8efb859567ad3 --- /dev/null +++ b/docs/snyk/v2.9.0-rc3/redis_7.0.11-alpine.html @@ -0,0 +1,1335 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    October 29th 2023, 12:19:03 am (UTC+00:00)

    +
    +
    + Scanned the following path: +
      +
    • redis:7.0.11-alpine (apk)
    • +
    +
    + +
    +
    5 known vulnerabilities
    +
    41 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

    + + +
    + + + +
    +
    +

    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:3.18. + 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

    + + +
    + + + +
    +
    +

    CVE-2023-5363

    +
    + +
    + 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

    +

    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

    + + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/user-guide/annotations-and-labels.md b/docs/user-guide/annotations-and-labels.md index 2b4e9968dcfb4..032824c8708f3 100644 --- a/docs/user-guide/annotations-and-labels.md +++ b/docs/user-guide/annotations-and-labels.md @@ -14,7 +14,6 @@ | 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. | -| argocd.argoproj.io/ignore-resource-updates | any | `"true"`, `false` | Used by Argo CD to ignore resource updates. See [reconcile docs](..%2Foperator-manual%2Freconcile.md)reconcile_docs 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") | diff --git a/docs/user-guide/application-set.md b/docs/user-guide/application-set.md index c8a05d4cb4bdd..682e3b1d44a1f 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) 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) 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 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 automatically generates Argo CD Applications based on the contents of an `ApplicationSet` Custom Resource (CR). +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). Here is an example of an `ApplicationSet` resource that can be used to target an Argo CD Application to multiple clusters: ```yaml @@ -17,8 +17,6 @@ kind: ApplicationSet metadata: name: guestbook spec: - goTemplate: true - goTemplateOptions: ["missingkey=error"] generators: - list: elements: @@ -30,15 +28,15 @@ spec: url: https://9.8.7.6 template: metadata: - name: '{{.cluster}}-guestbook' + name: '{{cluster}}-guestbook' spec: - project: my-project + project: default source: - repoURL: https://github.com/infra-team/cluster-deployments.git + repoURL: https://github.com/argoproj/argo-cd.git targetRevision: HEAD - path: guestbook/{{.cluster}} + path: applicationset/examples/list-generator/guestbook/{{cluster}} destination: - server: '{{.url}}' + server: '{{url}}' namespace: guestbook ``` @@ -48,4 +46,6 @@ 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 the [ApplicationSet documentation](../operator-manual/applicationset/index.md). +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. \ No newline at end of file diff --git a/docs/user-guide/build-environment.md b/docs/user-guide/build-environment.md index 52fc8b1d03a5a..8e2448f4f9e7f 100644 --- a/docs/user-guide/build-environment.md +++ b/docs/user-guide/build-environment.md @@ -8,7 +8,6 @@ | `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_REVISION_SHORT_8` | The resolved short revision with length 8, e.g. `f913b6cb`. | | `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/commands/argocd.md b/docs/user-guide/commands/argocd.md index 0514eb9447103..b03b3971284f6 100644 --- a/docs/user-guide/commands/argocd.md +++ b/docs/user-guide/commands/argocd.md @@ -11,8 +11,7 @@ argocd [flags] ### Options ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") @@ -46,7 +45,7 @@ argocd [flags] * [argocd appset](argocd_appset.md) - Manage ApplicationSets * [argocd cert](argocd_cert.md) - Manage repository certificates and SSH known hosts entries * [argocd cluster](argocd_cluster.md) - Manage cluster credentials -* [argocd completion](argocd_completion.md) - output shell completion code for the specified shell (bash, zsh or fish) +* [argocd completion](argocd_completion.md) - output shell completion code for the specified shell (bash or zsh) * [argocd context](argocd_context.md) - Switch between contexts * [argocd gpg](argocd_gpg.md) - Manage GPG keys used for signature verification * [argocd login](argocd_login.md) - Log in to Argo CD diff --git a/docs/user-guide/commands/argocd_account.md b/docs/user-guide/commands/argocd_account.md index 25eaa7d214542..88d483ffac68e 100644 --- a/docs/user-guide/commands/argocd_account.md +++ b/docs/user-guide/commands/argocd_account.md @@ -52,8 +52,7 @@ argocd account [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_account_bcrypt.md b/docs/user-guide/commands/argocd_account_bcrypt.md index d4bde8b933424..6bc282cfaab1e 100644 --- a/docs/user-guide/commands/argocd_account_bcrypt.md +++ b/docs/user-guide/commands/argocd_account_bcrypt.md @@ -25,8 +25,7 @@ argocd account bcrypt --password YOUR_PASSWORD ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_account_can-i.md b/docs/user-guide/commands/argocd_account_can-i.md index f6fd5a01880a8..6e6cb2bea524b 100644 --- a/docs/user-guide/commands/argocd_account_can-i.md +++ b/docs/user-guide/commands/argocd_account_can-i.md @@ -35,8 +35,7 @@ Resources: [clusters projects applications applicationsets repositories certific ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_account_delete-token.md b/docs/user-guide/commands/argocd_account_delete-token.md index 25d5b9a37d17a..6ef4cf11499fe 100644 --- a/docs/user-guide/commands/argocd_account_delete-token.md +++ b/docs/user-guide/commands/argocd_account_delete-token.md @@ -28,8 +28,7 @@ argocd account delete-token --account ID ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_account_generate-token.md b/docs/user-guide/commands/argocd_account_generate-token.md index e149548374894..0d21d36ad32ff 100644 --- a/docs/user-guide/commands/argocd_account_generate-token.md +++ b/docs/user-guide/commands/argocd_account_generate-token.md @@ -30,8 +30,7 @@ argocd account generate-token --account ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 577f103b48c0d..66603a52b2628 100644 --- a/docs/user-guide/commands/argocd_account_get-user-info.md +++ b/docs/user-guide/commands/argocd_account_get-user-info.md @@ -28,8 +28,7 @@ argocd account get-user-info [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_account_get.md b/docs/user-guide/commands/argocd_account_get.md index 70d181e702a17..fbe0ef6027141 100644 --- a/docs/user-guide/commands/argocd_account_get.md +++ b/docs/user-guide/commands/argocd_account_get.md @@ -29,8 +29,7 @@ argocd account get --account ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_account_list.md b/docs/user-guide/commands/argocd_account_list.md index 6af9d8a6d0ee0..0082c0260496c 100644 --- a/docs/user-guide/commands/argocd_account_list.md +++ b/docs/user-guide/commands/argocd_account_list.md @@ -24,8 +24,7 @@ argocd account list ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_account_update-password.md b/docs/user-guide/commands/argocd_account_update-password.md index 3a31b6ee06274..ed84a7da00617 100644 --- a/docs/user-guide/commands/argocd_account_update-password.md +++ b/docs/user-guide/commands/argocd_account_update-password.md @@ -40,8 +40,7 @@ argocd account update-password [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin.md b/docs/user-guide/commands/argocd_admin.md index ec1de309c135b..6f8587e68db4e 100644 --- a/docs/user-guide/commands/argocd_admin.md +++ b/docs/user-guide/commands/argocd_admin.md @@ -11,9 +11,84 @@ 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 @@ -30,8 +105,7 @@ $ argocd admin initial-password reset ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_app.md b/docs/user-guide/commands/argocd_admin_app.md index 12fa400c8c94d..58e0f50f25846 100644 --- a/docs/user-guide/commands/argocd_admin_app.md +++ b/docs/user-guide/commands/argocd_admin_app.md @@ -32,8 +32,7 @@ argocd admin app get-reconcile-results APPNAME ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 c77a3d3db57e0..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 @@ -17,8 +17,7 @@ argocd admin app diff-reconcile-results PATH1 PATH2 [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 2826917d4765c..78213de5c170c 100644 --- a/docs/user-guide/commands/argocd_admin_app_generate-spec.md +++ b/docs/user-guide/commands/argocd_admin_app_generate-spec.md @@ -47,10 +47,7 @@ argocd admin app generate-spec APPNAME [flags] --directory-recurse Recurse directory --env string Application environment to monitor -f, --file string Filename or URL to Kubernetes manifests for the app - --helm-api-versions stringArray Helm api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster --helm-chart string Helm Chart name - --helm-kube-version string Helm kube-version to use when running helm template. If not set, use the kube version from the destination cluster - --helm-namespace string Helm namespace to use when running helm template. If not set, use app.spec.destination.namespace --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) @@ -65,14 +62,11 @@ argocd admin app generate-spec APPNAME [flags] --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-api-versions stringArray api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster. Only applicable when Helm is enabled for Kustomize builds --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-kube-version string kube-version to use when running helm template. If not set, use the kube version from the destination cluster. Only applicable when Helm is enabled for Kustomize builds - --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 @@ -85,15 +79,13 @@ 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 - --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: manual (aliases of manual: none), automated (aliases of automated: auto, automatic)) + --sync-policy string Set the sync policy (one of: 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) @@ -106,8 +98,7 @@ argocd admin app generate-spec APPNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 a985a7d0e8484..4e696bd994903 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 @@ -43,8 +43,7 @@ argocd admin app get-reconcile-results PATH [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_cluster.md b/docs/user-guide/commands/argocd_admin_cluster.md index 7abe6fd1a42f8..544c0de08959c 100644 --- a/docs/user-guide/commands/argocd_admin_cluster.md +++ b/docs/user-guide/commands/argocd_admin_cluster.md @@ -31,8 +31,7 @@ argocd admin cluster namespaces my-cluster ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 01a0fe6ff3a07..cc24418b023f8 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_generate-spec.md +++ b/docs/user-guide/commands/argocd_admin_cluster_generate-spec.md @@ -13,7 +13,6 @@ 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'. @@ -40,8 +39,7 @@ argocd admin cluster generate-spec CONTEXT [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md b/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md index 37092a4ef303a..38f61ce5cd8a2 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md +++ b/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md @@ -54,8 +54,7 @@ argocd admin cluster kubeconfig https://cluster-api-url:6443 /path/to/output/kub ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_cluster_namespaces.md b/docs/user-guide/commands/argocd_admin_cluster_namespaces.md index 791f61ec1c1f0..fee5c7679e159 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_namespaces.md +++ b/docs/user-guide/commands/argocd_admin_cluster_namespaces.md @@ -37,8 +37,7 @@ argocd admin cluster namespaces [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 57b776ff1cc3d..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 @@ -38,8 +38,7 @@ argocd admin cluster namespaces disable-namespaced-mode PATTERN [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 cfbfd2fb891ab..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 @@ -40,8 +40,7 @@ argocd admin cluster namespaces enable-namespaced-mode PATTERN [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_cluster_shards.md b/docs/user-guide/commands/argocd_admin_cluster_shards.md index b624c8dbe6c49..48f6138d47b4a 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_shards.md +++ b/docs/user-guide/commands/argocd_admin_cluster_shards.md @@ -43,7 +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, consistent-hashing] (default "legacy") + --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,8 +53,7 @@ argocd admin cluster shards [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_cluster_stats.md b/docs/user-guide/commands/argocd_admin_cluster_stats.md index b894959c1c0c3..c5297ce7e35ed 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_stats.md +++ b/docs/user-guide/commands/argocd_admin_cluster_stats.md @@ -57,7 +57,7 @@ argocd admin cluster stats target-cluster --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, consistent-hashing] (default "legacy") + --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 @@ -67,8 +67,7 @@ argocd admin cluster stats target-cluster ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_dashboard.md b/docs/user-guide/commands/argocd_admin_dashboard.md index ecfee6be3a242..71e11a173906a 100644 --- a/docs/user-guide/commands/argocd_admin_dashboard.md +++ b/docs/user-guide/commands/argocd_admin_dashboard.md @@ -54,8 +54,7 @@ $ argocd admin dashboard --redis-compress gzip ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_export.md b/docs/user-guide/commands/argocd_admin_export.md index f4fe070f2d1f5..d168fe5450a74 100644 --- a/docs/user-guide/commands/argocd_admin_export.md +++ b/docs/user-guide/commands/argocd_admin_export.md @@ -11,37 +11,34 @@ argocd admin export [flags] ### Options ``` - --application-namespaces strings Comma separated list of namespace globs to export applications from. If not provided value from 'application.namespaces' in argocd-cmd-params-cm will be used,if it's not defined only applications from Argo CD namespace will be exported - --applicationset-namespaces strings Comma separated list of namespace globs to export applicationsets from. If not provided value from 'applicationsetcontroller.namespaces' in argocd-cmd-params-cm will be used,if it's not defined only applicationsets from Argo CD namespace will be exported - --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 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 - -n, --namespace string If present, the namespace scope for this CLI request - -o, --out string Output to the specified file instead of stdout (default "-") - --password string Password for basic authentication to the API server - --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 + --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 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 + -n, --namespace string If present, the namespace scope for this CLI request + -o, --out string Output to the specified file instead of stdout (default "-") + --password string Password for basic authentication to the API server + --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 ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_import.md b/docs/user-guide/commands/argocd_admin_import.md index b373184a3796d..dc8a4b2dbf947 100644 --- a/docs/user-guide/commands/argocd_admin_import.md +++ b/docs/user-guide/commands/argocd_admin_import.md @@ -11,41 +11,37 @@ argocd admin import SOURCE [flags] ### Options ``` - --application-namespaces strings Comma separated list of namespace globs to which import of applications is allowed. If not provided value from 'application.namespaces' in argocd-cmd-params-cm will be used,if it's not defined only applications without an explicit namespace will be imported to the Argo CD namespace - --applicationset-namespaces strings Comma separated list of namespace globs which import of applicationsets is allowed. If not provided value from 'applicationsetcontroller.namespaces' in argocd-cmd-params-cm will be used,if it's not defined only applicationsets without an explicit namespace will be imported to the Argo CD namespace - --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 - --dry-run Print what will be performed - -h, --help help for import - --ignore-tracking Do not update the tracking annotation if the resource is already tracked - --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 - --password string Password for basic authentication to the API server - --proxy-url string If provided, this URL will be used to connect via proxy - --prune Prune secrets, applications and projects which do not appear in the backup - --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 - --stop-operation Stop any existing operations - --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 - --verbose Verbose output (versus only changed output) + --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 + --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 + --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 + --password string Password for basic authentication to the API server + --proxy-url string If provided, this URL will be used to connect via proxy + --prune Prune secrets, applications and projects which do not appear in the backup + --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 + --stop-operation Stop any existing operations + --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 + --verbose Verbose output (versus only changed output) ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_initial-password.md b/docs/user-guide/commands/argocd_admin_initial-password.md index 92feb9e8ad9f5..dbc44561debdc 100644 --- a/docs/user-guide/commands/argocd_admin_initial-password.md +++ b/docs/user-guide/commands/argocd_admin_initial-password.md @@ -37,8 +37,7 @@ argocd admin initial-password [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_notifications.md b/docs/user-guide/commands/argocd_admin_notifications.md index 58f2b832bebbb..87429217f99e9 100644 --- a/docs/user-guide/commands/argocd_admin_notifications.md +++ b/docs/user-guide/commands/argocd_admin_notifications.md @@ -42,8 +42,7 @@ argocd admin notifications [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_notifications_template.md b/docs/user-guide/commands/argocd_admin_notifications_template.md index 2a93df1a9a9f1..75d5700aaac04 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_template.md +++ b/docs/user-guide/commands/argocd_admin_notifications_template.md @@ -17,14 +17,13 @@ argocd admin notifications template [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use --argocd-repo-server string Argo CD repo server address (default "argocd-repo-server:8081") --argocd-repo-server-plaintext Use a plaintext client (non-TLS) to connect to repository server --argocd-repo-server-strict-tls Perform strict validation of TLS certificates when connecting to repo server --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 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 e48bb8271d4db..214a8e5cd442b 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_template_get.md +++ b/docs/user-guide/commands/argocd_admin_notifications_template_get.md @@ -29,14 +29,13 @@ argocd admin notifications template get app-sync-succeeded -o=yaml ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use --argocd-repo-server string Argo CD repo server address (default "argocd-repo-server:8081") --argocd-repo-server-plaintext Use a plaintext client (non-TLS) to connect to repository server --argocd-repo-server-strict-tls Perform strict validation of TLS certificates when connecting to repo server --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 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 cfcb6bc08db89..4f94a9d960476 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_template_notify.md +++ b/docs/user-guide/commands/argocd_admin_notifications_template_notify.md @@ -30,14 +30,13 @@ argocd admin notifications template notify app-sync-succeeded guestbook ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use --argocd-repo-server string Argo CD repo server address (default "argocd-repo-server:8081") --argocd-repo-server-plaintext Use a plaintext client (non-TLS) to connect to repository server --argocd-repo-server-strict-tls Perform strict validation of TLS certificates when connecting to repo server --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 diff --git a/docs/user-guide/commands/argocd_admin_notifications_trigger.md b/docs/user-guide/commands/argocd_admin_notifications_trigger.md index 74b8460151e2e..d6ff9e53ab235 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_trigger.md +++ b/docs/user-guide/commands/argocd_admin_notifications_trigger.md @@ -17,14 +17,13 @@ argocd admin notifications trigger [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use --argocd-repo-server string Argo CD repo server address (default "argocd-repo-server:8081") --argocd-repo-server-plaintext Use a plaintext client (non-TLS) to connect to repository server --argocd-repo-server-strict-tls Perform strict validation of TLS certificates when connecting to repo server --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 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 6d3f7aa9b3200..acd2ab5af9553 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_trigger_get.md +++ b/docs/user-guide/commands/argocd_admin_notifications_trigger_get.md @@ -29,14 +29,13 @@ argocd admin notifications trigger get on-sync-failed -o=yaml ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use --argocd-repo-server string Argo CD repo server address (default "argocd-repo-server:8081") --argocd-repo-server-plaintext Use a plaintext client (non-TLS) to connect to repository server --argocd-repo-server-strict-tls Perform strict validation of TLS certificates when connecting to repo server --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 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 6b27a7a54d27f..f8bebb2937937 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_trigger_run.md +++ b/docs/user-guide/commands/argocd_admin_notifications_trigger_run.md @@ -29,14 +29,13 @@ argocd admin notifications trigger run on-sync-status-unknown ./sample-app.yaml ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use --argocd-repo-server string Argo CD repo server address (default "argocd-repo-server:8081") --argocd-repo-server-plaintext Use a plaintext client (non-TLS) to connect to repository server --argocd-repo-server-strict-tls Perform strict validation of TLS certificates when connecting to repo server --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 diff --git a/docs/user-guide/commands/argocd_admin_proj.md b/docs/user-guide/commands/argocd_admin_proj.md index e4f11de54cab1..b22a2513b7e4d 100644 --- a/docs/user-guide/commands/argocd_admin_proj.md +++ b/docs/user-guide/commands/argocd_admin_proj.md @@ -17,8 +17,7 @@ argocd admin proj [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 753d0fa68a704..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 @@ -45,8 +45,7 @@ argocd admin proj generate-allow-list /path/to/clusterrole.yaml my-project ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 c94eba4365ef8..b4f544367813d 100644 --- a/docs/user-guide/commands/argocd_admin_proj_generate-spec.md +++ b/docs/user-guide/commands/argocd_admin_proj_generate-spec.md @@ -44,8 +44,7 @@ argocd admin proj generate-spec PROJECT [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 09dc8994d2a7f..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 @@ -53,8 +53,7 @@ argocd admin proj update-role-policy PROJECT_GLOB MODIFICATION ACTION [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_redis-initial-password.md b/docs/user-guide/commands/argocd_admin_redis-initial-password.md index de2653e962f5e..85e56195758dd 100644 --- a/docs/user-guide/commands/argocd_admin_redis-initial-password.md +++ b/docs/user-guide/commands/argocd_admin_redis-initial-password.md @@ -37,8 +37,7 @@ argocd admin redis-initial-password [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_repo.md b/docs/user-guide/commands/argocd_admin_repo.md index 33944fda2d87c..411cf558bac5b 100644 --- a/docs/user-guide/commands/argocd_admin_repo.md +++ b/docs/user-guide/commands/argocd_admin_repo.md @@ -17,8 +17,7 @@ argocd admin repo [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 3616e057c53c7..10c722913258b 100644 --- a/docs/user-guide/commands/argocd_admin_repo_generate-spec.md +++ b/docs/user-guide/commands/argocd_admin_repo_generate-spec.md @@ -50,7 +50,6 @@ argocd admin repo generate-spec REPOURL [flags] --insecure-ignore-host-key disables SSH strict host key checking (deprecated, use --insecure-skip-server-verification instead) --insecure-skip-server-verification disables server certificate and host key checks --name string name of the repository, mandatory for repositories of type helm - --no-proxy string don't access these targets via proxy -o, --output string Output format. One of: json|yaml (default "yaml") --password string password to the repository --project string project of the repository @@ -65,8 +64,7 @@ argocd admin repo generate-spec REPOURL [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_settings.md b/docs/user-guide/commands/argocd_admin_settings.md index d2726048afc42..3c631cf8f123b 100644 --- a/docs/user-guide/commands/argocd_admin_settings.md +++ b/docs/user-guide/commands/argocd_admin_settings.md @@ -40,8 +40,7 @@ argocd admin settings [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_admin_settings_rbac.md b/docs/user-guide/commands/argocd_admin_settings_rbac.md index 5cb18ba1a0580..043c39979a98a 100644 --- a/docs/user-guide/commands/argocd_admin_settings_rbac.md +++ b/docs/user-guide/commands/argocd_admin_settings_rbac.md @@ -18,12 +18,11 @@ argocd admin settings rbac [flags] ``` --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-context string The name of the Argo-CD server context to use --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 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 5697d68530c4d..f14092785facf 100644 --- a/docs/user-guide/commands/argocd_admin_settings_rbac_can.md +++ b/docs/user-guide/commands/argocd_admin_settings_rbac_can.md @@ -73,9 +73,8 @@ argocd admin settings rbac can someuser create application 'default/app' --defau ``` --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-context string The name of the Argo-CD server context to use --argocd-secret-path string Path to local argocd-secret.yaml file - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 6e5721e705fb1..b051c7c63694b 100644 --- a/docs/user-guide/commands/argocd_admin_settings_rbac_validate.md +++ b/docs/user-guide/commands/argocd_admin_settings_rbac_validate.md @@ -26,8 +26,8 @@ argocd admin settings rbac validate --policy-file policy.csv # 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. +# 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. @@ -65,9 +65,8 @@ argocd admin settings rbac validate --namespace argocd ``` --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-context string The name of the Argo-CD server context to use --argocd-secret-path string Path to local argocd-secret.yaml file - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 095ae66f6fb39..eeec6bcf5f63a 100644 --- a/docs/user-guide/commands/argocd_admin_settings_resource-overrides.md +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides.md @@ -18,12 +18,11 @@ argocd admin settings resource-overrides [flags] ``` --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-context string The name of the Argo-CD server context to use --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 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 8d1afb6e81b42..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 @@ -29,12 +29,11 @@ argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path . ``` --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-context string The name of the Argo-CD server context to use --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 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 c10989511069e..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 @@ -29,12 +29,11 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo ``` --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-context string The name of the Argo-CD server context to use --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 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 index f562e3557ccfd..0eeefab2713ea 100644 --- 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 @@ -30,12 +30,11 @@ argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml - ``` --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-context string The name of the Argo-CD server context to use --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 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 144a7d0b9d92b..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 @@ -29,12 +29,11 @@ argocd admin settings resource-overrides action list /tmp/deploy.yaml --argocd-c ``` --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-context string The name of the Argo-CD server context to use --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 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 99f5c903d11c2..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 @@ -29,12 +29,11 @@ argocd admin settings resource-overrides action run /tmp/deploy.yaml restart --a ``` --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-context string The name of the Argo-CD server context to use --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 diff --git a/docs/user-guide/commands/argocd_admin_settings_validate.md b/docs/user-guide/commands/argocd_admin_settings_validate.md index 1565397fb5117..8e40a403441b5 100644 --- a/docs/user-guide/commands/argocd_admin_settings_validate.md +++ b/docs/user-guide/commands/argocd_admin_settings_validate.md @@ -34,12 +34,11 @@ argocd admin settings validate --group accounts --group plugins --load-cluster-s ``` --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-context string The name of the Argo-CD server context to use --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; set this or the ARGOCD_AUTH_TOKEN environment variable + --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 diff --git a/docs/user-guide/commands/argocd_app.md b/docs/user-guide/commands/argocd_app.md index ea5bf74d6a56a..543fcd96035ec 100644 --- a/docs/user-guide/commands/argocd_app.md +++ b/docs/user-guide/commands/argocd_app.md @@ -49,8 +49,7 @@ argocd app [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") @@ -79,7 +78,6 @@ argocd app [flags] * [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 @@ -92,7 +90,6 @@ 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. Counting 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 21df6d1f1564e..af336f1767b23 100644 --- a/docs/user-guide/commands/argocd_app_actions.md +++ b/docs/user-guide/commands/argocd_app_actions.md @@ -27,8 +27,7 @@ argocd app actions [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_actions_list.md b/docs/user-guide/commands/argocd_app_actions_list.md index 513042b746278..2d1f78524df50 100644 --- a/docs/user-guide/commands/argocd_app_actions_list.md +++ b/docs/user-guide/commands/argocd_app_actions_list.md @@ -29,8 +29,7 @@ argocd app actions list APPNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_actions_run.md b/docs/user-guide/commands/argocd_app_actions_run.md index 8dc105243793b..db8e29fc197b9 100644 --- a/docs/user-guide/commands/argocd_app_actions_run.md +++ b/docs/user-guide/commands/argocd_app_actions_run.md @@ -29,8 +29,7 @@ argocd app actions run APPNAME ACTION [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_add-source.md b/docs/user-guide/commands/argocd_app_add-source.md deleted file mode 100644 index b6bc3ae3de6c2..0000000000000 --- a/docs/user-guide/commands/argocd_app_add-source.md +++ /dev/null @@ -1,115 +0,0 @@ -# `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-api-versions stringArray Helm api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster - --helm-chart string Helm Chart name - --helm-kube-version string Helm kube-version to use when running helm template. If not set, use the kube version from the destination cluster - --helm-namespace string Helm namespace to use when running helm template. If not set, use app.spec.destination.namespace - --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-api-versions stringArray api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster. Only applicable when Helm is enabled for Kustomize builds - --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-kube-version string kube-version to use when running helm template. If not set, use the kube version from the destination cluster. Only applicable when Helm is enabled for Kustomize builds - --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 - -``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable - --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 662ee0b92644a..41a671f3efdcd 100644 --- a/docs/user-guide/commands/argocd_app_create.md +++ b/docs/user-guide/commands/argocd_app_create.md @@ -26,9 +26,6 @@ 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 ``` @@ -49,10 +46,7 @@ argocd app create APPNAME [flags] --directory-recurse Recurse directory --env string Application environment to monitor -f, --file string Filename or URL to Kubernetes manifests for the app - --helm-api-versions stringArray Helm api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster --helm-chart string Helm Chart name - --helm-kube-version string Helm kube-version to use when running helm template. If not set, use the kube version from the destination cluster - --helm-namespace string Helm namespace to use when running helm template. If not set, use app.spec.destination.namespace --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) @@ -66,14 +60,11 @@ argocd app create APPNAME [flags] --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-api-versions stringArray api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster. Only applicable when Helm is enabled for Kustomize builds --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-kube-version string kube-version to use when running helm template. If not set, use the kube version from the destination cluster. Only applicable when Helm is enabled for Kustomize builds - --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 @@ -85,7 +76,6 @@ 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 @@ -93,7 +83,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: manual (aliases of manual: none), automated (aliases of automated: auto, automatic)) + --sync-policy string Set the sync policy (one of: 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) @@ -107,8 +97,7 @@ argocd app create APPNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_delete-resource.md b/docs/user-guide/commands/argocd_app_delete-resource.md index 892de087ec811..4a305eb4b4489 100644 --- a/docs/user-guide/commands/argocd_app_delete-resource.md +++ b/docs/user-guide/commands/argocd_app_delete-resource.md @@ -12,12 +12,12 @@ argocd app delete-resource APPNAME [flags] ``` --all Indicates whether to patch multiple matching of resources - --force Indicates whether to force delete the resource + --force Indicates whether to orphan the dependents of the deleted resource --group string Group -h, --help help for delete-resource --kind string Kind --namespace string Namespace - --orphan Indicates whether to orphan the dependents of the deleted resource + --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 ``` @@ -25,8 +25,7 @@ argocd app delete-resource APPNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_delete.md b/docs/user-guide/commands/argocd_app_delete.md index 15ccb486220ad..f4ff666a4b919 100644 --- a/docs/user-guide/commands/argocd_app_delete.md +++ b/docs/user-guide/commands/argocd_app_delete.md @@ -28,20 +28,17 @@ 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 ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_diff.md b/docs/user-guide/commands/argocd_app_diff.md index 07efe70af6b40..6211f00c2eb54 100644 --- a/docs/user-guide/commands/argocd_app_diff.md +++ b/docs/user-guide/commands/argocd_app_diff.md @@ -9,7 +9,6 @@ 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] @@ -18,7 +17,6 @@ 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 @@ -28,16 +26,13 @@ argocd app diff APPNAME [flags] --local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/") --refresh Refresh application data when retrieving --revision string Compare live app to a particular revision - --revisions stringArray Show manifests at specific revisions for source position in source-positions --server-side-generate Used with --local, this will send your manifests to the server for diffing - --source-positions int64Slice List of source positions. Default is empty array. Counting start at 1. (default []) ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_edit.md b/docs/user-guide/commands/argocd_app_edit.md index 684e77e12ce5a..204e96cb76c0f 100644 --- a/docs/user-guide/commands/argocd_app_edit.md +++ b/docs/user-guide/commands/argocd_app_edit.md @@ -11,15 +11,13 @@ argocd app edit APPNAME [flags] ### Options ``` - -N, --app-namespace string Only edit application in namespace - -h, --help help for edit + -h, --help help for edit ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_get.md b/docs/user-guide/commands/argocd_app_get.md index 8785e5b52637b..cf766ed9eb0d7 100644 --- a/docs/user-guide/commands/argocd_app_get.md +++ b/docs/user-guide/commands/argocd_app_get.md @@ -26,9 +26,6 @@ argocd app get APPNAME [flags] # Show application parameters and overrides argocd app get my-app --show-params - # Show application parameters and overrides for a source at position 1 under spec.sources of app my-app - argocd app get my-app --show-params --source-position 1 - # Refresh application data when retrieving argocd app get my-app --refresh @@ -45,21 +42,18 @@ argocd app get APPNAME [flags] ### Options ``` - -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 - --source-position int Position of the source from the list of sources of the app. Counting starts at 1. (default -1) + --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 ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_history.md b/docs/user-guide/commands/argocd_app_history.md index 81c2127ab8d6c..253a1dec64dd5 100644 --- a/docs/user-guide/commands/argocd_app_history.md +++ b/docs/user-guide/commands/argocd_app_history.md @@ -11,16 +11,14 @@ argocd app history APPNAME [flags] ### Options ``` - -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") + -h, --help help for history + -o, --output string Output format. One of: wide|id (default "wide") ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_list.md b/docs/user-guide/commands/argocd_app_list.md index 843716f549771..17e00fcac9df3 100644 --- a/docs/user-guide/commands/argocd_app_list.md +++ b/docs/user-guide/commands/argocd_app_list.md @@ -37,8 +37,7 @@ argocd app list [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_logs.md b/docs/user-guide/commands/argocd_app_logs.md index decb6b05fd808..8dc1f6a9f1aae 100644 --- a/docs/user-guide/commands/argocd_app_logs.md +++ b/docs/user-guide/commands/argocd_app_logs.md @@ -68,8 +68,7 @@ argocd app logs APPNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_manifests.md b/docs/user-guide/commands/argocd_app_manifests.md index 3238a7cfcf2d3..d3b91756cbe04 100644 --- a/docs/user-guide/commands/argocd_app_manifests.md +++ b/docs/user-guide/commands/argocd_app_manifests.md @@ -8,36 +8,20 @@ Print manifests of an application argocd app manifests APPNAME [flags] ``` -### Examples - -``` - # Get manifests for an application - argocd app manifests my-app - - # Get manifests for an application at a specific revision - argocd app manifests my-app --revision 0.0.1 - - # Get manifests for a multi-source application at specific revisions for specific sources - argocd app manifests my-app --revisions 0.0.1 --source-positions 1 --revisions 0.0.2 --source-positions 2 -``` - ### Options ``` - -h, --help help for manifests - --local string If set, show locally-generated manifests. Value is the absolute path to app manifests within the manifest repo. Example: '/home/username/apps/env/app-1'. - --local-repo-root string Path to the local repository root. Used together with --local allows setting the repository root. Example: '/home/username/apps'. (default ".") - --revision string Show manifests at a specific revision - --revisions stringArray Show manifests at specific revisions for the source at position in source-positions - --source string Source of manifests. One of: live|git (default "git") - --source-positions int64Slice List of source positions. Default is empty array. Counting start at 1. (default []) + -h, --help help for manifests + --local string If set, show locally-generated manifests. Value is the absolute path to app manifests within the manifest repo. Example: '/home/username/apps/env/app-1'. + --local-repo-root string Path to the local repository root. Used together with --local allows setting the repository root. Example: '/home/username/apps'. (default ".") + --revision string Show manifests at a specific revision + --source string Source of manifests. One of: live|git (default "git") ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_patch-resource.md b/docs/user-guide/commands/argocd_app_patch-resource.md index 392c3c87e7014..c849395cb3ea8 100644 --- a/docs/user-guide/commands/argocd_app_patch-resource.md +++ b/docs/user-guide/commands/argocd_app_patch-resource.md @@ -25,8 +25,7 @@ argocd app patch-resource APPNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_patch.md b/docs/user-guide/commands/argocd_app_patch.md index 90375448ce3af..01147f022c755 100644 --- a/docs/user-guide/commands/argocd_app_patch.md +++ b/docs/user-guide/commands/argocd_app_patch.md @@ -21,17 +21,15 @@ argocd app patch APPNAME [flags] ### Options ``` - -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") + -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 ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_remove-source.md b/docs/user-guide/commands/argocd_app_remove-source.md deleted file mode 100644 index d9741e108ce86..0000000000000 --- a/docs/user-guide/commands/argocd_app_remove-source.md +++ /dev/null @@ -1,58 +0,0 @@ -# `argocd app remove-source` Command Reference - -## argocd app remove-source - -Remove a source from multiple sources application. Counting starts with 1. Default value is -1. - -``` -argocd app remove-source APPNAME [flags] -``` - -### Examples - -``` - # Remove the source at position 1 from application's sources. Counting starts at 1. - argocd app remove-source myapplication --source-position 1 -``` - -### Options - -``` - -N, --app-namespace string Namespace of the target application where the source will be appended - -h, --help help for remove-source - --source-position int Position of the source from the list of sources of the app. Counting starts at 1. (default -1) -``` - -### Options inherited from parent commands - -``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable - --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 9e3b43c5e1bfa..22027f74ba3d7 100644 --- a/docs/user-guide/commands/argocd_app_resources.md +++ b/docs/user-guide/commands/argocd_app_resources.md @@ -20,8 +20,7 @@ argocd app resources APPNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_rollback.md b/docs/user-guide/commands/argocd_app_rollback.md index 47adea4a19a3a..bfcbf89631854 100644 --- a/docs/user-guide/commands/argocd_app_rollback.md +++ b/docs/user-guide/commands/argocd_app_rollback.md @@ -11,18 +11,16 @@ argocd app rollback APPNAME [ID] [flags] ### Options ``` - -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 + -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 ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_set.md b/docs/user-guide/commands/argocd_app_set.md index 878d6e098e3ca..18096e16f256a 100644 --- a/docs/user-guide/commands/argocd_app_set.md +++ b/docs/user-guide/commands/argocd_app_set.md @@ -17,8 +17,11 @@ argocd app set APPNAME [flags] # 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 for a source at position 1 under spec.sources of app my-app. source-position starts at 1. - argocd app set my-app --source-position 1 --repo https://github.com/argoproj/argocd-example-apps.git + # 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 application parameters and specify the namespace argocd app set my-app --parameter key1=value1 --parameter key2=value2 --namespace my-namespace @@ -28,7 +31,6 @@ argocd app set APPNAME [flags] ``` --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,10 +40,7 @@ argocd app set APPNAME [flags] --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-api-versions stringArray Helm api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster --helm-chart string Helm Chart name - --helm-kube-version string Helm kube-version to use when running helm template. If not set, use the kube version from the destination cluster - --helm-namespace string Helm namespace to use when running helm template. If not set, use app.spec.destination.namespace --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) @@ -55,14 +54,11 @@ argocd app set APPNAME [flags] --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-api-versions stringArray api-versions (in format [group/]version/kind) to use when running helm template (Can be repeated to set several values: --helm-api-versions traefik.io/v1alpha1/TLSOption --helm-api-versions v1/Service). If not set, use the api-versions from the destination cluster. Only applicable when Helm is enabled for Kustomize builds --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-kube-version string kube-version to use when running helm template. If not set, use the kube version from the destination cluster. Only applicable when Helm is enabled for Kustomize builds - --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 @@ -72,15 +68,13 @@ 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-position int Position of the source from the list of sources of the app. Counting 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: manual (aliases of manual: none), automated (aliases of automated: auto, automatic)) + --sync-policy string Set the sync policy (one of: 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) @@ -93,8 +87,7 @@ argocd app set APPNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_sync.md b/docs/user-guide/commands/argocd_app_sync.md index 00d37d33747ff..332e0d50ff3d8 100644 --- a/docs/user-guide/commands/argocd_app_sync.md +++ b/docs/user-guide/commands/argocd_app_sync.md @@ -24,9 +24,6 @@ argocd app sync [APPNAME... | -l selector | --project project-name] [flags] argocd app sync -l '!app.kubernetes.io/instance' argocd app sync -l 'app.kubernetes.io/instance notin (my-app,other-app)' - # Sync a multi-source application for specific revision of specific sources - argocd app manifests my-app --revisions 0.0.1 --source-positions 1 --revisions 0.0.2 --source-positions 2 - # Sync a specific resource # Resource should be formatted as GROUP:KIND:NAME. If no GROUP is specified then :KIND:NAME argocd app sync my-app --resource :Service:my-service @@ -41,7 +38,6 @@ 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 @@ -64,10 +60,8 @@ argocd app sync [APPNAME... | -l selector | --project project-name] [flags] --retry-backoff-max-duration duration Max retry backoff duration. Input needs to be a duration (e.g. 2m, 1h) (default 3m0s) --retry-limit int Max number of allowed sync retries --revision string Sync to a specific revision. Preserves parameter overrides - --revisions stringArray Show manifests at specific revisions for source position in source-positions -l, --selector string Sync apps that match this label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints. --server-side Use server-side apply while syncing the application - --source-positions int64Slice List of source positions. Default is empty array. Counting start at 1. (default []) --strategy string Sync strategy (one of: apply|hook) --timeout uint Time out after this many seconds ``` @@ -75,8 +69,7 @@ argocd app sync [APPNAME... | -l selector | --project project-name] [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_terminate-op.md b/docs/user-guide/commands/argocd_app_terminate-op.md index 37cf70b9ea058..a6d04299ca313 100644 --- a/docs/user-guide/commands/argocd_app_terminate-op.md +++ b/docs/user-guide/commands/argocd_app_terminate-op.md @@ -17,8 +17,7 @@ argocd app terminate-op APPNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_unset.md b/docs/user-guide/commands/argocd_app_unset.md index 177f1b095dd69..9982a0fffbebd 100644 --- a/docs/user-guide/commands/argocd_app_unset.md +++ b/docs/user-guide/commands/argocd_app_unset.md @@ -14,12 +14,9 @@ argocd app unset APPNAME parameters [flags] # Unset kustomize override kustomize image argocd app unset my-app --kustomize-image=alpine - # Unset kustomize override suffix + # Unset kustomize override prefix argocd app unset my-app --namesuffix - # Unset kustomize override suffix for source at position 1 under spec.sources of app my-app. source-position starts at 1. - argocd app unset my-app --source-position 1 --namesuffix - # Unset parameter override argocd app unset my-app -p COMPONENT=PARAM ``` @@ -27,7 +24,6 @@ argocd app unset APPNAME parameters [flags] ### Options ``` - -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) @@ -39,8 +35,6 @@ argocd app unset APPNAME parameters [flags] -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-position int Position of the source from the list of sources of the app. Counting starts at 1. (default -1) --values stringArray Unset one or more Helm values files --values-literal Unset literal Helm values block ``` @@ -48,8 +42,7 @@ argocd app unset APPNAME parameters [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_app_wait.md b/docs/user-guide/commands/argocd_app_wait.md index 867484e3432b1..99e422167b76f 100644 --- a/docs/user-guide/commands/argocd_app_wait.md +++ b/docs/user-guide/commands/argocd_app_wait.md @@ -38,9 +38,7 @@ 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 @@ -55,8 +53,7 @@ argocd app wait [APPNAME.. | -l selector] [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_appset.md b/docs/user-guide/commands/argocd_appset.md index 39c25dcca8fa7..7b543ae318831 100644 --- a/docs/user-guide/commands/argocd_appset.md +++ b/docs/user-guide/commands/argocd_appset.md @@ -52,8 +52,7 @@ argocd appset [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") @@ -83,7 +82,6 @@ argocd appset [flags] * [argocd](argocd.md) - argocd controls a Argo CD server * [argocd appset create](argocd_appset_create.md) - Create one or more ApplicationSets * [argocd appset delete](argocd_appset_delete.md) - Delete one or more ApplicationSets -* [argocd appset generate](argocd_appset_generate.md) - Generate apps of ApplicationSet rendered templates * [argocd appset get](argocd_appset_get.md) - Get ApplicationSet details * [argocd appset list](argocd_appset_list.md) - List ApplicationSets diff --git a/docs/user-guide/commands/argocd_appset_create.md b/docs/user-guide/commands/argocd_appset_create.md index ac0b1427dd7af..fccc03fcc971c 100644 --- a/docs/user-guide/commands/argocd_appset_create.md +++ b/docs/user-guide/commands/argocd_appset_create.md @@ -13,25 +13,19 @@ argocd appset create [flags] ``` # Create ApplicationSets argocd appset create (...) - - # Dry-run AppSet creation to see what applications would be managed - argocd appset create --dry-run -o json | jq -r '.status.resources[].name' ``` ### Options ``` - --dry-run Allows to evaluate the ApplicationSet template on the server to get a preview of the applications that would be created - -h, --help help for create - -o, --output string Output format. One of: json|yaml|wide (default "wide") - --upsert Allows to override ApplicationSet with the same name even if supplied ApplicationSet spec is different from existing spec + -h, --help help for create + --upsert Allows to override ApplicationSet with the same name even if supplied ApplicationSet spec is different from existing spec ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_appset_delete.md b/docs/user-guide/commands/argocd_appset_delete.md index 90510a42073c0..d97ca51b604e8 100644 --- a/docs/user-guide/commands/argocd_appset_delete.md +++ b/docs/user-guide/commands/argocd_appset_delete.md @@ -25,8 +25,7 @@ argocd appset delete [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_appset_generate.md b/docs/user-guide/commands/argocd_appset_generate.md deleted file mode 100644 index 8c7db6e8ac9c0..0000000000000 --- a/docs/user-guide/commands/argocd_appset_generate.md +++ /dev/null @@ -1,57 +0,0 @@ -# `argocd appset generate` Command Reference - -## argocd appset generate - -Generate apps of ApplicationSet rendered templates - -``` -argocd appset generate [flags] -``` - -### Examples - -``` - # Generate apps of ApplicationSet rendered templates - argocd appset generate (...) -``` - -### Options - -``` - -h, --help help for generate - -o, --output string Output format. One of: json|yaml|wide (default "wide") -``` - -### Options inherited from parent commands - -``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable - --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 appset](argocd_appset.md) - Manage ApplicationSets - diff --git a/docs/user-guide/commands/argocd_appset_get.md b/docs/user-guide/commands/argocd_appset_get.md index 76b3e3946988b..8024d8ebf0a06 100644 --- a/docs/user-guide/commands/argocd_appset_get.md +++ b/docs/user-guide/commands/argocd_appset_get.md @@ -26,8 +26,7 @@ argocd appset get APPSETNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_appset_list.md b/docs/user-guide/commands/argocd_appset_list.md index fad42ce7e240c..92e0b21cb749b 100644 --- a/docs/user-guide/commands/argocd_appset_list.md +++ b/docs/user-guide/commands/argocd_appset_list.md @@ -28,8 +28,7 @@ argocd appset list [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cert.md b/docs/user-guide/commands/argocd_cert.md index 1e0db72b0452b..b126328a4372f 100644 --- a/docs/user-guide/commands/argocd_cert.md +++ b/docs/user-guide/commands/argocd_cert.md @@ -59,8 +59,7 @@ argocd cert [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cert_add-ssh.md b/docs/user-guide/commands/argocd_cert_add-ssh.md index a32d12e18ea32..94daf24bf376e 100644 --- a/docs/user-guide/commands/argocd_cert_add-ssh.md +++ b/docs/user-guide/commands/argocd_cert_add-ssh.md @@ -20,8 +20,7 @@ argocd cert add-ssh --batch [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cert_add-tls.md b/docs/user-guide/commands/argocd_cert_add-tls.md index 0208a502836ac..e8d3d733697e7 100644 --- a/docs/user-guide/commands/argocd_cert_add-tls.md +++ b/docs/user-guide/commands/argocd_cert_add-tls.md @@ -19,8 +19,7 @@ argocd cert add-tls SERVERNAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cert_list.md b/docs/user-guide/commands/argocd_cert_list.md index d3b80dfeac97f..43a4af5bea783 100644 --- a/docs/user-guide/commands/argocd_cert_list.md +++ b/docs/user-guide/commands/argocd_cert_list.md @@ -21,8 +21,7 @@ argocd cert list [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cert_rm.md b/docs/user-guide/commands/argocd_cert_rm.md index f76fb6a9a38c9..602a84aa6b85c 100644 --- a/docs/user-guide/commands/argocd_cert_rm.md +++ b/docs/user-guide/commands/argocd_cert_rm.md @@ -19,8 +19,7 @@ argocd cert rm REPOSERVER [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cluster.md b/docs/user-guide/commands/argocd_cluster.md index 6f30e5a9308e4..a30c357d54d71 100644 --- a/docs/user-guide/commands/argocd_cluster.md +++ b/docs/user-guide/commands/argocd_cluster.md @@ -56,8 +56,7 @@ argocd cluster [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cluster_add.md b/docs/user-guide/commands/argocd_cluster_add.md index cf1d9ba2d588e..6d3a094b4bf83 100644 --- a/docs/user-guide/commands/argocd_cluster_add.md +++ b/docs/user-guide/commands/argocd_cluster_add.md @@ -13,7 +13,6 @@ 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. @@ -39,8 +38,7 @@ argocd cluster add CONTEXT [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cluster_get.md b/docs/user-guide/commands/argocd_cluster_get.md index 8b3fd5e410a04..a020a94557e99 100644 --- a/docs/user-guide/commands/argocd_cluster_get.md +++ b/docs/user-guide/commands/argocd_cluster_get.md @@ -25,8 +25,7 @@ argocd cluster get in-cluster ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cluster_list.md b/docs/user-guide/commands/argocd_cluster_list.md index d7ffbeb7baa9f..9779a4fb8af0b 100644 --- a/docs/user-guide/commands/argocd_cluster_list.md +++ b/docs/user-guide/commands/argocd_cluster_list.md @@ -15,7 +15,7 @@ argocd cluster list [flags] # List Clusters in Default "Wide" Format argocd cluster list -# List Cluster via specifying the server +# List Cluster via specifing the server argocd cluster list --server # List Clusters in JSON Format @@ -40,8 +40,7 @@ argocd cluster list -o server ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cluster_rm.md b/docs/user-guide/commands/argocd_cluster_rm.md index 667e5f9143cd4..80057bb5a7082 100644 --- a/docs/user-guide/commands/argocd_cluster_rm.md +++ b/docs/user-guide/commands/argocd_cluster_rm.md @@ -25,8 +25,7 @@ argocd cluster rm cluster-name ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cluster_rotate-auth.md b/docs/user-guide/commands/argocd_cluster_rotate-auth.md index f91c10f3ea6e2..8dc3a5bf745d3 100644 --- a/docs/user-guide/commands/argocd_cluster_rotate-auth.md +++ b/docs/user-guide/commands/argocd_cluster_rotate-auth.md @@ -24,8 +24,7 @@ argocd cluster rotate-auth cluster-name ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_cluster_set.md b/docs/user-guide/commands/argocd_cluster_set.md index 3d26a6ec29702..2dba9c2ad16e8 100644 --- a/docs/user-guide/commands/argocd_cluster_set.md +++ b/docs/user-guide/commands/argocd_cluster_set.md @@ -19,18 +19,15 @@ argocd cluster set NAME [flags] ### Options ``` - --annotation stringArray Set metadata annotations (e.g. --annotation key=value) - -h, --help help for set - --label stringArray Set metadata labels (e.g. --label key=value) - --name string Overwrite the cluster name - --namespace stringArray List of namespaces which are allowed to manage. Specify '*' to manage all namespaces + -h, --help help for set + --name string Overwrite the cluster name + --namespace stringArray List of namespaces which are allowed to manage. Specify '*' to manage all namespaces ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_completion.md b/docs/user-guide/commands/argocd_completion.md index 32c91ccbc2707..3d6d981ef4c8f 100644 --- a/docs/user-guide/commands/argocd_completion.md +++ b/docs/user-guide/commands/argocd_completion.md @@ -2,11 +2,11 @@ ## argocd completion -output shell completion code for the specified shell (bash, zsh or fish) +output shell completion code for the specified shell (bash or zsh) ### Synopsis -Write bash, zsh or fish shell completion code to standard output. +Write bash or zsh shell completion code to standard output. For bash, ensure you have bash completions installed and enabled. To access completions in your current shell, run @@ -36,11 +36,6 @@ $ source <(argocd completion bash) $ argocd completion zsh > _argocd $ source _argocd -# For fish -$ argocd completion fish > ~/.config/fish/completions/argocd.fish -$ source ~/.config/fish/completions/argocd.fish - - ``` ### Options @@ -52,8 +47,7 @@ $ source ~/.config/fish/completions/argocd.fish ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_context.md b/docs/user-guide/commands/argocd_context.md index 1805bb7e0a1e0..f02944cf4f775 100644 --- a/docs/user-guide/commands/argocd_context.md +++ b/docs/user-guide/commands/argocd_context.md @@ -31,8 +31,7 @@ argocd context cd.argoproj.io --delete ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_gpg.md b/docs/user-guide/commands/argocd_gpg.md index 41941b1f2739c..bca15e98b7c87 100644 --- a/docs/user-guide/commands/argocd_gpg.md +++ b/docs/user-guide/commands/argocd_gpg.md @@ -36,8 +36,7 @@ argocd gpg [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_gpg_add.md b/docs/user-guide/commands/argocd_gpg_add.md index e0fd7ac55116f..3ef5d4e6c72d5 100644 --- a/docs/user-guide/commands/argocd_gpg_add.md +++ b/docs/user-guide/commands/argocd_gpg_add.md @@ -25,8 +25,7 @@ argocd gpg add [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_gpg_get.md b/docs/user-guide/commands/argocd_gpg_get.md index 5e738b60d8906..e0ad3d9ee25d6 100644 --- a/docs/user-guide/commands/argocd_gpg_get.md +++ b/docs/user-guide/commands/argocd_gpg_get.md @@ -31,8 +31,7 @@ argocd gpg get KEYID [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_gpg_list.md b/docs/user-guide/commands/argocd_gpg_list.md index 2d193caf677a6..50f0e72e83c0d 100644 --- a/docs/user-guide/commands/argocd_gpg_list.md +++ b/docs/user-guide/commands/argocd_gpg_list.md @@ -31,8 +31,7 @@ argocd gpg list [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_gpg_rm.md b/docs/user-guide/commands/argocd_gpg_rm.md index 125f193bb473c..ecf744988d0fd 100644 --- a/docs/user-guide/commands/argocd_gpg_rm.md +++ b/docs/user-guide/commands/argocd_gpg_rm.md @@ -17,8 +17,7 @@ argocd gpg rm KEYID [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_login.md b/docs/user-guide/commands/argocd_login.md index c20247b01b283..adf02fefbc454 100644 --- a/docs/user-guide/commands/argocd_login.md +++ b/docs/user-guide/commands/argocd_login.md @@ -28,21 +28,19 @@ argocd login cd.argoproj.io --core ### Options ``` - -h, --help help for login - --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-launch-browser Automatically launch the system default browser when performing SSO login (default true) - --sso-port int Port to run local OAuth2 login application (default 8085) - --username string The username of an account to authenticate + -h, --help help for login + --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 ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_logout.md b/docs/user-guide/commands/argocd_logout.md index 132e73fa5033f..3e18c70a063a0 100644 --- a/docs/user-guide/commands/argocd_logout.md +++ b/docs/user-guide/commands/argocd_logout.md @@ -30,8 +30,7 @@ $ argocd logout ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj.md b/docs/user-guide/commands/argocd_proj.md index 8f35188d33634..17aeef0cdfc27 100644 --- a/docs/user-guide/commands/argocd_proj.md +++ b/docs/user-guide/commands/argocd_proj.md @@ -52,8 +52,7 @@ argocd proj [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") @@ -82,11 +81,9 @@ argocd proj [flags] * [argocd](argocd.md) - argocd controls a Argo CD server * [argocd proj add-destination](argocd_proj_add-destination.md) - Add project destination -* [argocd proj add-destination-service-account](argocd_proj_add-destination-service-account.md) - Add project destination's default service account * [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 @@ -97,11 +94,9 @@ argocd proj [flags] * [argocd proj get](argocd_proj_get.md) - Get project details * [argocd proj list](argocd_proj_list.md) - List projects * [argocd proj remove-destination](argocd_proj_remove-destination.md) - Remove project destination -* [argocd proj remove-destination-service-account](argocd_proj_remove-destination-service-account.md) - Remove default destination service account from the project * [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-service-account.md b/docs/user-guide/commands/argocd_proj_add-destination-service-account.md deleted file mode 100644 index b1c0be6de7c85..0000000000000 --- a/docs/user-guide/commands/argocd_proj_add-destination-service-account.md +++ /dev/null @@ -1,60 +0,0 @@ -# `argocd proj add-destination-service-account` Command Reference - -## argocd proj add-destination-service-account - -Add project destination's default service account - -``` -argocd proj add-destination-service-account PROJECT SERVER NAMESPACE SERVICE_ACCOUNT [flags] -``` - -### Examples - -``` - # Add project destination service account (SERVICE_ACCOUNT) for a server URL (SERVER) in the specified namespace (NAMESPACE) on the project with name PROJECT - argocd proj add-destination-service-account PROJECT SERVER NAMESPACE SERVICE_ACCOUNT - - # Add project destination service account (SERVICE_ACCOUNT) from a different namespace - argocd proj add-destination PROJECT SERVER NAMESPACE SERVICE_ACCOUNT --service-account-namespace -``` - -### Options - -``` - -h, --help help for add-destination-service-account - --service-account-namespace string Use service-account-namespace as namespace where the service account is present -``` - -### Options inherited from parent commands - -``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable - --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-destination.md b/docs/user-guide/commands/argocd_proj_add-destination.md index d13f1a5234f7b..688aebf84156e 100644 --- a/docs/user-guide/commands/argocd_proj_add-destination.md +++ b/docs/user-guide/commands/argocd_proj_add-destination.md @@ -28,8 +28,7 @@ argocd proj add-destination PROJECT SERVER/NAME NAMESPACE [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 c32ba8c010300..1b36d8a5ff0f1 100644 --- a/docs/user-guide/commands/argocd_proj_add-orphaned-ignore.md +++ b/docs/user-guide/commands/argocd_proj_add-orphaned-ignore.md @@ -28,8 +28,7 @@ argocd proj add-orphaned-ignore PROJECT GROUP KIND [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 406f554b61195..404660510700b 100644 --- a/docs/user-guide/commands/argocd_proj_add-signature-key.md +++ b/docs/user-guide/commands/argocd_proj_add-signature-key.md @@ -24,8 +24,7 @@ argocd proj add-signature-key PROJECT KEY-ID [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_add-source-namespace.md b/docs/user-guide/commands/argocd_proj_add-source-namespace.md deleted file mode 100644 index 45c4b0cba6781..0000000000000 --- a/docs/user-guide/commands/argocd_proj_add-source-namespace.md +++ /dev/null @@ -1,56 +0,0 @@ -# `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 - -``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable - --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 0e64e29d0a3f4..f0c2f18fd9792 100644 --- a/docs/user-guide/commands/argocd_proj_add-source.md +++ b/docs/user-guide/commands/argocd_proj_add-source.md @@ -24,8 +24,7 @@ argocd proj add-source PROJECT URL [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 11a8cfc158ff0..338027e724bc2 100644 --- a/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md +++ b/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md @@ -25,8 +25,7 @@ argocd proj allow-cluster-resource PROJECT GROUP KIND [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 89bb7197cf2bc..3e4a410f32a47 100644 --- a/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md +++ b/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md @@ -25,8 +25,7 @@ argocd proj allow-namespace-resource PROJECT GROUP KIND [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_create.md b/docs/user-guide/commands/argocd_proj_create.md index c8b27e35bb762..fd8687c1b2982 100644 --- a/docs/user-guide/commands/argocd_proj_create.md +++ b/docs/user-guide/commands/argocd_proj_create.md @@ -40,8 +40,7 @@ argocd proj create PROJECT [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_delete.md b/docs/user-guide/commands/argocd_proj_delete.md index b955732eb6067..4d6c4a94f609c 100644 --- a/docs/user-guide/commands/argocd_proj_delete.md +++ b/docs/user-guide/commands/argocd_proj_delete.md @@ -24,8 +24,7 @@ argocd proj delete PROJECT [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 bc9bfcfae0d5a..4621b18c3efe1 100644 --- a/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md +++ b/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md @@ -25,8 +25,7 @@ argocd proj deny-cluster-resource PROJECT GROUP KIND [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 97367b23cb9c7..e8b55a7b0adb6 100644 --- a/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md +++ b/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md @@ -25,8 +25,7 @@ argocd proj deny-namespace-resource PROJECT GROUP KIND [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_edit.md b/docs/user-guide/commands/argocd_proj_edit.md index 1955aa11ba2c4..63a584accfad8 100644 --- a/docs/user-guide/commands/argocd_proj_edit.md +++ b/docs/user-guide/commands/argocd_proj_edit.md @@ -24,8 +24,7 @@ argocd proj edit PROJECT [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_get.md b/docs/user-guide/commands/argocd_proj_get.md index 930972018db05..2d2e437b79779 100644 --- a/docs/user-guide/commands/argocd_proj_get.md +++ b/docs/user-guide/commands/argocd_proj_get.md @@ -28,8 +28,7 @@ argocd proj get PROJECT [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_list.md b/docs/user-guide/commands/argocd_proj_list.md index 2a71f43d68c3a..d96c0c4bb13b8 100644 --- a/docs/user-guide/commands/argocd_proj_list.md +++ b/docs/user-guide/commands/argocd_proj_list.md @@ -28,8 +28,7 @@ argocd proj list [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_remove-destination-service-account.md b/docs/user-guide/commands/argocd_proj_remove-destination-service-account.md deleted file mode 100644 index bcb2a2bc3605e..0000000000000 --- a/docs/user-guide/commands/argocd_proj_remove-destination-service-account.md +++ /dev/null @@ -1,56 +0,0 @@ -# `argocd proj remove-destination-service-account` Command Reference - -## argocd proj remove-destination-service-account - -Remove default destination service account from the project - -``` -argocd proj remove-destination-service-account PROJECT SERVER NAMESPACE SERVICE_ACCOUNT [flags] -``` - -### Examples - -``` - # Remove the destination service account (SERVICE_ACCOUNT) from the specified destination (SERVER and NAMESPACE combination) on the project with name PROJECT - argocd proj remove-destination-service-account PROJECT SERVER NAMESPACE SERVICE_ACCOUNT -``` - -### Options - -``` - -h, --help help for remove-destination-service-account -``` - -### Options inherited from parent commands - -``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable - --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-destination.md b/docs/user-guide/commands/argocd_proj_remove-destination.md index 88a702b2ed2b5..612e1db68356a 100644 --- a/docs/user-guide/commands/argocd_proj_remove-destination.md +++ b/docs/user-guide/commands/argocd_proj_remove-destination.md @@ -24,8 +24,7 @@ argocd proj remove-destination PROJECT SERVER NAMESPACE [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 79ff167f1c394..857cf3c595177 100644 --- a/docs/user-guide/commands/argocd_proj_remove-orphaned-ignore.md +++ b/docs/user-guide/commands/argocd_proj_remove-orphaned-ignore.md @@ -28,8 +28,7 @@ argocd proj remove-orphaned-ignore PROJECT GROUP KIND [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 0f81b6ec52270..61d6085022662 100644 --- a/docs/user-guide/commands/argocd_proj_remove-signature-key.md +++ b/docs/user-guide/commands/argocd_proj_remove-signature-key.md @@ -24,8 +24,7 @@ argocd proj remove-signature-key PROJECT KEY-ID [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_remove-source-namespace.md b/docs/user-guide/commands/argocd_proj_remove-source-namespace.md deleted file mode 100644 index a26bebcee38bb..0000000000000 --- a/docs/user-guide/commands/argocd_proj_remove-source-namespace.md +++ /dev/null @@ -1,56 +0,0 @@ -# `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 - -``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable - --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 66c016fadd7e5..d6a1c353059f3 100644 --- a/docs/user-guide/commands/argocd_proj_remove-source.md +++ b/docs/user-guide/commands/argocd_proj_remove-source.md @@ -24,8 +24,7 @@ argocd proj remove-source PROJECT URL [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_role.md b/docs/user-guide/commands/argocd_proj_role.md index 89d7abe87de4d..9546cc4b7ab27 100644 --- a/docs/user-guide/commands/argocd_proj_role.md +++ b/docs/user-guide/commands/argocd_proj_role.md @@ -17,8 +17,7 @@ argocd proj role [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 9d818665cd487..4a3aa2f1e8be1 100644 --- a/docs/user-guide/commands/argocd_proj_role_add-group.md +++ b/docs/user-guide/commands/argocd_proj_role_add-group.md @@ -17,8 +17,7 @@ argocd proj role add-group PROJECT ROLE-NAME GROUP-CLAIM [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 d37469d1f8d36..d4804d31d66a1 100644 --- a/docs/user-guide/commands/argocd_proj_role_add-policy.md +++ b/docs/user-guide/commands/argocd_proj_role_add-policy.md @@ -49,8 +49,7 @@ ID ISSUED-AT EXPIRES-AT ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 faacc8a01b72d..fc7eaf93c2307 100644 --- a/docs/user-guide/commands/argocd_proj_role_create-token.md +++ b/docs/user-guide/commands/argocd_proj_role_create-token.md @@ -32,8 +32,7 @@ Create token succeeded for proj:test-project:test-role. ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_role_create.md b/docs/user-guide/commands/argocd_proj_role_create.md index 885c79f1672b3..60974c9e1b4e6 100644 --- a/docs/user-guide/commands/argocd_proj_role_create.md +++ b/docs/user-guide/commands/argocd_proj_role_create.md @@ -25,8 +25,7 @@ argocd proj role create PROJECT ROLE-NAME [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 c9c5a7fc17eca..006746f8faeeb 100644 --- a/docs/user-guide/commands/argocd_proj_role_delete-token.md +++ b/docs/user-guide/commands/argocd_proj_role_delete-token.md @@ -49,8 +49,7 @@ $ argocd proj role delete-token test-project test-role 1696769937 ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_role_delete.md b/docs/user-guide/commands/argocd_proj_role_delete.md index 97e01627e371a..fe94a2231db60 100644 --- a/docs/user-guide/commands/argocd_proj_role_delete.md +++ b/docs/user-guide/commands/argocd_proj_role_delete.md @@ -23,8 +23,7 @@ $ argocd proj role delete test-project test-role ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_role_get.md b/docs/user-guide/commands/argocd_proj_role_get.md index d35b8768b47b2..e21276ce85116 100644 --- a/docs/user-guide/commands/argocd_proj_role_get.md +++ b/docs/user-guide/commands/argocd_proj_role_get.md @@ -32,8 +32,7 @@ ID ISSUED-AT EXPIRES-AT ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 7df6b0c64e0aa..8d1fe93163dfc 100644 --- a/docs/user-guide/commands/argocd_proj_role_list-tokens.md +++ b/docs/user-guide/commands/argocd_proj_role_list-tokens.md @@ -28,8 +28,7 @@ fa9d3517-c52d-434c-9bff-215b38508842 2023-10-08T11:08:18+01:00 Never ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_role_list.md b/docs/user-guide/commands/argocd_proj_role_list.md index 34b62a3cf4beb..3cfd630ddc988 100644 --- a/docs/user-guide/commands/argocd_proj_role_list.md +++ b/docs/user-guide/commands/argocd_proj_role_list.md @@ -28,8 +28,7 @@ argocd proj role list PROJECT [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 b4db5dee8b882..a89e0bcfae315 100644 --- a/docs/user-guide/commands/argocd_proj_role_remove-group.md +++ b/docs/user-guide/commands/argocd_proj_role_remove-group.md @@ -17,8 +17,7 @@ argocd proj role remove-group PROJECT ROLE-NAME GROUP-CLAIM [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 df0fd403542af..96aee05da86eb 100644 --- a/docs/user-guide/commands/argocd_proj_role_remove-policy.md +++ b/docs/user-guide/commands/argocd_proj_role_remove-policy.md @@ -49,8 +49,7 @@ ID ISSUED-AT EXPIRES-AT ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_set.md b/docs/user-guide/commands/argocd_proj_set.md index 7b4d79ff13588..3dc0cc06ec787 100644 --- a/docs/user-guide/commands/argocd_proj_set.md +++ b/docs/user-guide/commands/argocd_proj_set.md @@ -38,8 +38,7 @@ argocd proj set PROJECT [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_windows.md b/docs/user-guide/commands/argocd_proj_windows.md index b02a6772a8582..0b22c2098dc82 100644 --- a/docs/user-guide/commands/argocd_proj_windows.md +++ b/docs/user-guide/commands/argocd_proj_windows.md @@ -34,8 +34,7 @@ argocd proj windows list ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_windows_add.md b/docs/user-guide/commands/argocd_proj_windows_add.md index beb158b9c6243..52fd3a8354ee3 100644 --- a/docs/user-guide/commands/argocd_proj_windows_add.md +++ b/docs/user-guide/commands/argocd_proj_windows_add.md @@ -48,8 +48,7 @@ argocd proj windows add PROJECT \ ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_windows_delete.md b/docs/user-guide/commands/argocd_proj_windows_delete.md index 2fc4ef2c43390..6faf7dbeedc19 100644 --- a/docs/user-guide/commands/argocd_proj_windows_delete.md +++ b/docs/user-guide/commands/argocd_proj_windows_delete.md @@ -28,8 +28,7 @@ argocd proj windows delete new-project 1 ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 011a394b8848a..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 @@ -19,7 +19,7 @@ argocd proj windows disable-manual-sync PROJECT ID [flags] #Disable manual sync for a sync window for the Project argocd proj windows disable-manual-sync PROJECT ID -#Disabling manual sync for a windows set on the default project with Id 0 +#Disbaling manual sync for a windows set on the default project with Id 0 argocd proj windows disable-manual-sync default 0 ``` @@ -32,8 +32,7 @@ argocd proj windows disable-manual-sync default 0 ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") 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 1f51fe038e3b8..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 @@ -35,8 +35,7 @@ argocd proj windows enable-manual-sync my-app-project --message "Manual sync ini ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_windows_list.md b/docs/user-guide/commands/argocd_proj_windows_list.md index 5f15f34dfe948..3c361f90d2a68 100644 --- a/docs/user-guide/commands/argocd_proj_windows_list.md +++ b/docs/user-guide/commands/argocd_proj_windows_list.md @@ -32,8 +32,7 @@ argocd proj windows list test-project ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_proj_windows_update.md b/docs/user-guide/commands/argocd_proj_windows_update.md index a3405c0650be8..e01e3787d51a2 100644 --- a/docs/user-guide/commands/argocd_proj_windows_update.md +++ b/docs/user-guide/commands/argocd_proj_windows_update.md @@ -36,8 +36,7 @@ argocd proj windows update PROJECT ID \ ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_relogin.md b/docs/user-guide/commands/argocd_relogin.md index 8891d35b896dd..430ab4a9222c9 100644 --- a/docs/user-guide/commands/argocd_relogin.md +++ b/docs/user-guide/commands/argocd_relogin.md @@ -32,17 +32,15 @@ argocd login cd.argoproj.io --core ### Options ``` - -h, --help help for relogin - --password string The password of an account to authenticate - --sso-launch-browser Automatically launch the default browser when performing SSO login (default true) - --sso-port int Port to run local OAuth2 login application (default 8085) + -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) ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_repo.md b/docs/user-guide/commands/argocd_repo.md index 3e6548df9a5a7..4df85f7b00d3d 100644 --- a/docs/user-guide/commands/argocd_repo.md +++ b/docs/user-guide/commands/argocd_repo.md @@ -54,8 +54,7 @@ argocd repo rm https://github.com/yourusername/your-repo.git ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_repo_add.md b/docs/user-guide/commands/argocd_repo_add.md index 4abb437cf7bdc..263dda07af7dc 100644 --- a/docs/user-guide/commands/argocd_repo_add.md +++ b/docs/user-guide/commands/argocd_repo_add.md @@ -17,12 +17,6 @@ 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 @@ -64,7 +58,6 @@ argocd repo add REPOURL [flags] --insecure-ignore-host-key disables SSH strict host key checking (deprecated, use --insecure-skip-server-verification instead) --insecure-skip-server-verification disables server certificate and host key checks --name string name of the repository, mandatory for repositories of type helm - --no-proxy string don't access these targets via proxy --password string password to the repository --project string project of the repository --proxy string use proxy to access repository @@ -79,8 +72,7 @@ argocd repo add REPOURL [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_repo_get.md b/docs/user-guide/commands/argocd_repo_get.md index b28d30e1e0037..5a900adb09487 100644 --- a/docs/user-guide/commands/argocd_repo_get.md +++ b/docs/user-guide/commands/argocd_repo_get.md @@ -13,15 +13,13 @@ argocd repo get [flags] ``` -h, --help help for get -o, --output string Output format. One of: json|yaml|wide|url (default "wide") - --project string project of the repository --refresh string Force a cache refresh on connection status , must be one of: 'hard' ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_repo_list.md b/docs/user-guide/commands/argocd_repo_list.md index 5a13cff85c5fc..06f1f788cb7c2 100644 --- a/docs/user-guide/commands/argocd_repo_list.md +++ b/docs/user-guide/commands/argocd_repo_list.md @@ -19,8 +19,7 @@ argocd repo list [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_repo_rm.md b/docs/user-guide/commands/argocd_repo_rm.md index 4b784e0a6d1c1..01e89d48e76a1 100644 --- a/docs/user-guide/commands/argocd_repo_rm.md +++ b/docs/user-guide/commands/argocd_repo_rm.md @@ -11,15 +11,13 @@ argocd repo rm REPO [flags] ### Options ``` - -h, --help help for rm - --project string project of the repository + -h, --help help for rm ``` ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_repocreds.md b/docs/user-guide/commands/argocd_repocreds.md index cac91d9700bf5..f073b2bbb6161 100644 --- a/docs/user-guide/commands/argocd_repocreds.md +++ b/docs/user-guide/commands/argocd_repocreds.md @@ -49,8 +49,7 @@ argocd repocreds [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_repocreds_add.md b/docs/user-guide/commands/argocd_repocreds_add.md index 39405519def40..ce66dc49cfe8c 100644 --- a/docs/user-guide/commands/argocd_repocreds_add.md +++ b/docs/user-guide/commands/argocd_repocreds_add.md @@ -43,7 +43,6 @@ argocd repocreds add REPOURL [flags] --github-app-private-key-path string private key of the GitHub Application -h, --help help for add --password string password to the repository - --proxy-url string If provided, this URL will be used to connect via proxy --ssh-private-key-path string path to the private ssh key (e.g. ~/.ssh/id_rsa) --tls-client-cert-key-path string path to the TLS client cert's key path (must be PEM format) --tls-client-cert-path string path to the TLS client cert (must be PEM format) @@ -55,8 +54,7 @@ argocd repocreds add REPOURL [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_repocreds_list.md b/docs/user-guide/commands/argocd_repocreds_list.md index ebcf308bdc766..ae358afab2056 100644 --- a/docs/user-guide/commands/argocd_repocreds_list.md +++ b/docs/user-guide/commands/argocd_repocreds_list.md @@ -34,8 +34,7 @@ argocd repocreds list [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_repocreds_rm.md b/docs/user-guide/commands/argocd_repocreds_rm.md index 6893bd3dc3db9..3bfee30eb40a3 100644 --- a/docs/user-guide/commands/argocd_repocreds_rm.md +++ b/docs/user-guide/commands/argocd_repocreds_rm.md @@ -24,8 +24,7 @@ argocd repocreds rm CREDSURL [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/commands/argocd_version.md b/docs/user-guide/commands/argocd_version.md index bd6505cc1c622..6a7fa7baf5ecb 100644 --- a/docs/user-guide/commands/argocd_version.md +++ b/docs/user-guide/commands/argocd_version.md @@ -56,8 +56,7 @@ argocd version [flags] ### Options inherited from parent commands ``` - --argocd-context string The name of the Argo-CD server context to use - --auth-token string Authentication token; set this or the ARGOCD_AUTH_TOKEN environment variable + --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") diff --git a/docs/user-guide/diff-strategies.md b/docs/user-guide/diff-strategies.md index ffd09660696ac..2890fe64cbb0e 100644 --- a/docs/user-guide/diff-strategies.md +++ b/docs/user-guide/diff-strategies.md @@ -114,7 +114,7 @@ metadata: ... ``` -Note: This annotation is only effective when Server-Side Diff is +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: diff --git a/docs/user-guide/diffing.md b/docs/user-guide/diffing.md index 7e5b72d97959c..2a69654b4aa1a 100644 --- a/docs/user-guide/diffing.md +++ b/docs/user-guide/diffing.md @@ -68,14 +68,13 @@ spec: 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 need to replace it with the `~1` character. For example: +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 + jsonPointers: /metadata/labels/node-role.kubernetes.io~1worker ``` ## System-Level Configuration diff --git a/docs/user-guide/gpg-verification.md b/docs/user-guide/gpg-verification.md index f0881df0f0846..07f9f474647b3 100644 --- a/docs/user-guide/gpg-verification.md +++ b/docs/user-guide/gpg-verification.md @@ -17,8 +17,8 @@ allowed public key. By default, signature verification is enabled but not enforced. If you wish to completely disable the GnuPG functionality in ArgoCD, you have to set the environment variable `ARGOCD_GPG_ENABLED` to `"false"` in the pod templates of -the `argocd-server`, `argocd-repo-server`, `argocd-application-controller` and -`argocd-applicationset-controller` deployment manifests. +the `argocd-server`, `argocd-repo-server` and `argocd-application-controller` +deployment manifests. Verification of GnuPG signatures is only supported with Git repositories. It is not possible using Helm repositories. @@ -29,11 +29,6 @@ not possible using Helm repositories. trust models, and it is not necessary (nor possible) to sign the public keys you are going to import into ArgoCD. - -!!!note Limitations - Signature verification is not supported for the templated `project` field when - using the Git generator. - ## Signature verification targets If signature verification is enforced, ArgoCD will verify the signature using @@ -287,8 +282,8 @@ spec: The GnuPG feature can be completely disabled if desired. In order to disable it, set the environment variable `ARGOCD_GPG_ENABLED` to `false` for the pod -templates of the `argocd-server`, `argocd-repo-server`, `argocd-application-controller` -and `argocd-applicationset-controller` deployments. +templates of the `argocd-server`, `argocd-repo-server` and + `argocd-application-controller` deployments. After the pods have been restarted, the GnuPG feature is disabled. diff --git a/docs/user-guide/helm.md b/docs/user-guide/helm.md index c768b50835a24..866f9c6d935aa 100644 --- a/docs/user-guide/helm.md +++ b/docs/user-guide/helm.md @@ -25,28 +25,9 @@ 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 @@ -72,22 +53,6 @@ source: - values-production.yaml ``` -If Helm is passed a non-existing value file during template expansion, it will error out. Missing -values files can be ignored (meaning, not passed to Helm) using the `--ignore-missing-value-files`. This can be -particularly helpful to implement a [default/override -pattern](https://github.com/argoproj/argo-cd/issues/7767#issue-1060611415) with [Application -Sets](./application-set.md). - -In the declarative syntax: -```yaml -source: - helm: - valueFiles: - - values-common.yaml - - values-optional-override.yaml - ignoreMissingValueFiles: true -``` - ## Values Argo CD supports the equivalent of a values file directly in the Application manifest using the `source.helm.valuesObject` key. @@ -177,7 +142,7 @@ Precedence of valueFiles themselves is the order they are defined in ``` if we have -valueFiles: +valuesFile: - values-file-2.yaml - values-file-1.yaml @@ -213,29 +178,10 @@ values: | the result will be param1=value5 ``` -!!! note "When valueFiles or values is used" +!!! 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 --set-file support - -The `--set-file` argument to helm can be used with the following syntax on -the cli: - -```bash -argocd app set helm-guestbook --helm-set-file some.key=path/to/file.ext -``` - -or using the fileParameters for yaml: - -```yaml -source: - helm: - fileParameters: - - name: some.key - path: path/to/file.ext -``` - ## Helm Release Name By default, the Helm release name is equal to the Application name to which it belongs. Sometimes, especially on a centralised Argo CD, diff --git a/docs/user-guide/kustomize.md b/docs/user-guide/kustomize.md index 100622cf03ea8..2827c265c63e8 100644 --- a/docs/user-guide/kustomize.md +++ b/docs/user-guide/kustomize.md @@ -1,27 +1,5 @@ # Kustomize -## Declarative - -You can define a Kustomize application manifest in the declarative GitOps way. Here is an example: - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: kustomize-example -spec: - project: default - source: - path: examples/helloWorld - repoURL: 'https://github.com/kubernetes-sigs/kustomize' - targetRevision: HEAD - destination: - namespace: default - server: 'https://kubernetes.default.svc' -``` - -If the `kustomization.yaml` file exists at the location pointed to by `repoURL` and `path`, Argo CD will render the manifests using Kustomize. - The following configuration options are available for Kustomize: * `namePrefix` is a prefix appended to resources for Kustomize apps @@ -29,7 +7,6 @@ The following configuration options are available for Kustomize: * `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 @@ -54,7 +31,7 @@ metadata: name: kustomize-inline-example namespace: test1 resources: - - https://github.com/argoproj/argocd-example-apps//kustomize-guestbook/ + - https://raw.githubusercontent.com/argoproj/argocd-example-apps/master/kustomize-guestbook/ patches: - target: kind: Deployment @@ -185,9 +162,6 @@ 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. @@ -234,7 +208,7 @@ argocd app set --kustomize-version v3.5.4 ## Build Environment -Kustomize apps have access to the [standard build environment](build-environment.md) which can be used in combination with a [config management plugin](../operator-manual/config-management-plugins.md) to alter the rendered manifests. +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. diff --git a/docs/user-guide/multiple_sources.md b/docs/user-guide/multiple_sources.md index f9be46d76f8fa..2547a4af7bf4a 100644 --- a/docs/user-guide/multiple_sources.md +++ b/docs/user-guide/multiple_sources.md @@ -1,7 +1,9 @@ # Multiple Sources for an Application -By default an Argo CD application is a link between a single source and a cluster. Sometimes however, you want to combine -files from multiple locations to form a single Application. +!!! warning "Beta Feature" + Specifying multiple sources for an application is a beta feature. The UI and CLI still generally behave as if only + the first source is specified. Full UI/CLI support will be added in a future release. + This feature is subject to change in backwards incompatible ways until it is marked stable. Argo CD has the ability to specify multiple sources for a single Application. Argo CD compiles all the sources and reconciles the combined resources. @@ -15,7 +17,7 @@ See the below example for specifying multiple sources: apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: my-billing-app + name: guestbook namespace: argocd spec: project: default @@ -23,34 +25,23 @@ spec: server: https://kubernetes.default.svc namespace: default sources: - - repoURL: https://github.com/mycompany/billing-app.git - path: manifests + - chart: elasticsearch + repoURL: https://helm.elastic.co targetRevision: 8.5.1 - - repoURL: https://github.com/mycompany/common-settings.git - path: configmaps-billing + - repoURL: https://github.com/argoproj/argocd-example-apps.git + path: guestbook targetRevision: HEAD ``` -The above example has two sources specified that need to be combined in order to create the "billing" application. Argo CD will generate the manifests for each source separately and combine +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 this feature is **NOT** destined as a generic way to group different/unrelated 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. ## Helm value files from external Git repository -One of the most common scenarios for using multiple sources is the following - -1. Your organization wants to use an external/public Helm chart -1. You want to override the Helm values with your own local values -1. You don't want to clone the Helm chart locally as well because that would lead to duplication and you would need to monitor it manually for upstream changes. - -In this scenario you can use the multiple sources features to combine the external chart with your own local values. - Helm sources can reference value files from git sources. This allows you to use a third-party Helm chart with custom, git-hosted values. @@ -80,6 +71,3 @@ 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/projects.md b/docs/user-guide/projects.md index d027193421d49..f5979cf3c47b3 100644 --- a/docs/user-guide/projects.md +++ b/docs/user-guide/projects.md @@ -1,6 +1,7 @@ ## Projects -Projects provide a logical grouping of applications, which is useful when Argo CD is used by multiple teams. Projects provide the following features: +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) @@ -9,7 +10,10 @@ Projects provide a logical grouping of applications, which is useful when Argo C ### The Default Project -Every application belongs to a single project. If unspecified, an application belongs to the `default` project, which is created automatically and by default, permits deployments from any source repo, to any cluster, and all resource Kinds. The default project can be modified, but not deleted. When initially created, it's specification is configured to be the most permissive: +Every application belongs to a single project. If unspecified, an application belongs to the +`default` project, which is created automatically and by default, permits deployments from any +source repo, to any cluster, and all resource Kinds. The default project can be modified, but not +deleted. When initially created, it's specification is configured to be the most permissive: ```yaml spec: @@ -25,7 +29,10 @@ spec: ### Creating Projects -Additional projects can be created to give separate teams different levels of access to namespaces. The following command creates a new project `myproject` which can deploy applications to namespace `mynamespace` of cluster `https://kubernetes.default.svc`. The permitted Git source repository is set to `https://github.com/argoproj/argocd-example-apps.git` repository. +Additional projects can be created to give separate teams different levels of access to namespaces. +The following command creates a new project `myproject` which can deploy applications to namespace +`mynamespace` of cluster `https://kubernetes.default.svc`. The permitted Git source repository is +set to `https://github.com/argoproj/argocd-example-apps.git` repository. ```bash argocd proj create myproject -d https://kubernetes.default.svc,mynamespace -s https://github.com/argoproj/argocd-example-apps.git @@ -102,9 +109,11 @@ As with sources, a destination is considered valid if the following conditions h 1. _Any_ allow destination rule (i.e. a rule which isn't prefixed with `!`) permits the destination 2. AND *no* deny destination (i.e. a rule which is prefixed with `!`) rejects the destination -Keep in mind that `!*` is an invalid rule, since it doesn't make any sense to disallow everything. +Keep in mind that `!*` is an invalid rule, since it doesn't make any sense to disallow everything. -Permitted destination K8s resource kinds are managed with the commands. Note that namespaced-scoped resources are restricted via a deny list, whereas cluster-scoped resources are restricted via allow list. +Permitted destination K8s resource kinds are managed with the commands. Note that namespaced-scoped +resources are restricted via a deny list, whereas cluster-scoped resources are restricted via +allow list. ```bash argocd proj allow-cluster-resource @@ -115,7 +124,8 @@ argocd proj deny-namespace-resource ### Assign Application To A Project -The application project can be changed using `app set` command. In order to change the project of an app, the user must have permissions to access the new project. +The application project can be changed using `app set` command. In order to change the project of +an app, the user must have permissions to access the new project. ``` argocd app set guestbook-default --project myproject @@ -123,39 +133,18 @@ argocd app set guestbook-default --project myproject ## Project Roles -Projects include a feature called roles that can be used to determine who and what can be done applications associated with the project. As an example, it can be used to give a CI pipeline a restricted set of permissions allowing sync operations on a single app (but not change its source or destination). - -Projects can have multiple roles, and those roles can have different access granted to them. These permissions are called policies which follows the same [RBAC pattern used in Argo CD configuration](../operator-manual/rbac.md). They are stored within the role as a list of policy strings. A role's policy can only grant access to that role. Users are associated with roles based on the groups list. Consider the hypothetical AppProject definition below: - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: sample-test-project -spec: - ... - roles: - - description: some-role - groups: - - some-user - name: admin - policies: - - p, proj:sample-test-project:some-role, applications, *, *, allow - ... -``` - -Argo CD will use the policies defined in the AppProject roles while authorizing users actions. To determine which role a given users is associated with, it will dynamically create groups based on the role name in runtime. The project definition above will generate the following Casbin RBAC rules: +Projects include a feature called roles that enable automated access to a project's applications. +These can be used to give a CI pipeline a restricted set of permissions. For example, a CI system +may only be able to sync a single app (but not change its source or destination). -``` - p, proj:sample-test-project:some-role, applications, *, *, allow - g, some-user, proj:sample-test-project:some-role -``` - -_Note 1_: It is very important that policy roles follow the pattern `proj::` or they won't be effective during the Argo CD authorization process. +Projects can have multiple roles, and those roles can have different access granted to them. These +permissions are called policies, and they are stored within the role as a list of policy strings. +A role's policy can only grant access to that role and are limited to applications within the role's +project. However, the policies have an option for granting wildcard access to any application +within a project. -_Note 2_: The example above used `applications` as the resource for the policy definition. However other types of resources can also be used: `repositories`, `clusters`, `logs`, `exec` and `projects`. See the [RBAC documentation](../operator-manual/rbac.md) for more details about those resources. - -In order to create roles in a project and add policies to a role, a user will need permission to update a project. The following commands can be used to manage a role. +In order to create roles in a project and add policies to a role, a user will need permission to +update a project. The following commands can be used to manage a role. ```bash argocd proj role list @@ -166,7 +155,10 @@ argocd proj role add-policy argocd proj role remove-policy ``` -Project roles in itself are not useful without generating a token to associate to that role. Argo CD supports JWT tokens as the means to authenticate to a role. Since the JWT token is associated with a role's policies, any changes to the role's policies will immediately take effect for that JWT token. +Project roles in itself are not useful without generating a token to associate to that role. Argo CD +supports JWT tokens as the means to authenticate to a role. Since the JWT token is +associated with a role's policies, any changes to the role's policies will immediately take effect +for that JWT token. The following commands are used to manage the JWT tokens. @@ -175,9 +167,16 @@ argocd proj role create-token PROJECT ROLE-NAME argocd proj role delete-token PROJECT ROLE-NAME ISSUED-AT ``` -Since the JWT tokens aren't stored in Argo CD, they can only be retrieved when they are created. A user can leverage them in the cli by either passing them in using the `--auth-token` flag or setting the ARGOCD_AUTH_TOKEN environment variable. The JWT tokens can be used until they expire or are revoked. The JWT tokens can created with or without an expiration, but the default on the cli is creates them without an expirations date. Even if a token has not expired, it cannot be used if the token has been revoked. +Since the JWT tokens aren't stored in Argo CD, they can only be retrieved when they are created. A +user can leverage them in the cli by either passing them in using the `--auth-token` flag or setting +the ARGOCD_AUTH_TOKEN environment variable. The JWT tokens can be used until they expire or are +revoked. The JWT tokens can created with or without an expiration, but the default on the cli is +creates them without an expirations date. Even if a token has not expired, it cannot be used if +the token has been revoked. -Below is an example of leveraging a JWT token to access a guestbook application. It makes the assumption that the user already has a project named myproject and an application called guestbook-default. +Below is an example of leveraging a JWT token to access a guestbook application. It makes the +assumption that the user already has a project named myproject and an application called +guestbook-default. ```bash PROJ=myproject @@ -212,7 +211,8 @@ argocd app get $APP --auth-token $JWT ## Configuring RBAC With Projects -The project Roles allows configuring RBAC rules scoped to the project. The following sample project provides read-only permissions on project applications to any member of `my-oidc-group` group. +The project Roles allows configuring RBAC rules scoped to the project. The following sample +project provides read-only permissions on project applications to any member of `my-oidc-group` group. *AppProject example:* @@ -234,11 +234,12 @@ spec: ``` You can use `argocd proj role` CLI commands or project details page in the user interface to configure the policy. -Note that each project role policy rule must be scoped to that project only. Use the `argocd-rbac-cm` ConfigMap described in [RBAC](../operator-manual/rbac.md) documentation if you want to configure cross project RBAC rules. +Note that each project role policy rule must be scoped to that project only. Use the `argocd-rbac-cm` ConfigMap described in +[RBAC](../operator-manual/rbac.md) documentation if you want to configure cross project RBAC rules. ## Configuring Global Projects (v1.8) -Global projects can be configured to provide configurations that other projects can inherit from. +Global projects can be configured to provide configurations that other projects can inherit from. Projects, which match `matchExpressions` specified in `argocd-cm` ConfigMap, inherit the following fields from the global project: @@ -270,14 +271,17 @@ projectName: `proj-global-test` should be replaced with your own global project ## Project scoped Repositories and Clusters -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 Argo CD admin again to update the project definition. +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 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 Argo CD supports project-scoped repositories and clusters. 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: +For example, to allow users to add project scoped repositories and admin would have to add +the following RBAC rules: ``` p, proj:my-project:admin, repositories, create, my-project/*, allow @@ -291,7 +295,8 @@ This provides extra flexibility so that admins can have stricter rules. e.g.: 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 they have the correct credentials) can add them in an existing project either from the UI or the CLI. +Once the appropriate RBAC rules are in place, developers can create their own Git repositories and (assuming +they have the correct credentials) can add them in an existing project either from the UI or the CLI. Both the User interface and the CLI have the ability to optionally specify a project. If a project is specified then the respective cluster/repository is considered project scoped: ```argocd repo add --name stable https://charts.helm.sh/stable --type helm --project my-project``` @@ -314,11 +319,6 @@ stringData: password: **** ``` -!!! warning -Please keep in mind when using a project-scoped repository, only applications from the same project can make use of -it. When using applicationsets with the Git generator, only non-scoped repositories can be used (i.e. repositories that -do _not_ have a `project` set). - All the examples above talk about Git repositories, but the same principles apply to clusters as well. ```yaml @@ -343,7 +343,9 @@ stringData: } ``` -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: +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: ```yaml apiVersion: argoproj.io/v1alpha1 @@ -358,11 +360,12 @@ spec: project: foo-project ``` -To prevent this behavior, we can set the attribute `permitOnlyProjectScopedClusters` on a project. +To prevent this behavior, we can set the attribute `permitOnlyProjectScopedClusters` on a project. ```yaml spec: permitOnlyProjectScopedClusters: true ``` -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. +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. diff --git a/docs/user-guide/resource_hooks.md b/docs/user-guide/resource_hooks.md index 6e15a55bb20c2..df907540ed740 100644 --- a/docs/user-guide/resource_hooks.md +++ b/docs/user-guide/resource_hooks.md @@ -63,7 +63,6 @@ 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. diff --git a/docs/user-guide/resource_tracking.md b/docs/user-guide/resource_tracking.md index e62a7c094f4e2..79eda63ce5d5a 100644 --- a/docs/user-guide/resource_tracking.md +++ b/docs/user-guide/resource_tracking.md @@ -65,11 +65,6 @@ 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. diff --git a/docs/user-guide/status-badge.md b/docs/user-guide/status-badge.md index a933a751d2550..8355be458f026 100644 --- a/docs/user-guide/status-badge.md +++ b/docs/user-guide/status-badge.md @@ -9,50 +9,7 @@ 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. -2. Scroll down to 'Status Badge' section. -3. Select required template such as URL, Markdown etc. +1. Scroll down to 'Status Badge' section. +1. Select required template such as URL, Markdown etc. for the status image URL in markdown, html, etc are available . -4. Copy the text and paste it into your README or website. - -## Additional query parameters options -### showAppName -Display the application name in the status badge. - -Available values: `true/false` - -Default value: `false` - -Example: `&showAppName=true` - -### revision -Display revision targeted by the application. - -It will also extend the badge width to 192px. - -Available values: `true/false` - -Default value: `false` - -Example: `&revision=true` -### keepFullRevision -By default, displayed revision is truncated to 7 characters. - -This parameter allows to display it fully if it exceeds that length. - -It will also extend the badge width to 400px. - -Available values: `true/false` - -Default value: `false` - -Example: `&keepFullRevision=true` -### width -Change width of the badge. - -Completely replace current calculated width. - -Available values: `integer` - -Default value: `nil` - -Example: `&width=500` \ No newline at end of file +1. Copy the text and paste it into your README or website. \ No newline at end of file diff --git a/docs/user-guide/sync-kubectl.md b/docs/user-guide/sync-kubectl.md index 53700afed4f67..100ec2cdf70b1 100644 --- a/docs/user-guide/sync-kubectl.md +++ b/docs/user-guide/sync-kubectl.md @@ -38,7 +38,7 @@ operation: username: sync: syncStrategy: - hook: {} + hook: {} ``` ```bash diff --git a/docs/user-guide/sync-options.md b/docs/user-guide/sync-options.md index 99f5eba6b85de..e905c11d81ed8 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 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. +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. Below you can find details about each available Sync Option: @@ -285,7 +285,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 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. +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. ## Create Namespace diff --git a/docs/user-guide/sync-waves.md b/docs/user-guide/sync-waves.md index f888ac42be3f9..932ba396d68d2 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 for creation & updation and higher values first for deletion) +* The wave they are in (lower values first) * 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,7 +49,7 @@ 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: there is a delay between each sync wave to give other controllers a chance to react to the applied spec change. This prevents Argo CD from assessing resource health too quickly (against a stale object), and firing -hooks prematurely. The default delay between each sync wave is 2 seconds. This can be adjusted by setting the `ARGOCD_SYNC_WAVE_DELAY` environment variable in the argocd-application-controller deployment. +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 +variable `ARGOCD_SYNC_WAVE_DELAY`. diff --git a/docs/user-guide/tracking_strategies.md b/docs/user-guide/tracking_strategies.md index 9cfc63811b6b4..57dfc5f907b65 100644 --- a/docs/user-guide/tracking_strategies.md +++ b/docs/user-guide/tracking_strategies.md @@ -25,15 +25,14 @@ Helm chart versions are [Semantic Versions](https://semver.org/). As a result, y ## Git -For Git, all versions are Git references but tags [Semantic Versions](https://semver.org/) can also be used: +For Git, all versions are Git references: | Use Case | How | Notes | |-|-|-| | Pin to a version (e.g. in production) | Either (a) tag the commit with (e.g. `v1.2.0`) and use that tag, or (b) using commit SHA. | See [commit pinning](#commit-pinning). | -| Track patches (e.g. in pre-production) | Use a range (e.g. `1.2.*` or `>=1.2.0 <1.3.0`) | See [tag tracking](#tag-tracking) | -| Track minor releases (e.g. in QA) | Use a range (e.g. `1.*` or `>=1.0.0 <2.0.0`) | See [tag tracking](#tag-tracking) | -| Use the latest (e.g. in local development) | Use `HEAD` or `master` (assuming `master` is your master branch). | See [HEAD / Branch Tracking](#head-branch-tracking) | -| Use the latest including pre-releases | Use star range with `-0` suffix | `*-0` or `>=0.0.0-0` | +| Track patches (e.g. in pre-production) | Tag/re-tag the commit, e.g. (e.g. `v1.2`) and use that tag. | See [tag tracking](#tag-tracking) | +| Track minor releases (e.g. in QA) | Re-tag the commit as (e.g. `v1`) and use that tag. | See [tag tracking](#tag-tracking) | +| Use the latest (e.g. in local development) | Use `HEAD` or `master` (assuming `master` is your master branch). | See [HEAD / Branch Tracking](#head-branch-tracking) | ### HEAD / Branch Tracking @@ -54,9 +53,6 @@ To redeploy an app, the user uses Git to change the meaning of a tag by retaggin different commit SHA. Argo CD will detect the new meaning of the tag when performing the comparison/sync. -But if you're using semantic versioning you can set the constraint in your service revision -and Argo CD will get the latest version following the constraint rules. - ### Commit Pinning If a Git commit SHA is specified, the app is effectively pinned to the manifests defined at diff --git a/examples/dashboard.json b/examples/dashboard.json index b21a008456e1a..7e992a5363324 100644 --- a/examples/dashboard.json +++ b/examples/dashboard.json @@ -3,10 +3,7 @@ "list": [ { "builtIn": 1, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, + "datasource": "-- Grafana --", "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", @@ -16,17 +13,15 @@ ] }, "editable": true, - "fiscalYearStartMonth": 0, + "gnetId": null, "graphTooltip": 0, - "id": 28, + "id": 1, + "iteration": 1605574886303, "links": [], - "liveNow": false, "panels": [ { "collapsed": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "gridPos": { "h": 1, "w": 24, @@ -35,21 +30,12 @@ }, "id": 68, "panels": [], - "targets": [ - { - "datasource": { - "uid": "$datasource" - }, - "refId": "A" - } - ], "title": "Overview", "type": "row" }, { - "datasource": { - "uid": "$datasource" - }, + "content": "![argoimage](https://avatars1.githubusercontent.com/u/30269780?s=110&v=4)", + "datasource": "$datasource", "gridPos": { "h": 4, "w": 2, @@ -58,64 +44,29 @@ }, "id": 26, "links": [], - "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" - } - ], + "mode": "markdown", + "options": {}, + "title": "", "transparent": true, "type": "text" }, { - "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": [] + "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 }, "gridPos": { "h": 4, @@ -124,77 +75,79 @@ "y": 1 }, "id": 32, + "interval": null, "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "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 }, - "pluginVersion": "10.3.1", + "tableColumn": "", "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": "stat" + "type": "singlestat", + "valueFontSize": "70%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" }, { - "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": [] + "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 }, "gridPos": { "h": 4, @@ -203,30 +156,43 @@ "y": 1 }, "id": 94, + "interval": null, "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "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 }, - "pluginVersion": "10.3.1", + "tableColumn": "", "targets": [ { - "datasource": { - "uid": "$datasource" - }, "expr": "count(count by (server) (argocd_cluster_info{namespace=~\"$namespace\"}))", "format": "time_series", "instant": false, @@ -234,47 +200,40 @@ "refId": "A" } ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, "title": "Clusters", - "type": "stat" + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "0", + "value": "null" + } + ], + "valueName": "current" }, { - "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": [] + "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 }, "gridPos": { "h": 4, @@ -283,31 +242,45 @@ "y": 1 }, "id": 75, + "interval": null, "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.3.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 + }, + "tableColumn": "", "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, @@ -315,47 +288,38 @@ "refId": "A" } ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, "title": "Applications", - "type": "stat" + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" }, { - "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": [] + "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 }, "gridPos": { "h": 4, @@ -364,30 +328,43 @@ "y": 1 }, "id": 107, + "interval": null, "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "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 }, - "pluginVersion": "10.3.1", + "tableColumn": "", "targets": [ { - "datasource": { - "uid": "$datasource" - }, "expr": "count(count by (repo) (argocd_app_info{namespace=~\"$namespace\"}))", "format": "time_series", "instant": false, @@ -395,47 +372,24 @@ "refId": "A" } ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, "title": "Repositories", - "type": "stat" + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "0", + "value": "null" + } + ], + "valueName": "current" }, { - "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": [] - }, + "cacheTimeout": null, + "datasource": "$datasource", "gridPos": { "h": 4, "w": 3, @@ -445,27 +399,47 @@ "id": 100, "links": [], "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "horizontal", - "reduceOptions": { + "fieldOptions": { "calcs": [ "lastNotNull" ], - "fields": "", + "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": [], "values": false }, + "orientation": "horizontal", "showThresholdLabels": false, - "showThresholdMarkers": true, - "sizing": "auto" + "showThresholdMarkers": true }, - "pluginVersion": "10.3.1", + "pluginVersion": "6.5.2", "repeatDirection": "h", "targets": [ { - "datasource": { - "uid": "$datasource" - }, "expr": "sum(argocd_app_info{namespace=~\"$namespace\",dest_server=~\"$cluster\",operation!=\"\"})", "format": "time_series", "instant": true, @@ -474,24 +448,19 @@ "refId": "A" } ], + "timeFrom": null, + "timeShift": null, "title": "Operations", "type": "gauge" }, { "aliasColors": {}, "bars": false, + "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", + "decimals": null, "fill": 1, "fillGradient": 0, "gridPos": { @@ -522,11 +491,10 @@ "links": [], "nullPointMode": "connected", "options": { - "alertThreshold": true + "dataLinks": [] }, "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -536,9 +504,6 @@ "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, @@ -548,7 +513,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Applications", "tooltip": { "shared": false, @@ -557,7 +524,9 @@ }, "type": "graph", "xaxis": { + "buckets": null, "mode": "time", + "name": null, "show": true, "values": [] }, @@ -565,24 +534,29 @@ { "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 + "align": false, + "alignLevel": null } }, { "collapsed": true, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "gridPos": { "h": 1, "w": 24, @@ -601,18 +575,11 @@ "Unknown": "rgb(255, 255, 255)" }, "bars": false, + "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", + "decimals": null, "fill": 1, "fillGradient": 0, "gridPos": { @@ -634,6 +601,7 @@ "min": false, "rightSide": true, "show": true, + "sideWidth": null, "sort": "current", "sortDesc": true, "total": false, @@ -644,11 +612,10 @@ "links": [], "nullPointMode": "null as zero", "options": { - "alertThreshold": true + "dataLinks": [] }, "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -658,9 +625,6 @@ "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, @@ -670,7 +634,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Health Status", "tooltip": { "shared": true, @@ -679,24 +645,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -711,18 +686,11 @@ "Unknown": "rgb(255, 255, 255)" }, "bars": false, + "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", + "decimals": null, "fill": 1, "fillGradient": 0, "gridPos": { @@ -744,6 +712,7 @@ "min": false, "rightSide": true, "show": true, + "sideWidth": null, "sort": "current", "sortDesc": true, "total": false, @@ -754,11 +723,10 @@ "links": [], "nullPointMode": "null as zero", "options": { - "alertThreshold": true + "dataLinks": [] }, "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -768,9 +736,6 @@ "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, @@ -780,7 +745,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Sync Status", "tooltip": { "shared": true, @@ -789,43 +756,42 @@ }, "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 + "align": false, + "alignLevel": null } } ], - "targets": [ - { - "datasource": { - "uid": "$datasource" - }, - "refId": "A" - } - ], "title": "Application Status", "type": "row" }, { "collapsed": true, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "gridPos": { "h": 1, "w": 24, @@ -839,9 +805,8 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", + "decimals": null, "fill": 1, "fillGradient": 0, "gridPos": { @@ -886,9 +851,6 @@ "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, @@ -897,7 +859,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Sync Activity", "tooltip": { "shared": true, @@ -906,7 +870,9 @@ }, "type": "graph", "xaxis": { + "buckets": null, "mode": "time", + "name": null, "show": true, "values": [] }, @@ -916,6 +882,7 @@ "format": "short", "label": "", "logBase": 1, + "max": null, "min": "0", "show": true }, @@ -924,11 +891,14 @@ "format": "short", "label": "", "logBase": 1, + "max": null, + "min": null, "show": true } ], "yaxis": { - "align": false + "align": false, + "alignLevel": null } }, { @@ -936,9 +906,8 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", + "decimals": null, "fill": 1, "fillGradient": 0, "gridPos": { @@ -982,9 +951,6 @@ "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, @@ -993,7 +959,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Sync Failures", "tooltip": { "shared": true, @@ -1002,7 +970,9 @@ }, "type": "graph", "xaxis": { + "buckets": null, "mode": "time", + "name": null, "show": true, "values": [] }, @@ -1012,6 +982,7 @@ "format": "none", "label": "", "logBase": 1, + "max": null, "min": "0", "show": true }, @@ -1019,30 +990,23 @@ "format": "short", "label": "", "logBase": 1, + "max": null, + "min": null, "show": true } ], "yaxis": { - "align": false + "align": false, + "alignLevel": null } } ], - "targets": [ - { - "datasource": { - "uid": "$datasource" - }, - "refId": "A" - } - ], "title": "Sync Stats", "type": "row" }, { "collapsed": true, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "gridPos": { "h": 1, "w": 24, @@ -1056,9 +1020,7 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { @@ -1100,9 +1062,6 @@ "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, @@ -1111,7 +1070,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Reconciliation Activity", "tooltip": { "shared": false, @@ -1120,39 +1081,50 @@ }, "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 + "align": false, + "alignLevel": null } }, { - "cards": {}, + "cards": { + "cardPadding": null, + "cardRound": null + }, "color": { "cardColor": "#b4ff00", "colorScale": "sqrt", "colorScheme": "interpolateSpectral", "exponent": 0.5, + "min": null, "mode": "spectrum" }, "dataFormat": "tsbuckets", - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "gridPos": { "h": 7, "w": 24, @@ -1171,9 +1143,6 @@ "reverseYBuckets": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, "expr": "sum(increase(argocd_app_reconcile_bucket{namespace=~\"$namespace\"}[$interval])) by (le)", "format": "heatmap", "instant": false, @@ -1182,6 +1151,8 @@ "refId": "A" } ], + "timeFrom": null, + "timeShift": null, "title": "Reconciliation Performance", "tooltip": { "show": true, @@ -1192,21 +1163,27 @@ "xAxis": { "show": true }, + "xBucketNumber": null, + "xBucketSize": null, "yAxis": { + "decimals": null, "format": "short", "logBase": 1, - "show": true + "max": null, + "min": null, + "show": true, + "splitFactor": null }, - "yBucketBound": "auto" + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null }, { "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { @@ -1227,6 +1204,7 @@ "min": false, "rightSide": true, "show": true, + "sideWidth": null, "sort": "current", "sortDesc": true, "total": false, @@ -1237,315 +1215,11 @@ "links": [], "nullPointMode": "null as zero", "options": { - "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 + "dataLinks": [] }, "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", - "pointradius": 5, + "pointradius": 2, "points": false, "renderer": "flot", "seriesOverrides": [], @@ -1554,44 +1228,53 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-server-metrics\",namespace=~\"$namespace\"}", + "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": "{{namespace}}", + "legendFormat": "{{verb}} {{resource_kind}}", "refId": "A" } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], - "title": "Memory Usage", + "timeShift": null, + "title": "K8s API Activity", "tooltip": { - "shared": false, + "shared": true, "sort": 2, "value_type": "individual" }, "type": "graph", "xaxis": { + "buckets": null, "mode": "time", + "name": null, "show": true, "values": [] }, "yaxes": [ { - "format": "bytes", + "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 + "align": false, + "alignLevel": null } }, { @@ -1599,52 +1282,39 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, - "w": 24, + "w": 12, "x": 0, - "y": 16 + "y": 31 }, "hiddenSeries": false, - "id": 108, + "id": 96, "legend": { "alignAsTable": true, "avg": true, "current": true, - "hideEmpty": true, "hideZero": true, "max": true, "min": false, - "rightSide": true, + "rightSide": false, "show": true, - "sort": "avg", - "sortDesc": true, + "sideWidth": null, "total": false, "values": true }, "lines": true, "linewidth": 1, "links": [], - "nullPointMode": "connected", + "nullPointMode": "null", "options": { - "alertThreshold": true + "dataLinks": [] }, - "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", - "pointradius": 5, + "pointradius": 2, "points": false, "renderer": "flot", "seriesOverrides": [], @@ -1653,45 +1323,52 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "irate(process_cpu_seconds_total{job=\"argocd-server-metrics\",namespace=~\"$namespace\"}[1m])", + "expr": "sum(workqueue_depth{namespace=~\"$namespace\",name=~\"app_.*\"}) by (name)", "format": "time_series", "intervalFactor": 1, - "legendFormat": "{{namespace}}", + "legendFormat": "{{name}}", "refId": "A" } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], - "title": "CPU Usage", + "timeShift": null, + "title": "Workqueue Depth", "tooltip": { - "shared": false, + "shared": true, "sort": 2, "value_type": "individual" }, "type": "graph", "xaxis": { + "buckets": null, "mode": "time", + "name": null, "show": true, "values": [] }, "yaxes": [ { - "decimals": 1, - "format": "none", + "format": "short", + "label": null, "logBase": 1, + "max": null, + "min": "0", "show": true }, { "format": "short", + "label": null, "logBase": 1, + "max": null, + "min": "0", "show": true } ], "yaxis": { - "align": false + "align": false, + "alignLevel": null } }, { @@ -1699,38 +1376,26 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", + "decimals": null, "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, - "w": 24, - "x": 0, - "y": 23 + "w": 12, + "x": 12, + "y": 31 }, "hiddenSeries": false, - "id": 62, + "id": 98, "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 }, @@ -1739,12 +1404,10 @@ "links": [], "nullPointMode": "null", "options": { - "alertThreshold": true + "dataLinks": [] }, - "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", - "pointradius": 5, + "pointradius": 2, "points": false, "renderer": "flot", "seriesOverrides": [], @@ -1753,64 +1416,63 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "go_goroutines{job=\"argocd-server-metrics\",namespace=~\"$namespace\"}", + "expr": "sum(argocd_kubectl_exec_pending{namespace=~\"$namespace\"}) by (command)", "format": "time_series", "intervalFactor": 1, - "legendFormat": "{{namespace}}", + "legendFormat": "{{command}}", "refId": "A" } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], - "title": "Goroutines", + "timeShift": null, + "title": "Pending kubectl run", "tooltip": { - "shared": false, + "shared": true, "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 + "align": false, + "alignLevel": null } } ], - "targets": [ - { - "datasource": { - "uid": "$datasource" - }, - "refId": "A" - } - ], - "title": "Controller Telemetry", + "title": "Controller Stats", "type": "row" }, - { "collapsed": true, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "gridPos": { "h": 1, "w": 24, @@ -1824,23 +1486,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 9 + "y": 26 }, "hiddenSeries": false, "id": 34, @@ -1862,11 +1515,10 @@ "links": [], "nullPointMode": "connected", "options": { - "alertThreshold": true + "dataLinks": [] }, "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -1876,10 +1528,7 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-applicationset-controller-metrics\",namespace=~\"$namespace\"}", + "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{namespace}}", @@ -1887,7 +1536,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Memory Usage", "tooltip": { "shared": false, @@ -1896,24 +1547,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -1921,23 +1581,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 16 + "y": 33 }, "hiddenSeries": false, "id": 108, @@ -1961,11 +1612,10 @@ "links": [], "nullPointMode": "connected", "options": { - "alertThreshold": true + "dataLinks": [] }, "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -1975,10 +1625,7 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "irate(process_cpu_seconds_total{job=\"argocd-applicationset-controller-metrics\",namespace=~\"$namespace\"}[1m])", + "expr": "irate(process_cpu_seconds_total{job=\"argocd-metrics\",namespace=~\"$namespace\"}[1m])", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{namespace}}", @@ -1986,7 +1633,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "CPU Usage", "tooltip": { "shared": false, @@ -1995,7 +1644,9 @@ }, "type": "graph", "xaxis": { + "buckets": null, "mode": "time", + "name": null, "show": true, "values": [] }, @@ -2003,17 +1654,24 @@ { "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 + "align": false, + "alignLevel": null } }, { @@ -2021,23 +1679,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 23 + "y": 40 }, "hiddenSeries": false, "id": 62, @@ -2061,11 +1710,10 @@ "links": [], "nullPointMode": "null", "options": { - "alertThreshold": true + "dataLinks": [] }, "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -2075,10 +1723,7 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "go_goroutines{job=\"argocd-applicationset-controller-metrics\",namespace=~\"$namespace\"}", + "expr": "go_goroutines{job=\"argocd-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{namespace}}", @@ -2086,7 +1731,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Goroutines", "tooltip": { "shared": false, @@ -2095,43 +1742,42 @@ }, "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 + "align": false, + "alignLevel": null } } ], - "targets": [ - { - "datasource": { - "uid": "$datasource" - }, - "refId": "A" - } - ], - "title": "AppSet Controller Telemetry", + "title": "Controller Telemetry", "type": "row" }, { "collapsed": true, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "gridPos": { "h": 1, "w": 24, @@ -2145,9 +1791,7 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { @@ -2188,9 +1832,6 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, "expr": "sum(argocd_cluster_api_resource_objects{namespace=~\"$namespace\",server=~\"$cluster\"}) by (server)", "format": "time_series", "intervalFactor": 1, @@ -2199,7 +1840,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Resource Objects Count", "tooltip": { "shared": false, @@ -2208,24 +1851,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -2233,9 +1885,7 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { @@ -2277,9 +1927,6 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, "expr": " sum(argocd_cluster_api_resources{namespace=~\"$namespace\",server=~\"$cluster\"}) by (server)", "format": "time_series", "intervalFactor": 1, @@ -2288,7 +1935,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "API Resources Count", "tooltip": { "shared": false, @@ -2297,24 +1946,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -2322,9 +1980,7 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { @@ -2365,9 +2021,6 @@ "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, @@ -2376,7 +2029,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Cluster Events Count", "tooltip": { "shared": false, @@ -2385,43 +2040,42 @@ }, "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 + "align": false, + "alignLevel": null } } ], - "targets": [ - { - "datasource": { - "uid": "$datasource" - }, - "refId": "A" - } - ], "title": "Cluster Stats", "type": "row" }, { "collapsed": true, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "gridPos": { "h": 1, "w": 24, @@ -2435,23 +2089,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 11 + "y": 7 }, "hiddenSeries": false, "id": 82, @@ -2469,10 +2114,9 @@ "links": [], "nullPointMode": "null", "options": { - "alertThreshold": true + "dataLinks": [] }, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -2482,9 +2126,6 @@ "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, @@ -2493,7 +2134,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Git Requests (ls-remote)", "tooltip": { "shared": true, @@ -2502,24 +2145,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -2527,23 +2179,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 11 + "y": 7 }, "hiddenSeries": false, "id": 84, @@ -2561,10 +2204,9 @@ "links": [], "nullPointMode": "null", "options": { - "alertThreshold": true + "dataLinks": [] }, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -2574,9 +2216,6 @@ "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, @@ -2585,7 +2224,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Git Requests (checkout)", "tooltip": { "shared": true, @@ -2594,7 +2235,9 @@ }, "type": "graph", "xaxis": { + "buckets": null, "mode": "time", + "name": null, "show": true, "values": [] }, @@ -2603,20 +2246,29 @@ "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 + "align": false, + "alignLevel": null } }, { - "cards": {}, + "cards": { + "cardPadding": null, + "cardRound": null + }, "color": { "cardColor": "#b4ff00", "colorScale": "sqrt", @@ -2625,30 +2277,12 @@ "mode": "spectrum" }, "dataFormat": "tsbuckets", - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "scaleDistribution": { - "type": "linear" - } - }, - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 19 + "y": 15 }, "heatmap": {}, "hideZeroBuckets": false, @@ -2657,51 +2291,10 @@ "legend": { "show": false }, - "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", + "options": {}, "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, @@ -2709,6 +2302,8 @@ "refId": "A" } ], + "timeFrom": null, + "timeShift": null, "title": "Git Fetch Performance", "tooltip": { "show": true, @@ -2718,15 +2313,26 @@ "xAxis": { "show": true }, + "xBucketNumber": null, + "xBucketSize": null, "yAxis": { + "decimals": null, "format": "short", "logBase": 1, - "show": true + "max": null, + "min": null, + "show": true, + "splitFactor": null }, - "yBucketBound": "auto" + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null }, { - "cards": {}, + "cards": { + "cardPadding": null, + "cardRound": null + }, "color": { "cardColor": "#b4ff00", "colorScale": "sqrt", @@ -2735,30 +2341,12 @@ "mode": "spectrum" }, "dataFormat": "tsbuckets", - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "scaleDistribution": { - "type": "linear" - } - }, - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 19 + "y": 15 }, "heatmap": {}, "hideZeroBuckets": false, @@ -2767,51 +2355,10 @@ "legend": { "show": false }, - "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", + "options": {}, "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, @@ -2819,6 +2366,8 @@ "refId": "A" } ], + "timeFrom": null, + "timeShift": null, "title": "Git Ls-Remote Performance", "tooltip": { "show": true, @@ -2828,28 +2377,34 @@ "xAxis": { "show": true }, + "xBucketNumber": null, + "xBucketSize": null, "yAxis": { + "decimals": null, "format": "short", "logBase": 1, - "show": true + "max": null, + "min": null, + "show": true, + "splitFactor": null }, - "yBucketBound": "auto" + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null }, { "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 24, "x": 0, - "y": 27 + "y": 23 }, "hiddenSeries": false, "id": 71, @@ -2880,10 +2435,7 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-repo-server-metrics\",namespace=~\"$namespace\"}", + "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-repo-server\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{pod}}", @@ -2891,7 +2443,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Memory Used", "tooltip": { "shared": true, @@ -2900,24 +2454,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -2925,16 +2488,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 35 + "y": 31 }, "hiddenSeries": false, "id": 72, @@ -2965,10 +2526,7 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "go_goroutines{job=\"argocd-repo-server-metrics\",namespace=~\"$namespace\"}", + "expr": "go_goroutines{job=\"argocd-repo-server\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{pod}}", @@ -2976,7 +2534,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Goroutines", "tooltip": { "shared": true, @@ -2985,43 +2545,42 @@ }, "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 + "align": false, + "alignLevel": null } } ], - "targets": [ - { - "datasource": { - "uid": "$datasource" - }, - "refId": "A" - } - ], "title": "Repo Server Stats", "type": "row" }, { "collapsed": true, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "gridPos": { "h": 1, "w": 24, @@ -3035,24 +2594,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "fill": 1, - "fillGradient": 0, "gridPos": { "h": 8, "w": 24, "x": 0, - "y": 12 + "y": 89 }, - "hiddenSeries": false, "id": 61, "legend": { "avg": false, @@ -3067,12 +2616,8 @@ "linewidth": 1, "links": [], "nullPointMode": "connected", - "options": { - "alertThreshold": true - }, "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -3082,10 +2627,7 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-repo-server-metrics\",namespace=~\"$namespace\"}", + "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-server-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{pod}}", @@ -3093,7 +2635,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Memory Used", "tooltip": { "shared": true, @@ -3102,25 +2646,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -3128,24 +2680,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "fill": 1, - "fillGradient": 0, "gridPos": { "h": 9, "w": 24, "x": 0, - "y": 20 + "y": 97 }, - "hiddenSeries": false, "id": 36, "legend": { "avg": false, @@ -3160,12 +2702,8 @@ "linewidth": 1, "links": [], "nullPointMode": "null", - "options": { - "alertThreshold": true - }, "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -3175,10 +2713,7 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "go_goroutines{job=\"argocd-repo-server-metrics\",namespace=~\"$namespace\"}", + "expr": "go_goroutines{job=\"argocd-server-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{pod}}", @@ -3186,7 +2721,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "Goroutines", "tooltip": { "shared": true, @@ -3195,24 +2732,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -3220,24 +2766,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "unitScale": true - }, - "overrides": [] - }, + "datasource": "$datasource", "fill": 1, - "fillGradient": 0, "gridPos": { "h": 9, "w": 24, "x": 0, - "y": 29 + "y": 106 }, - "hiddenSeries": false, "id": 38, "legend": { "avg": false, @@ -3252,12 +2788,8 @@ "linewidth": 1, "links": [], "nullPointMode": "connected", - "options": { - "alertThreshold": true - }, "paceLength": 10, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -3267,10 +2799,7 @@ "steppedLine": false, "targets": [ { - "datasource": { - "uid": "$datasource" - }, - "expr": "go_gc_duration_seconds{job=\"argocd-repo-server-metrics\", quantile=\"1\", namespace=~\"$namespace\"}", + "expr": "go_gc_duration_seconds{job=\"argocd-server-metrics\", quantile=\"1\", namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 2, "legendFormat": "{{pod}}", @@ -3278,7 +2807,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "GC Time Quantiles", "tooltip": { "shared": true, @@ -3287,24 +2818,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -3313,11 +2853,12 @@ "h": 2, "w": 24, "x": 0, - "y": 38 + "y": 115 }, "id": 54, "links": [], "mode": "markdown", + "title": "", "transparent": true, "type": "text" }, @@ -3326,15 +2867,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", + "decimals": null, "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 0, - "y": 40 + "y": 117 }, "id": 40, "legend": { @@ -3367,9 +2907,6 @@ "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, @@ -3378,7 +2915,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "ApplicationService Requests", "tooltip": { "shared": false, @@ -3387,24 +2926,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -3412,15 +2960,13 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 12, - "y": 40 + "y": 117 }, "id": 42, "legend": { @@ -3451,9 +2997,6 @@ "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, @@ -3462,7 +3005,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "ClusterService Requests", "tooltip": { "shared": false, @@ -3471,24 +3016,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -3496,15 +3050,13 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 0, - "y": 49 + "y": 126 }, "id": 44, "legend": { @@ -3535,9 +3087,6 @@ "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, @@ -3546,7 +3095,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "ProjectService Requests", "tooltip": { "shared": true, @@ -3555,24 +3106,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -3580,15 +3140,13 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 12, - "y": 49 + "y": 126 }, "id": 46, "legend": { @@ -3618,9 +3176,6 @@ "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, @@ -3637,7 +3192,9 @@ "yaxis": "left" } ], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "RepositoryService Requests", "tooltip": { "shared": true, @@ -3646,24 +3203,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -3671,15 +3237,13 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 0, - "y": 58 + "y": 135 }, "id": 48, "legend": { @@ -3709,9 +3273,6 @@ "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, @@ -3720,7 +3281,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "SessionService Requests", "tooltip": { "shared": true, @@ -3729,24 +3292,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -3754,15 +3326,13 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 12, - "y": 58 + "y": 135 }, "id": 49, "legend": { @@ -3792,9 +3362,6 @@ "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, @@ -3803,7 +3370,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "VersionService Requests", "tooltip": { "shared": true, @@ -3812,24 +3381,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -3837,15 +3415,13 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 0, - "y": 67 + "y": 144 }, "id": 50, "legend": { @@ -3875,9 +3451,6 @@ "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, @@ -3886,7 +3459,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "AccountService Requests", "tooltip": { "shared": true, @@ -3895,24 +3470,33 @@ }, "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 + "align": false, + "alignLevel": null } }, { @@ -3920,15 +3504,13 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "uid": "$datasource" - }, + "datasource": "$datasource", "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 12, - "y": 67 + "y": 144 }, "id": 99, "legend": { @@ -3958,10 +3540,7 @@ "steppedLine": false, "targets": [ { - "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)", + "expr": "sum(increase(grpc_server_handled_total{job=\"argocd-server-metrics\",grpc_service=\"settings.SettingsService\",namespace=~\"$namespace\"}[$interval])) by (grpc_code, grpc_method)", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{grpc_code}},{{grpc_method}}", @@ -3969,7 +3548,9 @@ } ], "thresholds": [], + "timeFrom": null, "timeRegions": [], + "timeShift": null, "title": "SettingsService Requests", "tooltip": { "shared": true, @@ -3978,44 +3559,42 @@ }, "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 + "align": false, + "alignLevel": null } } ], - "targets": [ - { - "datasource": { - "uid": "$datasource" - }, - "refId": "A" - } - ], "title": "Server Stats", "type": "row" }, { "collapsed": true, - "datasource": { - "type": "prometheus", - "uid": "$datasource" - }, + "datasource": null, "gridPos": { "h": 1, "w": 24, @@ -4029,24 +3608,14 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "$datasource" - }, - "fieldConfig": { - "defaults": { - "links": [], - "unitScale": true - }, - "overrides": [] - }, + "datasource": null, "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 13 + "y": 9 }, "hiddenSeries": false, "id": 112, @@ -4063,10 +3632,9 @@ "linewidth": 1, "nullPointMode": "null", "options": { - "alertThreshold": true + "dataLinks": [] }, "percentage": false, - "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -4076,16 +3644,14 @@ "steppedLine": false, "targets": [ { - "datasource": { - "type": "prometheus", - "uid": "$datasource" - }, "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, @@ -4094,58 +3660,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 + "align": false, + "alignLevel": null } } ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "$datasource" - }, - "refId": "A" - } - ], "title": "Redis Stats", "type": "row" } ], - "refresh": "", - "schemaVersion": 39, + "refresh": false, + "schemaVersion": 21, + "style": "dark", "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, @@ -4158,13 +3724,11 @@ "text": "All", "value": "$__all" }, - "datasource": { - "type": "prometheus", - "uid": "$datasource" - }, + "datasource": "$datasource", "definition": "label_values(kube_pod_info, namespace)", "hide": 0, "includeAll": true, + "label": null, "multi": false, "name": "namespace", "options": [], @@ -4174,6 +3738,7 @@ "skipUrlSync": false, "sort": 0, "tagValuesQuery": "", + "tags": [], "tagsQuery": "", "type": "query", "useTags": false @@ -4188,6 +3753,7 @@ "value": "$__auto_interval_interval" }, "hide": 0, + "label": null, "name": "interval", "options": [ { @@ -4244,12 +3810,13 @@ { "allValue": "", "current": { - "selected": false, + "selected": true, "text": "namespace", "value": "namespace" }, "hide": 0, "includeAll": false, + "label": null, "multi": false, "name": "grouping", "options": [ @@ -4270,7 +3837,6 @@ } ], "query": "namespace,name,project", - "queryValue": "", "skipUrlSync": false, "type": "custom" }, @@ -4281,13 +3847,11 @@ "text": "All", "value": "$__all" }, - "datasource": { - "type": "prometheus", - "uid": "$datasource" - }, + "datasource": "$datasource", "definition": "label_values(argocd_cluster_info, server)", "hide": 0, "includeAll": true, + "label": null, "multi": false, "name": "cluster", "options": [], @@ -4297,6 +3861,7 @@ "skipUrlSync": false, "sort": 1, "tagValuesQuery": "", + "tags": [], "tagsQuery": "", "type": "query", "useTags": false @@ -4304,12 +3869,13 @@ { "allValue": ".*", "current": { - "selected": false, + "selected": true, "text": "All", "value": "$__all" }, "hide": 0, "includeAll": true, + "label": null, "multi": false, "name": "health_status", "options": [ @@ -4356,12 +3922,13 @@ { "allValue": ".*", "current": { - "selected": false, + "selected": true, "text": "All", "value": "$__all" }, "hide": 0, "includeAll": true, + "label": null, "multi": false, "name": "sync_status", "options": [ @@ -4424,6 +3991,5 @@ "timezone": "", "title": "ArgoCD", "uid": "LCAgc9rWz", - "version": 2, - "weekStart": "" -} + "version": 1 +} \ No newline at end of file diff --git a/go.mod b/go.mod index 2d020f68e928a..330374d11a06f 100644 --- a/go.mod +++ b/go.mod @@ -1,119 +1,115 @@ module github.com/argoproj/argo-cd/v2 -go 1.22.0 +go 1.21 + +toolchain go1.21.0 require ( - code.gitea.io/sdk/gitea v0.19.0 + code.gitea.io/sdk/gitea v0.15.1 github.com/Azure/kubelogin v0.0.20 - github.com/Masterminds/semver/v3 v3.3.0 - github.com/Masterminds/sprig/v3 v3.3.0 + github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible + 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.33.0 - github.com/antonmedv/expr v1.15.1 - github.com/argoproj/gitops-engine v0.7.1-0.20240917171920-72bcdda3f0a5 - github.com/argoproj/notifications-engine v0.4.1-0.20240606074338-0802cd427621 + github.com/alicebob/miniredis/v2 v2.30.4 + github.com/antonmedv/expr v1.15.2 + github.com/argoproj/gitops-engine v0.7.1-0.20240715141017-b6ec82aedce5 + github.com/argoproj/notifications-engine v0.4.1-0.20240126143042-84b9f7913604 github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 - github.com/aws/aws-sdk-go v1.55.5 - github.com/bmatcuk/doublestar/v4 v4.6.1 + 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.11.0 - github.com/casbin/casbin/v2 v2.100.0 - github.com/casbin/govaluate v1.2.0 - github.com/cespare/xxhash/v2 v2.3.0 - github.com/chainguard-dev/git-urls v1.0.2 - github.com/coreos/go-oidc/v3 v3.11.0 - github.com/cyphar/filepath-securejoin v0.3.2 + 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/expr-lang/expr v1.16.9 - github.com/felixge/httpsnoop v1.0.4 - github.com/fsnotify/fsnotify v1.7.0 + github.com/evanphx/json-patch v5.6.0+incompatible + github.com/fsnotify/fsnotify v1.6.0 github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e - github.com/go-git/go-git/v5 v5.12.0 + 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.4.2 - github.com/go-openapi/loads v0.22.0 - github.com/go-openapi/runtime v0.28.0 - github.com/go-playground/webhooks/v6 v6.4.0 + github.com/go-logr/logr v1.3.0 + github.com/go-openapi/loads v0.21.2 + 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-20200905025246-8bb8a50cb355 github.com/gogo/protobuf v1.3.2 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/protobuf v1.5.4 - github.com/google/btree v1.1.3 github.com/google/go-cmp v0.6.0 - github.com/google/go-github/v63 v63.0.0 + github.com/google/go-github/v35 v35.3.0 github.com/google/go-jsonnet v0.20.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/google/uuid v1.6.0 - github.com/gorilla/handlers v1.5.2 - github.com/gorilla/websocket v1.5.3 - github.com/gosimple/slug v1.14.0 + github.com/google/uuid v1.3.1 + github.com/gorilla/handlers v1.5.1 + 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.7 github.com/imdario/mergo v0.3.16 github.com/improbable-eng/grpc-web v0.15.0 - github.com/itchyny/gojq v0.12.16 + 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.80 + github.com/ktrysmt/go-bitbucket v0.9.67 github.com/mattn/go-isatty v0.0.20 - github.com/mattn/go-zglob v0.0.6 + github.com/mattn/go-zglob v0.0.4 github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 - github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 github.com/olekukonko/tablewriter v0.0.5 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/prometheus/client_golang v1.20.4 + github.com/prometheus/client_golang v1.16.0 github.com/r3labs/diff v1.1.0 - github.com/redis/go-redis/v9 v9.6.1 + 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.8.1 + github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.8.4 github.com/valyala/fasttemplate v1.2.2 - github.com/xanzy/go-gitlab v0.109.0 - github.com/yuin/gopher-lua v1.1.1 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 - go.opentelemetry.io/otel v1.30.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 - go.opentelemetry.io/otel/sdk v1.30.0 - golang.org/x/crypto v0.27.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.46.1 + 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/net v0.29.0 - golang.org/x/oauth2 v0.23.0 - golang.org/x/sync v0.8.0 - golang.org/x/term v0.24.0 - golang.org/x/time v0.6.0 - google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 - google.golang.org/grpc v1.67.0 - google.golang.org/protobuf v1.34.2 + 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.33.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.31.0 - k8s.io/apiextensions-apiserver v0.31.2 - k8s.io/apimachinery v0.31.0 - k8s.io/apiserver v0.31.0 - k8s.io/client-go v0.31.0 - k8s.io/code-generator v0.31.0 - k8s.io/klog/v2 v2.130.1 - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 - k8s.io/kubectl v0.31.2 - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 + 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 - oras.land/oras-go/v2 v2.5.0 - sigs.k8s.io/controller-runtime v0.19.0 + 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.4.0 + sigs.k8s.io/yaml v1.3.0 ) require ( - dario.cat/mergo v1.0.1 // indirect + 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 @@ -132,43 +128,31 @@ require ( 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/davidmz/go-pageant v1.0.2 // indirect - github.com/distribution/reference v0.5.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-fed/httpsig v1.1.0 // indirect - github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-github/v62 v62.0.0 // indirect - github.com/google/s2a-go v0.1.7 // 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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect - github.com/x448/float16 v0.8.4 // 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 - go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // 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-20240903143218-8af14fe29dc1 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect gopkg.in/retry.v1 v1.0.3 // indirect - k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect k8s.io/klog v1.0.0 // indirect nhooyr.io/websocket v1.8.7 // indirect ) require ( - cloud.google.com/go/compute/metadata v0.5.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.29 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.23 // 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 @@ -176,165 +160,181 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/PagerDuty/go-pagerduty v1.7.0 // indirect - github.com/ProtonMail/go-crypto v1.0.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/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/cenkalti/backoff/v4 v4.3.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.7 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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/dlclark/regexp2 v1.11.4 - github.com/emicklei/go-restful/v3 v3.11.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.9.0 // 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/fvbommel/sortorder v1.0.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.23.0 // indirect - github.com/go-openapi/errors v0.22.0 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/strfmt v0.23.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-openapi/validate v0.24.0 // indirect + github.com/go-openapi/analysis v0.21.4 // 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.2.2 // indirect + github.com/golang/glog v1.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // 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.2.0 // indirect github.com/gosimple/unidecode v1.0.1 // 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.22.0 // 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.6.0 // indirect - github.com/huandu/xstrings v1.5.0 // indirect + github.com/hashicorp/go-version v1.2.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.6 // 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 v1.2.0 // indirect - github.com/klauspost/compress v1.17.9 // 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.15 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/moby/spdystream v0.4.0 // indirect - github.com/moby/term v0.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-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 // 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 - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.6.1 - github.com/prometheus/common v0.55.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/rivo/uniseg v0.4.7 // indirect - github.com/rs/cors v1.11.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 + 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.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/shopspring/decimal v1.4.0 // indirect + github.com/sergi/go-diff v1.1.0 // indirect + github.com/shopspring/decimal v1.2.0 // indirect github.com/skeema/knownhosts v1.2.2 // indirect github.com/slack-go/slack v0.12.2 // indirect - github.com/spf13/cast v1.7.0 // indirect - github.com/stretchr/objx v0.5.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.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.3 // indirect - github.com/xlab/treeprint v1.2.0 // indirect - go.mongodb.org/mongo-driver v1.14.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 // indirect - go.opentelemetry.io/otel/metric v1.30.0 // indirect - go.opentelemetry.io/otel/trace v1.30.0 // indirect - go.opentelemetry.io/proto/otlp v1.3.1 // indirect - go.uber.org/automaxprocs v1.6.0 + 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.20.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.4.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect gomodules.xyz/notify v0.1.1 // indirect - google.golang.org/appengine v1.6.8 // indirect + google.golang.org/appengine v1.6.7 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/cli-runtime v0.31.0 // indirect - k8s.io/component-base v0.31.0 // indirect - k8s.io/component-helpers v0.31.0 // indirect - k8s.io/kube-aggregator v0.31.2 // indirect - k8s.io/kubernetes v1.31.0 // 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.17.2 // indirect - sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect + sigs.k8s.io/kustomize/api v0.12.1 // indirect + sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect ) replace ( - github.com/go-telegram-bot-api/telegram-bot-api/v5 => github.com/OvyFlash/telegram-bot-api/v5 v5.0.0-20240108230938-63e5c59035bf + // https://github.com/golang/go/issues/33546#issuecomment-519656923 + github.com/go-check/check => github.com/go-check/check v0.0.0-20180628173108-788fd7840127 github.com/golang/protobuf => github.com/golang/protobuf v1.5.4 github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.16.0 + // Avoid CVE-2023-46402 + github.com/whilp/git-urls => github.com/chainguard-dev/git-urls v1.0.2 + // 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 - k8s.io/api => k8s.io/api v0.31.0 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.31.0 - k8s.io/apimachinery => k8s.io/apimachinery v0.31.0 - k8s.io/apiserver => k8s.io/apiserver v0.31.0 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.31.0 - k8s.io/client-go => k8s.io/client-go v0.31.0 - k8s.io/cloud-provider => k8s.io/cloud-provider v0.31.0 - k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.31.0 - k8s.io/code-generator => k8s.io/code-generator v0.31.0 - k8s.io/component-base => k8s.io/component-base v0.31.0 - k8s.io/component-helpers => k8s.io/component-helpers v0.31.0 - k8s.io/controller-manager => k8s.io/controller-manager v0.31.0 - k8s.io/cri-api => k8s.io/cri-api v0.31.0 - k8s.io/cri-client => k8s.io/cri-client v0.31.0 - k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.31.0 - k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.31.0 - k8s.io/endpointslice => k8s.io/endpointslice v0.31.0 - k8s.io/kms => k8s.io/kms v0.31.0 - k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.31.0 - k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.31.0 - k8s.io/kube-proxy => k8s.io/kube-proxy v0.31.0 - k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.31.0 - k8s.io/kubectl => k8s.io/kubectl v0.31.0 - k8s.io/kubelet => k8s.io/kubelet v0.31.0 - k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.31.0 - k8s.io/metrics => k8s.io/metrics v0.31.0 - k8s.io/mount-utils => k8s.io/mount-utils v0.31.0 - k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.31.0 - k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.31.0 - k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.31.0 - k8s.io/sample-controller => k8s.io/sample-controller v0.31.0 + 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 74da7903bd3dd..971ff31194890 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,610 @@ 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/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= -cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= -code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y= -code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI= -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +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= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +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= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +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/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/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= +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= @@ -18,11 +615,11 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl 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.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= -github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= -github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= -github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= -github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= +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/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= @@ -40,25 +637,30 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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 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/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= -github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= -github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +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/OvyFlash/telegram-bot-api/v5 v5.0.0-20240108230938-63e5c59035bf h1:a7VKhbjKYPO8twGy/1AxMpM2Fp0qT7bf25fmCVMVu4s= -github.com/OvyFlash/telegram-bot-api/v5 v5.0.0-20240108230938-63e5c59035bf/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 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 v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= -github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +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= github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210112200207-10ab4d695d60/go.mod h1:rjP7sIipbZcagro/6TCk6X0ZeFT2eyudH5+fve/cbBA= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= @@ -67,6 +669,10 @@ github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d h1:WtAMR0fPCOfK7 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/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= @@ -74,20 +680,24 @@ 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.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA= -github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= +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/antonmedv/expr v1.15.1 h1:mxeRIkH8GQJo4MRRFgp0ArlV4AA+0DmcJNXEsG70rGU= -github.com/antonmedv/expr v1.15.1/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE= +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.20240917171920-72bcdda3f0a5 h1:K/e+NsNmE4BccRu21QpqUxkTHxU9YWjU3M775Ck+V/E= -github.com/argoproj/gitops-engine v0.7.1-0.20240917171920-72bcdda3f0a5/go.mod h1:b1vuwkyMUszyUK+USUJqC8vJijnQsEPNDpC+sDdDLtM= -github.com/argoproj/notifications-engine v0.4.1-0.20240606074338-0802cd427621 h1:Yg1nt+D2uDK1SL2jSlfukA4yc7db184TTN7iWy3voRE= -github.com/argoproj/notifications-engine v0.4.1-0.20240606074338-0802cd427621/go.mod h1:N0A4sEws2soZjEpY4hgZpQS8mRIEw6otzwfkgc3g9uQ= +github.com/argoproj/gitops-engine v0.7.1-0.20240715141017-b6ec82aedce5 h1:YF0xxjIYPeZfsKfZtTd7rxEWQ7EeiTBJHO3PmQ2kV3c= +github.com/argoproj/gitops-engine v0.7.1-0.20240715141017-b6ec82aedce5/go.mod h1:d4eLldeEFyZIcVySAMhXhnh1tTa4qfvPYfut9B8UClw= +github.com/argoproj/notifications-engine v0.4.1-0.20240126143042-84b9f7913604 h1:pMfBao6Vm1Ax0xGIp9BWEia2nKkccHwV0dTEdrsFOpo= +github.com/argoproj/notifications-engine v0.4.1-0.20240126143042-84b9f7913604/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= @@ -97,13 +707,14 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 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-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.44.289/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +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= @@ -142,116 +753,138 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 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/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= -github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +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.11.0 h1:R9d0v+iobRHSaE4wKUnXFiZp53AL4ED5MzgEMwGTZag= -github.com/bradleyfalzon/ghinstallation/v2 v2.11.0/go.mod h1:0LWKQwOHewXO/1acI6TtyE0Xc4ObDb2rFN7eHBAG71M= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +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.100.0 h1:aeugSNjjHfCrgA22nHkVvw2xsscboHv5r0a13ljQKGQ= -github.com/casbin/casbin/v2 v2.100.0/go.mod h1:LO7YPez4dX3LgoTCqSQAleQDo0S0BeZBDxYnPUl95Ng= -github.com/casbin/govaluate v1.2.0 h1:wXCXFmqyY+1RwiKfYo3jMKyrtZmOL3kHwaqDyCPOYak= -github.com/casbin/govaluate v1.2.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= +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.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +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/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.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.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/chainguard-dev/git-urls v1.0.2 h1:pSpT7ifrpc5X55n4aTTm7FFUE+ZQHKiqpiwNkJrVcKQ= github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4+An04HJY7Ol+Y/o= -github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= -github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= -github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= 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/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/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= 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/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/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= -github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= +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-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/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.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= -github.com/cpuguy83/go-md2man/v2 v2.0.4/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.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.3.2 h1:QhZu5AxQ+o1XZH0Ye05YzvJ0kAdK6VQc0z9NNMek7gc= -github.com/cyphar/filepath-securejoin v0.3.2/go.mod h1:F7i41x/9cBF7lzCrVsYs9fuzwRZm4NQsGTBdpp6mETc= +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.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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= -github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= 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/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= -github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 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/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/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 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/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +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= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +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/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.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= -github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +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.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 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/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= -github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= @@ -260,20 +893,23 @@ github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +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/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.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +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/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= @@ -282,29 +918,34 @@ 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.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= -github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= +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-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= -github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= +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.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= -github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +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-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= -github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= 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-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= @@ -315,39 +956,53 @@ github.com/go-logr/logr v1.0.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV 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/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= -github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= -github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= -github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= -github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= +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/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/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/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +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/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= -github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= -github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= -github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= -github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= -github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= -github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= +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.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/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/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/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +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/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= -github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= +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= @@ -355,31 +1010,50 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 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.4.0 h1:KLa6y7bD19N48rxJDHM0DpE3T4grV7GxMy1b/aHMWPY= -github.com/go-playground/webhooks/v6 v6.4.0/go.mod h1:5lBxopx+cAJiBI4+kyRbuHrEi+hYRDdRHuRR4Ya5Ums= +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= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc= +github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +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/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/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= -github.com/gobwas/pool v0.2.1/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/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= -github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +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= @@ -392,40 +1066,58 @@ github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzq 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.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= -github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +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= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 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/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/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= -github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +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= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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= @@ -433,12 +1125,12 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN 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/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4= -github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4= -github.com/google/go-github/v63 v63.0.0 h1:13xwK/wk9alSokujB9lJkuzdmQuVn2QCPeck76wR3nE= -github.com/google/go-github/v63 v63.0.0/go.mod h1:IqbcrgUmIcEaioWrGYei/09o+ge5vhffGOcxrO0AfmA= +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= @@ -448,42 +1140,81 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ 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= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +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-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +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.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.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= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +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/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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= -github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +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/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.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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gosimple/slug v1.14.0 h1:RtTL/71mJNDfpUbCOmnf/XFkzKRtD6wL6Uy+3akm4Es= -github.com/gosimple/slug v1.14.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +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.2.1 h1:IPPJCdzXz60gMqnlzS0ZAW5z5aS1gI4nU+YM0Pe+ssA= @@ -499,8 +1230,10 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92Bcuy 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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +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.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -522,8 +1255,8 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 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= @@ -533,12 +1266,14 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 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.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= -github.com/huandu/xstrings v1.5.0/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/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= 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.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= @@ -547,15 +1282,16 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt 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/itchyny/gojq v0.12.16 h1:yLfgLxhIr/6sJNVmYfQjTIv0jGctu6/DgDoivmxTr7g= -github.com/itchyny/gojq v0.12.16/go.mod h1:6abHbdC2uB9ogMS38XsErnfqJ94UlngIJGlRAIj4jTM= -github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q= -github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg= +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= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 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/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= @@ -575,11 +1311,17 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.10/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= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 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/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 v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= @@ -587,19 +1329,24 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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/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/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/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +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= +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/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= @@ -608,27 +1355,32 @@ 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.80 h1:S+vZTXKx/VG5yCaX4I3Bmwo8lxWr4ifvuHdTboHTMMc= -github.com/ktrysmt/go-bitbucket v0.9.80/go.mod h1:b8ogWEGxQMWoeFnT1ZE4aHIPGindI+9z/zAW/OVFjk0= +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/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= 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 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= 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/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.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= github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8 h1:A6SLdFpRzUUF5v9F/7T1fu3DERmOCgTwwP6x54eyFfU= 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/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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -643,38 +1395,43 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D 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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-zglob v0.0.6 h1:mP8RnmCgho4oaUYDIDn6GNxYk+qJGUs8fJLn+twYj2A= -github.com/mattn/go-zglob v0.0.6/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY= +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.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/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +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.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.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +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-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +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 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.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= -github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +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/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +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= @@ -684,6 +1441,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 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/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -693,7 +1451,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW 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 h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= 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= @@ -702,6 +1459,7 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -729,19 +1487,8 @@ github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7 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/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= -github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= -github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= -github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= -github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= 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= @@ -752,26 +1499,17 @@ github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9 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.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= -github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= -github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= -github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= -github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= -github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= 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.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +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= @@ -782,17 +1520,21 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/opsgenie/opsgenie-go-sdk-v2 v1.0.5 h1:AnS8ZCC5dle8P4X4FZ+IOlX9v0jAkCMiZDIzRnYwBbs= github.com/opsgenie/opsgenie-go-sdk-v2 v1.0.5/go.mod h1:f0ezb0R/mrB9Hpm5RrIS6EX3ydjsR2nAB88nYYXZcNY= -github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 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.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= 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= @@ -804,78 +1546,83 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= 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 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.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +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= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +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.2.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.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +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.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.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +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/redis/go-redis/v9 v9.0.0-rc.4/go.mod h1:Vo3EsyWnicKnSKCA7HhgnvnyA74wOA69Cd2Meli5mmA= -github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= -github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +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.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +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/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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +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.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= -github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +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/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +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= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -898,26 +1645,31 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= 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/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +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/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.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.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 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/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/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.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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 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= @@ -927,10 +1679,15 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +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/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -948,131 +1705,185 @@ github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2el 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/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xanzy/go-gitlab v0.109.0 h1:RcRme5w8VpLXTSTTMZdVoQWY37qTJWg+gwdQl4aAttE= -github.com/xanzy/go-gitlab v0.109.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= +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 v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= -github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +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= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +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 v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= -github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +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/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= -go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +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/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= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +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.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.55.0 h1:hCq2hNMwsegUvPzI7sPOvtO9cqyy5GbWt/Ybp2xrx8Q= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0/go.mod h1:LqaApwGx/oUmzsbqxkzuBvyoPpkxk3JQWnqfVrJ3wCA= -go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= -go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 h1:m0yTiGDLUvVYaTFbAvCkVYIYcvwKt3G7OLoN77NUs/8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0/go.mod h1:wBQbT4UekBfegL2nx0Xk1vBcnzyBPsIVm9hRG4fYcr4= -go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= -go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= -go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= -go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= -go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= -go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +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.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +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.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= 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.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 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.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +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-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= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +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-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/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.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 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.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= 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.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +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/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +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-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= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +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/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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/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.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/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 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.10.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/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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= @@ -1084,75 +1895,130 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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-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= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +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-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-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= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +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-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.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/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.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= 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= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +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-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= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +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-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= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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= @@ -1161,12 +2027,19 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h 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-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= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/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= @@ -1176,84 +2049,121 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w 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= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-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-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= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-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= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210330210617-4fbd30eecc44/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-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-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-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-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-20220310020820-b874c991c1a5/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-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.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.10.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.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 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.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= 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.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +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/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +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= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= @@ -1264,130 +2174,427 @@ 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.11.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/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 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-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/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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-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= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +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-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= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +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-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= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +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.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.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= 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.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/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= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +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.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= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +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/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= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +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-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= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +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-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-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= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +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/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-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +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= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +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.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= -google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +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.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/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/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/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 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= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= @@ -1405,69 +2612,119 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= -k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= -k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= -k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= -k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= -k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= -k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= -k8s.io/cli-runtime v0.31.0 h1:V2Q1gj1u3/WfhD475HBQrIYsoryg/LrhhK4RwpN+DhA= -k8s.io/cli-runtime v0.31.0/go.mod h1:vg3H94wsubuvWfSmStDbekvbla5vFGC+zLWqcf+bGDw= -k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= -k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= -k8s.io/code-generator v0.31.0 h1:w607nrMi1KeDKB3/F/J4lIoOgAwc+gV9ZKew4XRfMp8= -k8s.io/code-generator v0.31.0/go.mod h1:84y4w3es8rOJOUUP1rLsIiGlO1JuEaPFXQPA9e/K6U0= -k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= -k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= -k8s.io/component-helpers v0.31.0 h1:jyRUKA+GX+q19o81k4x94imjNICn+e6Gzi6T89va1/A= -k8s.io/component-helpers v0.31.0/go.mod h1:MrNIvT4iB7wXIseYSWfHUJB/aNUiFvbilp4qDfBQi6s= -k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo= -k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= +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.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-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.5.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-aggregator v0.31.0 h1:3DqSpmqHF8rey7fY+qYXLJms0tYPhxrgWvjpnKVnS0Y= -k8s.io/kube-aggregator v0.31.0/go.mod h1:Fa+OVSpMQC7zbTTz7/QG7FXe9jZ8usuJQej5sMdCrkM= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/kubectl v0.31.0 h1:kANwAAPVY02r4U4jARP/C+Q1sssCcN/1p9Nk+7BQKVg= -k8s.io/kubectl v0.31.0/go.mod h1:pB47hhFypGsaHAPjlwrNbvhXgmuAr01ZBvAIIUaI8d4= -k8s.io/kubernetes v1.31.0 h1:sYAB12TTWexXKp4RxqJMm/7EC+P0mNOgn4Xdj5eu7HM= -k8s.io/kubernetes v1.31.0/go.mod h1:UTpGn7nxrUrPWw5hNIYTAjodcWIvLakgHpLtfrr6GC8= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +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-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= +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.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c= -oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg= -sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= -sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +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/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.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= -sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0= -sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ= -sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= +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.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/hack/dev-mounter/main.go b/hack/dev-mounter/main.go index b74d46c170326..61988b2daa275 100644 --- a/hack/dev-mounter/main.go +++ b/hack/dev-mounter/main.go @@ -33,7 +33,7 @@ func newCommand() *cobra.Command { clientConfig clientcmd.ClientConfig configMaps []string ) - command := cobra.Command{ + var command = cobra.Command{ Run: func(cmd *cobra.Command, args []string) { config, err := clientConfig.ClientConfig() errors.CheckError(err) @@ -87,7 +87,7 @@ func newCommand() *cobra.Command { // Create or update files that are specified in ConfigMap for name, data := range cm.Data { p := path.Join(destPath, name) - err := os.WriteFile(p, []byte(data), 0o644) + err := os.WriteFile(p, []byte(data), 0644) if err != nil { log.Warnf("Failed to create file %s: %v", p, err) } diff --git a/hack/gen-catalog/main.go b/hack/gen-catalog/main.go index c7963dbf83ab4..486327e33ee6e 100644 --- a/hack/gen-catalog/main.go +++ b/hack/gen-catalog/main.go @@ -25,7 +25,7 @@ import ( ) func main() { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "gen", Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) @@ -81,8 +81,9 @@ func newCatalogCommand() *cobra.Command { d, err := yaml.Marshal(cm) dieOnError(err, "Failed to marshal final configmap") - err = os.WriteFile(target, d, 0o644) + err = os.WriteFile(target, d, 0644) dieOnError(err, "Failed to write builtin configmap") + }, } } @@ -101,14 +102,14 @@ func newDocsCommand() *cobra.Command { notificationTemplates, notificationTriggers, err := buildConfigFromFS(templatesDir, triggersDir) dieOnError(err, "Failed to build builtin config") generateBuiltInTriggersDocs(&builtItDocsData, notificationTriggers, notificationTemplates) - if err := os.WriteFile("./docs/operator-manual/notifications/catalog.md", builtItDocsData.Bytes(), 0o644); err != nil { + if err := os.WriteFile("./docs/operator-manual/notifications/catalog.md", builtItDocsData.Bytes(), 0644); err != nil { log.Fatal(err) } var commandDocs bytes.Buffer if err := generateCommandsDocs(&commandDocs); err != nil { log.Fatal(err) } - if err := os.WriteFile("./docs/operator-manual/notifications/troubleshooting-commands.md", commandDocs.Bytes(), 0o644); err != nil { + if err := os.WriteFile("./docs/operator-manual/notifications/troubleshooting-commands.md", commandDocs.Bytes(), 0644); err != nil { log.Fatal(err) } }, @@ -117,13 +118,6 @@ func newDocsCommand() *cobra.Command { func generateBuiltInTriggersDocs(out io.Writer, triggers map[string][]triggers.Condition, templates map[string]services.Notification) { _, _ = fmt.Fprintln(out, "# Triggers and Templates Catalog") - - _, _ = fmt.Fprintln(out, "## Getting Started") - _, _ = fmt.Fprintln(out, "* Install Triggers and Templates from the catalog") - _, _ = fmt.Fprintln(out, " ```bash") - _, _ = fmt.Fprintln(out, " kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/notifications_catalog/install.yaml") - _, _ = fmt.Fprintln(out, " ```") - _, _ = fmt.Fprintln(out, "## Triggers") w := tablewriter.NewWriter(out) @@ -168,7 +162,7 @@ func generateCommandsDocs(out io.Writer) error { for _, c := range toolSubCommand.Commands() { var cmdDesc bytes.Buffer if err := doc.GenMarkdown(c, &cmdDesc); err != nil { - return fmt.Errorf("error generating Markdown for command: %v : %w", c, err) + return err } for _, line := range strings.Split(cmdDesc.String(), "\n") { if strings.HasPrefix(line, "### SEE ALSO") { @@ -194,19 +188,19 @@ func buildConfigFromFS(templatesDir string, triggersDir string) (map[string]serv templatesCfg := map[string]services.Notification{} err := filepath.Walk(templatesDir, func(p string, info os.FileInfo, e error) error { if e != nil { - return fmt.Errorf("error navigating the templates dirctory: %s : %w", templatesDir, e) + return e } if info.IsDir() { return nil } data, err := os.ReadFile(p) if err != nil { - return fmt.Errorf("error reading the template file: %s : %w", p, err) + return err } name := strings.Split(path.Base(p), ".")[0] var template services.Notification if err := yaml.Unmarshal(data, &template); err != nil { - return fmt.Errorf("error unmarshaling the data from file: %s : %w", p, err) + return err } templatesCfg[name] = template return nil @@ -218,19 +212,19 @@ func buildConfigFromFS(templatesDir string, triggersDir string) (map[string]serv triggersCfg := map[string][]triggers.Condition{} err = filepath.Walk(triggersDir, func(p string, info os.FileInfo, e error) error { if e != nil { - return fmt.Errorf("error navigating the triggers dirctory: %s : %w", triggersDir, e) + return e } if info.IsDir() { return nil } data, err := os.ReadFile(p) if err != nil { - return fmt.Errorf("error reading the trigger file: %s : %w", p, err) + return err } name := strings.Split(path.Base(p), ".")[0] var trigger []triggers.Condition if err := yaml.Unmarshal(data, &trigger); err != nil { - return fmt.Errorf("error unmarshaling the data from file: %s : %w", p, err) + return err } triggersCfg[name] = trigger return nil diff --git a/hack/gen-crd-spec/main.go b/hack/gen-crd-spec/main.go index 16c0022dd1cb1..e7dcd658ef26a 100644 --- a/hack/gen-crd-spec/main.go +++ b/hack/gen-crd-spec/main.go @@ -2,7 +2,6 @@ package main import ( "encoding/json" - "errors" "fmt" "os" "os/exec" @@ -16,16 +15,19 @@ import ( "sigs.k8s.io/yaml" ) -var kindToCRDPath = map[string]string{ - application.ApplicationFullName: "manifests/crds/application-crd.yaml", - application.AppProjectFullName: "manifests/crds/appproject-crd.yaml", - application.ApplicationSetFullName: "manifests/crds/applicationset-crd.yaml", -} +var ( + kindToCRDPath = map[string]string{ + application.ApplicationFullName: "manifests/crds/application-crd.yaml", + application.AppProjectFullName: "manifests/crds/appproject-crd.yaml", + application.ApplicationSetFullName: "manifests/crds/applicationset-crd.yaml", + } +) func getCustomResourceDefinitions() map[string]*extensionsobj.CustomResourceDefinition { crdYamlBytes, err := exec.Command( "controller-gen", "paths=./pkg/apis/application/...", + "crd:trivialVersions=true", "crd:crdVersions=v1", "output:crd:stdout", ).Output() @@ -115,10 +117,6 @@ func removeDescription(v interface{}) { func checkErr(err error) { if err != nil { - var execError *exec.ExitError - if errors.As(err, &execError) { - fmt.Println(string(execError.Stderr)) - } panic(err) } } @@ -151,6 +149,6 @@ func writeCRDintoFile(crd *extensionsobj.CustomResourceDefinition, path string) yamlBytes, err := yaml.JSONToYAML(jsonBytes) checkErr(err) - err = os.WriteFile(path, yamlBytes, 0o644) + err = os.WriteFile(path, yamlBytes, 0644) checkErr(err) } diff --git a/hack/gen-docs/main.go b/hack/gen-docs/main.go index c39f4628a432c..f102f4c1d7e89 100644 --- a/hack/gen-docs/main.go +++ b/hack/gen-docs/main.go @@ -20,7 +20,7 @@ func main() { func generateNotificationsDocs() { _ = os.RemoveAll("./docs/operator-manual/notifications/services") - _ = os.MkdirAll("./docs/operator-manual/notifications/services", 0o755) + _ = os.MkdirAll("./docs/operator-manual/notifications/services", 0755) files, err := docs.CopyServicesDocs("./docs/operator-manual/notifications/services") if err != nil { log.Fatal(err) @@ -37,7 +37,7 @@ func updateMkDocsNav(parent string, child string, subchild string, files []strin sort.Strings(files) data, err := os.ReadFile("mkdocs.yml") if err != nil { - return fmt.Errorf("error reading mkdocs.yml: %w", err) + return err } var un unstructured.Unstructured if e := yaml.Unmarshal(data, &un.Object); e != nil { @@ -46,12 +46,12 @@ func updateMkDocsNav(parent string, child string, subchild string, files []strin nav := un.Object["nav"].([]interface{}) rootitem, _ := findNavItem(nav, parent) if rootitem == nil { - return fmt.Errorf("can't find '%s' root item in mkdoc.yml", parent) + return fmt.Errorf("Can't find '%s' root item in mkdoc.yml", parent) } 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) + return fmt.Errorf("Can't find '%s' chile item under '%s' parent item in mkdoc.yml", child, parent) } childnavmap := childnav.(map[interface{}]interface{}) @@ -63,14 +63,14 @@ func updateMkDocsNav(parent string, child string, subchild string, files []strin childnavmap[child] = append(childnavitems, commands) newmkdocs, err := yaml.Marshal(un.Object) if err != nil { - return fmt.Errorf("error in marshaling final configmap: %w", err) + return err } // The marshaller drops custom tags, so re-add this one. Turns out this is much less invasive than trying to handle // it at the YAML parser level. newmkdocs = bytes.Replace(newmkdocs, []byte("site_url: READTHEDOCS_CANONICAL_URL"), []byte("site_url: !ENV READTHEDOCS_CANONICAL_URL"), 1) - return os.WriteFile("mkdocs.yml", newmkdocs, 0o644) + return os.WriteFile("mkdocs.yml", newmkdocs, 0644) } func trimPrefixes(files []string, prefix string) { diff --git a/hack/gen-resources/cmd/commands/cmd.go b/hack/gen-resources/cmd/commands/cmd.go index 5804e4cda8910..ba9ded0c37577 100644 --- a/hack/gen-resources/cmd/commands/cmd.go +++ b/hack/gen-resources/cmd/commands/cmd.go @@ -30,9 +30,10 @@ func initConfig() { // NewCommand returns a new instance of an argocd command func NewCommand() *cobra.Command { + var generateOpts util.GenerateOpts - command := &cobra.Command{ + var command = &cobra.Command{ Use: cliName, Short: "Generator for argocd resources", Run: func(c *cobra.Command, args []string) { @@ -49,7 +50,7 @@ func NewCommand() *cobra.Command { func NewGenerateCommand(opts *util.GenerateOpts) *cobra.Command { var file string - command := &cobra.Command{ + var command = &cobra.Command{ Use: "generate [-f file]", Short: "Generate entities", Long: "Generate entities", @@ -93,7 +94,7 @@ func NewGenerateCommand(opts *util.GenerateOpts) *cobra.Command { } func NewCleanCommand(opts *util.GenerateOpts) *cobra.Command { - command := &cobra.Command{ + var command = &cobra.Command{ Use: "clean", Short: "Clean entities", Long: "Clean entities", diff --git a/hack/gen-resources/generators/cluster_generator.go b/hack/gen-resources/generators/cluster_generator.go index ff5e03e8755fe..6f125723c35ef 100644 --- a/hack/gen-resources/generators/cluster_generator.go +++ b/hack/gen-resources/generators/cluster_generator.go @@ -139,7 +139,7 @@ func (cg *ClusterGenerator) getClusterCredentials(namespace string, releaseSuffi // TODO: also should provision service for vcluster pod func (cg *ClusterGenerator) installVCluster(opts *util.GenerateOpts, namespace string, releaseName string) error { - cmd, err := helm.NewCmd("/tmp", "v3", "", "") + cmd, err := helm.NewCmd("/tmp", "v3", "") if err != nil { return err } @@ -157,22 +157,22 @@ func (cg *ClusterGenerator) getClusterServerUri(namespace string, releaseSuffix return "", err } // TODO: should be moved to service instead pod - log.Printf("Get service for https://%s:8443", pod.Status.PodIP) + log.Printf("Get service for https://" + pod.Status.PodIP + ":8443") return "https://" + pod.Status.PodIP + ":8443", nil } -func (cg *ClusterGenerator) retrieveClusterUri(namespace, releaseSuffix string) string { +func (cg *ClusterGenerator) retrieveClusterUri(namespace, releaseSuffix string) (string, error) { for i := 0; i < 10; i++ { - log.Print("Attempting to get cluster uri") + log.Printf("Attempting to get cluster uri") uri, err := cg.getClusterServerUri(namespace, releaseSuffix) if err != nil { log.Printf("Failed to get cluster uri due to %s", err.Error()) time.Sleep(10 * time.Second) continue } - return uri + return uri, nil } - return "" + return "", nil } func (cg *ClusterGenerator) generate(i int, opts *util.GenerateOpts) error { @@ -208,7 +208,11 @@ func (cg *ClusterGenerator) generate(i int, opts *util.GenerateOpts) error { log.Print("Get cluster server uri") - uri := cg.retrieveClusterUri(namespace, releaseSuffix) + uri, err := cg.retrieveClusterUri(namespace, releaseSuffix) + if err != nil { + return err + } + log.Printf("Cluster server uri is %s", uri) log.Print("Create cluster") diff --git a/hack/gen-resources/generators/project_generator.go b/hack/gen-resources/generators/project_generator.go index 943ecf0239f0a..7eee295af7f07 100644 --- a/hack/gen-resources/generators/project_generator.go +++ b/hack/gen-resources/generators/project_generator.go @@ -3,9 +3,8 @@ package generator import ( "context" "fmt" - "log" - 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/util/gen_options_parser.go b/hack/gen-resources/util/gen_options_parser.go index 22c36ab661a58..08fb37ab9b653 100644 --- a/hack/gen-resources/util/gen_options_parser.go +++ b/hack/gen-resources/util/gen_options_parser.go @@ -1,10 +1,8 @@ package util import ( - "fmt" - "os" - "gopkg.in/yaml.v2" + "os" ) type SourceOpts struct { @@ -56,7 +54,7 @@ func setDefaults(opts *GenerateOpts) { func Parse(opts *GenerateOpts, file string) error { fp, err := os.ReadFile(file) if err != nil { - return fmt.Errorf("error reading the template file: %s : %w", file, err) + return err } if e := yaml.Unmarshal(fp, &opts); e != nil { diff --git a/hack/generate-actions-list.sh b/hack/generate-actions-list.sh deleted file mode 100755 index 61b0b4c7aa5ce..0000000000000 --- a/hack/generate-actions-list.sh +++ /dev/null @@ -1 +0,0 @@ -find resource_customizations -name action.lua | sed 's/resource_customizations\/\(.*\)\/actions\/\(.*\)\/action.lua/- [\1\/\2](https:\/\/github.com\/argoproj\/argo-cd\/blob\/master\/resource_customizations\/\1\/actions\/\2\/action.lua)/' | sort | uniq > docs/operator-manual/resource_actions_builtin.md \ No newline at end of file diff --git a/hack/generate-mock.sh b/hack/generate-mock.sh deleted file mode 100755 index 0371b156ac139..0000000000000 --- a/hack/generate-mock.sh +++ /dev/null @@ -1,18 +0,0 @@ -#! /usr/bin/env bash - -set -x -set -o errexit -set -o nounset -set -o pipefail - -# shellcheck disable=SC2128 -PROJECT_ROOT=$( - cd "$(dirname "${BASH_SOURCE}")"/.. - pwd -) -PATH="${PROJECT_ROOT}/dist:${PATH}" - -# output tool versions -mockery --version - -mockery --config ${PROJECT_ROOT}/.mockery.yaml \ No newline at end of file diff --git a/hack/generate-proto.sh b/hack/generate-proto.sh index 83f542a9d21ab..8466993ebc544 100755 --- a/hack/generate-proto.sh +++ b/hack/generate-proto.sh @@ -10,13 +10,9 @@ 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 @@ -45,7 +41,6 @@ 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 @@ -56,28 +51,12 @@ else protoc_include=${PROJECT_ROOT}/dist/protoc-include fi -# go-to-protobuf expects dependency proto files to be in $GOPATH/src. Copy them there. -rm -rf "${GOPATH}/src/github.com/gogo/protobuf" && mkdir -p "${GOPATH}/src/github.com/gogo" && cp -r "${PROJECT_ROOT}/vendor/github.com/gogo/protobuf" "${GOPATH}/src/github.com/gogo" -rm -rf "${GOPATH}/src/k8s.io/apimachinery" && mkdir -p "${GOPATH}/src/k8s.io" && cp -r "${PROJECT_ROOT}/vendor/k8s.io/apimachinery" "${GOPATH}/src/k8s.io" -rm -rf "${GOPATH}/src/k8s.io/api" && mkdir -p "${GOPATH}/src/k8s.io" && cp -r "${PROJECT_ROOT}/vendor/k8s.io/api" "${GOPATH}/src/k8s.io" -rm -rf "${GOPATH}/src/k8s.io/apiextensions-apiserver" && mkdir -p "${GOPATH}/src/k8s.io" && cp -r "${PROJECT_ROOT}/vendor/k8s.io/apiextensions-apiserver" "${GOPATH}/src/k8s.io" - 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="${PROJECT_ROOT}"/vendor \ - --proto-import="${protoc_include}" \ - --output-dir="${GOPATH}/src/" - -# go-to-protobuf modifies vendored code. Re-vendor code so it's available for subsequent steps. -go mod vendor + --packages="$(IFS=, ; echo "${PACKAGES[*]}")" \ + --apimachinery-packages="$(IFS=, ; echo "${APIMACHINERY_PKGS[*]}")" \ + --proto-import=./vendor \ + --proto-import="${protoc_include}" # 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 @@ -107,11 +86,9 @@ 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 - -[ -L "${GOPATH_PROJECT_ROOT}" ] && rm -rf "${GOPATH_PROJECT_ROOT}" -[ -L ./v2 ] && rm -rf v2 +[ -e ./v2 ] && rm -rf v2 # collect_swagger gathers swagger files into a subdirectory collect_swagger() { @@ -120,7 +97,7 @@ collect_swagger() { PRIMARY_SWAGGER=$(mktemp) COMBINED_SWAGGER=$(mktemp) - cat <"${PRIMARY_SWAGGER}" + cat < "${PRIMARY_SWAGGER}" { "swagger": "2.0", "info": { @@ -134,7 +111,7 @@ EOF rm -f "${SWAGGER_OUT}" - find "${SWAGGER_ROOT}" -name '*.swagger.json' -exec swagger mixin --ignore-conflicts "${PRIMARY_SWAGGER}" '{}' \+ >"${COMBINED_SWAGGER}" + 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"}} | @@ -143,10 +120,10 @@ EOF 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}" + ' "${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}" } @@ -162,3 +139,4 @@ clean_swagger server clean_swagger reposerver clean_swagger controller clean_swagger cmpserver + diff --git a/hack/installers/checksums/add-helm-checksums.sh b/hack/installers/checksums/add-helm-checksums.sh index 95bf2b2566b69..47292390d8789 100755 --- a/hack/installers/checksums/add-helm-checksums.sh +++ b/hack/installers/checksums/add-helm-checksums.sh @@ -3,10 +3,7 @@ # 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/add-protoc-checksums.sh b/hack/installers/checksums/add-protoc-checksums.sh deleted file mode 100755 index 1c13e6cfaefdf..0000000000000 --- a/hack/installers/checksums/add-protoc-checksums.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env sh - -# Usage: ./add-protoc-checksums.sh 27.2 # use the desired version - -set -e -for arch in aarch_64 ppcle_64 s390_64 x86_64; do - wget "https://github.com/protocolbuffers/protobuf/releases/download/v$1/protoc-$1-linux-$arch.zip" -O "protoc-$1-linux-$arch.zip" - sha256sum "protoc-$1-linux-$arch.zip" > "protoc-$1-linux-$arch.zip.sha256" - rm "protoc-$1-linux-$arch.zip" -done - -for arch in aarch_64 x86_64; do - wget "https://github.com/protocolbuffers/protobuf/releases/download/v$1/protoc-$1-osx-$arch.zip" -O "protoc-$1-osx-$arch.zip" - sha256sum "protoc-$1-osx-$arch.zip" > "protoc-$1-osx-$arch.zip.sha256" - rm "protoc-$1-osx-$arch.zip" -done \ No newline at end of file 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 deleted file mode 100644 index 8c2cdef022af2..0000000000000 --- a/hack/installers/checksums/helm-v3.14.2-darwin-amd64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index a81e6ce01561f..0000000000000 --- a/hack/installers/checksums/helm-v3.14.2-darwin-arm64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -ff502fd39b06497fa3d5a51ec2ced02b9fcfdb0e9a948d315fb1b2f13ddc39fb helm-v3.14.2-darwin-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.2-darwin-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.2-darwin-amd64.tar.gz.sha256 deleted file mode 100644 index 29fbec1a8217e..0000000000000 --- a/hack/installers/checksums/helm-v3.15.2-darwin-amd64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -e99a9266a5328cb575d81ef10247911f42d9e90c76ef6eef154c5c535565658b helm-v3.15.2-darwin-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.2-darwin-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.2-darwin-arm64.tar.gz.sha256 deleted file mode 100644 index fa28023088fd1..0000000000000 --- a/hack/installers/checksums/helm-v3.15.2-darwin-arm64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -30143dabc1da9d32c7d6c589fad04b1f1ecc73841393d5823fa21c5d7f5bf8f6 helm-v3.15.2-darwin-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.2-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.2-linux-amd64.tar.gz.sha256 deleted file mode 100644 index 6112735da391e..0000000000000 --- a/hack/installers/checksums/helm-v3.15.2-linux-amd64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -2694b91c3e501cff57caf650e639604a274645f61af2ea4d601677b746b44fe2 helm-v3.15.2-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.2-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.2-linux-arm64.tar.gz.sha256 deleted file mode 100644 index 397aa7df1ce58..0000000000000 --- a/hack/installers/checksums/helm-v3.15.2-linux-arm64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -adcf07b08484b52508e5cbc8b5f4b0b0db50342f7bc487ecd88b8948b680e6a7 helm-v3.15.2-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.2-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.2-linux-ppc64le.tar.gz.sha256 deleted file mode 100644 index 8cd9a695b81c8..0000000000000 --- a/hack/installers/checksums/helm-v3.15.2-linux-ppc64le.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -9d95528fb797f6429f7f9b6dee0cf87bf8c71f6470e1db4a51e844c169c285a3 helm-v3.15.2-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.2-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.2-linux-s390x.tar.gz.sha256 deleted file mode 100644 index 354d5237efd70..0000000000000 --- a/hack/installers/checksums/helm-v3.15.2-linux-s390x.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -5b42bc3d08fd0ffaf4f9ed810f28464f52ec4ea431b809c7179071d76f3d6f16 helm-v3.15.2-linux-s390x.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.2_darwin_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.2_darwin_amd64.tar.gz.sha256 deleted file mode 100644 index 33f50b8b23a52..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.2_darwin_amd64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -d1dadf6d51058cdda6470344c95767e1c283cc5a36d5019eb32f8e43e63bd0df kustomize_5.4.2_darwin_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.2_darwin_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.2_darwin_arm64.tar.gz.sha256 deleted file mode 100644 index daa903d3b0bf8..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.2_darwin_arm64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -9b7da623cb40542f2dd220fa31d906d9254759b4e27583706e4e846fccba9fab kustomize_5.4.2_darwin_arm64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.2_linux_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.2_linux_amd64.tar.gz.sha256 deleted file mode 100644 index 71cb7ef37cda5..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.2_linux_amd64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -881c6e9007c7ea2b9ecc214d13f4cdd1f837635dcf4db49ce4479898f7d911a3 kustomize_5.4.2_linux_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.2_linux_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.2_linux_arm64.tar.gz.sha256 deleted file mode 100644 index ad7a240a0aaac..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.2_linux_arm64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -175af88af8a7d8d7d6b1f26659060950f0764d00b9979b4e11b61b8b212b7c22 kustomize_5.4.2_linux_arm64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.2_linux_ppc64le.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.2_linux_ppc64le.tar.gz.sha256 deleted file mode 100644 index ab8410045bd73..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.2_linux_ppc64le.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -be71e5cb95362a111dcee315ee5fb50ec5faac0446571ecc84ba4aa6e1298feb kustomize_5.4.2_linux_ppc64le.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.2_linux_s390x.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.2_linux_s390x.tar.gz.sha256 deleted file mode 100644 index 735958aeece44..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.2_linux_s390x.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -3724d3a711a6f06650ef31e9d6a7c8aaaed0727183d6f61b2103ffc717af68a1 kustomize_5.4.2_linux_s390x.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.3_darwin_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.3_darwin_amd64.tar.gz.sha256 deleted file mode 100644 index 296ad23aa6ff2..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.3_darwin_amd64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -6a708ef727594bbb5f2b8f9f8049375a6028d57fa8897c1f9e78effde4e403a2 kustomize_5.4.3_darwin_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.3_darwin_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.3_darwin_arm64.tar.gz.sha256 deleted file mode 100644 index 36c2d941fc11d..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.3_darwin_arm64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -3e159813a5feae46726fb22736b8764f2dbac83ba982c91ccd0244762456272c kustomize_5.4.3_darwin_arm64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.3_linux_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.3_linux_amd64.tar.gz.sha256 deleted file mode 100644 index 5dbbd76cb3a39..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.3_linux_amd64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -3669470b454d865c8184d6bce78df05e977c9aea31c30df3c669317d43bcc7a7 kustomize_5.4.3_linux_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.3_linux_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.3_linux_arm64.tar.gz.sha256 deleted file mode 100644 index a825d8b1a7f48..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.3_linux_arm64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -1b515578b0af12c15d9856720066ce2fe66756d63785b2cbccaf2885beb2381c kustomize_5.4.3_linux_arm64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.3_linux_ppc64le.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.3_linux_ppc64le.tar.gz.sha256 deleted file mode 100644 index f258e84c6579a..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.3_linux_ppc64le.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -56bbb3d0f5e499410932da0b0c347ea3dcd18006a93039e0e993b5193933e721 kustomize_5.4.3_linux_ppc64le.tar.gz diff --git a/hack/installers/checksums/kustomize_5.4.3_linux_s390x.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.4.3_linux_s390x.tar.gz.sha256 deleted file mode 100644 index 4b4e98494b45d..0000000000000 --- a/hack/installers/checksums/kustomize_5.4.3_linux_s390x.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -c575eba2f46c4701d1897a9b27c422d42d6381adb679435bc3e0d7c0da5abe44 kustomize_5.4.3_linux_s390x.tar.gz diff --git a/hack/installers/checksums/protoc-27.2-linux-aarch_64.zip.sha256 b/hack/installers/checksums/protoc-27.2-linux-aarch_64.zip.sha256 deleted file mode 100644 index c5afce5689ae1..0000000000000 --- a/hack/installers/checksums/protoc-27.2-linux-aarch_64.zip.sha256 +++ /dev/null @@ -1 +0,0 @@ -ff4760bd4ae510d533e528cc6deb8e32e53f383f0ec01b0327233b4c2e8db314 protoc-27.2-linux-aarch_64.zip diff --git a/hack/installers/checksums/protoc-27.2-linux-ppcle_64.zip.sha256 b/hack/installers/checksums/protoc-27.2-linux-ppcle_64.zip.sha256 deleted file mode 100644 index ca890c445cd74..0000000000000 --- a/hack/installers/checksums/protoc-27.2-linux-ppcle_64.zip.sha256 +++ /dev/null @@ -1 +0,0 @@ -35076bf2074eaef76a88546c09f4894dfe84c3f2d06615c14d87d97850f2d907 protoc-27.2-linux-ppcle_64.zip diff --git a/hack/installers/checksums/protoc-27.2-linux-s390_64.zip.sha256 b/hack/installers/checksums/protoc-27.2-linux-s390_64.zip.sha256 deleted file mode 100644 index 513a72876d311..0000000000000 --- a/hack/installers/checksums/protoc-27.2-linux-s390_64.zip.sha256 +++ /dev/null @@ -1 +0,0 @@ -4f01c22339734187dc7878507ee80346d63da3989908b716990f40876fc96f30 protoc-27.2-linux-s390_64.zip diff --git a/hack/installers/checksums/protoc-27.2-linux-x86_64.zip.sha256 b/hack/installers/checksums/protoc-27.2-linux-x86_64.zip.sha256 deleted file mode 100644 index 3e20c6886aef6..0000000000000 --- a/hack/installers/checksums/protoc-27.2-linux-x86_64.zip.sha256 +++ /dev/null @@ -1 +0,0 @@ -4a95e0ea2e51720af86a92f48d4997c8756923a9d0c58fd8a850657cd7479caf protoc-27.2-linux-x86_64.zip diff --git a/hack/installers/checksums/protoc-27.2-osx-aarch_64.zip.sha256 b/hack/installers/checksums/protoc-27.2-osx-aarch_64.zip.sha256 deleted file mode 100644 index 36e92d7242c85..0000000000000 --- a/hack/installers/checksums/protoc-27.2-osx-aarch_64.zip.sha256 +++ /dev/null @@ -1 +0,0 @@ -877de17b5d2662b96e68a6e208cb1851437ab3e2b419c2ef5b7b873ffac5357d protoc-27.2-osx-aarch_64.zip diff --git a/hack/installers/checksums/protoc-27.2-osx-x86_64.zip.sha256 b/hack/installers/checksums/protoc-27.2-osx-x86_64.zip.sha256 deleted file mode 100644 index 38b28b6e726ee..0000000000000 --- a/hack/installers/checksums/protoc-27.2-osx-x86_64.zip.sha256 +++ /dev/null @@ -1 +0,0 @@ -abc25a236571612d45eb4b6b6e6abe3ac9aecc34b195f76f248786844f5619c7 protoc-27.2-osx-x86_64.zip diff --git a/hack/installers/install-codegen-go-tools.sh b/hack/installers/install-codegen-go-tools.sh index 1fd3ea5434afe..c6ebfc8902cee 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 .proto files +# protoc-gen-grpc-gateway is used to build .pb.gw.go files from 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 @@ -45,13 +45,10 @@ go_mod_install k8s.io/code-generator/cmd/lister-gen go_mod_install k8s.io/kube-openapi/cmd/openapi-gen # controller-gen is run by ./hack/gen-crd-spec to generate the CRDs -go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.14.0 +go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1 # swagger cli is used to generate swagger docs go install github.com/go-swagger/go-swagger/cmd/swagger@v0.28.0 # goimports is used to auto-format generated code go install golang.org/x/tools/cmd/goimports@v0.1.8 - -# mockery is used to generate mock -go install github.com/vektra/mockery/v2@v2.43.2 \ No newline at end of file diff --git a/hack/installers/install-helm.sh b/hack/installers/install-helm-linux.sh similarity index 63% rename from hack/installers/install-helm.sh rename to hack/installers/install-helm-linux.sh index ef3882fdaf688..6371fd452c204 100755 --- a/hack/installers/install-helm.sh +++ b/hack/installers/install-helm-linux.sh @@ -3,10 +3,10 @@ set -eux -o pipefail . $(dirname $0)/../tool-versions.sh -export TARGET_FILE=helm-v${helm3_version}-${INSTALL_OS}-${ARCHITECTURE}.tar.gz +export TARGET_FILE=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 +[ -e $DOWNLOADS/${TARGET_FILE} ] || curl -sLf --retry 3 -o $DOWNLOADS/${TARGET_FILE} https://get.helm.sh/helm-v${helm3_version}-linux-$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/$INSTALL_OS-$ARCHITECTURE/helm $BIN/helm +sudo install -m 0755 /tmp/helm/linux-$ARCHITECTURE/helm $BIN/helm helm version --client diff --git a/hack/installers/install-lint-tools.sh b/hack/installers/install-lint-tools.sh index 54e7b725478c8..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.58.2 +GO111MODULE=on go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.0 diff --git a/hack/installers/install-protoc.sh b/hack/installers/install-protoc.sh index f0fb244064ef7..82d491c81c3c0 100755 --- a/hack/installers/install-protoc.sh +++ b/hack/installers/install-protoc.sh @@ -34,7 +34,7 @@ case $OS in ;; esac -export TARGET_FILE=protoc-${protoc_version}-${protoc_os}-${protoc_arch}.zip +export TARGET_FILE=protoc_${protoc_version}_${OS}_${ARCHITECTURE}.zip url=https://github.com/protocolbuffers/protobuf/releases/download/v${protoc_version}/protoc-${protoc_version}-${protoc_os}-${protoc_arch}.zip [ -e $DOWNLOADS/${TARGET_FILE} ] || curl -sLf --retry 3 -o $DOWNLOADS/${TARGET_FILE} ${url} $(dirname $0)/compare-chksum.sh diff --git a/hack/known_types/main.go b/hack/known_types/main.go index 247fadffd908c..be8bcfdc7b50c 100644 --- a/hack/known_types/main.go +++ b/hack/known_types/main.go @@ -23,8 +23,10 @@ const ( ) func newCommand() *cobra.Command { - var docsOutputPath string = "" - command := &cobra.Command{ + var ( + docsOutputPath string = "" + ) + var command = &cobra.Command{ Use: "go run github.com/argoproj/argo-cd/hack/known_types ALIAS PACKAGE_PATH OUTPUT_PATH", Example: "go run github.com/argoproj/argo-cd/hack/known_types corev1 k8s.io/api/core/v1 corev1_known_types.go", RunE: func(c *cobra.Command, args []string) error { @@ -42,7 +44,7 @@ func newCommand() *cobra.Command { imprt := importer.ForCompiler(token.NewFileSet(), "source", nil) pkg, err := imprt.Import(packagePath) if err != nil { - return fmt.Errorf("error while importing the package at: %s : %w", packagePath, err) + return err } shortPackagePath := strings.TrimPrefix(packagePath, packagePrefix) @@ -77,12 +79,12 @@ import corev1 "k8s.io/api/core/v1" func init() {%s }`, strings.Join(mapItems, "")) if docsOutputPath != "" { - if err = os.WriteFile(docsOutputPath, []byte(strings.Join(docs, "\n")), 0o644); err != nil { - return fmt.Errorf("error while writing to the %s: %w", docsOutputPath, err) + if err = os.WriteFile(docsOutputPath, []byte(strings.Join(docs, "\n")), 0644); err != nil { + return err } } - return os.WriteFile(outputPath, []byte(res+"\n"), 0o644) + return os.WriteFile(outputPath, []byte(res+"\n"), 0644) }, } command.Flags().StringVar(&docsOutputPath, "docs", "", "Docs output file path") diff --git a/hack/snyk-report.sh b/hack/snyk-report.sh index 8147c3bba3bc4..074f218289c43 100755 --- a/hack/snyk-report.sh +++ b/hack/snyk-report.sh @@ -37,8 +37,8 @@ git clone https://github.com/argoproj/argo-cd.git cd argo-cd git checkout master -minor_version=$(git tag -l | sort -V | tail -n 1 | grep -Eo '[0-9]+\.[0-9]+') -patch_num=$(git tag -l | grep "v$minor_version." | grep -o "[a-z[:digit:]-]*$" | sort -V | tail -n 1) +minor_version=$(git tag -l | sort -g | tail -n 1 | grep -Eo '[0-9]+\.[0-9]+') +patch_num=$(git tag -l | grep "v$minor_version." | grep -o "[a-z[:digit:]-]*$" | sort -g | tail -n 1) version="v$minor_version.$patch_num" versions="master " @@ -54,7 +54,7 @@ for i in $(seq "$version_count"); do minor_num=$(printf '%s' "$minor_version" | sed -E 's/[0-9]+\.//') minor_num=$((minor_num-1)) minor_version=$(printf '%s' "$minor_version" | sed -E "s/\.[0-9]+$/.$minor_num/g") - patch_num=$(git tag -l | grep "v$minor_version." | grep -o "[a-z[:digit:]-]*$" | sort -V | tail -n 1) + patch_num=$(git tag -l | grep "v$minor_version." | grep -o "[a-z[:digit:]-]*$" | sort -g | tail -n 1) version="v$minor_version.$patch_num" done diff --git a/hack/start-redis-with-password.sh b/hack/start-redis-with-password.sh deleted file mode 100755 index e42ecaf28f54d..0000000000000 --- a/hack/start-redis-with-password.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -# Default values for environment variables -REDIS_PORT="${ARGOCD_E2E_REDIS_PORT:-6379}" -REDIS_IMAGE_TAG=$(grep 'image: redis' manifests/base/redis/argocd-redis-deployment.yaml | cut -d':' -f3) - -if [ "$ARGOCD_REDIS_LOCAL" = 'true' ]; then - if ! command -v redis-server &>/dev/null; then - echo "Redis server is not installed locally. Please install Redis or set ARGOCD_REDIS_LOCAL to false." - exit 1 - fi - - # Start local Redis server with password if defined - if [ -z "$REDIS_PASSWORD" ]; then - echo "Starting local Redis server without password." - redis-server --save '' --appendonly no --port "$REDIS_PORT" - else - echo "Starting local Redis server with password." - redis-server --save '' --appendonly no --port "$REDIS_PORT" --requirepass "$REDIS_PASSWORD" - fi -else - # Run Redis in a Docker container with password if defined - if [ -z "$REDIS_PASSWORD" ]; then - echo "Starting Docker container without password." - docker run --rm --name argocd-redis -i -p "$REDIS_PORT:$REDIS_PORT" docker.io/library/redis:"$REDIS_IMAGE_TAG" --save '' --appendonly no --port "$REDIS_PORT" - else - echo "Starting Docker container with password." - docker run --rm --name argocd-redis -i -p "$REDIS_PORT:$REDIS_PORT" -e REDIS_PASSWORD="$REDIS_PASSWORD" docker.io/library/redis:"$REDIS_IMAGE_TAG" redis-server --save '' --requirepass "$REDIS_PASSWORD" --appendonly no --port "$REDIS_PORT" - fi -fi \ No newline at end of file diff --git a/hack/test.sh b/hack/test.sh index c13718063b862..454a58d749291 100755 --- a/hack/test.sh +++ b/hack/test.sh @@ -4,7 +4,7 @@ set -eux -o pipefail which go-junit-report || go install github.com/jstemmer/go-junit-report@latest TEST_RESULTS=${TEST_RESULTS:-test-results} -TEST_FLAGS=${TEST_FLAGS:-} +TEST_FLAGS= if test "${ARGOCD_TEST_PARALLELISM:-}" != ""; then TEST_FLAGS="$TEST_FLAGS -p $ARGOCD_TEST_PARALLELISM" @@ -15,4 +15,4 @@ fi mkdir -p $TEST_RESULTS -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" -- -cover $TEST_FLAGS $* +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 28ca1cda431da..a49285c88000d 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.15.2 +helm3_version=3.14.4 kubectl_version=1.17.8 kubectx_version=0.6.3 -kustomize5_version=5.4.3 -protoc_version=27.2 +kustomize5_version=5.2.1 +protoc_version=3.17.3 diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index cdd932807d784..abee0493ead86 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -19,31 +19,21 @@ 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/kube_codegen.sh +TARGET_SCRIPT=/tmp/generate-groups.sh -# codegen utilities are installed outside of kube_codegen.sh so remove the `go install` step in the script. -sed -e '/go install/d' ${PROJECT_ROOT}/vendor/k8s.io/code-generator/kube_codegen.sh >${TARGET_SCRIPT} +# 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} # 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" \ - --output-base "${GOPATH}/src" - -[ -L "${GOPATH_PROJECT_ROOT}" ] && rm -rf "${GOPATH_PROJECT_ROOT}" -[ -L ./v2 ] && rm -rf v2 + --go-header-file ${PROJECT_ROOT}/hack/custom-boilerplate.go.txt +[ -e ./v2 ] && rm -rf v2 \ No newline at end of file diff --git a/hack/update-openapi.sh b/hack/update-openapi.sh index 39d821b99212a..2db84ed5f6242 100755 --- a/hack/update-openapi.sh +++ b/hack/update-openapi.sh @@ -5,29 +5,20 @@ 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 \ - --output-pkg github.com/argoproj/argo-cd/v2/pkg/apis/application/${VERSION} \ + --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-dir "${GOPATH}/src" \ $@ - -[ -L "${GOPATH_PROJECT_ROOT}" ] && rm -rf "${GOPATH_PROJECT_ROOT}" -[ -L ./v2 ] && rm -rf v2 +[ -e ./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/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml b/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml index 619c4ca4817b8..815e4123d05e3 100644 --- a/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml +++ b/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml @@ -225,8 +225,6 @@ spec: mountPath: /app/config/controller/tls - name: argocd-home mountPath: /home/argocd - - name: argocd-cmd-params-cm - mountPath: /home/argocd/params serviceAccountName: argocd-application-controller affinity: podAntiAffinity: @@ -257,10 +255,3 @@ spec: path: tls.key - key: ca.crt path: ca.crt - - name: argocd-cmd-params-cm - configMap: - optional: true - name: argocd-cmd-params-cm - items: - - key: controller.profile.enabled - path: profiler.enabled diff --git a/manifests/base/application-controller-deployment/argocd-application-controller-statefulset.yaml b/manifests/base/application-controller-deployment/argocd-application-controller-statefulset.yaml deleted file mode 100644 index 10e4ea2ac7e3e..0000000000000 --- a/manifests/base/application-controller-deployment/argocd-application-controller-statefulset.yaml +++ /dev/null @@ -1,15 +0,0 @@ -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 index 733a378e013e0..8f35ec8bd388f 100644 --- a/manifests/base/application-controller-deployment/kustomization.yaml +++ b/manifests/base/application-controller-deployment/kustomization.yaml @@ -2,8 +2,5 @@ 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-roles/kustomization.yaml b/manifests/base/application-controller-roles/kustomization.yaml deleted file mode 100644 index f834d2ef3dbc4..0000000000000 --- a/manifests/base/application-controller-roles/kustomization.yaml +++ /dev/null @@ -1,7 +0,0 @@ -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-roles/argocd-application-controller-role.yaml b/manifests/base/application-controller/argocd-application-controller-role.yaml similarity index 100% rename from manifests/base/application-controller-roles/argocd-application-controller-role.yaml rename to manifests/base/application-controller/argocd-application-controller-role.yaml diff --git a/manifests/base/application-controller-roles/argocd-application-controller-rolebinding.yaml b/manifests/base/application-controller/argocd-application-controller-rolebinding.yaml similarity index 100% rename from manifests/base/application-controller-roles/argocd-application-controller-rolebinding.yaml rename to manifests/base/application-controller/argocd-application-controller-rolebinding.yaml diff --git a/manifests/base/application-controller-roles/argocd-application-controller-sa.yaml b/manifests/base/application-controller/argocd-application-controller-sa.yaml similarity index 100% rename from manifests/base/application-controller-roles/argocd-application-controller-sa.yaml rename to manifests/base/application-controller/argocd-application-controller-sa.yaml diff --git a/manifests/base/application-controller/argocd-application-controller-statefulset.yaml b/manifests/base/application-controller/argocd-application-controller-statefulset.yaml index ca09f482c35f7..2219f5f9b4731 100644 --- a/manifests/base/application-controller/argocd-application-controller-statefulset.yaml +++ b/manifests/base/application-controller/argocd-application-controller-statefulset.yaml @@ -234,8 +234,6 @@ spec: mountPath: /app/config/controller/tls - name: argocd-home mountPath: /home/argocd - - name: argocd-cmd-params-cm - mountPath: /home/argocd/params serviceAccountName: argocd-application-controller affinity: podAntiAffinity: @@ -266,10 +264,3 @@ spec: path: tls.key - key: ca.crt path: ca.crt - - name: argocd-cmd-params-cm - configMap: - optional: true - name: argocd-cmd-params-cm - items: - - key: controller.profile.enabled - path: profiler.enabled \ No newline at end of file diff --git a/manifests/base/application-controller/kustomization.yaml b/manifests/base/application-controller/kustomization.yaml index 616977fb9b08b..9a801ad877bd2 100644 --- a/manifests/base/application-controller/kustomization.yaml +++ b/manifests/base/application-controller/kustomization.yaml @@ -2,7 +2,9 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- ../application-controller-roles +- argocd-application-controller-sa.yaml +- argocd-application-controller-role.yaml +- argocd-application-controller-rolebinding.yaml - 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 6bade745f76c1..b24124ccb833f 100644 --- a/manifests/base/applicationset-controller/argocd-applicationset-controller-deployment.yaml +++ b/manifests/base/applicationset-controller/argocd-applicationset-controller-deployment.yaml @@ -157,12 +157,6 @@ spec: name: argocd-cmd-params-cm key: applicationsetcontroller.enable.scm.providers optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: applicationsetcontroller.webhook.parallelism.limit - optional: true volumeMounts: - mountPath: /app/config/ssh name: ssh-known-hosts diff --git a/manifests/base/applicationset-controller/argocd-applicationset-controller-role.yaml b/manifests/base/applicationset-controller/argocd-applicationset-controller-role.yaml index 3eaf779dd6c31..47c7ac9805a50 100644 --- a/manifests/base/applicationset-controller/argocd-applicationset-controller-role.yaml +++ b/manifests/base/applicationset-controller/argocd-applicationset-controller-role.yaml @@ -27,8 +27,6 @@ rules: - appprojects verbs: - get - - list - - watch - apiGroups: - argoproj.io resources: @@ -64,4 +62,4 @@ rules: verbs: - get - list - - watch + - watch \ 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 f2d77c6ac1f6a..8d3b37d177913 100644 --- a/manifests/base/dex/argocd-dex-server-deployment.yaml +++ b/manifests/base/dex/argocd-dex-server-deployment.yaml @@ -37,22 +37,10 @@ spec: type: RuntimeDefault containers: - name: dex - image: ghcr.io/dexidp/dex:v2.41.1 + image: ghcr.io/dexidp/dex:v2.37.0 imagePullPolicy: Always command: [/shared/argocd-dex, rundex] env: - - name: ARGOCD_DEX_SERVER_LOGFORMAT - valueFrom: - configMapKeyRef: - key: dexserver.log.format - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_DEX_SERVER_LOGLEVEL - valueFrom: - configMapKeyRef: - key: dexserver.log.level - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_DEX_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: diff --git a/manifests/base/kustomization.yaml b/manifests/base/kustomization.yaml index e80274cddc620..34e8bd6576676 100644 --- a/manifests/base/kustomization.yaml +++ b/manifests/base/kustomization.yaml @@ -5,7 +5,7 @@ kind: Kustomization images: - name: quay.io/argoproj/argocd newName: quay.io/argoproj/argocd - newTag: latest + newTag: v2.10.16 resources: - ./application-controller - ./dex diff --git a/manifests/base/notification/argocd-notifications-controller-deployment.yaml b/manifests/base/notification/argocd-notifications-controller-deployment.yaml index b13acf718f93c..876a207c16e42 100644 --- a/manifests/base/notification/argocd-notifications-controller-deployment.yaml +++ b/manifests/base/notification/argocd-notifications-controller-deployment.yaml @@ -60,12 +60,6 @@ spec: key: notificationscontroller.selfservice.enabled name: argocd-cmd-params-cm optional: true - - name: ARGOCD_NOTIFICATION_CONTROLLER_REPO_SERVER_PLAINTEXT - valueFrom: - configMapKeyRef: - key: notificationscontroller.repo.server.plaintext - name: argocd-cmd-params-cm - optional: true workingDir: /app livenessProbe: tcpSocket: diff --git a/manifests/base/repo-server/argocd-repo-server-deployment.yaml b/manifests/base/repo-server/argocd-repo-server-deployment.yaml index f6a073c32d6e9..7093acd27702a 100644 --- a/manifests/base/repo-server/argocd-repo-server-deployment.yaml +++ b/manifests/base/repo-server/argocd-repo-server-deployment.yaml @@ -149,12 +149,6 @@ spec: name: argocd-cmd-params-cm key: reposerver.plugin.tar.exclusions optional: true - - name: ARGOCD_REPO_SERVER_PLUGIN_USE_MANIFEST_GENERATE_PATHS - valueFrom: - configMapKeyRef: - key: reposerver.plugin.use.manifest.generate.paths - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_REPO_SERVER_ALLOW_OUT_OF_BOUNDS_SYMLINKS valueFrom: configMapKeyRef: @@ -185,12 +179,6 @@ spec: 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: @@ -209,18 +197,6 @@ spec: key: reposerver.git.request.timeout name: argocd-cmd-params-cm optional: true - - name: ARGOCD_GRPC_MAX_SIZE_MB - valueFrom: - configMapKeyRef: - key: reposerver.grpc.max.size - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_REPO_SERVER_INCLUDE_HIDDEN_DIRECTORIES - valueFrom: - configMapKeyRef: - key: reposerver.include.hidden.directories - name: argocd-cmd-params-cm - optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME diff --git a/manifests/base/server/argocd-server-deployment.yaml b/manifests/base/server/argocd-server-deployment.yaml index 56b479fdcfd44..1107323b2e3b9 100644 --- a/manifests/base/server/argocd-server-deployment.yaml +++ b/manifests/base/server/argocd-server-deployment.yaml @@ -17,371 +17,332 @@ spec: spec: serviceAccountName: argocd-server containers: - - name: argocd-server - image: quay.io/argoproj/argocd:latest - imagePullPolicy: Always - args: - - /usr/local/bin/argocd-server - env: - - name: REDIS_PASSWORD - valueFrom: - secretKeyRef: - key: auth - name: argocd-redis - - name: ARGOCD_SERVER_INSECURE - valueFrom: - 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 - - name: ARGOCD_SERVER_ROOTPATH - valueFrom: - 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 - - name: ARGOCD_SERVER_LOG_LEVEL - valueFrom: - 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 - - name: ARGOCD_SERVER_DEX_SERVER - valueFrom: - 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 - - name: ARGOCD_SERVER_ENABLE_GZIP - valueFrom: - 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 - - name: ARGOCD_SERVER_X_FRAME_OPTIONS - valueFrom: - 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 - - name: ARGOCD_SERVER_REPO_SERVER_PLAINTEXT - valueFrom: - 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 - - name: ARGOCD_SERVER_DEX_SERVER_PLAINTEXT - valueFrom: - 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 - - name: ARGOCD_TLS_MIN_VERSION - valueFrom: - 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 - - name: ARGOCD_TLS_CIPHERS - valueFrom: - 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 - - name: ARGOCD_SERVER_OIDC_CACHE_EXPIRATION - valueFrom: - 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 - - name: ARGOCD_SERVER_STATIC_ASSETS - valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.staticassets - optional: true - - name: ARGOCD_APP_STATE_CACHE_EXPIRATION - valueFrom: - 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 - - 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: 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 - - 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 - - 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 - - name: ARGOCD_SERVER_ENABLE_PROXY_EXTENSION - valueFrom: - 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 - - name: ARGOCD_SERVER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.webhook.parallelism.limit - 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_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: - - name: ssh-known-hosts - mountPath: /app/config/ssh - - name: tls-certs - mountPath: /app/config/tls - - name: argocd-repo-server-tls - mountPath: /app/config/server/tls - - name: argocd-dex-server-tls - mountPath: /app/config/dex/tls - - mountPath: /home/argocd - name: plugins-home - - mountPath: /tmp - name: tmp - - name: argocd-cmd-params-cm - mountPath: /home/argocd/params - ports: - - containerPort: 8080 - - containerPort: 8083 - livenessProbe: - httpGet: - path: /healthz?full=true - port: 8080 - initialDelaySeconds: 3 - periodSeconds: 30 - timeoutSeconds: 5 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - initialDelaySeconds: 3 - periodSeconds: 30 - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - capabilities: - drop: - - ALL - seccompProfile: - type: RuntimeDefault - volumes: - - emptyDir: {} - name: plugins-home - - emptyDir: {} - name: tmp + - name: argocd-server + image: quay.io/argoproj/argocd:latest + imagePullPolicy: Always + args: + - /usr/local/bin/argocd-server + env: + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + key: auth + name: argocd-redis + - name: ARGOCD_SERVER_INSECURE + valueFrom: + 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 + - name: ARGOCD_SERVER_ROOTPATH + valueFrom: + 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 + - name: ARGOCD_SERVER_LOG_LEVEL + valueFrom: + 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 + - name: ARGOCD_SERVER_DEX_SERVER + valueFrom: + 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 + - name: ARGOCD_SERVER_ENABLE_GZIP + valueFrom: + 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 + - name: ARGOCD_SERVER_X_FRAME_OPTIONS + valueFrom: + 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 + - name: ARGOCD_SERVER_REPO_SERVER_PLAINTEXT + valueFrom: + 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 + - name: ARGOCD_SERVER_DEX_SERVER_PLAINTEXT + valueFrom: + 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 + - name: ARGOCD_TLS_MIN_VERSION + valueFrom: + 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 + - name: ARGOCD_TLS_CIPHERS + valueFrom: + 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 + - name: ARGOCD_SERVER_OIDC_CACHE_EXPIRATION + valueFrom: + 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 + - name: ARGOCD_SERVER_STATIC_ASSETS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.staticassets + optional: true + - name: ARGOCD_APP_STATE_CACHE_EXPIRATION + valueFrom: + 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 + - 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: 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 + - 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 + - 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 + - name: ARGOCD_SERVER_ENABLE_PROXY_EXTENSION + valueFrom: + 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 - configMap: - name: argocd-ssh-known-hosts-cm + mountPath: /app/config/ssh - name: tls-certs - configMap: - name: argocd-tls-certs-cm + mountPath: /app/config/tls - 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 + mountPath: /app/config/server/tls - name: argocd-dex-server-tls - secret: - secretName: argocd-dex-server-tls - optional: true - items: - - key: tls.crt - path: tls.crt - - key: ca.crt - path: ca.crt - - name: argocd-cmd-params-cm - configMap: - optional: true - name: argocd-cmd-params-cm - items: - - key: server.profile.enabled - path: profiler.enabled + mountPath: /app/config/dex/tls + - mountPath: /home/argocd + name: plugins-home + - mountPath: /tmp + name: tmp + ports: + - containerPort: 8080 + - containerPort: 8083 + livenessProbe: + httpGet: + path: /healthz?full=true + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 30 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 30 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + volumes: + - emptyDir: {} + name: plugins-home + - emptyDir: {} + name: tmp + - name: ssh-known-hosts + configMap: + name: argocd-ssh-known-hosts-cm + - name: tls-certs + configMap: + name: argocd-tls-certs-cm + - 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 + - name: argocd-dex-server-tls + secret: + secretName: argocd-dex-server-tls + optional: true + items: + - key: tls.crt + path: tls.crt + - key: ca.crt + path: ca.crt affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchLabels: - app.kubernetes.io/name: argocd-server - topologyKey: kubernetes.io/hostname - - weight: 5 - podAffinityTerm: - labelSelector: - matchLabels: - app.kubernetes.io/part-of: argocd - topologyKey: kubernetes.io/hostname + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: argocd-server + topologyKey: kubernetes.io/hostname + - weight: 5 + podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/part-of: argocd + topologyKey: kubernetes.io/hostname diff --git a/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml b/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml index b550e36f9ad0b..259a48e7aee9e 100644 --- a/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml +++ b/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml @@ -35,8 +35,6 @@ rules: - appprojects verbs: - get - - list - - watch - apiGroups: - "" resources: diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml index b9d59aae424d0..3df03bdfa5f20 100644 --- a/manifests/core-install.yaml +++ b/manifests/core-install.yaml @@ -29,29 +29,20 @@ spec: name: Revision priority: 10 type: string - - jsonPath: .spec.project - name: Project - priority: 10 - type: string name: v1alpha1 schema: openAPIV3Schema: description: Application is a definition of Application resource. properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -149,21 +140,22 @@ spec: type: object type: array revision: - description: |- - Revision is the revision (Git) or chart version (Helm) which to sync the application to - If omitted, will use the revision specified in app spec. + description: Revision is the revision (Git) or chart version (Helm) + which to sync the application to If omitted, will use the revision + specified in app spec. type: string revisions: - description: |- - Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to - If omitted, will use the revision specified in app spec. + description: Revisions is the list of revision (Git) or chart + version (Helm) which to sync each source in sources field for + the application to If omitted, will use the revision specified + in app spec. items: type: string type: array source: - description: |- - Source overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Source overrides the source definition set in the + application. This is typically set in a Rollback operation and + is nil during a Sync operation properties: chart: description: Chart is a Helm chart name, and must be specified @@ -234,13 +226,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -262,16 +247,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest @@ -329,13 +304,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -375,15 +343,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -513,18 +472,18 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be + commit, tag, or branch. If omitted, will equal to HEAD. In case of Helm, this is a semver tag for the Chart's version. type: string required: - repoURL type: object sources: - description: |- - Sources overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Sources overrides the source definition set in the + application. This is typically set in a Rollback operation and + is nil during a Sync operation items: description: ApplicationSource contains all required information about the source of an application @@ -599,13 +558,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -627,16 +579,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -695,13 +637,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -743,15 +678,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -881,10 +807,11 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -903,10 +830,10 @@ spec: the sync. properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to supply + the --force flag to `kubectl apply`. The --force flag + deletes and re-create the resource, when PATCH encounters + conflict and has retried for 5 times. type: boolean type: object hook: @@ -914,10 +841,10 @@ spec: perform the sync. This is the default strategy properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to supply + the --force flag to `kubectl apply`. The --force flag + deletes and re-create the resource, when PATCH encounters + conflict and has retried for 5 times. type: boolean type: object type: object @@ -938,9 +865,9 @@ spec: not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -969,9 +896,10 @@ spec: 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 + 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 @@ -998,17 +926,18 @@ spec: type: object type: array project: - description: |- - Project is a reference to the project this application belongs to. - The empty string means that application belongs to the 'default' project. + description: Project is a reference to the project this application + belongs to. The empty string means that application belongs to the + 'default' project. type: string 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. - This should only be changed in exceptional circumstances. - Setting to zero will store no history. This will reduce storage used. - Increasing will increase the space used to store the history, so we do not recommend increasing it. - Default is 10. + 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. This should + only be changed in exceptional circumstances. Setting to zero will + store no history. This will reduce storage used. Increasing will + increase the space used to store the history, so we do not recommend + increasing it. Default is 10. format: int64 type: integer source: @@ -1083,13 +1012,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1111,15 +1033,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest generation @@ -1176,13 +1089,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1221,15 +1127,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -1355,10 +1252,10 @@ spec: that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be commit, + tag, or branch. If omitted, will equal to HEAD. In case of Helm, + this is a semver tag for the Chart's version. type: string required: - repoURL @@ -1438,13 +1335,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1466,16 +1356,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest @@ -1533,13 +1413,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1579,15 +1452,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -1716,10 +1580,10 @@ spec: that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be commit, + tag, or branch. If omitted, will equal to HEAD. In case of + Helm, this is a semver tag for the Chart's version. type: string required: - repoURL @@ -1862,19 +1726,6 @@ 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 @@ -1959,13 +1810,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1987,16 +1831,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -2055,13 +1889,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2103,15 +1930,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -2241,10 +2059,11 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -2326,13 +2145,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -2355,16 +2167,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -2425,13 +2227,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2473,16 +2268,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -2615,10 +2400,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -2630,9 +2416,9 @@ spec: type: object type: array observedAt: - description: |- - ObservedAt indicates when the application state was updated without querying latest git state - Deprecated: controller no longer updates ObservedAt field + description: 'ObservedAt indicates when the application state was + updated without querying latest git state Deprecated: controller + no longer updates ObservedAt field' format: date-time type: string operationState: @@ -2745,21 +2531,22 @@ spec: type: object type: array revision: - description: |- - Revision is the revision (Git) or chart version (Helm) which to sync the application to - If omitted, will use the revision specified in app spec. + description: Revision is the revision (Git) or chart version + (Helm) which to sync the application to If omitted, + will use the revision specified in app spec. type: string revisions: - description: |- - Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to - If omitted, will use the revision specified in app spec. + description: Revisions is the list of revision (Git) or + chart version (Helm) which to sync each source in sources + field for the application to If omitted, will use the + revision specified in app spec. items: type: string type: array source: - description: |- - Source overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Source overrides the source definition set + in the application. This is typically set in a Rollback + operation and is nil during a Sync operation properties: chart: description: Chart is a Helm chart name, and must @@ -2836,13 +2623,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -2867,16 +2647,6 @@ spec: not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to - the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -2939,13 +2709,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2988,16 +2751,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -3131,18 +2884,19 @@ spec: (Git or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL type: object sources: - description: |- - Sources overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Sources overrides the source definition set + in the application. This is typically set in a Rollback + operation and is nil during a Sync operation items: description: ApplicationSource contains all required information about the source of an application @@ -3222,13 +2976,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -3253,16 +3000,6 @@ spec: do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults - to the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -3327,13 +3064,6 @@ spec: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3377,16 +3107,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -3522,10 +3242,11 @@ spec: (Git or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision + of the source to sync the application to. In case + of Git, this can be commit, tag, or branch. If + omitted, will equal to HEAD. In case of Helm, + this is a semver tag for the Chart's version. type: string required: - repoURL @@ -3546,10 +3267,11 @@ spec: to perform the sync. properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to + supply the --force flag to `kubectl apply`. + The --force flag deletes and re-create the resource, + when PATCH encounters conflict and has retried + for 5 times. type: boolean type: object hook: @@ -3557,10 +3279,11 @@ spec: to perform the sync. This is the default strategy properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to + supply the --force flag to `kubectl apply`. + The --force flag deletes and re-create the resource, + when PATCH encounters conflict and has retried + for 5 times. type: boolean type: object type: object @@ -3604,9 +3327,9 @@ spec: description: Group specifies the API group of the resource type: string hookPhase: - description: |- - HookPhase contains the state of any operation associated with this resource OR hook - This can also contain values for non-hook resources. + description: HookPhase contains the state of any operation + associated with this resource OR hook This can also + contain values for non-hook resources. type: string hookType: description: HookType specifies the type of the hook. @@ -3731,13 +3454,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -3760,16 +3476,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -3830,13 +3536,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3878,16 +3577,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4020,10 +3709,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -4108,13 +3798,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -4139,16 +3822,6 @@ spec: locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to the - app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -4211,13 +3884,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4260,16 +3926,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4403,10 +4059,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL @@ -4433,9 +4090,8 @@ spec: description: Resources is a list of Kubernetes resources managed by this application items: - description: |- - ResourceStatus holds the current sync and health status of a resource - TODO: describe members of this type + description: 'ResourceStatus holds the current sync and health status + of a resource TODO: describe members of this type' properties: group: type: string @@ -4518,9 +4174,10 @@ spec: if Server is not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -4549,9 +4206,10 @@ spec: 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 + 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 @@ -4637,13 +4295,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -4666,16 +4317,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -4736,13 +4377,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4784,16 +4418,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4926,10 +4550,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -5014,13 +4639,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -5045,16 +4663,6 @@ spec: locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to the - app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -5117,13 +4725,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5166,16 +4767,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -5309,10 +4900,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL @@ -5399,19 +4991,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -5543,10 +5132,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -5558,10 +5143,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -5593,10 +5174,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5619,10 +5196,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -5773,10 +5346,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -5788,10 +5357,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -5823,10 +5388,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5849,10 +5410,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6026,19 +5583,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -6165,10 +5719,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6180,10 +5730,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6215,10 +5761,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6241,10 +5783,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6395,10 +5933,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6410,10 +5944,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6445,10 +5975,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6471,10 +5997,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6788,10 +6310,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6803,10 +6321,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6838,10 +6352,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6864,10 +6374,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7018,10 +6524,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7033,10 +6535,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7068,10 +6566,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7094,10 +6588,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7391,10 +6881,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7406,10 +6892,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7441,10 +6923,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7467,10 +6945,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7621,10 +7095,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7636,10 +7106,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7671,10 +7137,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7697,10 +7159,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7875,19 +7333,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -8019,10 +7474,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8034,10 +7485,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8069,10 +7516,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8095,10 +7538,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8249,10 +7688,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8264,10 +7699,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8299,10 +7730,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8325,10 +7752,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8502,19 +7925,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -8641,10 +8061,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8656,10 +8072,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8691,10 +8103,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8717,10 +8125,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8871,10 +8275,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8886,10 +8286,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8921,10 +8317,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8947,10 +8339,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9264,10 +8652,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9279,10 +8663,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9314,10 +8694,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9340,10 +8716,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9494,10 +8866,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9509,10 +8877,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9544,10 +8908,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9570,10 +8930,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9867,10 +9223,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9882,10 +9234,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9917,10 +9265,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9943,10 +9287,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10097,10 +9437,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10112,10 +9448,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10147,10 +9479,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10173,10 +9501,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10478,10 +9802,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10493,10 +9813,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10528,10 +9844,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10554,10 +9866,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10708,10 +10016,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10723,10 +10027,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10758,10 +10058,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10784,10 +10080,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11044,33 +10336,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -11146,16 +10411,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -11308,10 +10563,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -11323,10 +10574,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -11358,10 +10605,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -11384,10 +10627,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11538,10 +10777,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -11553,10 +10788,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -11588,10 +10819,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -11614,10 +10841,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11864,33 +11087,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -11971,16 +11167,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -12133,10 +11319,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12148,10 +11330,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12183,10 +11361,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12209,10 +11383,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12363,10 +11533,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12378,10 +11544,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12413,10 +11575,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12439,10 +11597,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12612,19 +11766,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array template: @@ -12753,10 +11904,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12768,10 +11915,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12803,10 +11946,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12829,10 +11968,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12983,10 +12118,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12998,10 +12129,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13033,10 +12160,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13059,10 +12182,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13239,19 +12358,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -13383,10 +12499,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -13398,10 +12510,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13433,10 +12541,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13459,10 +12563,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13613,10 +12713,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -13628,10 +12724,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13663,10 +12755,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13689,10 +12777,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13866,19 +12950,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -14005,10 +13086,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14020,10 +13097,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14055,10 +13128,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14081,10 +13150,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14235,10 +13300,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14250,10 +13311,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14285,10 +13342,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14311,10 +13364,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14628,10 +13677,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14643,10 +13688,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14678,10 +13719,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14704,10 +13741,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14858,10 +13891,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14873,10 +13902,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14908,10 +13933,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14934,10 +13955,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15231,10 +14248,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15246,10 +14259,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15281,10 +14290,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15307,10 +14312,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15461,10 +14462,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15476,10 +14473,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15511,10 +14504,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15537,10 +14526,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15842,10 +14827,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15857,10 +14838,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15892,10 +14869,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15918,10 +14891,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -16072,10 +15041,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -16087,10 +15052,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -16122,10 +15083,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -16148,10 +15105,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -16408,33 +15361,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -16510,16 +15436,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -16672,10 +15588,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -16687,10 +15599,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -16722,10 +15630,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -16748,10 +15652,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -16902,10 +15802,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -16917,10 +15813,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -16952,10 +15844,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -16978,10 +15866,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -17228,33 +16112,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -17335,16 +16192,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -17497,10 +16344,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -17512,10 +16355,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -17547,10 +16386,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -17573,10 +16408,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -17727,10 +16558,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -17742,10 +16569,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -17777,10 +16600,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -17803,10 +16622,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -17976,19 +16791,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array mergeKeys: @@ -18121,10 +16933,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18136,10 +16944,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -18171,10 +16975,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -18197,10 +16997,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -18351,10 +17147,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18366,10 +17158,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -18401,10 +17189,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -18427,10 +17211,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -18731,10 +17511,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18746,10 +17522,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -18781,10 +17553,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -18807,10 +17575,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -18961,10 +17725,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18976,10 +17736,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -19011,10 +17767,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -19037,10 +17789,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -19297,33 +18045,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -19399,16 +18120,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -19561,10 +18272,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -19576,10 +18283,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -19611,10 +18314,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -19637,10 +18336,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -19791,10 +18486,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -19806,10 +18497,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -19841,10 +18528,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -19867,10 +18550,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -20117,33 +18796,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -20224,16 +18876,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -20386,10 +19028,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -20401,10 +19039,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -20436,10 +19070,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -20462,10 +19092,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -20616,10 +19242,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -20631,10 +19253,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -20666,10 +19284,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -20692,10 +19306,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -20865,19 +19475,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array goTemplate: @@ -21081,10 +19688,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -21096,10 +19699,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -21131,10 +19730,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -21157,10 +19752,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -21311,10 +19902,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -21326,10 +19913,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -21361,10 +19944,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -21387,10 +19966,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -21564,16 +20139,11 @@ spec: type: string step: type: string - targetRevisions: - items: - type: string - type: array required: - application - message - status - step - - targetRevisions type: object type: array conditions: @@ -21597,37 +20167,6 @@ spec: - type type: object type: array - resources: - items: - properties: - group: - type: string - health: - properties: - message: - type: string - status: - type: string - type: object - hook: - type: boolean - kind: - type: string - name: - type: string - namespace: - type: string - requiresPruning: - type: boolean - status: - type: string - syncWave: - format: int64 - type: integer - version: - type: string - type: object - type: array type: object required: - metadata @@ -21660,28 +20199,22 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: |- - AppProject provides a logical grouping of applications, providing controls for: - * where the apps may deploy to (cluster whitelist) - * what may be deployed (repository whitelist, resource whitelist/blacklist) - * who can access these applications (roles, OIDC group claims bindings) - * and what they can do (RBAC policies) - * automation access to these roles (JWT tokens) + description: 'AppProject provides a logical grouping of applications, providing + controls for: * where the apps may deploy to (cluster whitelist) * what + may be deployed (repository whitelist, resource whitelist/blacklist) * who + can access these applications (roles, OIDC group claims bindings) * and + what they can do (RBAC policies) * automation access to these roles (JWT + tokens)' properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -21692,9 +20225,9 @@ spec: description: ClusterResourceBlacklist contains list of blacklisted cluster level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -21709,9 +20242,9 @@ spec: description: ClusterResourceWhitelist contains list of whitelisted cluster level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -21725,29 +20258,6 @@ spec: description: description: Description contains optional project description type: string - destinationServiceAccounts: - description: DestinationServiceAccounts holds information about the - service accounts to be impersonated for the application sync operation - for each destination. - items: - description: ApplicationDestinationServiceAccount holds information - about the service account to be impersonated for the application - sync operation. - properties: - defaultServiceAccount: - description: ServiceAccountName to be used for impersonation - during the sync operation - type: string - namespace: - description: Namespace specifies the target namespace for the - application's resources. - type: string - server: - description: Server specifies the URL of the target cluster's - Kubernetes control plane API. - type: string - type: object - type: array destinations: description: Destinations contains list of destinations available for deployment @@ -21761,9 +20271,9 @@ spec: not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -21776,9 +20286,9 @@ spec: description: NamespaceResourceBlacklist contains list of blacklisted namespace level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -21793,9 +20303,9 @@ spec: description: NamespaceResourceWhitelist contains list of whitelisted namespace level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -22104,8 +20614,6 @@ rules: - appprojects verbs: - get - - list - - watch - apiGroups: - argoproj.io resources: @@ -22552,13 +21060,7 @@ spec: key: applicationsetcontroller.enable.scm.providers name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.webhook.parallelism.limit - name: argocd-cmd-params-cm - optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: argocd-applicationset-controller ports: @@ -22676,7 +21178,7 @@ spec: - argocd - admin - redis-initial-password - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: IfNotPresent name: secret-init securityContext: @@ -22857,12 +21359,6 @@ spec: key: reposerver.plugin.tar.exclusions name: argocd-cmd-params-cm optional: true - - configMapKeyRef: - key: reposerver.plugin.use.manifest.generate.paths - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_REPO_SERVER_PLUGIN_USE_MANIFEST_GENERATE_PATHS - valueFrom: null - name: ARGOCD_REPO_SERVER_ALLOW_OUT_OF_BOUNDS_SYMLINKS valueFrom: configMapKeyRef: @@ -22893,12 +21389,6 @@ spec: 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: @@ -22917,25 +21407,13 @@ spec: key: reposerver.git.request.timeout name: argocd-cmd-params-cm optional: true - - name: ARGOCD_GRPC_MAX_SIZE_MB - valueFrom: - configMapKeyRef: - key: reposerver.grpc.max.size - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_REPO_SERVER_INCLUDE_HIDDEN_DIRECTORIES - valueFrom: - configMapKeyRef: - key: reposerver.include.hidden.directories - name: argocd-cmd-params-cm - optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME value: /helm-working-dir - name: HELM_DATA_HOME value: /helm-working-dir - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: failureThreshold: 3 @@ -22987,7 +21465,7 @@ spec: - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 name: copyutil securityContext: allowPrivilegeEscalation: false @@ -23259,7 +21737,7 @@ spec: key: controller.ignore.normalizer.jq.timeout name: argocd-cmd-params-cm optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: argocd-application-controller ports: @@ -23284,8 +21762,6 @@ spec: name: argocd-repo-server-tls - mountPath: /home/argocd name: argocd-home - - mountPath: /home/argocd/params - name: argocd-cmd-params-cm workingDir: /home/argocd serviceAccountName: argocd-application-controller volumes: @@ -23302,13 +21778,6 @@ spec: path: ca.crt optional: true secretName: argocd-repo-server-tls - - configMap: - items: - - key: controller.profile.enabled - path: profiler.enabled - name: argocd-cmd-params-cm - optional: true - name: argocd-cmd-params-cm --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy diff --git a/manifests/core-install/kustomization.yaml b/manifests/core-install/kustomization.yaml index 07a82b3707700..92fab7388ec26 100644 --- a/manifests/core-install/kustomization.yaml +++ b/manifests/core-install/kustomization.yaml @@ -12,4 +12,4 @@ resources: images: - name: quay.io/argoproj/argocd newName: quay.io/argoproj/argocd - newTag: latest + newTag: v2.10.16 diff --git a/manifests/crds/application-crd.yaml b/manifests/crds/application-crd.yaml index 5878e6eacd6a7..962543f80eb97 100644 --- a/manifests/crds/application-crd.yaml +++ b/manifests/crds/application-crd.yaml @@ -28,29 +28,20 @@ spec: name: Revision priority: 10 type: string - - jsonPath: .spec.project - name: Project - priority: 10 - type: string name: v1alpha1 schema: openAPIV3Schema: description: Application is a definition of Application resource. properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -148,21 +139,22 @@ spec: type: object type: array revision: - description: |- - Revision is the revision (Git) or chart version (Helm) which to sync the application to - If omitted, will use the revision specified in app spec. + description: Revision is the revision (Git) or chart version (Helm) + which to sync the application to If omitted, will use the revision + specified in app spec. type: string revisions: - description: |- - Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to - If omitted, will use the revision specified in app spec. + description: Revisions is the list of revision (Git) or chart + version (Helm) which to sync each source in sources field for + the application to If omitted, will use the revision specified + in app spec. items: type: string type: array source: - description: |- - Source overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Source overrides the source definition set in the + application. This is typically set in a Rollback operation and + is nil during a Sync operation properties: chart: description: Chart is a Helm chart name, and must be specified @@ -233,13 +225,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -261,16 +246,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest @@ -328,13 +303,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -374,15 +342,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -512,18 +471,18 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be + commit, tag, or branch. If omitted, will equal to HEAD. In case of Helm, this is a semver tag for the Chart's version. type: string required: - repoURL type: object sources: - description: |- - Sources overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Sources overrides the source definition set in the + application. This is typically set in a Rollback operation and + is nil during a Sync operation items: description: ApplicationSource contains all required information about the source of an application @@ -598,13 +557,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -626,16 +578,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -694,13 +636,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -742,15 +677,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -880,10 +806,11 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -902,10 +829,10 @@ spec: the sync. properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to supply + the --force flag to `kubectl apply`. The --force flag + deletes and re-create the resource, when PATCH encounters + conflict and has retried for 5 times. type: boolean type: object hook: @@ -913,10 +840,10 @@ spec: perform the sync. This is the default strategy properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to supply + the --force flag to `kubectl apply`. The --force flag + deletes and re-create the resource, when PATCH encounters + conflict and has retried for 5 times. type: boolean type: object type: object @@ -937,9 +864,9 @@ spec: not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -968,9 +895,10 @@ spec: 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 + 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 @@ -997,17 +925,18 @@ spec: type: object type: array project: - description: |- - Project is a reference to the project this application belongs to. - The empty string means that application belongs to the 'default' project. + description: Project is a reference to the project this application + belongs to. The empty string means that application belongs to the + 'default' project. type: string 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. - This should only be changed in exceptional circumstances. - Setting to zero will store no history. This will reduce storage used. - Increasing will increase the space used to store the history, so we do not recommend increasing it. - Default is 10. + 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. This should + only be changed in exceptional circumstances. Setting to zero will + store no history. This will reduce storage used. Increasing will + increase the space used to store the history, so we do not recommend + increasing it. Default is 10. format: int64 type: integer source: @@ -1082,13 +1011,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1110,15 +1032,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest generation @@ -1175,13 +1088,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1220,15 +1126,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -1354,10 +1251,10 @@ spec: that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be commit, + tag, or branch. If omitted, will equal to HEAD. In case of Helm, + this is a semver tag for the Chart's version. type: string required: - repoURL @@ -1437,13 +1334,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1465,16 +1355,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest @@ -1532,13 +1412,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1578,15 +1451,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -1715,10 +1579,10 @@ spec: that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be commit, + tag, or branch. If omitted, will equal to HEAD. In case of + Helm, this is a semver tag for the Chart's version. type: string required: - repoURL @@ -1861,19 +1725,6 @@ 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 @@ -1958,13 +1809,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1986,16 +1830,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -2054,13 +1888,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2102,15 +1929,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -2240,10 +2058,11 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -2325,13 +2144,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -2354,16 +2166,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -2424,13 +2226,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2472,16 +2267,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -2614,10 +2399,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -2629,9 +2415,9 @@ spec: type: object type: array observedAt: - description: |- - ObservedAt indicates when the application state was updated without querying latest git state - Deprecated: controller no longer updates ObservedAt field + description: 'ObservedAt indicates when the application state was + updated without querying latest git state Deprecated: controller + no longer updates ObservedAt field' format: date-time type: string operationState: @@ -2744,21 +2530,22 @@ spec: type: object type: array revision: - description: |- - Revision is the revision (Git) or chart version (Helm) which to sync the application to - If omitted, will use the revision specified in app spec. + description: Revision is the revision (Git) or chart version + (Helm) which to sync the application to If omitted, + will use the revision specified in app spec. type: string revisions: - description: |- - Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to - If omitted, will use the revision specified in app spec. + description: Revisions is the list of revision (Git) or + chart version (Helm) which to sync each source in sources + field for the application to If omitted, will use the + revision specified in app spec. items: type: string type: array source: - description: |- - Source overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Source overrides the source definition set + in the application. This is typically set in a Rollback + operation and is nil during a Sync operation properties: chart: description: Chart is a Helm chart name, and must @@ -2835,13 +2622,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -2866,16 +2646,6 @@ spec: not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to - the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -2938,13 +2708,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2987,16 +2750,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -3130,18 +2883,19 @@ spec: (Git or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL type: object sources: - description: |- - Sources overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Sources overrides the source definition set + in the application. This is typically set in a Rollback + operation and is nil during a Sync operation items: description: ApplicationSource contains all required information about the source of an application @@ -3221,13 +2975,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -3252,16 +2999,6 @@ spec: do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults - to the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -3326,13 +3063,6 @@ spec: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3376,16 +3106,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -3521,10 +3241,11 @@ spec: (Git or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision + of the source to sync the application to. In case + of Git, this can be commit, tag, or branch. If + omitted, will equal to HEAD. In case of Helm, + this is a semver tag for the Chart's version. type: string required: - repoURL @@ -3545,10 +3266,11 @@ spec: to perform the sync. properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to + supply the --force flag to `kubectl apply`. + The --force flag deletes and re-create the resource, + when PATCH encounters conflict and has retried + for 5 times. type: boolean type: object hook: @@ -3556,10 +3278,11 @@ spec: to perform the sync. This is the default strategy properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to + supply the --force flag to `kubectl apply`. + The --force flag deletes and re-create the resource, + when PATCH encounters conflict and has retried + for 5 times. type: boolean type: object type: object @@ -3603,9 +3326,9 @@ spec: description: Group specifies the API group of the resource type: string hookPhase: - description: |- - HookPhase contains the state of any operation associated with this resource OR hook - This can also contain values for non-hook resources. + description: HookPhase contains the state of any operation + associated with this resource OR hook This can also + contain values for non-hook resources. type: string hookType: description: HookType specifies the type of the hook. @@ -3730,13 +3453,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -3759,16 +3475,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -3829,13 +3535,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3877,16 +3576,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4019,10 +3708,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -4107,13 +3797,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -4138,16 +3821,6 @@ spec: locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to the - app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -4210,13 +3883,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4259,16 +3925,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4402,10 +4058,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL @@ -4432,9 +4089,8 @@ spec: description: Resources is a list of Kubernetes resources managed by this application items: - description: |- - ResourceStatus holds the current sync and health status of a resource - TODO: describe members of this type + description: 'ResourceStatus holds the current sync and health status + of a resource TODO: describe members of this type' properties: group: type: string @@ -4517,9 +4173,10 @@ spec: if Server is not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -4548,9 +4205,10 @@ spec: 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 + 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 @@ -4636,13 +4294,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -4665,16 +4316,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -4735,13 +4376,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4783,16 +4417,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4925,10 +4549,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -5013,13 +4638,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -5044,16 +4662,6 @@ spec: locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to the - app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -5116,13 +4724,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5165,16 +4766,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -5308,10 +4899,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL diff --git a/manifests/crds/applicationset-crd.yaml b/manifests/crds/applicationset-crd.yaml index ddfa9b27be056..35fba5d99be92 100644 --- a/manifests/crds/applicationset-crd.yaml +++ b/manifests/crds/applicationset-crd.yaml @@ -51,19 +51,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -195,10 +192,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -210,10 +203,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -245,10 +234,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -271,10 +256,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -425,10 +406,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -440,10 +417,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -475,10 +448,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -501,10 +470,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -678,19 +643,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -817,10 +779,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -832,10 +790,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -867,10 +821,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -893,10 +843,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -1047,10 +993,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -1062,10 +1004,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -1097,10 +1035,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1123,10 +1057,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -1440,10 +1370,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -1455,10 +1381,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -1490,10 +1412,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1516,10 +1434,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -1670,10 +1584,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -1685,10 +1595,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -1720,10 +1626,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1746,10 +1648,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -2043,10 +1941,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -2058,10 +1952,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -2093,10 +1983,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2119,10 +2005,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -2273,10 +2155,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -2288,10 +2166,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -2323,10 +2197,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2349,10 +2219,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -2527,19 +2393,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -2671,10 +2534,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -2686,10 +2545,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -2721,10 +2576,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2747,10 +2598,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -2901,10 +2748,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -2916,10 +2759,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -2951,10 +2790,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2977,10 +2812,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -3154,19 +2985,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -3293,10 +3121,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -3308,10 +3132,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -3343,10 +3163,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3369,10 +3185,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -3523,10 +3335,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -3538,10 +3346,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -3573,10 +3377,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3599,10 +3399,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -3916,10 +3712,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -3931,10 +3723,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -3966,10 +3754,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3992,10 +3776,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -4146,10 +3926,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -4161,10 +3937,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -4196,10 +3968,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4222,10 +3990,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -4519,10 +4283,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -4534,10 +4294,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -4569,10 +4325,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4595,10 +4347,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -4749,10 +4497,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -4764,10 +4508,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -4799,10 +4539,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4825,10 +4561,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -5130,10 +4862,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -5145,10 +4873,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -5180,10 +4904,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5206,10 +4926,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -5360,10 +5076,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -5375,10 +5087,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -5410,10 +5118,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5436,10 +5140,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -5696,33 +5396,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -5798,16 +5471,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -5960,10 +5623,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -5975,10 +5634,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6010,10 +5665,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6036,10 +5687,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6190,10 +5837,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6205,10 +5848,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6240,10 +5879,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6266,10 +5901,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6516,33 +6147,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -6623,16 +6227,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -6785,10 +6379,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6800,10 +6390,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6835,10 +6421,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6861,10 +6443,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7015,10 +6593,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7030,10 +6604,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7065,10 +6635,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7091,10 +6657,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7264,19 +6826,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array template: @@ -7405,10 +6964,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7420,10 +6975,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7455,10 +7006,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7481,10 +7028,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7635,10 +7178,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7650,10 +7189,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7685,10 +7220,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7711,10 +7242,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7891,19 +7418,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -8035,10 +7559,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8050,10 +7570,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8085,10 +7601,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8111,10 +7623,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8265,10 +7773,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8280,10 +7784,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8315,10 +7815,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8341,10 +7837,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8518,19 +8010,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -8657,10 +8146,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8672,10 +8157,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8707,10 +8188,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8733,10 +8210,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8887,10 +8360,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8902,10 +8371,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8937,10 +8402,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8963,10 +8424,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9280,10 +8737,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9295,10 +8748,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9330,10 +8779,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9356,10 +8801,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9510,10 +8951,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9525,10 +8962,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9560,10 +8993,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9586,10 +9015,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9883,10 +9308,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9898,10 +9319,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9933,10 +9350,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9959,10 +9372,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10113,10 +9522,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10128,10 +9533,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10163,10 +9564,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10189,10 +9586,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10494,10 +9887,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10509,10 +9898,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10544,10 +9929,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10570,10 +9951,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10724,10 +10101,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10739,10 +10112,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10774,10 +10143,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10800,10 +10165,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11060,33 +10421,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -11162,16 +10496,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -11324,10 +10648,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -11339,10 +10659,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -11374,10 +10690,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -11400,10 +10712,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11554,10 +10862,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -11569,10 +10873,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -11604,10 +10904,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -11630,10 +10926,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11880,33 +11172,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -11987,16 +11252,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -12149,10 +11404,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12164,10 +11415,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12199,10 +11446,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12225,10 +11468,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12379,10 +11618,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12394,10 +11629,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12429,10 +11660,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12455,10 +11682,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12628,19 +11851,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array mergeKeys: @@ -12773,10 +11993,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12788,10 +12004,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12823,10 +12035,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12849,10 +12057,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13003,10 +12207,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -13018,10 +12218,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13053,10 +12249,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13079,10 +12271,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13383,10 +12571,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -13398,10 +12582,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13433,10 +12613,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13459,10 +12635,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13613,10 +12785,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -13628,10 +12796,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13663,10 +12827,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13689,10 +12849,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13949,33 +13105,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -14051,16 +13180,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -14213,10 +13332,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14228,10 +13343,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14263,10 +13374,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14289,10 +13396,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14443,10 +13546,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14458,10 +13557,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14493,10 +13588,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14519,10 +13610,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14769,33 +13856,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -14876,16 +13936,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -15038,10 +14088,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15053,10 +14099,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15088,10 +14130,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15114,10 +14152,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15268,10 +14302,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15283,10 +14313,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15318,10 +14344,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15344,10 +14366,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15517,19 +14535,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array goTemplate: @@ -15733,10 +14748,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15748,10 +14759,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15783,10 +14790,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15809,10 +14812,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15963,10 +14962,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15978,10 +14973,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -16013,10 +15004,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -16039,10 +15026,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -16216,16 +15199,11 @@ spec: type: string step: type: string - targetRevisions: - items: - type: string - type: array required: - application - message - status - step - - targetRevisions type: object type: array conditions: @@ -16249,37 +15227,6 @@ spec: - type type: object type: array - resources: - items: - properties: - group: - type: string - health: - properties: - message: - type: string - status: - type: string - type: object - hook: - type: boolean - kind: - type: string - name: - type: string - namespace: - type: string - requiresPruning: - type: boolean - status: - type: string - syncWave: - format: int64 - type: integer - version: - type: string - type: object - type: array type: object required: - metadata diff --git a/manifests/crds/appproject-crd.yaml b/manifests/crds/appproject-crd.yaml index 07e98e13b5928..989b3004892f6 100644 --- a/manifests/crds/appproject-crd.yaml +++ b/manifests/crds/appproject-crd.yaml @@ -20,28 +20,22 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: |- - AppProject provides a logical grouping of applications, providing controls for: - * where the apps may deploy to (cluster whitelist) - * what may be deployed (repository whitelist, resource whitelist/blacklist) - * who can access these applications (roles, OIDC group claims bindings) - * and what they can do (RBAC policies) - * automation access to these roles (JWT tokens) + description: 'AppProject provides a logical grouping of applications, providing + controls for: * where the apps may deploy to (cluster whitelist) * what + may be deployed (repository whitelist, resource whitelist/blacklist) * who + can access these applications (roles, OIDC group claims bindings) * and + what they can do (RBAC policies) * automation access to these roles (JWT + tokens)' properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -52,9 +46,9 @@ spec: description: ClusterResourceBlacklist contains list of blacklisted cluster level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -69,9 +63,9 @@ spec: description: ClusterResourceWhitelist contains list of whitelisted cluster level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -85,29 +79,6 @@ spec: description: description: Description contains optional project description type: string - destinationServiceAccounts: - description: DestinationServiceAccounts holds information about the - service accounts to be impersonated for the application sync operation - for each destination. - items: - description: ApplicationDestinationServiceAccount holds information - about the service account to be impersonated for the application - sync operation. - properties: - defaultServiceAccount: - description: ServiceAccountName to be used for impersonation - during the sync operation - type: string - namespace: - description: Namespace specifies the target namespace for the - application's resources. - type: string - server: - description: Server specifies the URL of the target cluster's - Kubernetes control plane API. - type: string - type: object - type: array destinations: description: Destinations contains list of destinations available for deployment @@ -121,9 +92,9 @@ spec: not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -136,9 +107,9 @@ spec: description: NamespaceResourceBlacklist contains list of blacklisted namespace level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -153,9 +124,9 @@ spec: description: NamespaceResourceWhitelist contains list of whitelisted namespace level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string diff --git a/manifests/ha/base/controller-deployment/kustomization.yaml b/manifests/ha/base/controller-deployment/kustomization.yaml index e98bd250d699e..d6d20d99b4516 100644 --- a/manifests/ha/base/controller-deployment/kustomization.yaml +++ b/manifests/ha/base/controller-deployment/kustomization.yaml @@ -1,17 +1,20 @@ 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-application-controller-statefulset.yaml - path: argocd-cmd-params-cm.yaml + images: - name: quay.io/argoproj/argocd newName: quay.io/argoproj/argocd newTag: latest resources: +- ../../../base/application-controller - ../../../base/application-controller-deployment - ../../../base/applicationset-controller - ../../../base/dex diff --git a/manifests/ha/base/kustomization.yaml b/manifests/ha/base/kustomization.yaml index ae40b96e8657e..c295fe18ef611 100644 --- a/manifests/ha/base/kustomization.yaml +++ b/manifests/ha/base/kustomization.yaml @@ -12,7 +12,7 @@ patches: images: - name: quay.io/argoproj/argocd newName: quay.io/argoproj/argocd - newTag: latest + newTag: v2.10.16 resources: - ../../base/application-controller - ../../base/applicationset-controller diff --git a/manifests/ha/base/redis-ha/chart/upstream.yaml b/manifests/ha/base/redis-ha/chart/upstream.yaml index a9963b70cce1d..3af047e07c540 100644 --- a/manifests/ha/base/redis-ha/chart/upstream.yaml +++ b/manifests/ha/base/redis-ha/chart/upstream.yaml @@ -1091,7 +1091,7 @@ spec: topologyKey: kubernetes.io/hostname initContainers: - name: config-init - image: public.ecr.aws/docker/library/haproxy:2.6.17-alpine + image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent resources: {} @@ -1115,7 +1115,7 @@ spec: mountPath: /data containers: - name: haproxy - image: public.ecr.aws/docker/library/haproxy:2.6.17-alpine + image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent securityContext: allowPrivilegeEscalation: false @@ -1219,7 +1219,7 @@ spec: automountServiceAccountToken: false initContainers: - name: config-init - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent resources: {} @@ -1258,7 +1258,7 @@ spec: containers: - name: redis - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent command: - redis-server @@ -1321,7 +1321,7 @@ spec: - /bin/sh - /readonly-config/trigger-failover-if-master.sh - name: sentinel - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent command: - redis-sentinel @@ -1378,7 +1378,7 @@ spec: {} - name: split-brain-fix - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent command: - sh diff --git a/manifests/ha/base/redis-ha/chart/values.yaml b/manifests/ha/base/redis-ha/chart/values.yaml index fdf1846bcef5b..abe256292aa09 100644 --- a/manifests/ha/base/redis-ha/chart/values.yaml +++ b/manifests/ha/base/redis-ha/chart/values.yaml @@ -14,7 +14,8 @@ redis-ha: IPv6: enabled: false image: - tag: 2.6.17-alpine + repository: haproxy + tag: 2.6.14-alpine containerSecurityContext: null timeout: server: 6m @@ -23,6 +24,7 @@ redis-ha: metrics: enabled: true image: + repository: redis tag: 7.0.15-alpine containerSecurityContext: null sentinel: diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index 334b2befea9f1..bd4aa6ad52d79 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -29,29 +29,20 @@ spec: name: Revision priority: 10 type: string - - jsonPath: .spec.project - name: Project - priority: 10 - type: string name: v1alpha1 schema: openAPIV3Schema: description: Application is a definition of Application resource. properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -149,21 +140,22 @@ spec: type: object type: array revision: - description: |- - Revision is the revision (Git) or chart version (Helm) which to sync the application to - If omitted, will use the revision specified in app spec. + description: Revision is the revision (Git) or chart version (Helm) + which to sync the application to If omitted, will use the revision + specified in app spec. type: string revisions: - description: |- - Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to - If omitted, will use the revision specified in app spec. + description: Revisions is the list of revision (Git) or chart + version (Helm) which to sync each source in sources field for + the application to If omitted, will use the revision specified + in app spec. items: type: string type: array source: - description: |- - Source overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Source overrides the source definition set in the + application. This is typically set in a Rollback operation and + is nil during a Sync operation properties: chart: description: Chart is a Helm chart name, and must be specified @@ -234,13 +226,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -262,16 +247,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest @@ -329,13 +304,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -375,15 +343,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -513,18 +472,18 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be + commit, tag, or branch. If omitted, will equal to HEAD. In case of Helm, this is a semver tag for the Chart's version. type: string required: - repoURL type: object sources: - description: |- - Sources overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Sources overrides the source definition set in the + application. This is typically set in a Rollback operation and + is nil during a Sync operation items: description: ApplicationSource contains all required information about the source of an application @@ -599,13 +558,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -627,16 +579,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -695,13 +637,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -743,15 +678,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -881,10 +807,11 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -903,10 +830,10 @@ spec: the sync. properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to supply + the --force flag to `kubectl apply`. The --force flag + deletes and re-create the resource, when PATCH encounters + conflict and has retried for 5 times. type: boolean type: object hook: @@ -914,10 +841,10 @@ spec: perform the sync. This is the default strategy properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to supply + the --force flag to `kubectl apply`. The --force flag + deletes and re-create the resource, when PATCH encounters + conflict and has retried for 5 times. type: boolean type: object type: object @@ -938,9 +865,9 @@ spec: not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -969,9 +896,10 @@ spec: 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 + 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 @@ -998,17 +926,18 @@ spec: type: object type: array project: - description: |- - Project is a reference to the project this application belongs to. - The empty string means that application belongs to the 'default' project. + description: Project is a reference to the project this application + belongs to. The empty string means that application belongs to the + 'default' project. type: string 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. - This should only be changed in exceptional circumstances. - Setting to zero will store no history. This will reduce storage used. - Increasing will increase the space used to store the history, so we do not recommend increasing it. - Default is 10. + 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. This should + only be changed in exceptional circumstances. Setting to zero will + store no history. This will reduce storage used. Increasing will + increase the space used to store the history, so we do not recommend + increasing it. Default is 10. format: int64 type: integer source: @@ -1083,13 +1012,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1111,15 +1033,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest generation @@ -1176,13 +1089,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1221,15 +1127,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -1355,10 +1252,10 @@ spec: that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be commit, + tag, or branch. If omitted, will equal to HEAD. In case of Helm, + this is a semver tag for the Chart's version. type: string required: - repoURL @@ -1438,13 +1335,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1466,16 +1356,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest @@ -1533,13 +1413,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1579,15 +1452,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -1716,10 +1580,10 @@ spec: that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be commit, + tag, or branch. If omitted, will equal to HEAD. In case of + Helm, this is a semver tag for the Chart's version. type: string required: - repoURL @@ -1862,19 +1726,6 @@ 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 @@ -1959,13 +1810,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1987,16 +1831,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -2055,13 +1889,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2103,15 +1930,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -2241,10 +2059,11 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -2326,13 +2145,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -2355,16 +2167,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -2425,13 +2227,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2473,16 +2268,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -2615,10 +2400,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -2630,9 +2416,9 @@ spec: type: object type: array observedAt: - description: |- - ObservedAt indicates when the application state was updated without querying latest git state - Deprecated: controller no longer updates ObservedAt field + description: 'ObservedAt indicates when the application state was + updated without querying latest git state Deprecated: controller + no longer updates ObservedAt field' format: date-time type: string operationState: @@ -2745,21 +2531,22 @@ spec: type: object type: array revision: - description: |- - Revision is the revision (Git) or chart version (Helm) which to sync the application to - If omitted, will use the revision specified in app spec. + description: Revision is the revision (Git) or chart version + (Helm) which to sync the application to If omitted, + will use the revision specified in app spec. type: string revisions: - description: |- - Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to - If omitted, will use the revision specified in app spec. + description: Revisions is the list of revision (Git) or + chart version (Helm) which to sync each source in sources + field for the application to If omitted, will use the + revision specified in app spec. items: type: string type: array source: - description: |- - Source overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Source overrides the source definition set + in the application. This is typically set in a Rollback + operation and is nil during a Sync operation properties: chart: description: Chart is a Helm chart name, and must @@ -2836,13 +2623,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -2867,16 +2647,6 @@ spec: not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to - the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -2939,13 +2709,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2988,16 +2751,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -3131,18 +2884,19 @@ spec: (Git or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL type: object sources: - description: |- - Sources overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Sources overrides the source definition set + in the application. This is typically set in a Rollback + operation and is nil during a Sync operation items: description: ApplicationSource contains all required information about the source of an application @@ -3222,13 +2976,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -3253,16 +3000,6 @@ spec: do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults - to the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -3327,13 +3064,6 @@ spec: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3377,16 +3107,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -3522,10 +3242,11 @@ spec: (Git or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision + of the source to sync the application to. In case + of Git, this can be commit, tag, or branch. If + omitted, will equal to HEAD. In case of Helm, + this is a semver tag for the Chart's version. type: string required: - repoURL @@ -3546,10 +3267,11 @@ spec: to perform the sync. properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to + supply the --force flag to `kubectl apply`. + The --force flag deletes and re-create the resource, + when PATCH encounters conflict and has retried + for 5 times. type: boolean type: object hook: @@ -3557,10 +3279,11 @@ spec: to perform the sync. This is the default strategy properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to + supply the --force flag to `kubectl apply`. + The --force flag deletes and re-create the resource, + when PATCH encounters conflict and has retried + for 5 times. type: boolean type: object type: object @@ -3604,9 +3327,9 @@ spec: description: Group specifies the API group of the resource type: string hookPhase: - description: |- - HookPhase contains the state of any operation associated with this resource OR hook - This can also contain values for non-hook resources. + description: HookPhase contains the state of any operation + associated with this resource OR hook This can also + contain values for non-hook resources. type: string hookType: description: HookType specifies the type of the hook. @@ -3731,13 +3454,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -3760,16 +3476,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -3830,13 +3536,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3878,16 +3577,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4020,10 +3709,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -4108,13 +3798,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -4139,16 +3822,6 @@ spec: locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to the - app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -4211,13 +3884,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4260,16 +3926,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4403,10 +4059,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL @@ -4433,9 +4090,8 @@ spec: description: Resources is a list of Kubernetes resources managed by this application items: - description: |- - ResourceStatus holds the current sync and health status of a resource - TODO: describe members of this type + description: 'ResourceStatus holds the current sync and health status + of a resource TODO: describe members of this type' properties: group: type: string @@ -4518,9 +4174,10 @@ spec: if Server is not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -4549,9 +4206,10 @@ spec: 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 + 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 @@ -4637,13 +4295,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -4666,16 +4317,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -4736,13 +4377,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4784,16 +4418,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4926,10 +4550,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -5014,13 +4639,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -5045,16 +4663,6 @@ spec: locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to the - app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -5117,13 +4725,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5166,16 +4767,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -5309,10 +4900,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL @@ -5399,19 +4991,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -5543,10 +5132,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -5558,10 +5143,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -5593,10 +5174,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5619,10 +5196,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -5773,10 +5346,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -5788,10 +5357,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -5823,10 +5388,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5849,10 +5410,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6026,19 +5583,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -6165,10 +5719,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6180,10 +5730,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6215,10 +5761,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6241,10 +5783,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6395,10 +5933,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6410,10 +5944,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6445,10 +5975,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6471,10 +5997,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6788,10 +6310,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6803,10 +6321,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6838,10 +6352,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6864,10 +6374,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7018,10 +6524,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7033,10 +6535,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7068,10 +6566,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7094,10 +6588,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7391,10 +6881,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7406,10 +6892,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7441,10 +6923,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7467,10 +6945,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7621,10 +7095,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7636,10 +7106,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7671,10 +7137,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7697,10 +7159,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7875,19 +7333,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -8019,10 +7474,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8034,10 +7485,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8069,10 +7516,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8095,10 +7538,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8249,10 +7688,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8264,10 +7699,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8299,10 +7730,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8325,10 +7752,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8502,19 +7925,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -8641,10 +8061,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8656,10 +8072,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8691,10 +8103,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8717,10 +8125,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8871,10 +8275,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8886,10 +8286,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8921,10 +8317,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8947,10 +8339,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9264,10 +8652,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9279,10 +8663,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9314,10 +8694,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9340,10 +8716,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9494,10 +8866,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9509,10 +8877,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9544,10 +8908,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9570,10 +8930,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9867,10 +9223,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9882,10 +9234,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9917,10 +9265,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9943,10 +9287,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10097,10 +9437,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10112,10 +9448,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10147,10 +9479,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10173,10 +9501,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10478,10 +9802,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10493,10 +9813,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10528,10 +9844,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10554,10 +9866,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10708,10 +10016,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10723,10 +10027,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10758,10 +10058,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10784,10 +10080,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11044,33 +10336,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -11146,16 +10411,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -11308,10 +10563,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -11323,10 +10574,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -11358,10 +10605,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -11384,10 +10627,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11538,10 +10777,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -11553,10 +10788,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -11588,10 +10819,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -11614,10 +10841,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11864,33 +11087,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -11971,16 +11167,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -12133,10 +11319,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12148,10 +11330,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12183,10 +11361,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12209,10 +11383,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12363,10 +11533,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12378,10 +11544,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12413,10 +11575,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12439,10 +11597,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12612,19 +11766,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array template: @@ -12753,10 +11904,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12768,10 +11915,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12803,10 +11946,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12829,10 +11968,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12983,10 +12118,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12998,10 +12129,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13033,10 +12160,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13059,10 +12182,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13239,19 +12358,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -13383,10 +12499,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -13398,10 +12510,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13433,10 +12541,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13459,10 +12563,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13613,10 +12713,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -13628,10 +12724,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13663,10 +12755,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13689,10 +12777,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13866,19 +12950,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -14005,10 +13086,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14020,10 +13097,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14055,10 +13128,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14081,10 +13150,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14235,10 +13300,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14250,10 +13311,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14285,10 +13342,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14311,10 +13364,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14628,10 +13677,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14643,10 +13688,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14678,10 +13719,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14704,10 +13741,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14858,10 +13891,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14873,10 +13902,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14908,10 +13933,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14934,10 +13955,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15231,10 +14248,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15246,10 +14259,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15281,10 +14290,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15307,10 +14312,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15461,10 +14462,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15476,10 +14473,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15511,10 +14504,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15537,10 +14526,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15842,10 +14827,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15857,10 +14838,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15892,10 +14869,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15918,10 +14891,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -16072,10 +15041,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -16087,10 +15052,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -16122,10 +15083,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -16148,10 +15105,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -16408,33 +15361,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -16510,16 +15436,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -16672,10 +15588,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -16687,10 +15599,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -16722,10 +15630,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -16748,10 +15652,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -16902,10 +15802,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -16917,10 +15813,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -16952,10 +15844,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -16978,10 +15866,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -17228,33 +16112,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -17335,16 +16192,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -17497,10 +16344,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -17512,10 +16355,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -17547,10 +16386,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -17573,10 +16408,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -17727,10 +16558,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -17742,10 +16569,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -17777,10 +16600,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -17803,10 +16622,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -17976,19 +16791,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array mergeKeys: @@ -18121,10 +16933,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18136,10 +16944,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -18171,10 +16975,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -18197,10 +16997,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -18351,10 +17147,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18366,10 +17158,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -18401,10 +17189,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -18427,10 +17211,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -18731,10 +17511,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18746,10 +17522,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -18781,10 +17553,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -18807,10 +17575,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -18961,10 +17725,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18976,10 +17736,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -19011,10 +17767,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -19037,10 +17789,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -19297,33 +18045,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -19399,16 +18120,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -19561,10 +18272,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -19576,10 +18283,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -19611,10 +18314,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -19637,10 +18336,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -19791,10 +18486,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -19806,10 +18497,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -19841,10 +18528,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -19867,10 +18550,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -20117,33 +18796,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -20224,16 +18876,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -20386,10 +19028,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -20401,10 +19039,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -20436,10 +19070,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -20462,10 +19092,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -20616,10 +19242,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -20631,10 +19253,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -20666,10 +19284,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -20692,10 +19306,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -20865,19 +19475,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array goTemplate: @@ -21081,10 +19688,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -21096,10 +19699,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -21131,10 +19730,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -21157,10 +19752,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -21311,10 +19902,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -21326,10 +19913,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -21361,10 +19944,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -21387,10 +19966,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -21564,16 +20139,11 @@ spec: type: string step: type: string - targetRevisions: - items: - type: string - type: array required: - application - message - status - step - - targetRevisions type: object type: array conditions: @@ -21597,37 +20167,6 @@ spec: - type type: object type: array - resources: - items: - properties: - group: - type: string - health: - properties: - message: - type: string - status: - type: string - type: object - hook: - type: boolean - kind: - type: string - name: - type: string - namespace: - type: string - requiresPruning: - type: boolean - status: - type: string - syncWave: - format: int64 - type: integer - version: - type: string - type: object - type: array type: object required: - metadata @@ -21660,28 +20199,22 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: |- - AppProject provides a logical grouping of applications, providing controls for: - * where the apps may deploy to (cluster whitelist) - * what may be deployed (repository whitelist, resource whitelist/blacklist) - * who can access these applications (roles, OIDC group claims bindings) - * and what they can do (RBAC policies) - * automation access to these roles (JWT tokens) + description: 'AppProject provides a logical grouping of applications, providing + controls for: * where the apps may deploy to (cluster whitelist) * what + may be deployed (repository whitelist, resource whitelist/blacklist) * who + can access these applications (roles, OIDC group claims bindings) * and + what they can do (RBAC policies) * automation access to these roles (JWT + tokens)' properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -21692,9 +20225,9 @@ spec: description: ClusterResourceBlacklist contains list of blacklisted cluster level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -21709,9 +20242,9 @@ spec: description: ClusterResourceWhitelist contains list of whitelisted cluster level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -21725,29 +20258,6 @@ spec: description: description: Description contains optional project description type: string - destinationServiceAccounts: - description: DestinationServiceAccounts holds information about the - service accounts to be impersonated for the application sync operation - for each destination. - items: - description: ApplicationDestinationServiceAccount holds information - about the service account to be impersonated for the application - sync operation. - properties: - defaultServiceAccount: - description: ServiceAccountName to be used for impersonation - during the sync operation - type: string - namespace: - description: Namespace specifies the target namespace for the - application's resources. - type: string - server: - description: Server specifies the URL of the target cluster's - Kubernetes control plane API. - type: string - type: object - type: array destinations: description: Destinations contains list of destinations available for deployment @@ -21761,9 +20271,9 @@ spec: not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -21776,9 +20286,9 @@ spec: description: NamespaceResourceBlacklist contains list of blacklisted namespace level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -21793,9 +20303,9 @@ spec: description: NamespaceResourceWhitelist contains list of whitelisted namespace level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -22142,8 +20652,6 @@ rules: - appprojects verbs: - get - - list - - watch - apiGroups: - argoproj.io resources: @@ -22392,8 +20900,6 @@ rules: - appprojects verbs: - get - - list - - watch - apiGroups: - "" resources: @@ -23895,13 +22401,7 @@ spec: key: applicationsetcontroller.enable.scm.providers name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.webhook.parallelism.limit - name: argocd-cmd-params-cm - optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: argocd-applicationset-controller ports: @@ -23989,25 +22489,13 @@ spec: - /shared/argocd-dex - rundex env: - - name: ARGOCD_DEX_SERVER_LOGFORMAT - valueFrom: - configMapKeyRef: - key: dexserver.log.format - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_DEX_SERVER_LOGLEVEL - valueFrom: - configMapKeyRef: - key: dexserver.log.level - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_DEX_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: key: dexserver.disable.tls name: argocd-cmd-params-cm optional: true - image: ghcr.io/dexidp/dex:v2.41.1 + image: ghcr.io/dexidp/dex:v2.37.0 imagePullPolicy: Always name: dex ports: @@ -24036,7 +22524,7 @@ spec: - -n - /usr/local/bin/argocd - /shared/argocd-dex - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: copyutil securityContext: @@ -24118,13 +22606,7 @@ spec: key: notificationscontroller.selfservice.enabled name: argocd-cmd-params-cm optional: true - - name: ARGOCD_NOTIFICATION_CONTROLLER_REPO_SERVER_PLAINTEXT - valueFrom: - configMapKeyRef: - key: notificationscontroller.repo.server.plaintext - name: argocd-cmd-params-cm - optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: tcpSocket: @@ -24204,7 +22686,7 @@ spec: secretKeyRef: key: auth name: argocd-redis - image: public.ecr.aws/docker/library/haproxy:2.6.17-alpine + image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent lifecycle: {} livenessProbe: @@ -24243,7 +22725,7 @@ spec: - argocd - admin - redis-initial-password - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: IfNotPresent name: secret-init securityContext: @@ -24259,7 +22741,7 @@ spec: - /readonly/haproxy_init.sh command: - sh - image: public.ecr.aws/docker/library/haproxy:2.6.17-alpine + image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent name: config-init securityContext: @@ -24452,11 +22934,6 @@ spec: key: reposerver.plugin.tar.exclusions name: argocd-cmd-params-cm optional: true - - configMapKeyRef: - key: reposerver.plugin.use.manifest.generate.paths - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_REPO_SERVER_PLUGIN_USE_MANIFEST_GENERATE_PATHS - name: ARGOCD_REPO_SERVER_ALLOW_OUT_OF_BOUNDS_SYMLINKS valueFrom: configMapKeyRef: @@ -24487,12 +22964,6 @@ spec: 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: @@ -24511,25 +22982,13 @@ spec: key: reposerver.git.request.timeout name: argocd-cmd-params-cm optional: true - - name: ARGOCD_GRPC_MAX_SIZE_MB - valueFrom: - configMapKeyRef: - key: reposerver.grpc.max.size - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_REPO_SERVER_INCLUDE_HIDDEN_DIRECTORIES - valueFrom: - configMapKeyRef: - key: reposerver.include.hidden.directories - name: argocd-cmd-params-cm - optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME value: /helm-working-dir - name: HELM_DATA_HOME value: /helm-working-dir - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: failureThreshold: 3 @@ -24581,7 +23040,7 @@ spec: - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 name: copyutil securityContext: allowPrivilegeEscalation: false @@ -24905,37 +23364,7 @@ spec: key: server.api.content.types name: argocd-cmd-params-cm optional: true - - name: ARGOCD_SERVER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - key: server.webhook.parallelism.limit - 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_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 + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: httpGet: @@ -24976,8 +23405,6 @@ spec: name: plugins-home - mountPath: /tmp name: tmp - - mountPath: /home/argocd/params - name: argocd-cmd-params-cm serviceAccountName: argocd-server volumes: - emptyDir: {} @@ -25010,13 +23437,6 @@ spec: path: ca.crt optional: true secretName: argocd-dex-server-tls - - configMap: - items: - - key: server.profile.enabled - path: profiler.enabled - name: argocd-cmd-params-cm - optional: true - name: argocd-cmd-params-cm --- apiVersion: apps/v1 kind: StatefulSet @@ -25243,7 +23663,7 @@ spec: key: controller.ignore.normalizer.jq.timeout name: argocd-cmd-params-cm optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: argocd-application-controller ports: @@ -25268,8 +23688,6 @@ spec: name: argocd-repo-server-tls - mountPath: /home/argocd name: argocd-home - - mountPath: /home/argocd/params - name: argocd-cmd-params-cm workingDir: /home/argocd serviceAccountName: argocd-application-controller volumes: @@ -25286,13 +23704,6 @@ spec: path: ca.crt optional: true secretName: argocd-repo-server-tls - - configMap: - items: - - key: controller.profile.enabled - path: profiler.enabled - name: argocd-cmd-params-cm - optional: true - name: argocd-cmd-params-cm --- apiVersion: apps/v1 kind: StatefulSet @@ -25335,7 +23746,7 @@ spec: secretKeyRef: key: auth name: argocd-redis - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -25395,7 +23806,7 @@ spec: secretKeyRef: key: auth name: argocd-redis - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent lifecycle: {} livenessProbe: @@ -25453,7 +23864,7 @@ spec: secretKeyRef: key: auth name: argocd-redis - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent name: split-brain-fix resources: {} @@ -25488,7 +23899,7 @@ spec: secretKeyRef: key: auth name: argocd-redis - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent name: config-init securityContext: diff --git a/manifests/ha/namespace-install.yaml b/manifests/ha/namespace-install.yaml index eb8efee763024..178279633c712 100644 --- a/manifests/ha/namespace-install.yaml +++ b/manifests/ha/namespace-install.yaml @@ -149,8 +149,6 @@ rules: - appprojects verbs: - get - - list - - watch - apiGroups: - argoproj.io resources: @@ -1688,13 +1686,7 @@ spec: key: applicationsetcontroller.enable.scm.providers name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.webhook.parallelism.limit - name: argocd-cmd-params-cm - optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: argocd-applicationset-controller ports: @@ -1782,25 +1774,13 @@ spec: - /shared/argocd-dex - rundex env: - - name: ARGOCD_DEX_SERVER_LOGFORMAT - valueFrom: - configMapKeyRef: - key: dexserver.log.format - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_DEX_SERVER_LOGLEVEL - valueFrom: - configMapKeyRef: - key: dexserver.log.level - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_DEX_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: key: dexserver.disable.tls name: argocd-cmd-params-cm optional: true - image: ghcr.io/dexidp/dex:v2.41.1 + image: ghcr.io/dexidp/dex:v2.37.0 imagePullPolicy: Always name: dex ports: @@ -1829,7 +1809,7 @@ spec: - -n - /usr/local/bin/argocd - /shared/argocd-dex - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: copyutil securityContext: @@ -1911,13 +1891,7 @@ spec: key: notificationscontroller.selfservice.enabled name: argocd-cmd-params-cm optional: true - - name: ARGOCD_NOTIFICATION_CONTROLLER_REPO_SERVER_PLAINTEXT - valueFrom: - configMapKeyRef: - key: notificationscontroller.repo.server.plaintext - name: argocd-cmd-params-cm - optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: tcpSocket: @@ -1997,7 +1971,7 @@ spec: secretKeyRef: key: auth name: argocd-redis - image: public.ecr.aws/docker/library/haproxy:2.6.17-alpine + image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent lifecycle: {} livenessProbe: @@ -2036,7 +2010,7 @@ spec: - argocd - admin - redis-initial-password - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: IfNotPresent name: secret-init securityContext: @@ -2052,7 +2026,7 @@ spec: - /readonly/haproxy_init.sh command: - sh - image: public.ecr.aws/docker/library/haproxy:2.6.17-alpine + image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent name: config-init securityContext: @@ -2245,11 +2219,6 @@ spec: key: reposerver.plugin.tar.exclusions name: argocd-cmd-params-cm optional: true - - configMapKeyRef: - key: reposerver.plugin.use.manifest.generate.paths - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_REPO_SERVER_PLUGIN_USE_MANIFEST_GENERATE_PATHS - name: ARGOCD_REPO_SERVER_ALLOW_OUT_OF_BOUNDS_SYMLINKS valueFrom: configMapKeyRef: @@ -2280,12 +2249,6 @@ spec: 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: @@ -2304,25 +2267,13 @@ spec: key: reposerver.git.request.timeout name: argocd-cmd-params-cm optional: true - - name: ARGOCD_GRPC_MAX_SIZE_MB - valueFrom: - configMapKeyRef: - key: reposerver.grpc.max.size - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_REPO_SERVER_INCLUDE_HIDDEN_DIRECTORIES - valueFrom: - configMapKeyRef: - key: reposerver.include.hidden.directories - name: argocd-cmd-params-cm - optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME value: /helm-working-dir - name: HELM_DATA_HOME value: /helm-working-dir - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: failureThreshold: 3 @@ -2374,7 +2325,7 @@ spec: - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 name: copyutil securityContext: allowPrivilegeEscalation: false @@ -2698,37 +2649,7 @@ spec: key: server.api.content.types name: argocd-cmd-params-cm optional: true - - name: ARGOCD_SERVER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - key: server.webhook.parallelism.limit - 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_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 + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: httpGet: @@ -2769,8 +2690,6 @@ spec: name: plugins-home - mountPath: /tmp name: tmp - - mountPath: /home/argocd/params - name: argocd-cmd-params-cm serviceAccountName: argocd-server volumes: - emptyDir: {} @@ -2803,13 +2722,6 @@ spec: path: ca.crt optional: true secretName: argocd-dex-server-tls - - configMap: - items: - - key: server.profile.enabled - path: profiler.enabled - name: argocd-cmd-params-cm - optional: true - name: argocd-cmd-params-cm --- apiVersion: apps/v1 kind: StatefulSet @@ -3036,7 +2948,7 @@ spec: key: controller.ignore.normalizer.jq.timeout name: argocd-cmd-params-cm optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: argocd-application-controller ports: @@ -3061,8 +2973,6 @@ spec: name: argocd-repo-server-tls - mountPath: /home/argocd name: argocd-home - - mountPath: /home/argocd/params - name: argocd-cmd-params-cm workingDir: /home/argocd serviceAccountName: argocd-application-controller volumes: @@ -3079,13 +2989,6 @@ spec: path: ca.crt optional: true secretName: argocd-repo-server-tls - - configMap: - items: - - key: controller.profile.enabled - path: profiler.enabled - name: argocd-cmd-params-cm - optional: true - name: argocd-cmd-params-cm --- apiVersion: apps/v1 kind: StatefulSet @@ -3128,7 +3031,7 @@ spec: secretKeyRef: key: auth name: argocd-redis - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -3188,7 +3091,7 @@ spec: secretKeyRef: key: auth name: argocd-redis - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent lifecycle: {} livenessProbe: @@ -3246,7 +3149,7 @@ spec: secretKeyRef: key: auth name: argocd-redis - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent name: split-brain-fix resources: {} @@ -3281,7 +3184,7 @@ spec: secretKeyRef: key: auth name: argocd-redis - image: public.ecr.aws/docker/library/redis:7.0.15-alpine + image: redis:7.0.15-alpine imagePullPolicy: IfNotPresent name: config-init securityContext: diff --git a/manifests/install.yaml b/manifests/install.yaml index 868793d2f8e52..f4b485e5eb492 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -29,29 +29,20 @@ spec: name: Revision priority: 10 type: string - - jsonPath: .spec.project - name: Project - priority: 10 - type: string name: v1alpha1 schema: openAPIV3Schema: description: Application is a definition of Application resource. properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -149,21 +140,22 @@ spec: type: object type: array revision: - description: |- - Revision is the revision (Git) or chart version (Helm) which to sync the application to - If omitted, will use the revision specified in app spec. + description: Revision is the revision (Git) or chart version (Helm) + which to sync the application to If omitted, will use the revision + specified in app spec. type: string revisions: - description: |- - Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to - If omitted, will use the revision specified in app spec. + description: Revisions is the list of revision (Git) or chart + version (Helm) which to sync each source in sources field for + the application to If omitted, will use the revision specified + in app spec. items: type: string type: array source: - description: |- - Source overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Source overrides the source definition set in the + application. This is typically set in a Rollback operation and + is nil during a Sync operation properties: chart: description: Chart is a Helm chart name, and must be specified @@ -234,13 +226,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -262,16 +247,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest @@ -329,13 +304,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -375,15 +343,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -513,18 +472,18 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be + commit, tag, or branch. If omitted, will equal to HEAD. In case of Helm, this is a semver tag for the Chart's version. type: string required: - repoURL type: object sources: - description: |- - Sources overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Sources overrides the source definition set in the + application. This is typically set in a Rollback operation and + is nil during a Sync operation items: description: ApplicationSource contains all required information about the source of an application @@ -599,13 +558,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -627,16 +579,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -695,13 +637,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -743,15 +678,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -881,10 +807,11 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -903,10 +830,10 @@ spec: the sync. properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to supply + the --force flag to `kubectl apply`. The --force flag + deletes and re-create the resource, when PATCH encounters + conflict and has retried for 5 times. type: boolean type: object hook: @@ -914,10 +841,10 @@ spec: perform the sync. This is the default strategy properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to supply + the --force flag to `kubectl apply`. The --force flag + deletes and re-create the resource, when PATCH encounters + conflict and has retried for 5 times. type: boolean type: object type: object @@ -938,9 +865,9 @@ spec: not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -969,9 +896,10 @@ spec: 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 + 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 @@ -998,17 +926,18 @@ spec: type: object type: array project: - description: |- - Project is a reference to the project this application belongs to. - The empty string means that application belongs to the 'default' project. + description: Project is a reference to the project this application + belongs to. The empty string means that application belongs to the + 'default' project. type: string 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. - This should only be changed in exceptional circumstances. - Setting to zero will store no history. This will reduce storage used. - Increasing will increase the space used to store the history, so we do not recommend increasing it. - Default is 10. + 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. This should + only be changed in exceptional circumstances. Setting to zero will + store no history. This will reduce storage used. Increasing will + increase the space used to store the history, so we do not recommend + increasing it. Default is 10. format: int64 type: integer source: @@ -1083,13 +1012,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1111,15 +1033,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest generation @@ -1176,13 +1089,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1221,15 +1127,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -1355,10 +1252,10 @@ spec: that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be commit, + tag, or branch. If omitted, will equal to HEAD. In case of Helm, + this is a semver tag for the Chart's version. type: string required: - repoURL @@ -1438,13 +1335,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1466,16 +1356,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest @@ -1533,13 +1413,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -1579,15 +1452,6 @@ spec: definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -1716,10 +1580,10 @@ spec: that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be commit, + tag, or branch. If omitted, will equal to HEAD. In case of + Helm, this is a semver tag for the Chart's version. type: string required: - repoURL @@ -1862,19 +1726,6 @@ 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 @@ -1959,13 +1810,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -1987,16 +1831,6 @@ spec: from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to template - with. If left empty, defaults to the app's destination - namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -2055,13 +1889,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2103,15 +1930,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -2241,10 +2059,11 @@ spec: Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -2326,13 +2145,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -2355,16 +2167,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -2425,13 +2227,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2473,16 +2268,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -2615,10 +2400,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -2630,9 +2416,9 @@ spec: type: object type: array observedAt: - description: |- - ObservedAt indicates when the application state was updated without querying latest git state - Deprecated: controller no longer updates ObservedAt field + description: 'ObservedAt indicates when the application state was + updated without querying latest git state Deprecated: controller + no longer updates ObservedAt field' format: date-time type: string operationState: @@ -2745,21 +2531,22 @@ spec: type: object type: array revision: - description: |- - Revision is the revision (Git) or chart version (Helm) which to sync the application to - If omitted, will use the revision specified in app spec. + description: Revision is the revision (Git) or chart version + (Helm) which to sync the application to If omitted, + will use the revision specified in app spec. type: string revisions: - description: |- - Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to - If omitted, will use the revision specified in app spec. + description: Revisions is the list of revision (Git) or + chart version (Helm) which to sync each source in sources + field for the application to If omitted, will use the + revision specified in app spec. items: type: string type: array source: - description: |- - Source overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Source overrides the source definition set + in the application. This is typically set in a Rollback + operation and is nil during a Sync operation properties: chart: description: Chart is a Helm chart name, and must @@ -2836,13 +2623,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -2867,16 +2647,6 @@ spec: not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to - the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -2939,13 +2709,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -2988,16 +2751,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -3131,18 +2884,19 @@ spec: (Git or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL type: object sources: - description: |- - Sources overrides the source definition set in the application. - This is typically set in a Rollback operation and is nil during a Sync operation + description: Sources overrides the source definition set + in the application. This is typically set in a Rollback + operation and is nil during a Sync operation items: description: ApplicationSource contains all required information about the source of an application @@ -3222,13 +2976,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -3253,16 +3000,6 @@ spec: do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults - to the app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -3327,13 +3064,6 @@ spec: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3377,16 +3107,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -3522,10 +3242,11 @@ spec: (Git or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision + of the source to sync the application to. In case + of Git, this can be commit, tag, or branch. If + omitted, will equal to HEAD. In case of Helm, + this is a semver tag for the Chart's version. type: string required: - repoURL @@ -3546,10 +3267,11 @@ spec: to perform the sync. properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to + supply the --force flag to `kubectl apply`. + The --force flag deletes and re-create the resource, + when PATCH encounters conflict and has retried + for 5 times. type: boolean type: object hook: @@ -3557,10 +3279,11 @@ spec: to perform the sync. This is the default strategy properties: force: - description: |- - Force indicates whether or not to supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, when PATCH encounters conflict and has - retried for 5 times. + description: Force indicates whether or not to + supply the --force flag to `kubectl apply`. + The --force flag deletes and re-create the resource, + when PATCH encounters conflict and has retried + for 5 times. type: boolean type: object type: object @@ -3604,9 +3327,9 @@ spec: description: Group specifies the API group of the resource type: string hookPhase: - description: |- - HookPhase contains the state of any operation associated with this resource OR hook - This can also contain values for non-hook resources. + description: HookPhase contains the state of any operation + associated with this resource OR hook This can also + contain values for non-hook resources. type: string hookType: description: HookType specifies the type of the hook. @@ -3731,13 +3454,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -3760,16 +3476,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -3830,13 +3536,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -3878,16 +3577,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4020,10 +3709,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -4108,13 +3798,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -4139,16 +3822,6 @@ spec: locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to the - app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -4211,13 +3884,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4260,16 +3926,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4403,10 +4059,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL @@ -4433,9 +4090,8 @@ spec: description: Resources is a list of Kubernetes resources managed by this application items: - description: |- - ResourceStatus holds the current sync and health status of a resource - TODO: describe members of this type + description: 'ResourceStatus holds the current sync and health status + of a resource TODO: describe members of this type' properties: group: type: string @@ -4518,9 +4174,10 @@ spec: if Server is not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -4549,9 +4206,10 @@ spec: 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 + 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 @@ -4637,13 +4295,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -4666,16 +4317,6 @@ spec: template from failing when valueFiles do not exist locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace to - template with. If left empty, defaults to the app's - destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -4736,13 +4377,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -4784,16 +4418,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -4926,10 +4550,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. type: string required: - repoURL @@ -5014,13 +4639,6 @@ spec: helm: description: Helm holds helm specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array fileParameters: description: FileParameters are file parameters to the helm template @@ -5045,16 +4663,6 @@ spec: locally by not appending them to helm template --values type: boolean - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - namespace: - description: Namespace is an optional namespace - to template with. If left empty, defaults to the - app's destination namespace. - type: string parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -5117,13 +4725,6 @@ spec: kustomize: description: Kustomize holds kustomize specific options properties: - apiVersions: - description: |- - APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5166,16 +4767,6 @@ spec: image definition in the format [old_image_name=]: type: string type: array - kubeVersion: - description: |- - KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - uses the Kubernetes version of the target cluster. - type: string - 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 @@ -5309,10 +4900,11 @@ spec: or Helm) that contains the application manifests type: string targetRevision: - description: |- - TargetRevision defines the revision of the source to sync the application to. - In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. type: string required: - repoURL @@ -5399,19 +4991,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -5543,10 +5132,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -5558,10 +5143,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -5593,10 +5174,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5619,10 +5196,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -5773,10 +5346,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -5788,10 +5357,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -5823,10 +5388,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -5849,10 +5410,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6026,19 +5583,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -6165,10 +5719,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6180,10 +5730,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6215,10 +5761,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6241,10 +5783,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6395,10 +5933,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6410,10 +5944,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6445,10 +5975,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6471,10 +5997,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -6788,10 +6310,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -6803,10 +6321,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -6838,10 +6352,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -6864,10 +6374,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7018,10 +6524,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7033,10 +6535,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7068,10 +6566,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7094,10 +6588,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7391,10 +6881,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7406,10 +6892,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7441,10 +6923,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7467,10 +6945,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7621,10 +7095,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -7636,10 +7106,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -7671,10 +7137,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -7697,10 +7159,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -7875,19 +7333,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -8019,10 +7474,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8034,10 +7485,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8069,10 +7516,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8095,10 +7538,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8249,10 +7688,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8264,10 +7699,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8299,10 +7730,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8325,10 +7752,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8502,19 +7925,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -8641,10 +8061,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8656,10 +8072,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8691,10 +8103,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8717,10 +8125,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -8871,10 +8275,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -8886,10 +8286,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -8921,10 +8317,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -8947,10 +8339,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9264,10 +8652,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9279,10 +8663,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9314,10 +8694,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9340,10 +8716,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9494,10 +8866,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9509,10 +8877,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9544,10 +8908,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9570,10 +8930,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -9867,10 +9223,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -9882,10 +9234,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -9917,10 +9265,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -9943,10 +9287,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10097,10 +9437,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10112,10 +9448,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10147,10 +9479,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10173,10 +9501,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10478,10 +9802,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10493,10 +9813,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10528,10 +9844,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10554,10 +9866,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -10708,10 +10016,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -10723,10 +10027,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -10758,10 +10058,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -10784,10 +10080,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11044,33 +10336,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -11146,16 +10411,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -11308,10 +10563,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -11323,10 +10574,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -11358,10 +10605,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -11384,10 +10627,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11538,10 +10777,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -11553,10 +10788,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -11588,10 +10819,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -11614,10 +10841,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -11864,33 +11087,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -11971,16 +11167,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -12133,10 +11319,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12148,10 +11330,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12183,10 +11361,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12209,10 +11383,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12363,10 +11533,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12378,10 +11544,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12413,10 +11575,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12439,10 +11597,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12612,19 +11766,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array template: @@ -12753,10 +11904,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12768,10 +11915,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -12803,10 +11946,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -12829,10 +11968,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -12983,10 +12118,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -12998,10 +12129,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13033,10 +12160,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13059,10 +12182,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13239,19 +12358,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic name: type: string requeueAfterSeconds: @@ -13383,10 +12499,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -13398,10 +12510,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13433,10 +12541,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13459,10 +12563,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13613,10 +12713,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -13628,10 +12724,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -13663,10 +12755,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -13689,10 +12777,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -13866,19 +12950,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic template: properties: metadata: @@ -14005,10 +13086,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14020,10 +13097,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14055,10 +13128,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14081,10 +13150,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14235,10 +13300,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14250,10 +13311,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14285,10 +13342,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14311,10 +13364,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14628,10 +13677,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14643,10 +13688,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14678,10 +13719,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14704,10 +13741,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -14858,10 +13891,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -14873,10 +13902,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -14908,10 +13933,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -14934,10 +13955,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15231,10 +14248,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15246,10 +14259,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15281,10 +14290,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15307,10 +14312,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15461,10 +14462,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15476,10 +14473,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15511,10 +14504,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15537,10 +14526,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -15842,10 +14827,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -15857,10 +14838,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -15892,10 +14869,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -15918,10 +14891,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -16072,10 +15041,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -16087,10 +15052,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -16122,10 +15083,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -16148,10 +15105,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -16408,33 +15361,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -16510,16 +15436,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -16672,10 +15588,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -16687,10 +15599,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -16722,10 +15630,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -16748,10 +15652,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -16902,10 +15802,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -16917,10 +15813,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -16952,10 +15844,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -16978,10 +15866,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -17228,33 +16112,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -17335,16 +16192,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -17497,10 +16344,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -17512,10 +16355,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -17547,10 +16386,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -17573,10 +16408,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -17727,10 +16558,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -17742,10 +16569,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -17777,10 +16600,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -17803,10 +16622,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -17976,19 +16791,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array mergeKeys: @@ -18121,10 +16933,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18136,10 +16944,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -18171,10 +16975,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -18197,10 +16997,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -18351,10 +17147,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18366,10 +17158,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -18401,10 +17189,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -18427,10 +17211,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -18731,10 +17511,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18746,10 +17522,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -18781,10 +17553,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -18807,10 +17575,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -18961,10 +17725,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -18976,10 +17736,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -19011,10 +17767,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -19037,10 +17789,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -19297,33 +18045,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string repo: @@ -19399,16 +18120,6 @@ spec: properties: api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object insecure: type: boolean labels: @@ -19561,10 +18272,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -19576,10 +18283,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -19611,10 +18314,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -19637,10 +18336,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -19791,10 +18486,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -19806,10 +18497,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -19841,10 +18528,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -19867,10 +18550,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -20117,33 +18796,6 @@ spec: - passwordRef - username type: object - bearerToken: - properties: - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - tokenRef - type: object - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object - insecure: - type: boolean project: type: string required: @@ -20224,16 +18876,6 @@ spec: type: boolean api: type: string - caRef: - properties: - configMapName: - type: string - key: - type: string - required: - - configMapName - - key - type: object group: type: string includeSharedProjects: @@ -20386,10 +19028,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -20401,10 +19039,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -20436,10 +19070,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -20462,10 +19092,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -20616,10 +19242,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -20631,10 +19253,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -20666,10 +19284,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -20692,10 +19306,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -20865,19 +19475,16 @@ spec: items: type: string type: array - x-kubernetes-list-type: atomic required: - key - operator type: object type: array - x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string type: object type: object - x-kubernetes-map-type: atomic type: object type: array goTemplate: @@ -21081,10 +19688,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -21096,10 +19699,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -21131,10 +19730,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -21157,10 +19752,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -21311,10 +19902,6 @@ spec: type: object helm: properties: - apiVersions: - items: - type: string - type: array fileParameters: items: properties: @@ -21326,10 +19913,6 @@ spec: type: array ignoreMissingValueFiles: type: boolean - kubeVersion: - type: string - namespace: - type: string parameters: items: properties: @@ -21361,10 +19944,6 @@ spec: type: object kustomize: properties: - apiVersions: - items: - type: string - type: array commonAnnotations: additionalProperties: type: string @@ -21387,10 +19966,6 @@ spec: items: type: string type: array - kubeVersion: - type: string - labelWithoutSelector: - type: boolean namePrefix: type: string nameSuffix: @@ -21564,16 +20139,11 @@ spec: type: string step: type: string - targetRevisions: - items: - type: string - type: array required: - application - message - status - step - - targetRevisions type: object type: array conditions: @@ -21597,37 +20167,6 @@ spec: - type type: object type: array - resources: - items: - properties: - group: - type: string - health: - properties: - message: - type: string - status: - type: string - type: object - hook: - type: boolean - kind: - type: string - name: - type: string - namespace: - type: string - requiresPruning: - type: boolean - status: - type: string - syncWave: - format: int64 - type: integer - version: - type: string - type: object - type: array type: object required: - metadata @@ -21660,28 +20199,22 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: |- - AppProject provides a logical grouping of applications, providing controls for: - * where the apps may deploy to (cluster whitelist) - * what may be deployed (repository whitelist, resource whitelist/blacklist) - * who can access these applications (roles, OIDC group claims bindings) - * and what they can do (RBAC policies) - * automation access to these roles (JWT tokens) + description: 'AppProject provides a logical grouping of applications, providing + controls for: * where the apps may deploy to (cluster whitelist) * what + may be deployed (repository whitelist, resource whitelist/blacklist) * who + can access these applications (roles, OIDC group claims bindings) * and + what they can do (RBAC policies) * automation access to these roles (JWT + tokens)' properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -21692,9 +20225,9 @@ spec: description: ClusterResourceBlacklist contains list of blacklisted cluster level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -21709,9 +20242,9 @@ spec: description: ClusterResourceWhitelist contains list of whitelisted cluster level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -21725,29 +20258,6 @@ spec: description: description: Description contains optional project description type: string - destinationServiceAccounts: - description: DestinationServiceAccounts holds information about the - service accounts to be impersonated for the application sync operation - for each destination. - items: - description: ApplicationDestinationServiceAccount holds information - about the service account to be impersonated for the application - sync operation. - properties: - defaultServiceAccount: - description: ServiceAccountName to be used for impersonation - during the sync operation - type: string - namespace: - description: Namespace specifies the target namespace for the - application's resources. - type: string - server: - description: Server specifies the URL of the target cluster's - Kubernetes control plane API. - type: string - type: object - type: array destinations: description: Destinations contains list of destinations available for deployment @@ -21761,9 +20271,9 @@ spec: not set. type: string namespace: - description: |- - 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 + description: 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 type: string server: description: Server specifies the URL of the target cluster's @@ -21776,9 +20286,9 @@ spec: description: NamespaceResourceBlacklist contains list of blacklisted namespace level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -21793,9 +20303,9 @@ spec: description: NamespaceResourceWhitelist contains list of whitelisted namespace level resources items: - description: |- - GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying - concepts during lookup stages without having partially valid types + description: GroupKind specifies a Group and a Kind, but does not + force a version. This is useful for identifying concepts during + lookup stages without having partially valid types properties: group: type: string @@ -22131,8 +20641,6 @@ rules: - appprojects verbs: - get - - list - - watch - apiGroups: - argoproj.io resources: @@ -22359,8 +20867,6 @@ rules: - appprojects verbs: - get - - list - - watch - apiGroups: - "" resources: @@ -23012,13 +21518,7 @@ spec: key: applicationsetcontroller.enable.scm.providers name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.webhook.parallelism.limit - name: argocd-cmd-params-cm - optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: argocd-applicationset-controller ports: @@ -23106,25 +21606,13 @@ spec: - /shared/argocd-dex - rundex env: - - name: ARGOCD_DEX_SERVER_LOGFORMAT - valueFrom: - configMapKeyRef: - key: dexserver.log.format - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_DEX_SERVER_LOGLEVEL - valueFrom: - configMapKeyRef: - key: dexserver.log.level - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_DEX_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: key: dexserver.disable.tls name: argocd-cmd-params-cm optional: true - image: ghcr.io/dexidp/dex:v2.41.1 + image: ghcr.io/dexidp/dex:v2.37.0 imagePullPolicy: Always name: dex ports: @@ -23153,7 +21641,7 @@ spec: - -n - /usr/local/bin/argocd - /shared/argocd-dex - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: copyutil securityContext: @@ -23235,13 +21723,7 @@ spec: key: notificationscontroller.selfservice.enabled name: argocd-cmd-params-cm optional: true - - name: ARGOCD_NOTIFICATION_CONTROLLER_REPO_SERVER_PLAINTEXT - valueFrom: - configMapKeyRef: - key: notificationscontroller.repo.server.plaintext - name: argocd-cmd-params-cm - optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: tcpSocket: @@ -23341,7 +21823,7 @@ spec: - argocd - admin - redis-initial-password - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: IfNotPresent name: secret-init securityContext: @@ -23522,12 +22004,6 @@ spec: key: reposerver.plugin.tar.exclusions name: argocd-cmd-params-cm optional: true - - configMapKeyRef: - key: reposerver.plugin.use.manifest.generate.paths - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_REPO_SERVER_PLUGIN_USE_MANIFEST_GENERATE_PATHS - valueFrom: null - name: ARGOCD_REPO_SERVER_ALLOW_OUT_OF_BOUNDS_SYMLINKS valueFrom: configMapKeyRef: @@ -23558,12 +22034,6 @@ spec: 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: @@ -23582,25 +22052,13 @@ spec: key: reposerver.git.request.timeout name: argocd-cmd-params-cm optional: true - - name: ARGOCD_GRPC_MAX_SIZE_MB - valueFrom: - configMapKeyRef: - key: reposerver.grpc.max.size - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_REPO_SERVER_INCLUDE_HIDDEN_DIRECTORIES - valueFrom: - configMapKeyRef: - key: reposerver.include.hidden.directories - name: argocd-cmd-params-cm - optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME value: /helm-working-dir - name: HELM_DATA_HOME value: /helm-working-dir - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: failureThreshold: 3 @@ -23652,7 +22110,7 @@ spec: - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 name: copyutil securityContext: allowPrivilegeEscalation: false @@ -23974,37 +22432,7 @@ spec: key: server.api.content.types name: argocd-cmd-params-cm optional: true - - name: ARGOCD_SERVER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - key: server.webhook.parallelism.limit - 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_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 + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: httpGet: @@ -24045,8 +22473,6 @@ spec: name: plugins-home - mountPath: /tmp name: tmp - - mountPath: /home/argocd/params - name: argocd-cmd-params-cm serviceAccountName: argocd-server volumes: - emptyDir: {} @@ -24079,13 +22505,6 @@ spec: path: ca.crt optional: true secretName: argocd-dex-server-tls - - configMap: - items: - - key: server.profile.enabled - path: profiler.enabled - name: argocd-cmd-params-cm - optional: true - name: argocd-cmd-params-cm --- apiVersion: apps/v1 kind: StatefulSet @@ -24312,7 +22731,7 @@ spec: key: controller.ignore.normalizer.jq.timeout name: argocd-cmd-params-cm optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: argocd-application-controller ports: @@ -24337,8 +22756,6 @@ spec: name: argocd-repo-server-tls - mountPath: /home/argocd name: argocd-home - - mountPath: /home/argocd/params - name: argocd-cmd-params-cm workingDir: /home/argocd serviceAccountName: argocd-application-controller volumes: @@ -24355,13 +22772,6 @@ spec: path: ca.crt optional: true secretName: argocd-repo-server-tls - - configMap: - items: - - key: controller.profile.enabled - path: profiler.enabled - name: argocd-cmd-params-cm - optional: true - name: argocd-cmd-params-cm --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 905bd717f05b9..d2d70407cbb09 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -138,8 +138,6 @@ rules: - appprojects verbs: - get - - list - - watch - apiGroups: - argoproj.io resources: @@ -805,13 +803,7 @@ spec: key: applicationsetcontroller.enable.scm.providers name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.webhook.parallelism.limit - name: argocd-cmd-params-cm - optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: argocd-applicationset-controller ports: @@ -899,25 +891,13 @@ spec: - /shared/argocd-dex - rundex env: - - name: ARGOCD_DEX_SERVER_LOGFORMAT - valueFrom: - configMapKeyRef: - key: dexserver.log.format - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_DEX_SERVER_LOGLEVEL - valueFrom: - configMapKeyRef: - key: dexserver.log.level - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_DEX_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: key: dexserver.disable.tls name: argocd-cmd-params-cm optional: true - image: ghcr.io/dexidp/dex:v2.41.1 + image: ghcr.io/dexidp/dex:v2.37.0 imagePullPolicy: Always name: dex ports: @@ -946,7 +926,7 @@ spec: - -n - /usr/local/bin/argocd - /shared/argocd-dex - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: copyutil securityContext: @@ -1028,13 +1008,7 @@ spec: key: notificationscontroller.selfservice.enabled name: argocd-cmd-params-cm optional: true - - name: ARGOCD_NOTIFICATION_CONTROLLER_REPO_SERVER_PLAINTEXT - valueFrom: - configMapKeyRef: - key: notificationscontroller.repo.server.plaintext - name: argocd-cmd-params-cm - optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: tcpSocket: @@ -1134,7 +1108,7 @@ spec: - argocd - admin - redis-initial-password - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: IfNotPresent name: secret-init securityContext: @@ -1315,12 +1289,6 @@ spec: key: reposerver.plugin.tar.exclusions name: argocd-cmd-params-cm optional: true - - configMapKeyRef: - key: reposerver.plugin.use.manifest.generate.paths - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_REPO_SERVER_PLUGIN_USE_MANIFEST_GENERATE_PATHS - valueFrom: null - name: ARGOCD_REPO_SERVER_ALLOW_OUT_OF_BOUNDS_SYMLINKS valueFrom: configMapKeyRef: @@ -1351,12 +1319,6 @@ spec: 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: @@ -1375,25 +1337,13 @@ spec: key: reposerver.git.request.timeout name: argocd-cmd-params-cm optional: true - - name: ARGOCD_GRPC_MAX_SIZE_MB - valueFrom: - configMapKeyRef: - key: reposerver.grpc.max.size - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_REPO_SERVER_INCLUDE_HIDDEN_DIRECTORIES - valueFrom: - configMapKeyRef: - key: reposerver.include.hidden.directories - name: argocd-cmd-params-cm - optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME value: /helm-working-dir - name: HELM_DATA_HOME value: /helm-working-dir - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: failureThreshold: 3 @@ -1445,7 +1395,7 @@ spec: - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 name: copyutil securityContext: allowPrivilegeEscalation: false @@ -1767,37 +1717,7 @@ spec: key: server.api.content.types name: argocd-cmd-params-cm optional: true - - name: ARGOCD_SERVER_WEBHOOK_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - key: server.webhook.parallelism.limit - 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_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 + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always livenessProbe: httpGet: @@ -1838,8 +1758,6 @@ spec: name: plugins-home - mountPath: /tmp name: tmp - - mountPath: /home/argocd/params - name: argocd-cmd-params-cm serviceAccountName: argocd-server volumes: - emptyDir: {} @@ -1872,13 +1790,6 @@ spec: path: ca.crt optional: true secretName: argocd-dex-server-tls - - configMap: - items: - - key: server.profile.enabled - path: profiler.enabled - name: argocd-cmd-params-cm - optional: true - name: argocd-cmd-params-cm --- apiVersion: apps/v1 kind: StatefulSet @@ -2105,7 +2016,7 @@ spec: key: controller.ignore.normalizer.jq.timeout name: argocd-cmd-params-cm optional: true - image: quay.io/argoproj/argocd:latest + image: quay.io/argoproj/argocd:v2.10.16 imagePullPolicy: Always name: argocd-application-controller ports: @@ -2130,8 +2041,6 @@ spec: name: argocd-repo-server-tls - mountPath: /home/argocd name: argocd-home - - mountPath: /home/argocd/params - name: argocd-cmd-params-cm workingDir: /home/argocd serviceAccountName: argocd-application-controller volumes: @@ -2148,13 +2057,6 @@ spec: path: ca.crt optional: true secretName: argocd-repo-server-tls - - configMap: - items: - - key: controller.profile.enabled - path: profiler.enabled - name: argocd-cmd-params-cm - optional: true - name: argocd-cmd-params-cm --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy diff --git a/mkdocs.yml b/mkdocs.yml index 1fea9734a8710..7da6ca1db0a41 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -47,7 +47,6 @@ nav: - snyk/index.md - operator-manual/signed-release-assets.md - operator-manual/tls.md - - operator-manual/cluster-management.md - operator-manual/cluster-bootstrapping.md - operator-manual/secret-management.md - operator-manual/disaster_recovery.md @@ -129,9 +128,6 @@ nav: - operator-manual/server-commands/additional-configuration-method.md - Upgrading: - operator-manual/upgrading/overview.md - - operator-manual/upgrading/2.12-2.13.md - - operator-manual/upgrading/2.11-2.12.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 @@ -211,7 +207,7 @@ nav: - developer-guide/dependencies.md - developer-guide/ci.md - developer-guide/releasing.md - - developer-guide/docs-site.md + - developer-guide/site.md - developer-guide/static-code-analysis.md - Extensions: - developer-guide/extensions/ui-extensions.md diff --git a/notification_controller/controller/controller.go b/notification_controller/controller/controller.go index 5137e0dfc12cb..7d871af4c44a3 100644 --- a/notification_controller/controller/controller.go +++ b/notification_controller/controller/controller.go @@ -18,6 +18,7 @@ import ( "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" @@ -31,8 +32,6 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" - - "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) const ( @@ -91,8 +90,7 @@ func NewController( configMapInformer: configMapInformer, appInformer: appInformer, appProjInformer: appProjInformer, - apiFactory: apiFactory, - } + apiFactory: apiFactory} skipProcessingOpt := controller.WithSkipProcessing(func(obj v1.Object) (bool, string) { app, ok := (obj).(*unstructured.Unstructured) if !ok { @@ -122,7 +120,7 @@ func NewController( // 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(), glob.REGEXP) + 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 { @@ -139,6 +137,7 @@ func (c *notificationController) alterDestinations(obj v1.Object, destinations s } func newInformer(resClient dynamic.ResourceInterface, controllerNamespace string, applicationNamespaces []string, selector string) cache.SharedIndexInformer { + informer := cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { @@ -151,7 +150,7 @@ func newInformer(resClient dynamic.ResourceInterface, controllerNamespace string } newItems := []unstructured.Unstructured{} for _, res := range appList.Items { - if controllerNamespace == res.GetNamespace() || glob.MatchStringInList(applicationNamespaces, res.GetNamespace(), glob.REGEXP) { + if controllerNamespace == res.GetNamespace() || glob.MatchStringInList(applicationNamespaces, res.GetNamespace(), false) { newItems = append(newItems, res) } } @@ -166,7 +165,9 @@ func newInformer(resClient dynamic.ResourceInterface, controllerNamespace string &unstructured.Unstructured{}, resyncPeriod, cache.Indexers{ - cache.NamespaceIndex: cache.MetaNamespaceIndexFunc, + cache.NamespaceIndex: func(obj interface{}) ([]string, error) { + return cache.MetaNamespaceIndexFunc(obj) + }, }, ) return informer diff --git a/notification_controller/controller/controller_test.go b/notification_controller/controller/controller_test.go index ca901cf2c1890..4eedb28f5e001 100644 --- a/notification_controller/controller/controller_test.go +++ b/notification_controller/controller/controller_test.go @@ -5,16 +5,14 @@ import ( "testing" "time" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "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" - - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) func TestIsAppSyncStatusRefreshed(t *testing.T) { @@ -134,7 +132,7 @@ func TestInit(t *testing.T) { err = nc.Init(ctx) - require.NoError(t, err) + assert.NoError(t, err) } } @@ -170,7 +168,7 @@ func TestInitTimeout(t *testing.T) { err = nc.Init(ctx) // Expect an error & add assertion for the error message - require.Error(t, err) + assert.Error(t, err) assert.Equal(t, "Timed out waiting for caches to sync", err.Error()) } diff --git a/notifications_catalog/install.yaml b/notifications_catalog/install.yaml index 7457b25ddad89..59b3665b9a2e3 100644 --- a/notifications_catalog/install.yaml +++ b/notifications_catalog/install.yaml @@ -40,7 +40,8 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -67,7 +68,8 @@ 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}}" @@ -117,7 +119,8 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -140,7 +143,8 @@ 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}}" @@ -190,7 +194,8 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -217,7 +222,8 @@ 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}}" @@ -267,7 +273,8 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -294,7 +301,8 @@ 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}}" @@ -348,7 +356,8 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -371,7 +380,8 @@ 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}}" @@ -420,7 +430,8 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -447,7 +458,8 @@ 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}}" diff --git a/notifications_catalog/templates/app-deployed.yaml b/notifications_catalog/templates/app-deployed.yaml index ee58c775f1fd8..843bf57e21a89 100644 --- a/notifications_catalog/templates/app-deployed.yaml +++ b/notifications_catalog/templates/app-deployed.yaml @@ -25,7 +25,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -51,7 +52,8 @@ 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 59115c9a14935..46c39b2e9ca0c 100644 --- a/notifications_catalog/templates/app-health-degraded.yaml +++ b/notifications_catalog/templates/app-health-degraded.yaml @@ -21,7 +21,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -43,7 +44,8 @@ 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 a4c23787dde8b..4a5ece85ba541 100644 --- a/notifications_catalog/templates/app-sync-failed.yaml +++ b/notifications_catalog/templates/app-sync-failed.yaml @@ -21,7 +21,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -47,7 +48,8 @@ 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 434132ad86d89..b2a86042e3ce2 100644 --- a/notifications_catalog/templates/app-sync-running.yaml +++ b/notifications_catalog/templates/app-sync-running.yaml @@ -21,7 +21,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -46,7 +47,8 @@ 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 c893070bfcc63..b1af244fb6d2d 100644 --- a/notifications_catalog/templates/app-sync-status-unknown.yaml +++ b/notifications_catalog/templates/app-sync-status-unknown.yaml @@ -26,7 +26,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -47,7 +48,8 @@ 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 76e467bd1c37d..d791de55149a4 100644 --- a/notifications_catalog/templates/app-sync-succeeded.yaml +++ b/notifications_catalog/templates/app-sync-succeeded.yaml @@ -21,7 +21,8 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - , + {{if not $index}},{{end}} + {{if $index}},{{end}} { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -47,7 +48,8 @@ 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/pkg/apiclient/apiclient.go b/pkg/apiclient/apiclient.go index 52164255164ae..83e841dd99bea 100644 --- a/pkg/apiclient/apiclient.go +++ b/pkg/apiclient/apiclient.go @@ -62,10 +62,14 @@ const ( EnvArgoCDServer = "ARGOCD_SERVER" // EnvArgoCDAuthToken is the environment variable to look for an Argo CD auth token EnvArgoCDAuthToken = "ARGOCD_AUTH_TOKEN" + // EnvArgoCDgRPCMaxSizeMB is the environment variable to look for a max gRPC message size + EnvArgoCDgRPCMaxSizeMB = "ARGOCD_GRPC_MAX_SIZE_MB" ) -// MaxGRPCMessageSize contains max grpc message size -var MaxGRPCMessageSize = env.ParseNumFromEnv(common.EnvGRPCMaxSizeMB, 200, 0, math.MaxInt32) * 1024 * 1024 +var ( + // MaxGRPCMessageSize contains max grpc message size + MaxGRPCMessageSize = env.ParseNumFromEnv(EnvArgoCDgRPCMaxSizeMB, 200, 0, math.MaxInt32) * 1024 * 1024 +) // Client defines an interface for interaction with an Argo CD server. type Client interface { @@ -334,11 +338,11 @@ func (c *client) OIDCConfig(ctx context.Context, set *settingspkg.Settings) (*oa } provider, err := oidc.NewProvider(ctx, issuerURL) if err != nil { - return nil, nil, fmt.Errorf("Failed to query provider %q: %w", issuerURL, err) + return nil, nil, fmt.Errorf("Failed to query provider %q: %v", issuerURL, err) } oidcConf, err := oidcutil.ParseConfig(provider) if err != nil { - return nil, nil, fmt.Errorf("Failed to parse provider config: %w", err) + return nil, nil, fmt.Errorf("Failed to parse provider config: %v", err) } scopes = oidcutil.GetScopesOrDefault(scopes) if oidcutil.OfflineAccess(oidcConf.ScopesSupported) { @@ -845,7 +849,7 @@ func (c *client) WatchApplicationWithRetry(ctx context.Context, appName string, } func isCanceledContextErr(err error) bool { - if err != nil && errors.Is(err, context.Canceled) { + if err == context.Canceled { return true } if stat, ok := status.FromError(err); ok { diff --git a/pkg/apiclient/apiclient_test.go b/pkg/apiclient/apiclient_test.go index 221b20eb07bcc..7bb3b36befdde 100644 --- a/pkg/apiclient/apiclient_test.go +++ b/pkg/apiclient/apiclient_test.go @@ -4,38 +4,13 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func Test_parseHeaders(t *testing.T) { - t.Run("Header parsed successfully", func(t *testing.T) { - headerString := []string{"foo:", "foo1:bar1", "foo2:bar2:bar2"} - headers, err := parseHeaders(headerString) - require.NoError(t, err) - assert.Equal(t, "", headers.Get("foo")) - assert.Equal(t, "bar1", headers.Get("foo1")) - assert.Equal(t, "bar2:bar2", headers.Get("foo2")) - }) - - 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) - require.NoError(t, err) - assert.Equal(t, []string{" https://foo.bar"}, headers.Get("origin")) - assert.Equal(t, []string{" 123"}, headers.Get("content-length")) - }) - - 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") - }) + 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") } diff --git a/pkg/apiclient/application/application.pb.go b/pkg/apiclient/application/application.pb.go index 2f73469d1049f..70c63c36bc333 100644 --- a/pkg/apiclient/application/application.pb.go +++ b/pkg/apiclient/application/application.pb.go @@ -214,12 +214,8 @@ type RevisionMetadataQuery struct { // the revision of the app 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"` - // source index (for multi source apps) - SourceIndex *int32 `protobuf:"varint,5,opt,name=sourceIndex" json:"sourceIndex,omitempty"` - // versionId from historical data (for multi source apps) - VersionId *int32 `protobuf:"varint,6,opt,name=versionId" json:"versionId,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:"-"` @@ -286,20 +282,6 @@ func (m *RevisionMetadataQuery) GetProject() string { return "" } -func (m *RevisionMetadataQuery) GetSourceIndex() int32 { - if m != nil && m.SourceIndex != nil { - return *m.SourceIndex - } - return 0 -} - -func (m *RevisionMetadataQuery) GetVersionId() int32 { - if m != nil && m.VersionId != nil { - return *m.VersionId - } - return 0 -} - // ApplicationEventsQuery is a query for application resource events type ApplicationResourceEventsQuery struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` @@ -394,8 +376,6 @@ type ApplicationManifestQuery struct { 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"` - SourcePositions []int64 `protobuf:"varint,5,rep,name=sourcePositions" json:"sourcePositions,omitempty"` - Revisions []string `protobuf:"bytes,6,rep,name=revisions" json:"revisions,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -462,20 +442,6 @@ func (m *ApplicationManifestQuery) GetProject() string { return "" } -func (m *ApplicationManifestQuery) GetSourcePositions() []int64 { - if m != nil { - return m.SourcePositions - } - return nil -} - -func (m *ApplicationManifestQuery) GetRevisions() []string { - if m != nil { - return m.Revisions - } - return nil -} - type FileChunk struct { Chunk []byte `protobuf:"bytes,1,req,name=chunk" json:"chunk,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -991,8 +957,6 @@ type ApplicationSyncRequest struct { 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"` - SourcePositions []int64 `protobuf:"varint,14,rep,name=sourcePositions" json:"sourcePositions,omitempty"` - Revisions []string `protobuf:"bytes,15,rep,name=revisions" json:"revisions,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1115,20 +1079,6 @@ func (m *ApplicationSyncRequest) GetProject() string { return "" } -func (m *ApplicationSyncRequest) GetSourcePositions() []int64 { - if m != nil { - return m.SourcePositions - } - return nil -} - -func (m *ApplicationSyncRequest) GetRevisions() []string { - if m != nil { - return m.Revisions - } - return nil -} - // ApplicationUpdateSpecRequest is a request to update application spec type ApplicationUpdateSpecRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` @@ -2842,179 +2792,175 @@ func init() { } var fileDescriptor_df6e82b174b5eaec = []byte{ - // 2742 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0x4d, 0x8c, 0x1b, 0x49, - 0x15, 0xa6, 0xec, 0xf1, 0x8c, 0xe7, 0x79, 0x26, 0x93, 0xd4, 0x26, 0x83, 0xd7, 0x99, 0x0d, 0xde, - 0x4e, 0xb2, 0x71, 0x26, 0x19, 0x3b, 0x31, 0x01, 0x65, 0x67, 0x77, 0x05, 0xc9, 0xe4, 0x17, 0x26, - 0xd9, 0xd0, 0x93, 0x10, 0xb4, 0x1c, 0xa0, 0xb6, 0xbb, 0xc6, 0xd3, 0x4c, 0xbb, 0xbb, 0xd3, 0xdd, - 0x76, 0x18, 0x85, 0x5c, 0x16, 0xed, 0x05, 0xad, 0x40, 0xc0, 0x1e, 0x10, 0x42, 0x80, 0x16, 0xad, - 0x84, 0x10, 0x88, 0x0b, 0x42, 0x48, 0x08, 0x09, 0x0e, 0x20, 0x38, 0x20, 0xad, 0xe0, 0xc8, 0x05, - 0x45, 0x88, 0x23, 0x5c, 0xf6, 0x8c, 0x50, 0x55, 0x57, 0x75, 0x57, 0xfb, 0xa7, 0xed, 0xc1, 0x46, - 0x9b, 0x5b, 0xbf, 0x72, 0xd5, 0x7b, 0xdf, 0x7b, 0xf5, 0xea, 0xbd, 0x57, 0xaf, 0x0c, 0x27, 0x02, - 0xea, 0x77, 0xa9, 0xdf, 0x20, 0x9e, 0x67, 0x5b, 0x06, 0x09, 0x2d, 0xd7, 0x51, 0xbf, 0xeb, 0x9e, - 0xef, 0x86, 0x2e, 0x2e, 0x29, 0x43, 0x95, 0x95, 0x96, 0xeb, 0xb6, 0x6c, 0xda, 0x20, 0x9e, 0xd5, - 0x20, 0x8e, 0xe3, 0x86, 0x7c, 0x38, 0x88, 0xa6, 0x56, 0xb4, 0xdd, 0x8b, 0x41, 0xdd, 0x72, 0xf9, - 0xaf, 0x86, 0xeb, 0xd3, 0x46, 0xf7, 0x7c, 0xa3, 0x45, 0x1d, 0xea, 0x93, 0x90, 0x9a, 0x62, 0xce, - 0x85, 0x64, 0x4e, 0x9b, 0x18, 0x3b, 0x96, 0x43, 0xfd, 0xbd, 0x86, 0xb7, 0xdb, 0x62, 0x03, 0x41, - 0xa3, 0x4d, 0x43, 0x32, 0x68, 0xd5, 0x66, 0xcb, 0x0a, 0x77, 0x3a, 0xaf, 0xd7, 0x0d, 0xb7, 0xdd, - 0x20, 0x7e, 0xcb, 0xf5, 0x7c, 0xf7, 0x4b, 0xfc, 0x63, 0xcd, 0x30, 0x1b, 0xdd, 0x66, 0xc2, 0x40, - 0xd5, 0xa5, 0x7b, 0x9e, 0xd8, 0xde, 0x0e, 0xe9, 0xe7, 0x76, 0x75, 0x04, 0x37, 0x9f, 0x7a, 0xae, - 0xb0, 0x0d, 0xff, 0xb4, 0x42, 0xd7, 0xdf, 0x53, 0x3e, 0x23, 0x36, 0xda, 0xfb, 0x08, 0x0e, 0x5e, - 0x4a, 0xe4, 0x7d, 0xa6, 0x43, 0xfd, 0x3d, 0x8c, 0x61, 0xc6, 0x21, 0x6d, 0x5a, 0x46, 0x55, 0x54, - 0x9b, 0xd7, 0xf9, 0x37, 0x2e, 0xc3, 0x9c, 0x4f, 0xb7, 0x7d, 0x1a, 0xec, 0x94, 0x73, 0x7c, 0x58, - 0x92, 0xb8, 0x02, 0x45, 0x26, 0x9c, 0x1a, 0x61, 0x50, 0xce, 0x57, 0xf3, 0xb5, 0x79, 0x3d, 0xa6, - 0x71, 0x0d, 0x96, 0x7c, 0x1a, 0xb8, 0x1d, 0xdf, 0xa0, 0x9f, 0xa5, 0x7e, 0x60, 0xb9, 0x4e, 0x79, - 0x86, 0xaf, 0xee, 0x1d, 0x66, 0x5c, 0x02, 0x6a, 0x53, 0x23, 0x74, 0xfd, 0x72, 0x81, 0x4f, 0x89, - 0x69, 0x86, 0x87, 0x01, 0x2f, 0xcf, 0x46, 0x78, 0xd8, 0x37, 0xd6, 0x60, 0x81, 0x78, 0xde, 0x6d, - 0xd2, 0xa6, 0x81, 0x47, 0x0c, 0x5a, 0x9e, 0xe3, 0xbf, 0xa5, 0xc6, 0x18, 0x66, 0x81, 0xa4, 0x5c, - 0xe4, 0xc0, 0x24, 0xa9, 0x6d, 0xc0, 0xfc, 0x6d, 0xd7, 0xa4, 0xc3, 0xd5, 0xed, 0x65, 0x9f, 0xeb, - 0x67, 0xaf, 0xfd, 0x1e, 0xc1, 0x11, 0x9d, 0x76, 0x2d, 0x86, 0xff, 0x16, 0x0d, 0x89, 0x49, 0x42, - 0xd2, 0xcb, 0x31, 0x17, 0x73, 0xac, 0x40, 0xd1, 0x17, 0x93, 0xcb, 0x39, 0x3e, 0x1e, 0xd3, 0x7d, - 0xd2, 0xf2, 0xd9, 0xca, 0x44, 0x26, 0x94, 0x24, 0xae, 0x42, 0x29, 0xb2, 0xe5, 0x4d, 0xc7, 0xa4, - 0x5f, 0xe6, 0xd6, 0x2b, 0xe8, 0xea, 0x10, 0x5e, 0x81, 0xf9, 0x6e, 0x64, 0xe7, 0x9b, 0x26, 0xb7, - 0x62, 0x41, 0x4f, 0x06, 0xb4, 0x7f, 0x22, 0x38, 0xa6, 0xf8, 0x80, 0x2e, 0x76, 0xe6, 0x6a, 0x97, - 0x3a, 0x61, 0x30, 0x5c, 0xa1, 0xb3, 0x70, 0x48, 0x6e, 0x62, 0xaf, 0x9d, 0xfa, 0x7f, 0x60, 0x2a, - 0xaa, 0x83, 0x52, 0x45, 0x75, 0x8c, 0x29, 0x22, 0xe9, 0x7b, 0x37, 0xaf, 0x08, 0x35, 0xd5, 0xa1, - 0x3e, 0x43, 0x15, 0xb2, 0x0d, 0x35, 0x9b, 0x32, 0x94, 0xf6, 0x1e, 0x82, 0xb2, 0xa2, 0xe8, 0x2d, - 0xe2, 0x58, 0xdb, 0x34, 0x08, 0xc7, 0xdd, 0x33, 0x34, 0xc5, 0x3d, 0xab, 0xc1, 0x52, 0xa4, 0xd5, - 0x1d, 0x76, 0x1e, 0x59, 0xfc, 0x29, 0x17, 0xaa, 0xf9, 0x5a, 0x5e, 0xef, 0x1d, 0x66, 0x7b, 0x27, - 0x65, 0x06, 0xe5, 0x59, 0xee, 0xc6, 0xc9, 0x80, 0xf6, 0x3c, 0xcc, 0x5f, 0xb3, 0x6c, 0xba, 0xb1, - 0xd3, 0x71, 0x76, 0xf1, 0x61, 0x28, 0x18, 0xec, 0x83, 0xeb, 0xb0, 0xa0, 0x47, 0x84, 0xf6, 0x4d, - 0x04, 0xcf, 0x0f, 0xd3, 0xfa, 0xbe, 0x15, 0xee, 0xb0, 0xf5, 0xc1, 0x30, 0xf5, 0x8d, 0x1d, 0x6a, - 0xec, 0x06, 0x9d, 0xb6, 0x74, 0x59, 0x49, 0x4f, 0xa6, 0xbe, 0xf6, 0x13, 0x04, 0xb5, 0x91, 0x98, - 0xee, 0xfb, 0xc4, 0xf3, 0xa8, 0x8f, 0xaf, 0x41, 0xe1, 0x01, 0xfb, 0x81, 0x1f, 0xd0, 0x52, 0xb3, - 0x5e, 0x57, 0x03, 0xfc, 0x48, 0x2e, 0x37, 0x3e, 0xa4, 0x47, 0xcb, 0x71, 0x5d, 0x9a, 0x27, 0xc7, - 0xf9, 0x2c, 0xa7, 0xf8, 0xc4, 0x56, 0x64, 0xf3, 0xf9, 0xb4, 0xcb, 0xb3, 0x30, 0xe3, 0x11, 0x3f, - 0xd4, 0x8e, 0xc0, 0x33, 0xe9, 0xe3, 0xe1, 0xb9, 0x4e, 0x40, 0xb5, 0x5f, 0xa7, 0xbd, 0x69, 0xc3, - 0xa7, 0x24, 0xa4, 0x3a, 0x7d, 0xd0, 0xa1, 0x41, 0x88, 0x77, 0x41, 0xcd, 0x39, 0xdc, 0xaa, 0xa5, - 0xe6, 0xcd, 0x7a, 0x12, 0xb4, 0xeb, 0x32, 0x68, 0xf3, 0x8f, 0x2f, 0x18, 0x66, 0xbd, 0xdb, 0xac, - 0x7b, 0xbb, 0xad, 0x3a, 0x4b, 0x01, 0x29, 0x64, 0x32, 0x05, 0xa8, 0xaa, 0xea, 0x2a, 0x77, 0xbc, - 0x0c, 0xb3, 0x1d, 0x2f, 0xa0, 0x7e, 0xc8, 0x35, 0x2b, 0xea, 0x82, 0x62, 0xfb, 0xd7, 0x25, 0xb6, - 0x65, 0x92, 0x30, 0xda, 0x9f, 0xa2, 0x1e, 0xd3, 0xda, 0x6f, 0xd2, 0xe8, 0xef, 0x79, 0xe6, 0x07, - 0x85, 0x5e, 0x45, 0x99, 0x4b, 0xa3, 0x54, 0x3d, 0x28, 0x9f, 0xf6, 0xa0, 0x5f, 0xa4, 0xf1, 0x5f, - 0xa1, 0x36, 0x4d, 0xf0, 0x0f, 0x72, 0xe6, 0x32, 0xcc, 0x19, 0x24, 0x30, 0x88, 0x29, 0xa5, 0x48, - 0x92, 0x05, 0x32, 0xcf, 0x77, 0x3d, 0xd2, 0xe2, 0x9c, 0xee, 0xb8, 0xb6, 0x65, 0xec, 0x09, 0x71, - 0xfd, 0x3f, 0xf4, 0x39, 0xfe, 0x4c, 0xb6, 0xe3, 0x17, 0xd2, 0xb0, 0x8f, 0x43, 0x69, 0x6b, 0xcf, - 0x31, 0x5e, 0xf5, 0xa2, 0xc3, 0x7d, 0x18, 0x0a, 0x56, 0x48, 0xdb, 0x41, 0x19, 0xf1, 0x83, 0x1d, - 0x11, 0xda, 0x7f, 0x0a, 0xb0, 0xac, 0xe8, 0xc6, 0x16, 0x64, 0x69, 0x96, 0x15, 0xa5, 0x96, 0x61, - 0xd6, 0xf4, 0xf7, 0xf4, 0x8e, 0x23, 0x1c, 0x40, 0x50, 0x4c, 0xb0, 0xe7, 0x77, 0x9c, 0x08, 0x7e, - 0x51, 0x8f, 0x08, 0xbc, 0x0d, 0xc5, 0x20, 0x64, 0x55, 0x46, 0x6b, 0x8f, 0x03, 0x2f, 0x35, 0x3f, - 0x35, 0xd9, 0xa6, 0x33, 0xe8, 0x5b, 0x82, 0xa3, 0x1e, 0xf3, 0xc6, 0x0f, 0x58, 0x4c, 0x8b, 0x02, - 0x5d, 0x50, 0x9e, 0xab, 0xe6, 0x6b, 0xa5, 0xe6, 0xd6, 0xe4, 0x82, 0x5e, 0xf5, 0x58, 0x85, 0xa4, - 0x64, 0x30, 0x3d, 0x91, 0xc2, 0xc2, 0x68, 0x5b, 0xc4, 0x87, 0x40, 0x54, 0x03, 0xc9, 0x00, 0xfe, - 0x1c, 0x14, 0x2c, 0x67, 0xdb, 0x0d, 0xca, 0xf3, 0x1c, 0xcc, 0xe5, 0xc9, 0xc0, 0xdc, 0x74, 0xb6, - 0x5d, 0x3d, 0x62, 0x88, 0x1f, 0xc0, 0xa2, 0x4f, 0x43, 0x7f, 0x4f, 0x5a, 0xa1, 0x0c, 0xdc, 0xae, - 0x9f, 0x9e, 0x4c, 0x82, 0xae, 0xb2, 0xd4, 0xd3, 0x12, 0xf0, 0x3a, 0x94, 0x82, 0xc4, 0xc7, 0xca, - 0x25, 0x2e, 0xb0, 0x9c, 0x62, 0xa4, 0xf8, 0xa0, 0xae, 0x4e, 0xee, 0xf3, 0xee, 0x85, 0x6c, 0xef, - 0x5e, 0x1c, 0x99, 0xd5, 0x0e, 0x8c, 0x91, 0xd5, 0x96, 0x7a, 0xb3, 0xda, 0xbf, 0x11, 0xac, 0xf4, - 0x05, 0xa7, 0x2d, 0x8f, 0x66, 0x1e, 0x03, 0x02, 0x33, 0x81, 0x47, 0x0d, 0x9e, 0xa9, 0x4a, 0xcd, - 0x5b, 0x53, 0x8b, 0x56, 0x5c, 0x2e, 0x67, 0x9d, 0x15, 0x50, 0x27, 0x8c, 0x0b, 0x3f, 0x40, 0xf0, - 0x61, 0x45, 0xe6, 0x1d, 0x12, 0x1a, 0x3b, 0x59, 0xca, 0xb2, 0xf3, 0xcb, 0xe6, 0x88, 0xbc, 0x1c, - 0x11, 0xcc, 0xaa, 0xfc, 0xe3, 0xee, 0x9e, 0xc7, 0x00, 0xb2, 0x5f, 0x92, 0x81, 0x09, 0x8b, 0xa7, - 0x9f, 0x22, 0xa8, 0xa8, 0x31, 0xdc, 0xb5, 0xed, 0xd7, 0x89, 0xb1, 0x9b, 0x05, 0xf2, 0x00, 0xe4, - 0x2c, 0x93, 0x23, 0xcc, 0xeb, 0x39, 0xcb, 0xdc, 0x67, 0x30, 0xea, 0x85, 0x3b, 0x9b, 0x0d, 0x77, - 0x2e, 0x0d, 0xf7, 0xfd, 0x1e, 0xb8, 0x32, 0x24, 0x64, 0xc0, 0x5d, 0x81, 0x79, 0xa7, 0xa7, 0x90, - 0x4d, 0x06, 0x06, 0x14, 0xb0, 0xb9, 0xbe, 0x02, 0xb6, 0x0c, 0x73, 0xdd, 0xf8, 0x9a, 0xc3, 0x7e, - 0x96, 0x24, 0x53, 0xb1, 0xe5, 0xbb, 0x1d, 0x4f, 0x18, 0x3d, 0x22, 0x18, 0x8a, 0x5d, 0xcb, 0x61, - 0x25, 0x39, 0x47, 0xc1, 0xbe, 0xf7, 0x7f, 0xb1, 0x49, 0xa9, 0xfd, 0xb3, 0x1c, 0x7c, 0x64, 0x80, - 0xda, 0x23, 0xfd, 0xe9, 0xe9, 0xd0, 0x3d, 0xf6, 0xea, 0xb9, 0xa1, 0x5e, 0x5d, 0x1c, 0xe5, 0xd5, - 0xf3, 0xd9, 0xf6, 0x82, 0xb4, 0xbd, 0x7e, 0x9c, 0x83, 0xea, 0x00, 0x7b, 0x8d, 0x2e, 0x27, 0x9e, - 0x1a, 0x83, 0x6d, 0xbb, 0xbe, 0xf0, 0x92, 0xa2, 0x1e, 0x11, 0xec, 0x9c, 0xb9, 0xbe, 0xb7, 0x43, - 0x1c, 0xee, 0x1d, 0x45, 0x5d, 0x50, 0x13, 0x9a, 0xea, 0x6b, 0x39, 0x28, 0x4b, 0xfb, 0x5c, 0x32, - 0xb8, 0xb5, 0x3a, 0xce, 0xd3, 0x6f, 0xa2, 0x65, 0x98, 0x25, 0x1c, 0xad, 0x70, 0x2a, 0x41, 0xf5, - 0x19, 0xa3, 0x98, 0x6d, 0x8c, 0xf9, 0xb4, 0x31, 0xde, 0x44, 0x70, 0x34, 0x6d, 0x8c, 0x60, 0xd3, - 0x0a, 0x42, 0x79, 0x39, 0xc0, 0xdb, 0x30, 0x17, 0xc9, 0x89, 0x4a, 0xbb, 0x52, 0x73, 0x73, 0xd2, - 0x84, 0x9f, 0x32, 0xbc, 0x64, 0xae, 0xbd, 0x08, 0x47, 0x07, 0x46, 0x39, 0x01, 0xa3, 0x02, 0x45, - 0x59, 0xe4, 0x88, 0xad, 0x89, 0x69, 0xed, 0xcd, 0x99, 0x74, 0xca, 0x71, 0xcd, 0x4d, 0xb7, 0x95, - 0x71, 0xdf, 0xcf, 0xde, 0x4e, 0x66, 0x2a, 0xd7, 0x54, 0xae, 0xf6, 0x92, 0x64, 0xeb, 0x0c, 0xd7, - 0x09, 0x89, 0xe5, 0x50, 0x5f, 0x64, 0xc5, 0x64, 0x80, 0x6d, 0x43, 0x60, 0x39, 0x06, 0xdd, 0xa2, - 0x86, 0xeb, 0x98, 0x01, 0xdf, 0xcf, 0xbc, 0x9e, 0x1a, 0xc3, 0x37, 0x60, 0x9e, 0xd3, 0x77, 0xad, - 0x76, 0x94, 0x06, 0x4a, 0xcd, 0xd5, 0x7a, 0xd4, 0x83, 0xab, 0xab, 0x3d, 0xb8, 0xc4, 0x86, 0x6d, - 0x1a, 0x92, 0x7a, 0xf7, 0x7c, 0x9d, 0xad, 0xd0, 0x93, 0xc5, 0x0c, 0x4b, 0x48, 0x2c, 0x7b, 0xd3, - 0x72, 0x78, 0xe1, 0xc9, 0x44, 0x25, 0x03, 0xcc, 0x55, 0xb6, 0x5d, 0xdb, 0x76, 0x1f, 0xca, 0x73, - 0x13, 0x51, 0x6c, 0x55, 0xc7, 0x09, 0x2d, 0x9b, 0xcb, 0x8f, 0x1c, 0x21, 0x19, 0xe0, 0xab, 0x2c, - 0x3b, 0xa4, 0xbe, 0x38, 0x30, 0x82, 0x8a, 0x9d, 0xb1, 0x14, 0xb5, 0x95, 0xe4, 0x79, 0x8d, 0xdc, - 0x76, 0x41, 0x75, 0xdb, 0xde, 0xa3, 0xb0, 0x38, 0xa0, 0x37, 0xc2, 0xbb, 0x6c, 0xb4, 0x6b, 0xb9, - 0x1d, 0x56, 0x53, 0xf1, 0xd2, 0x43, 0xd2, 0x7d, 0xae, 0xbc, 0x94, 0xed, 0xca, 0x07, 0xd3, 0xae, - 0xfc, 0x5b, 0x04, 0xc5, 0x4d, 0xb7, 0x75, 0xd5, 0x09, 0xfd, 0x3d, 0x7e, 0x4b, 0x72, 0x9d, 0x90, - 0x3a, 0xd2, 0x5f, 0x24, 0xc9, 0x36, 0x21, 0xb4, 0xda, 0x74, 0x2b, 0x24, 0x6d, 0x4f, 0xd4, 0x58, - 0xfb, 0xda, 0x84, 0x78, 0x31, 0x33, 0x8c, 0x4d, 0x82, 0x90, 0x9f, 0xf8, 0xa2, 0xce, 0xbf, 0x99, - 0x0a, 0xf1, 0x84, 0xad, 0xd0, 0x17, 0xc7, 0x3d, 0x35, 0xa6, 0xba, 0x58, 0x21, 0xc2, 0x26, 0x48, - 0xad, 0x0d, 0xcf, 0xc6, 0xc5, 0xff, 0x5d, 0xea, 0xb7, 0x2d, 0x87, 0x64, 0x47, 0xef, 0x31, 0xda, - 0x7b, 0x19, 0x77, 0x4f, 0x37, 0x75, 0xe8, 0x58, 0x2d, 0x7d, 0xdf, 0x72, 0x4c, 0xf7, 0x61, 0xc6, - 0xe1, 0x99, 0x4c, 0xe0, 0x5f, 0xd2, 0x1d, 0x3a, 0x45, 0x62, 0x7c, 0xd2, 0x6f, 0xc0, 0x22, 0x8b, - 0x09, 0x5d, 0x2a, 0x7e, 0x10, 0x61, 0x47, 0x1b, 0xd6, 0x2c, 0x49, 0x78, 0xe8, 0xe9, 0x85, 0x78, - 0x13, 0x96, 0x48, 0x10, 0x58, 0x2d, 0x87, 0x9a, 0x92, 0x57, 0x6e, 0x6c, 0x5e, 0xbd, 0x4b, 0xa3, - 0x6b, 0x37, 0x9f, 0x21, 0xf6, 0x5b, 0x92, 0xda, 0x57, 0x11, 0x1c, 0x19, 0xc8, 0x24, 0x3e, 0x39, - 0x48, 0x09, 0xe3, 0x15, 0x28, 0x06, 0xc6, 0x0e, 0x35, 0x3b, 0x36, 0x95, 0xbd, 0x28, 0x49, 0xb3, - 0xdf, 0xcc, 0x4e, 0xb4, 0xfb, 0x22, 0x8d, 0xc4, 0x34, 0x3e, 0x06, 0xd0, 0x26, 0x4e, 0x87, 0xd8, - 0x1c, 0xc2, 0x0c, 0x87, 0xa0, 0x8c, 0x68, 0x2b, 0x50, 0x19, 0xe4, 0x3a, 0xa2, 0xc7, 0xf3, 0x2f, - 0x04, 0x07, 0x64, 0x50, 0x15, 0xbb, 0x5b, 0x83, 0x25, 0xc5, 0x0c, 0xb7, 0x93, 0x8d, 0xee, 0x1d, - 0x1e, 0x11, 0x30, 0xa5, 0x97, 0xe4, 0xd3, 0x4d, 0xf6, 0x6e, 0xaa, 0x4d, 0x3e, 0x76, 0xbe, 0x43, - 0x53, 0xaa, 0x1f, 0xbf, 0x02, 0xe5, 0x5b, 0xc4, 0x21, 0x2d, 0x6a, 0xc6, 0x6a, 0xc7, 0x2e, 0xf6, - 0x45, 0xb5, 0x59, 0x31, 0x71, 0x6b, 0x20, 0x2e, 0xb5, 0xac, 0xed, 0x6d, 0xd9, 0xf8, 0xf0, 0xa1, - 0xb8, 0x69, 0x39, 0xbb, 0xec, 0xfe, 0xcc, 0x34, 0x0e, 0xad, 0xd0, 0x96, 0xd6, 0x8d, 0x08, 0x7c, - 0x10, 0xf2, 0x1d, 0xdf, 0x16, 0x1e, 0xc0, 0x3e, 0x71, 0x15, 0x4a, 0x26, 0x0d, 0x0c, 0xdf, 0xf2, - 0xc4, 0xfe, 0xf3, 0xa6, 0xb1, 0x32, 0xc4, 0xf6, 0xc1, 0x32, 0x5c, 0x67, 0xc3, 0x26, 0x41, 0x20, - 0x13, 0x50, 0x3c, 0xa0, 0xbd, 0x0c, 0x8b, 0x4c, 0x66, 0xa2, 0xe6, 0x99, 0xb4, 0x9a, 0x47, 0x52, - 0xf0, 0x25, 0x3c, 0x89, 0x98, 0xc0, 0x33, 0x2c, 0xef, 0x5f, 0xf2, 0x3c, 0xc1, 0x64, 0xcc, 0x72, - 0x28, 0x3f, 0x28, 0x7f, 0x0e, 0xec, 0x95, 0x36, 0xff, 0x76, 0x1c, 0xb0, 0x7a, 0x4e, 0xa8, 0xdf, - 0xb5, 0x0c, 0x8a, 0xbf, 0x85, 0x60, 0x86, 0x89, 0xc6, 0xcf, 0x0d, 0x3b, 0x96, 0xdc, 0x5f, 0x2b, - 0xd3, 0xbb, 0x08, 0x33, 0x69, 0xda, 0xca, 0x1b, 0x7f, 0xfd, 0xc7, 0xb7, 0x73, 0xcb, 0xf8, 0x30, - 0x7f, 0x21, 0xeb, 0x9e, 0x57, 0x5f, 0xab, 0x02, 0xfc, 0x16, 0x02, 0x2c, 0xea, 0x20, 0xe5, 0x0d, - 0x01, 0x9f, 0x19, 0x06, 0x71, 0xc0, 0x5b, 0x43, 0xe5, 0x39, 0x25, 0xab, 0xd4, 0x0d, 0xd7, 0xa7, - 0x2c, 0x87, 0xf0, 0x09, 0x1c, 0xc0, 0x2a, 0x07, 0x70, 0x02, 0x6b, 0x83, 0x00, 0x34, 0x1e, 0x31, - 0x8b, 0x3e, 0x6e, 0xd0, 0x48, 0xee, 0x3b, 0x08, 0x0a, 0xf7, 0xf9, 0x1d, 0x62, 0x84, 0x91, 0xb6, - 0xa6, 0x66, 0x24, 0x2e, 0x8e, 0xa3, 0xd5, 0x8e, 0x73, 0xa4, 0xcf, 0xe1, 0xa3, 0x12, 0x69, 0x10, - 0xfa, 0x94, 0xb4, 0x53, 0x80, 0xcf, 0x21, 0xfc, 0x2e, 0x82, 0xd9, 0xa8, 0x79, 0x8c, 0x4f, 0x0e, - 0x43, 0x99, 0x6a, 0x2e, 0x57, 0xa6, 0xd7, 0x89, 0xd5, 0x4e, 0x73, 0x8c, 0xc7, 0xb5, 0x81, 0xdb, - 0xb9, 0x9e, 0xea, 0xd3, 0xbe, 0x8d, 0x20, 0x7f, 0x9d, 0x8e, 0xf4, 0xb7, 0x29, 0x82, 0xeb, 0x33, - 0xe0, 0x80, 0xad, 0xc6, 0x3f, 0x42, 0xf0, 0xec, 0x75, 0x1a, 0x0e, 0x4e, 0x8f, 0xb8, 0x36, 0x3a, - 0x67, 0x09, 0xb7, 0x3b, 0x33, 0xc6, 0xcc, 0x38, 0x2f, 0x34, 0x38, 0xb2, 0xd3, 0xf8, 0x54, 0x96, - 0x13, 0x06, 0x7b, 0x8e, 0xf1, 0x50, 0xe0, 0xf8, 0x13, 0x82, 0x83, 0xbd, 0x6f, 0x85, 0x38, 0x9d, - 0x50, 0x07, 0x3e, 0x25, 0x56, 0x6e, 0x4f, 0x1a, 0x65, 0xd3, 0x4c, 0xb5, 0x4b, 0x1c, 0xf9, 0x4b, - 0xf8, 0xc5, 0x2c, 0xe4, 0x71, 0x27, 0xae, 0xf1, 0x48, 0x7e, 0x3e, 0xe6, 0xef, 0xda, 0x1c, 0xf6, - 0x9f, 0x11, 0x1c, 0x96, 0x7c, 0x37, 0x76, 0x88, 0x1f, 0x5e, 0xa1, 0xac, 0x86, 0x0e, 0xc6, 0xd2, - 0x67, 0xc2, 0xac, 0xa1, 0xca, 0xd3, 0xae, 0x72, 0x5d, 0x3e, 0x81, 0x5f, 0xd9, 0xb7, 0x2e, 0x06, - 0x63, 0x63, 0x0a, 0xd8, 0x6f, 0x20, 0x58, 0xb8, 0x4e, 0xc3, 0x5b, 0x71, 0x37, 0xf8, 0xe4, 0x58, - 0x2f, 0x4c, 0x95, 0x95, 0xba, 0xf2, 0x9c, 0x2e, 0x7f, 0x8a, 0x5d, 0x64, 0x8d, 0x83, 0x3b, 0x85, - 0x4f, 0x66, 0x81, 0x4b, 0x3a, 0xd0, 0xef, 0x20, 0x38, 0xa2, 0x82, 0x48, 0x5e, 0xe6, 0x3e, 0xb6, - 0xbf, 0xf7, 0x2e, 0xf1, 0x6a, 0x36, 0x02, 0x5d, 0x93, 0xa3, 0x3b, 0xab, 0x0d, 0x76, 0xe0, 0x76, - 0x1f, 0x8a, 0x75, 0xb4, 0x5a, 0x43, 0xf8, 0x77, 0x08, 0x66, 0xa3, 0x66, 0xec, 0x70, 0x1b, 0xa5, - 0x5e, 0x92, 0xa6, 0x19, 0x0d, 0xc4, 0x6e, 0x57, 0xce, 0x0d, 0x36, 0xa8, 0xba, 0x5e, 0xba, 0x6a, - 0x9d, 0x5b, 0x39, 0x1d, 0xc6, 0x7e, 0x89, 0x00, 0x92, 0x86, 0x32, 0x3e, 0x9d, 0xad, 0x87, 0xd2, - 0x74, 0xae, 0x4c, 0xb7, 0xa5, 0xac, 0xd5, 0xb9, 0x3e, 0xb5, 0x4a, 0x35, 0x33, 0x86, 0x78, 0xd4, - 0x58, 0x8f, 0x9a, 0xcf, 0x3f, 0x44, 0x50, 0xe0, 0x7d, 0x3c, 0x7c, 0x62, 0x18, 0x66, 0xb5, 0xcd, - 0x37, 0x4d, 0xd3, 0xbf, 0xc0, 0xa1, 0x56, 0x9b, 0x59, 0x81, 0x78, 0x1d, 0xad, 0xe2, 0x2e, 0xcc, - 0x46, 0x9d, 0xb3, 0xe1, 0xee, 0x91, 0xea, 0xac, 0x55, 0xaa, 0x19, 0x85, 0x41, 0xe4, 0xa8, 0x22, - 0x07, 0xac, 0x8e, 0xca, 0x01, 0x33, 0x2c, 0x4c, 0xe3, 0xe3, 0x59, 0x41, 0xfc, 0xff, 0x60, 0x98, - 0x33, 0x1c, 0xdd, 0x49, 0xad, 0x3a, 0x2a, 0x0f, 0x30, 0xeb, 0x7c, 0x07, 0xc1, 0xc1, 0xde, 0xe2, - 0x1a, 0x1f, 0xed, 0x89, 0x99, 0xea, 0x5d, 0xa3, 0x92, 0xb6, 0xe2, 0xb0, 0xc2, 0x5c, 0xfb, 0x24, - 0x47, 0xb1, 0x8e, 0x2f, 0x8e, 0x3c, 0x19, 0xb7, 0x65, 0xd4, 0x61, 0x8c, 0xd6, 0x92, 0xd7, 0xb1, - 0x5f, 0x21, 0x58, 0x90, 0x7c, 0xef, 0xfa, 0x94, 0x66, 0xc3, 0x9a, 0xde, 0x41, 0x60, 0xb2, 0xb4, - 0x97, 0x39, 0xfc, 0x8f, 0xe3, 0x0b, 0x63, 0xc2, 0x97, 0xb0, 0xd7, 0x42, 0x86, 0xf4, 0x0f, 0x08, - 0x0e, 0xdd, 0x8f, 0xfc, 0xfe, 0x03, 0xc2, 0xbf, 0xc1, 0xf1, 0xbf, 0x82, 0x5f, 0xca, 0xa8, 0xf3, - 0x46, 0xa9, 0x71, 0x0e, 0xe1, 0x9f, 0x23, 0x28, 0xca, 0x57, 0x15, 0x7c, 0x6a, 0xe8, 0xc1, 0x48, - 0xbf, 0xbb, 0x4c, 0xd3, 0x99, 0x45, 0x51, 0xa3, 0x9d, 0xc8, 0x4c, 0xa7, 0x42, 0x3e, 0x73, 0xe8, - 0xb7, 0x11, 0xe0, 0xf8, 0xce, 0x1c, 0xdf, 0xa2, 0xf1, 0x0b, 0x29, 0x51, 0x43, 0x1b, 0x33, 0x95, - 0x53, 0x23, 0xe7, 0xa5, 0x53, 0xe9, 0x6a, 0x66, 0x2a, 0x75, 0x63, 0xf9, 0x5f, 0x47, 0x50, 0xba, - 0x4e, 0xe3, 0x3b, 0x48, 0x86, 0x2d, 0xd3, 0x8f, 0x42, 0x95, 0xda, 0xe8, 0x89, 0x02, 0xd1, 0x59, - 0x8e, 0xe8, 0x05, 0x9c, 0x6d, 0x2a, 0x09, 0xe0, 0x7b, 0x08, 0x16, 0xef, 0xa8, 0x2e, 0x8a, 0xcf, - 0x8e, 0x92, 0x94, 0x8a, 0xe4, 0xe3, 0xe3, 0xfa, 0x28, 0xc7, 0xb5, 0xa6, 0x8d, 0x85, 0x6b, 0x5d, - 0xbc, 0xaf, 0x7c, 0x1f, 0x45, 0x97, 0xd8, 0x9e, 0x7e, 0xf6, 0xff, 0x6a, 0xb7, 0x8c, 0xb6, 0xb8, - 0x76, 0x81, 0xe3, 0xab, 0xe3, 0xb3, 0xe3, 0xe0, 0x6b, 0x88, 0x26, 0x37, 0xfe, 0x2e, 0x82, 0x43, - 0xfc, 0xad, 0x41, 0x65, 0xdc, 0x93, 0x62, 0x86, 0xbd, 0x4c, 0x8c, 0x91, 0x62, 0x44, 0xfc, 0xd1, - 0xf6, 0x05, 0x6a, 0x5d, 0xbe, 0x23, 0x7c, 0x03, 0xc1, 0x01, 0x99, 0xd4, 0xc4, 0xee, 0xae, 0x8d, - 0x32, 0xdc, 0x7e, 0x93, 0xa0, 0x70, 0xb7, 0xd5, 0xf1, 0xdc, 0xed, 0x5d, 0x04, 0x73, 0xa2, 0x9b, - 0x9f, 0x51, 0x2a, 0x28, 0xed, 0xfe, 0x4a, 0x4f, 0x8f, 0x43, 0x34, 0x83, 0xb5, 0xcf, 0x73, 0xb1, - 0xf7, 0x70, 0x23, 0x4b, 0xac, 0xe7, 0x9a, 0x41, 0xe3, 0x91, 0xe8, 0xc4, 0x3e, 0x6e, 0xd8, 0x6e, - 0x2b, 0x78, 0x4d, 0xc3, 0x99, 0x09, 0x91, 0xcd, 0x39, 0x87, 0x70, 0x08, 0xf3, 0xcc, 0x39, 0x78, - 0xe3, 0x04, 0x57, 0x7b, 0xda, 0x2c, 0x7d, 0x3d, 0x95, 0x4a, 0xa5, 0xaf, 0x11, 0x93, 0x64, 0x40, - 0x71, 0x8d, 0xc5, 0xcf, 0x67, 0x8a, 0xe5, 0x82, 0xde, 0x42, 0x70, 0x48, 0xf5, 0xf6, 0x48, 0xfc, - 0xd8, 0xbe, 0x9e, 0x85, 0x42, 0x14, 0xd5, 0x78, 0x75, 0x2c, 0x47, 0xe2, 0x70, 0x2e, 0x5f, 0xfb, - 0xe3, 0x93, 0x63, 0xe8, 0xbd, 0x27, 0xc7, 0xd0, 0xdf, 0x9f, 0x1c, 0x43, 0xaf, 0x5d, 0x1c, 0xef, - 0x3f, 0xc2, 0x86, 0x6d, 0x51, 0x27, 0x54, 0xd9, 0xff, 0x37, 0x00, 0x00, 0xff, 0xff, 0x45, 0x63, - 0x3b, 0x00, 0x09, 0x2d, 0x00, 0x00, + // 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, 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. @@ -4393,16 +4339,6 @@ func (m *RevisionMetadataQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.VersionId != nil { - i = encodeVarintApplication(dAtA, i, uint64(*m.VersionId)) - i-- - dAtA[i] = 0x30 - } - if m.SourceIndex != nil { - i = encodeVarintApplication(dAtA, i, uint64(*m.SourceIndex)) - i-- - dAtA[i] = 0x28 - } if m.Project != nil { i -= len(*m.Project) copy(dAtA[i:], *m.Project) @@ -4533,22 +4469,6 @@ func (m *ApplicationManifestQuery) MarshalToSizedBuffer(dAtA []byte) (int, error i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Revisions) > 0 { - for iNdEx := len(m.Revisions) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Revisions[iNdEx]) - copy(dAtA[i:], m.Revisions[iNdEx]) - i = encodeVarintApplication(dAtA, i, uint64(len(m.Revisions[iNdEx]))) - i-- - dAtA[i] = 0x32 - } - } - if len(m.SourcePositions) > 0 { - for iNdEx := len(m.SourcePositions) - 1; iNdEx >= 0; iNdEx-- { - i = encodeVarintApplication(dAtA, i, uint64(m.SourcePositions[iNdEx])) - i-- - dAtA[i] = 0x28 - } - } if m.Project != nil { i -= len(*m.Project) copy(dAtA[i:], *m.Project) @@ -5028,22 +4948,6 @@ func (m *ApplicationSyncRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Revisions) > 0 { - for iNdEx := len(m.Revisions) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Revisions[iNdEx]) - copy(dAtA[i:], m.Revisions[iNdEx]) - i = encodeVarintApplication(dAtA, i, uint64(len(m.Revisions[iNdEx]))) - i-- - dAtA[i] = 0x7a - } - } - if len(m.SourcePositions) > 0 { - for iNdEx := len(m.SourcePositions) - 1; iNdEx >= 0; iNdEx-- { - i = encodeVarintApplication(dAtA, i, uint64(m.SourcePositions[iNdEx])) - i-- - dAtA[i] = 0x70 - } - } if m.Project != nil { i -= len(*m.Project) copy(dAtA[i:], *m.Project) @@ -6744,12 +6648,6 @@ func (m *RevisionMetadataQuery) Size() (n int) { l = len(*m.Project) n += 1 + l + sovApplication(uint64(l)) } - if m.SourceIndex != nil { - n += 1 + sovApplication(uint64(*m.SourceIndex)) - } - if m.VersionId != nil { - n += 1 + sovApplication(uint64(*m.VersionId)) - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6814,17 +6712,6 @@ func (m *ApplicationManifestQuery) Size() (n int) { l = len(*m.Project) n += 1 + l + sovApplication(uint64(l)) } - if len(m.SourcePositions) > 0 { - for _, e := range m.SourcePositions { - n += 1 + sovApplication(uint64(e)) - } - } - if len(m.Revisions) > 0 { - for _, s := range m.Revisions { - l = len(s) - n += 1 + l + sovApplication(uint64(l)) - } - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -7078,17 +6965,6 @@ func (m *ApplicationSyncRequest) Size() (n int) { l = len(*m.Project) n += 1 + l + sovApplication(uint64(l)) } - if len(m.SourcePositions) > 0 { - for _, e := range m.SourcePositions { - n += 1 + sovApplication(uint64(e)) - } - } - if len(m.Revisions) > 0 { - for _, s := range m.Revisions { - l = len(s) - n += 1 + l + sovApplication(uint64(l)) - } - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -8368,46 +8244,6 @@ func (m *RevisionMetadataQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.Project = &s iNdEx = postIndex - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field SourceIndex", wireType) - } - var v int32 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplication - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.SourceIndex = &v - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field VersionId", wireType) - } - var v int32 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplication - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.VersionId = &v default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -8853,114 +8689,6 @@ func (m *ApplicationManifestQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.Project = &s iNdEx = postIndex - case 5: - if wireType == 0 { - var v int64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplication - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.SourcePositions = append(m.SourcePositions, v) - } else if wireType == 2 { - var packedLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplication - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - packedLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if packedLen < 0 { - return ErrInvalidLengthApplication - } - postIndex := iNdEx + packedLen - if postIndex < 0 { - return ErrInvalidLengthApplication - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - var elementCount int - var count int - for _, integer := range dAtA[iNdEx:postIndex] { - if integer < 128 { - count++ - } - } - elementCount = count - if elementCount != 0 && len(m.SourcePositions) == 0 { - m.SourcePositions = make([]int64, 0, elementCount) - } - for iNdEx < postIndex { - var v int64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplication - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.SourcePositions = append(m.SourcePositions, v) - } - } else { - return fmt.Errorf("proto: wrong wireType = %d for field SourcePositions", wireType) - } - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Revisions", 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.Revisions = append(m.Revisions, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -10425,114 +10153,6 @@ func (m *ApplicationSyncRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.Project = &s iNdEx = postIndex - case 14: - if wireType == 0 { - var v int64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplication - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.SourcePositions = append(m.SourcePositions, v) - } else if wireType == 2 { - var packedLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplication - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - packedLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if packedLen < 0 { - return ErrInvalidLengthApplication - } - postIndex := iNdEx + packedLen - if postIndex < 0 { - return ErrInvalidLengthApplication - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - var elementCount int - var count int - for _, integer := range dAtA[iNdEx:postIndex] { - if integer < 128 { - count++ - } - } - elementCount = count - if elementCount != 0 && len(m.SourcePositions) == 0 { - m.SourcePositions = make([]int64, 0, elementCount) - } - for iNdEx < postIndex { - var v int64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplication - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.SourcePositions = append(m.SourcePositions, v) - } - } else { - return fmt.Errorf("proto: wrong wireType = %d for field SourcePositions", wireType) - } - case 15: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Revisions", 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.Revisions = append(m.Revisions, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) diff --git a/pkg/apiclient/application/forwarder_overwrite.go b/pkg/apiclient/application/forwarder_overwrite.go index 4769775f9671f..9a4bcae10e5a7 100644 --- a/pkg/apiclient/application/forwarder_overwrite.go +++ b/pkg/apiclient/application/forwarder_overwrite.go @@ -116,13 +116,10 @@ func init() { if req.URL.Query().Get("download") == "true" { w.Header().Set("Content-Type", "application/octet-stream") fileName := "log" - namespace := req.URL.Query().Get("namespace") - podName := req.URL.Query().Get("podName") - container := req.URL.Query().Get("container") - if kube.IsValidResourceName(namespace) && kube.IsValidResourceName(podName) && kube.IsValidResourceName(container) { - fileName = fmt.Sprintf("%s-%s-%s", namespace, podName, container) + if container := req.URL.Query().Get("container"); len(container) > 0 && kube.IsValidResourceName(container) { + fileName = container } - w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment;filename="%s.log"`, fileName)) + w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment;filename="%s.txt"`, fileName)) for { msg, err := recv() if err != nil { diff --git a/pkg/apiclient/applicationset/applicationset.pb.go b/pkg/apiclient/applicationset/applicationset.pb.go index 874ed5663b7c9..8f717d1f6920f 100644 --- a/pkg/apiclient/applicationset/applicationset.pb.go +++ b/pkg/apiclient/applicationset/applicationset.pb.go @@ -214,7 +214,6 @@ func (m *ApplicationSetResponse) GetApplicationset() *v1alpha1.ApplicationSet { type ApplicationSetCreateRequest struct { Applicationset *v1alpha1.ApplicationSet `protobuf:"bytes,1,opt,name=applicationset,proto3" json:"applicationset,omitempty"` Upsert bool `protobuf:"varint,2,opt,name=upsert,proto3" json:"upsert,omitempty"` - DryRun bool `protobuf:"varint,3,opt,name=dryRun,proto3" json:"dryRun,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -267,13 +266,6 @@ func (m *ApplicationSetCreateRequest) GetUpsert() bool { return false } -func (m *ApplicationSetCreateRequest) GetDryRun() bool { - if m != nil { - return m.DryRun - } - return false -} - type ApplicationSetDeleteRequest struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // The application set namespace. Default empty is argocd control plane namespace @@ -330,168 +322,12 @@ func (m *ApplicationSetDeleteRequest) GetAppsetNamespace() string { return "" } -type ApplicationSetTreeQuery struct { - 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:"-"` -} - -func (m *ApplicationSetTreeQuery) Reset() { *m = ApplicationSetTreeQuery{} } -func (m *ApplicationSetTreeQuery) String() string { return proto.CompactTextString(m) } -func (*ApplicationSetTreeQuery) ProtoMessage() {} -func (*ApplicationSetTreeQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_eacb9df0ce5738fa, []int{5} -} -func (m *ApplicationSetTreeQuery) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ApplicationSetTreeQuery) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ApplicationSetTreeQuery.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 *ApplicationSetTreeQuery) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplicationSetTreeQuery.Merge(m, src) -} -func (m *ApplicationSetTreeQuery) XXX_Size() int { - return m.Size() -} -func (m *ApplicationSetTreeQuery) XXX_DiscardUnknown() { - xxx_messageInfo_ApplicationSetTreeQuery.DiscardUnknown(m) -} - -var xxx_messageInfo_ApplicationSetTreeQuery proto.InternalMessageInfo - -func (m *ApplicationSetTreeQuery) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *ApplicationSetTreeQuery) GetAppsetNamespace() string { - if m != nil { - return m.AppsetNamespace - } - return "" -} - -// ApplicationSetGetQuery is a query for applicationset resources -type ApplicationSetGenerateRequest struct { - // the applicationsets - ApplicationSet *v1alpha1.ApplicationSet `protobuf:"bytes,1,opt,name=applicationSet,proto3" json:"applicationSet,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ApplicationSetGenerateRequest) Reset() { *m = ApplicationSetGenerateRequest{} } -func (m *ApplicationSetGenerateRequest) String() string { return proto.CompactTextString(m) } -func (*ApplicationSetGenerateRequest) ProtoMessage() {} -func (*ApplicationSetGenerateRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_eacb9df0ce5738fa, []int{6} -} -func (m *ApplicationSetGenerateRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ApplicationSetGenerateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ApplicationSetGenerateRequest.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 *ApplicationSetGenerateRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplicationSetGenerateRequest.Merge(m, src) -} -func (m *ApplicationSetGenerateRequest) XXX_Size() int { - return m.Size() -} -func (m *ApplicationSetGenerateRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ApplicationSetGenerateRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_ApplicationSetGenerateRequest proto.InternalMessageInfo - -func (m *ApplicationSetGenerateRequest) GetApplicationSet() *v1alpha1.ApplicationSet { - if m != nil { - return m.ApplicationSet - } - return nil -} - -// ApplicationSetGenerateResponse is a response for applicationset generate request -type ApplicationSetGenerateResponse struct { - Applications []*v1alpha1.Application `protobuf:"bytes,1,rep,name=applications,proto3" json:"applications,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ApplicationSetGenerateResponse) Reset() { *m = ApplicationSetGenerateResponse{} } -func (m *ApplicationSetGenerateResponse) String() string { return proto.CompactTextString(m) } -func (*ApplicationSetGenerateResponse) ProtoMessage() {} -func (*ApplicationSetGenerateResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_eacb9df0ce5738fa, []int{7} -} -func (m *ApplicationSetGenerateResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ApplicationSetGenerateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ApplicationSetGenerateResponse.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 *ApplicationSetGenerateResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplicationSetGenerateResponse.Merge(m, src) -} -func (m *ApplicationSetGenerateResponse) XXX_Size() int { - return m.Size() -} -func (m *ApplicationSetGenerateResponse) XXX_DiscardUnknown() { - xxx_messageInfo_ApplicationSetGenerateResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_ApplicationSetGenerateResponse proto.InternalMessageInfo - -func (m *ApplicationSetGenerateResponse) GetApplications() []*v1alpha1.Application { - if m != nil { - return m.Applications - } - return nil -} - func init() { proto.RegisterType((*ApplicationSetGetQuery)(nil), "applicationset.ApplicationSetGetQuery") proto.RegisterType((*ApplicationSetListQuery)(nil), "applicationset.ApplicationSetListQuery") proto.RegisterType((*ApplicationSetResponse)(nil), "applicationset.ApplicationSetResponse") proto.RegisterType((*ApplicationSetCreateRequest)(nil), "applicationset.ApplicationSetCreateRequest") proto.RegisterType((*ApplicationSetDeleteRequest)(nil), "applicationset.ApplicationSetDeleteRequest") - proto.RegisterType((*ApplicationSetTreeQuery)(nil), "applicationset.ApplicationSetTreeQuery") - proto.RegisterType((*ApplicationSetGenerateRequest)(nil), "applicationset.ApplicationSetGenerateRequest") - proto.RegisterType((*ApplicationSetGenerateResponse)(nil), "applicationset.ApplicationSetGenerateResponse") } func init() { @@ -499,49 +335,40 @@ func init() { } var fileDescriptor_eacb9df0ce5738fa = []byte{ - // 660 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x96, 0x4f, 0x6b, 0xd4, 0x4e, - 0x18, 0xc7, 0x99, 0xb6, 0x6c, 0xb7, 0xd3, 0xf2, 0xfb, 0xc1, 0x80, 0xed, 0x1a, 0xeb, 0x5a, 0x72, - 0xa8, 0xb5, 0xda, 0x09, 0x5d, 0x3d, 0xe9, 0xc9, 0x3f, 0x50, 0x0a, 0x45, 0x34, 0x2b, 0x0a, 0x7a, - 0x90, 0x69, 0xf6, 0x21, 0x8d, 0xcd, 0x26, 0xe3, 0xcc, 0x24, 0x50, 0x8a, 0x17, 0xc1, 0xa3, 0x78, - 0x10, 0xdf, 0x80, 0x5e, 0x7c, 0x01, 0xde, 0x3d, 0x78, 0xf1, 0x28, 0xf8, 0x06, 0xa4, 0xf8, 0x0e, - 0x7c, 0x03, 0x92, 0x49, 0xf6, 0x4f, 0x86, 0xfd, 0x53, 0x30, 0x7a, 0x9b, 0x67, 0x66, 0xf2, 0xcc, - 0x67, 0xbe, 0xcf, 0x93, 0x2f, 0x83, 0x37, 0x25, 0x88, 0x14, 0x84, 0xc3, 0x38, 0x0f, 0x03, 0x8f, - 0xa9, 0x20, 0x8e, 0x24, 0x28, 0x23, 0xa4, 0x5c, 0xc4, 0x2a, 0x26, 0xff, 0x95, 0x67, 0xad, 0x55, - 0x3f, 0x8e, 0xfd, 0x10, 0x1c, 0xc6, 0x03, 0x87, 0x45, 0x51, 0xac, 0xf2, 0x95, 0x7c, 0xb7, 0xb5, - 0xe7, 0x07, 0xea, 0x20, 0xd9, 0xa7, 0x5e, 0xdc, 0x75, 0x98, 0xf0, 0x63, 0x2e, 0xe2, 0x67, 0x7a, - 0xb0, 0xe5, 0x75, 0x9c, 0xb4, 0xe5, 0xf0, 0x43, 0x3f, 0xfb, 0x52, 0x0e, 0x9f, 0xe5, 0xa4, 0xdb, - 0x2c, 0xe4, 0x07, 0x6c, 0xdb, 0xf1, 0x21, 0x02, 0xc1, 0x14, 0x74, 0xf2, 0x6c, 0xf6, 0x43, 0xbc, - 0x7c, 0x73, 0xb0, 0xaf, 0x0d, 0x6a, 0x07, 0xd4, 0xfd, 0x04, 0xc4, 0x11, 0x21, 0x78, 0x2e, 0x62, - 0x5d, 0x68, 0xa0, 0x35, 0xb4, 0xb1, 0xe0, 0xea, 0x31, 0xd9, 0xc0, 0xff, 0x33, 0xce, 0x25, 0xa8, - 0xbb, 0xac, 0x0b, 0x92, 0x33, 0x0f, 0x1a, 0x33, 0x7a, 0xd9, 0x9c, 0xb6, 0x8f, 0xf1, 0x4a, 0x39, - 0xef, 0x5e, 0x20, 0x8b, 0xc4, 0x16, 0xae, 0x67, 0xcc, 0xe0, 0x29, 0xd9, 0x40, 0x6b, 0xb3, 0x1b, - 0x0b, 0x6e, 0x3f, 0xce, 0xd6, 0x24, 0x84, 0xe0, 0xa9, 0x58, 0x14, 0x99, 0xfb, 0xf1, 0xa8, 0xc3, - 0x67, 0x47, 0x1f, 0xfe, 0x11, 0x99, 0xb7, 0x72, 0x41, 0xf2, 0x4c, 0x5c, 0xd2, 0xc0, 0xf3, 0xc5, - 0x61, 0xc5, 0xc5, 0x7a, 0x21, 0x51, 0xd8, 0xa8, 0x83, 0x06, 0x58, 0x6c, 0xed, 0xd1, 0x81, 0xe0, - 0xb4, 0x27, 0xb8, 0x1e, 0x3c, 0xf5, 0x3a, 0x34, 0x6d, 0x51, 0x7e, 0xe8, 0xd3, 0x4c, 0x70, 0x3a, - 0xf4, 0x39, 0xed, 0x09, 0x4e, 0x0d, 0x0e, 0xe3, 0x0c, 0xfb, 0x0b, 0xc2, 0xe7, 0xca, 0x5b, 0x6e, - 0x0b, 0x60, 0x0a, 0x5c, 0x78, 0x9e, 0x80, 0x1c, 0x45, 0x85, 0xfe, 0x3e, 0x15, 0x59, 0xc6, 0xb5, - 0x84, 0x4b, 0x10, 0xb9, 0x06, 0x75, 0xb7, 0x88, 0xb2, 0xf9, 0x8e, 0x38, 0x72, 0x93, 0x48, 0x2b, - 0x5f, 0x77, 0x8b, 0xc8, 0x7e, 0x62, 0x5e, 0xe2, 0x0e, 0x84, 0x30, 0xb8, 0xc4, 0x9f, 0xb5, 0xd2, - 0x23, 0xb3, 0x95, 0x1e, 0x08, 0x80, 0x2a, 0x7a, 0xf4, 0x1d, 0xc2, 0xe7, 0xcd, 0xe6, 0xcf, 0xff, - 0x8e, 0xd1, 0xea, 0xb7, 0xff, 0x81, 0xfa, 0x6d, 0x50, 0xf6, 0x1b, 0x84, 0x9b, 0xe3, 0xb8, 0x8a, - 0x36, 0xee, 0xe2, 0xa5, 0xe1, 0x92, 0xe9, 0xff, 0x68, 0xb1, 0xb5, 0x5b, 0x19, 0x96, 0x5b, 0x4a, - 0xdf, 0xfa, 0x35, 0x8f, 0xcf, 0x94, 0x89, 0xda, 0x20, 0xd2, 0xc0, 0x03, 0xf2, 0x01, 0xe1, 0xd9, - 0x1d, 0x50, 0x64, 0x9d, 0x1a, 0xd6, 0x36, 0xda, 0x55, 0xac, 0x4a, 0x95, 0xb3, 0xd7, 0x5f, 0x7e, - 0xff, 0xf9, 0x76, 0x66, 0x8d, 0x34, 0xb5, 0x57, 0xa6, 0xdb, 0x86, 0xbf, 0x4a, 0xe7, 0x38, 0x6b, - 0x89, 0x17, 0xe4, 0x35, 0xc2, 0xf5, 0x9e, 0x86, 0x64, 0x6b, 0x1a, 0x6a, 0xa9, 0x07, 0x2c, 0x7a, - 0xda, 0xed, 0x79, 0x69, 0x6c, 0x5b, 0x33, 0xad, 0xda, 0x2b, 0x63, 0x98, 0xae, 0xa3, 0x4d, 0xf2, - 0x1e, 0xe1, 0xb9, 0xcc, 0x10, 0xc9, 0xc5, 0xc9, 0xc9, 0xfb, 0xa6, 0x69, 0xdd, 0xab, 0x52, 0xb7, - 0x2c, 0xad, 0x7d, 0x41, 0x73, 0x9e, 0x25, 0xe3, 0x38, 0xc9, 0x27, 0x84, 0x6b, 0xb9, 0x19, 0x91, - 0xcb, 0x93, 0x31, 0x4b, 0x96, 0x55, 0x71, 0x89, 0x1d, 0x8d, 0x79, 0x69, 0xbc, 0x9c, 0xa6, 0x77, - 0xbd, 0x42, 0xb8, 0x96, 0xdb, 0xcf, 0x34, 0xec, 0x92, 0x49, 0x59, 0x53, 0x3a, 0xb8, 0x5f, 0xdf, - 0xa2, 0xe7, 0x36, 0xa7, 0xf5, 0xdc, 0x67, 0x84, 0x97, 0x5c, 0x90, 0x71, 0x22, 0x3c, 0xc8, 0x1c, - 0x6b, 0x5a, 0xad, 0xfb, 0xae, 0x56, 0x6d, 0xad, 0xb3, 0xb4, 0xf6, 0x35, 0xcd, 0x4c, 0xc9, 0x95, - 0xc9, 0xcc, 0x8e, 0x28, 0x78, 0xb7, 0x94, 0x00, 0xb8, 0xb5, 0xfb, 0xf5, 0xa4, 0x89, 0xbe, 0x9d, - 0x34, 0xd1, 0x8f, 0x93, 0x26, 0x7a, 0x7c, 0xe3, 0x74, 0xef, 0x0e, 0x2f, 0x0c, 0x20, 0x32, 0x1f, - 0x3a, 0xfb, 0x35, 0xfd, 0xda, 0xb8, 0xfa, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x30, 0x08, 0x85, 0x97, - 0x17, 0x09, 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. @@ -558,16 +385,12 @@ const _ = grpc.SupportPackageIsVersion4 type ApplicationSetServiceClient interface { // Get returns an applicationset by name Get(ctx context.Context, in *ApplicationSetGetQuery, opts ...grpc.CallOption) (*v1alpha1.ApplicationSet, error) - // Generate generates - Generate(ctx context.Context, in *ApplicationSetGenerateRequest, opts ...grpc.CallOption) (*ApplicationSetGenerateResponse, error) //List returns list of applicationset List(ctx context.Context, in *ApplicationSetListQuery, opts ...grpc.CallOption) (*v1alpha1.ApplicationSetList, error) //Create creates an applicationset Create(ctx context.Context, in *ApplicationSetCreateRequest, opts ...grpc.CallOption) (*v1alpha1.ApplicationSet, error) // Delete deletes an application set Delete(ctx context.Context, in *ApplicationSetDeleteRequest, opts ...grpc.CallOption) (*ApplicationSetResponse, error) - // ResourceTree returns resource tree - ResourceTree(ctx context.Context, in *ApplicationSetTreeQuery, opts ...grpc.CallOption) (*v1alpha1.ApplicationSetTree, error) } type applicationSetServiceClient struct { @@ -587,15 +410,6 @@ func (c *applicationSetServiceClient) Get(ctx context.Context, in *ApplicationSe return out, nil } -func (c *applicationSetServiceClient) Generate(ctx context.Context, in *ApplicationSetGenerateRequest, opts ...grpc.CallOption) (*ApplicationSetGenerateResponse, error) { - out := new(ApplicationSetGenerateResponse) - err := c.cc.Invoke(ctx, "/applicationset.ApplicationSetService/Generate", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *applicationSetServiceClient) List(ctx context.Context, in *ApplicationSetListQuery, opts ...grpc.CallOption) (*v1alpha1.ApplicationSetList, error) { out := new(v1alpha1.ApplicationSetList) err := c.cc.Invoke(ctx, "/applicationset.ApplicationSetService/List", in, out, opts...) @@ -623,29 +437,16 @@ func (c *applicationSetServiceClient) Delete(ctx context.Context, in *Applicatio return out, nil } -func (c *applicationSetServiceClient) ResourceTree(ctx context.Context, in *ApplicationSetTreeQuery, opts ...grpc.CallOption) (*v1alpha1.ApplicationSetTree, error) { - out := new(v1alpha1.ApplicationSetTree) - err := c.cc.Invoke(ctx, "/applicationset.ApplicationSetService/ResourceTree", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - // ApplicationSetServiceServer is the server API for ApplicationSetService service. type ApplicationSetServiceServer interface { // Get returns an applicationset by name Get(context.Context, *ApplicationSetGetQuery) (*v1alpha1.ApplicationSet, error) - // Generate generates - Generate(context.Context, *ApplicationSetGenerateRequest) (*ApplicationSetGenerateResponse, error) //List returns list of applicationset List(context.Context, *ApplicationSetListQuery) (*v1alpha1.ApplicationSetList, error) //Create creates an applicationset Create(context.Context, *ApplicationSetCreateRequest) (*v1alpha1.ApplicationSet, error) // Delete deletes an application set Delete(context.Context, *ApplicationSetDeleteRequest) (*ApplicationSetResponse, error) - // ResourceTree returns resource tree - ResourceTree(context.Context, *ApplicationSetTreeQuery) (*v1alpha1.ApplicationSetTree, error) } // UnimplementedApplicationSetServiceServer can be embedded to have forward compatible implementations. @@ -655,9 +456,6 @@ type UnimplementedApplicationSetServiceServer struct { func (*UnimplementedApplicationSetServiceServer) Get(ctx context.Context, req *ApplicationSetGetQuery) (*v1alpha1.ApplicationSet, error) { return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") } -func (*UnimplementedApplicationSetServiceServer) Generate(ctx context.Context, req *ApplicationSetGenerateRequest) (*ApplicationSetGenerateResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Generate not implemented") -} func (*UnimplementedApplicationSetServiceServer) List(ctx context.Context, req *ApplicationSetListQuery) (*v1alpha1.ApplicationSetList, error) { return nil, status.Errorf(codes.Unimplemented, "method List not implemented") } @@ -667,9 +465,6 @@ func (*UnimplementedApplicationSetServiceServer) Create(ctx context.Context, req func (*UnimplementedApplicationSetServiceServer) Delete(ctx context.Context, req *ApplicationSetDeleteRequest) (*ApplicationSetResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") } -func (*UnimplementedApplicationSetServiceServer) ResourceTree(ctx context.Context, req *ApplicationSetTreeQuery) (*v1alpha1.ApplicationSetTree, error) { - return nil, status.Errorf(codes.Unimplemented, "method ResourceTree not implemented") -} func RegisterApplicationSetServiceServer(s *grpc.Server, srv ApplicationSetServiceServer) { s.RegisterService(&_ApplicationSetService_serviceDesc, srv) @@ -693,24 +488,6 @@ func _ApplicationSetService_Get_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } -func _ApplicationSetService_Generate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ApplicationSetGenerateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ApplicationSetServiceServer).Generate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/applicationset.ApplicationSetService/Generate", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ApplicationSetServiceServer).Generate(ctx, req.(*ApplicationSetGenerateRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _ApplicationSetService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ApplicationSetListQuery) if err := dec(in); err != nil { @@ -765,24 +542,6 @@ func _ApplicationSetService_Delete_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } -func _ApplicationSetService_ResourceTree_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ApplicationSetTreeQuery) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ApplicationSetServiceServer).ResourceTree(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/applicationset.ApplicationSetService/ResourceTree", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ApplicationSetServiceServer).ResourceTree(ctx, req.(*ApplicationSetTreeQuery)) - } - return interceptor(ctx, in, info, handler) -} - var _ApplicationSetService_serviceDesc = grpc.ServiceDesc{ ServiceName: "applicationset.ApplicationSetService", HandlerType: (*ApplicationSetServiceServer)(nil), @@ -791,10 +550,6 @@ var _ApplicationSetService_serviceDesc = grpc.ServiceDesc{ MethodName: "Get", Handler: _ApplicationSetService_Get_Handler, }, - { - MethodName: "Generate", - Handler: _ApplicationSetService_Generate_Handler, - }, { MethodName: "List", Handler: _ApplicationSetService_List_Handler, @@ -807,10 +562,6 @@ var _ApplicationSetService_serviceDesc = grpc.ServiceDesc{ MethodName: "Delete", Handler: _ApplicationSetService_Delete_Handler, }, - { - MethodName: "ResourceTree", - Handler: _ApplicationSetService_ResourceTree_Handler, - }, }, Streams: []grpc.StreamDesc{}, Metadata: "server/applicationset/applicationset.proto", @@ -977,16 +728,6 @@ func (m *ApplicationSetCreateRequest) MarshalToSizedBuffer(dAtA []byte) (int, er i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.DryRun { - i-- - if m.DryRun { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x18 - } if m.Upsert { i-- if m.Upsert { @@ -1053,127 +794,6 @@ func (m *ApplicationSetDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (int, er return len(dAtA) - i, nil } -func (m *ApplicationSetTreeQuery) 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 *ApplicationSetTreeQuery) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ApplicationSetTreeQuery) 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.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) - i = encodeVarintApplicationset(dAtA, i, uint64(len(m.Name))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ApplicationSetGenerateRequest) 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 *ApplicationSetGenerateRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ApplicationSetGenerateRequest) 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.ApplicationSet != nil { - { - size, err := m.ApplicationSet.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintApplicationset(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ApplicationSetGenerateResponse) 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 *ApplicationSetGenerateResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ApplicationSetGenerateResponse) 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.Applications) > 0 { - for iNdEx := len(m.Applications) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Applications[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintApplicationset(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - func encodeVarintApplicationset(dAtA []byte, offset int, v uint64) int { offset -= sovApplicationset(v) base := offset @@ -1264,9 +884,6 @@ func (m *ApplicationSetCreateRequest) Size() (n int) { if m.Upsert { n += 2 } - if m.DryRun { - n += 2 - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -1293,60 +910,6 @@ func (m *ApplicationSetDeleteRequest) Size() (n int) { return n } -func (m *ApplicationSetTreeQuery) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - 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) - } - return n -} - -func (m *ApplicationSetGenerateRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.ApplicationSet != nil { - l = m.ApplicationSet.Size() - n += 1 + l + sovApplicationset(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *ApplicationSetGenerateResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Applications) > 0 { - for _, e := range m.Applications { - l = e.Size() - n += 1 + l + sovApplicationset(uint64(l)) - } - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - func sovApplicationset(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1819,26 +1382,6 @@ func (m *ApplicationSetCreateRequest) Unmarshal(dAtA []byte) error { } } m.Upsert = bool(v != 0) - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DryRun", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplicationset - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.DryRun = bool(v != 0) default: iNdEx = preIndex skippy, err := skipApplicationset(dAtA[iNdEx:]) @@ -1976,293 +1519,6 @@ func (m *ApplicationSetDeleteRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *ApplicationSetTreeQuery) 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 ErrIntOverflowApplicationset - } - 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: ApplicationSetTreeQuery: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ApplicationSetTreeQuery: 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 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.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:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthApplicationset - } - 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 *ApplicationSetGenerateRequest) 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 ErrIntOverflowApplicationset - } - 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: ApplicationSetGenerateRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ApplicationSetGenerateRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ApplicationSet", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplicationset - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthApplicationset - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthApplicationset - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ApplicationSet == nil { - m.ApplicationSet = &v1alpha1.ApplicationSet{} - } - if err := m.ApplicationSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipApplicationset(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthApplicationset - } - 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 *ApplicationSetGenerateResponse) 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 ErrIntOverflowApplicationset - } - 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: ApplicationSetGenerateResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ApplicationSetGenerateResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Applications", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowApplicationset - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthApplicationset - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthApplicationset - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Applications = append(m.Applications, &v1alpha1.Application{}) - if err := m.Applications[len(m.Applications)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipApplicationset(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthApplicationset - } - 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 skipApplicationset(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/pkg/apiclient/applicationset/applicationset.pb.gw.go b/pkg/apiclient/applicationset/applicationset.pb.gw.go index 349c5729bea94..5e4c73f7add3b 100644 --- a/pkg/apiclient/applicationset/applicationset.pb.gw.go +++ b/pkg/apiclient/applicationset/applicationset.pb.gw.go @@ -105,40 +105,6 @@ func local_request_ApplicationSetService_Get_0(ctx context.Context, marshaler ru } -func request_ApplicationSetService_Generate_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationSetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ApplicationSetGenerateRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.Generate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_ApplicationSetService_Generate_0(ctx context.Context, marshaler runtime.Marshaler, server ApplicationSetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ApplicationSetGenerateRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := server.Generate(ctx, &protoReq) - return msg, metadata, err - -} - var ( filter_ApplicationSetService_List_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) @@ -299,78 +265,6 @@ func local_request_ApplicationSetService_Delete_0(ctx context.Context, marshaler } -var ( - filter_ApplicationSetService_ResourceTree_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} -) - -func request_ApplicationSetService_ResourceTree_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationSetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ApplicationSetTreeQuery - 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.String(val) - - if err != nil { - 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_ResourceTree_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.ResourceTree(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_ApplicationSetService_ResourceTree_0(ctx context.Context, marshaler runtime.Marshaler, server ApplicationSetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ApplicationSetTreeQuery - 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.String(val) - - if err != nil { - 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_ResourceTree_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := server.ResourceTree(ctx, &protoReq) - return msg, metadata, err - -} - // RegisterApplicationSetServiceHandlerServer registers the http handlers for service ApplicationSetService to "mux". // UnaryRPC :call ApplicationSetServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -400,29 +294,6 @@ func RegisterApplicationSetServiceHandlerServer(ctx context.Context, mux *runtim }) - mux.Handle("POST", pattern_ApplicationSetService_Generate_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_ApplicationSetService_Generate_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_ApplicationSetService_Generate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_ApplicationSetService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -492,29 +363,6 @@ func RegisterApplicationSetServiceHandlerServer(ctx context.Context, mux *runtim }) - mux.Handle("GET", pattern_ApplicationSetService_ResourceTree_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_ApplicationSetService_ResourceTree_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_ApplicationSetService_ResourceTree_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - return nil } @@ -576,26 +424,6 @@ func RegisterApplicationSetServiceHandlerClient(ctx context.Context, mux *runtim }) - mux.Handle("POST", pattern_ApplicationSetService_Generate_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_ApplicationSetService_Generate_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_ApplicationSetService_Generate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_ApplicationSetService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -656,53 +484,25 @@ func RegisterApplicationSetServiceHandlerClient(ctx context.Context, mux *runtim }) - mux.Handle("GET", pattern_ApplicationSetService_ResourceTree_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_ApplicationSetService_ResourceTree_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_ApplicationSetService_ResourceTree_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - return nil } var ( pattern_ApplicationSetService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "applicationsets", "name"}, "", runtime.AssumeColonVerbOpt(true))) - pattern_ApplicationSetService_Generate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "applicationsets"}, "", runtime.AssumeColonVerbOpt(true))) - pattern_ApplicationSetService_List_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "applicationsets"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ApplicationSetService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "applicationsets"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ApplicationSetService_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "applicationsets", "name"}, "", runtime.AssumeColonVerbOpt(true))) - - pattern_ApplicationSetService_ResourceTree_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "applicationsets", "name", "resource-tree"}, "", runtime.AssumeColonVerbOpt(true))) ) var ( forward_ApplicationSetService_Get_0 = runtime.ForwardResponseMessage - forward_ApplicationSetService_Generate_0 = runtime.ForwardResponseMessage - forward_ApplicationSetService_List_0 = runtime.ForwardResponseMessage forward_ApplicationSetService_Create_0 = runtime.ForwardResponseMessage forward_ApplicationSetService_Delete_0 = runtime.ForwardResponseMessage - - forward_ApplicationSetService_ResourceTree_0 = runtime.ForwardResponseMessage ) diff --git a/pkg/apiclient/cluster/mocks/ClusterServiceServer.go b/pkg/apiclient/cluster/mocks/ClusterServiceServer.go index 27e33721be747..f6118b7a43f95 100644 --- a/pkg/apiclient/cluster/mocks/ClusterServiceServer.go +++ b/pkg/apiclient/cluster/mocks/ClusterServiceServer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v1.0.0. DO NOT EDIT. package mocks @@ -21,15 +21,7 @@ type ClusterServiceServer struct { func (_m *ClusterServiceServer) Create(_a0 context.Context, _a1 *cluster.ClusterCreateRequest) (*v1alpha1.Cluster, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for Create") - } - var r0 *v1alpha1.Cluster - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterCreateRequest) (*v1alpha1.Cluster, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterCreateRequest) *v1alpha1.Cluster); ok { r0 = rf(_a0, _a1) } else { @@ -38,6 +30,7 @@ func (_m *ClusterServiceServer) Create(_a0 context.Context, _a1 *cluster.Cluster } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, *cluster.ClusterCreateRequest) error); ok { r1 = rf(_a0, _a1) } else { @@ -51,15 +44,7 @@ func (_m *ClusterServiceServer) Create(_a0 context.Context, _a1 *cluster.Cluster func (_m *ClusterServiceServer) Delete(_a0 context.Context, _a1 *cluster.ClusterQuery) (*cluster.ClusterResponse, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for Delete") - } - var r0 *cluster.ClusterResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterQuery) (*cluster.ClusterResponse, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterQuery) *cluster.ClusterResponse); ok { r0 = rf(_a0, _a1) } else { @@ -68,6 +53,7 @@ func (_m *ClusterServiceServer) Delete(_a0 context.Context, _a1 *cluster.Cluster } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, *cluster.ClusterQuery) error); ok { r1 = rf(_a0, _a1) } else { @@ -81,15 +67,7 @@ func (_m *ClusterServiceServer) Delete(_a0 context.Context, _a1 *cluster.Cluster func (_m *ClusterServiceServer) Get(_a0 context.Context, _a1 *cluster.ClusterQuery) (*v1alpha1.Cluster, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for Get") - } - var r0 *v1alpha1.Cluster - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterQuery) (*v1alpha1.Cluster, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterQuery) *v1alpha1.Cluster); ok { r0 = rf(_a0, _a1) } else { @@ -98,36 +76,7 @@ func (_m *ClusterServiceServer) Get(_a0 context.Context, _a1 *cluster.ClusterQue } } - if rf, ok := ret.Get(1).(func(context.Context, *cluster.ClusterQuery) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// InvalidateCache provides a mock function with given fields: _a0, _a1 -func (_m *ClusterServiceServer) InvalidateCache(_a0 context.Context, _a1 *cluster.ClusterQuery) (*v1alpha1.Cluster, error) { - ret := _m.Called(_a0, _a1) - - if len(ret) == 0 { - panic("no return value specified for InvalidateCache") - } - - var r0 *v1alpha1.Cluster var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterQuery) (*v1alpha1.Cluster, error)); ok { - return rf(_a0, _a1) - } - if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterQuery) *v1alpha1.Cluster); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.Cluster) - } - } - if rf, ok := ret.Get(1).(func(context.Context, *cluster.ClusterQuery) error); ok { r1 = rf(_a0, _a1) } else { @@ -141,15 +90,7 @@ func (_m *ClusterServiceServer) InvalidateCache(_a0 context.Context, _a1 *cluste func (_m *ClusterServiceServer) List(_a0 context.Context, _a1 *cluster.ClusterQuery) (*v1alpha1.ClusterList, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for List") - } - var r0 *v1alpha1.ClusterList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterQuery) (*v1alpha1.ClusterList, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterQuery) *v1alpha1.ClusterList); ok { r0 = rf(_a0, _a1) } else { @@ -158,36 +99,7 @@ func (_m *ClusterServiceServer) List(_a0 context.Context, _a1 *cluster.ClusterQu } } - if rf, ok := ret.Get(1).(func(context.Context, *cluster.ClusterQuery) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RotateAuth provides a mock function with given fields: _a0, _a1 -func (_m *ClusterServiceServer) RotateAuth(_a0 context.Context, _a1 *cluster.ClusterQuery) (*cluster.ClusterResponse, error) { - ret := _m.Called(_a0, _a1) - - if len(ret) == 0 { - panic("no return value specified for RotateAuth") - } - - var r0 *cluster.ClusterResponse var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterQuery) (*cluster.ClusterResponse, error)); ok { - return rf(_a0, _a1) - } - if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterQuery) *cluster.ClusterResponse); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*cluster.ClusterResponse) - } - } - if rf, ok := ret.Get(1).(func(context.Context, *cluster.ClusterQuery) error); ok { r1 = rf(_a0, _a1) } else { @@ -201,15 +113,7 @@ func (_m *ClusterServiceServer) RotateAuth(_a0 context.Context, _a1 *cluster.Clu func (_m *ClusterServiceServer) Update(_a0 context.Context, _a1 *cluster.ClusterUpdateRequest) (*v1alpha1.Cluster, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for Update") - } - var r0 *v1alpha1.Cluster - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterUpdateRequest) (*v1alpha1.Cluster, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, *cluster.ClusterUpdateRequest) *v1alpha1.Cluster); ok { r0 = rf(_a0, _a1) } else { @@ -218,6 +122,7 @@ func (_m *ClusterServiceServer) Update(_a0 context.Context, _a1 *cluster.Cluster } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, *cluster.ClusterUpdateRequest) error); ok { r1 = rf(_a0, _a1) } else { @@ -226,17 +131,3 @@ func (_m *ClusterServiceServer) Update(_a0 context.Context, _a1 *cluster.Cluster return r0, r1 } - -// NewClusterServiceServer creates a new instance of ClusterServiceServer. 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 NewClusterServiceServer(t interface { - mock.TestingT - Cleanup(func()) -}) *ClusterServiceServer { - mock := &ClusterServiceServer{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/apiclient/grpcproxy.go b/pkg/apiclient/grpcproxy.go index b426db178a2ac..28af7b62783df 100644 --- a/pkg/apiclient/grpcproxy.go +++ b/pkg/apiclient/grpcproxy.go @@ -3,7 +3,6 @@ package apiclient import ( "bytes" "encoding/binary" - "errors" "fmt" "io" "net" @@ -66,6 +65,7 @@ func (c *client) executeRequest(fullMethodName string, msg []byte, md metadata.M requestURL = fmt.Sprintf("%s://%s%s", schema, c.ServerAddr, fullMethodName) } req, err := http.NewRequest(http.MethodPost, requestURL, bytes.NewReader(toFrame(msg))) + if err != nil { return nil, err } @@ -108,6 +108,7 @@ func (c *client) startGRPCProxy() (*grpc.Server, net.Listener, error) { } serverAddr := fmt.Sprintf("%s/argocd-%s.sock", os.TempDir(), randSuffix) ln, err := net.Listen("unix", serverAddr) + if err != nil { return nil, nil, err } @@ -130,12 +131,13 @@ func (c *client) startGRPCProxy() (*grpc.Server, net.Listener, error) { } md, _ := metadata.FromIncomingContext(stream.Context()) - headersMD, err := parseGRPCHeaders(c.Headers) - if err != nil { - return err - } - md = metadata.Join(md, headersMD) + 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]) + } resp, err := c.executeRequest(fullMethodName, msg, md) if err != nil { @@ -152,7 +154,7 @@ func (c *client) startGRPCProxy() (*grpc.Server, net.Listener, error) { for { header := make([]byte, frameHeaderLength) if _, err := io.ReadAtLeast(resp.Body, header, frameHeaderLength); err != nil { - if errors.Is(err, io.EOF) { + if err == io.EOF { err = io.ErrUnexpectedEOF } return err @@ -165,7 +167,7 @@ func (c *client) startGRPCProxy() (*grpc.Server, net.Listener, error) { data := make([]byte, length) if read, err := io.ReadAtLeast(resp.Body, data, length); err != nil { - if !errors.Is(err, io.EOF) { + if err != io.EOF { return err } else if read < length { return io.ErrUnexpectedEOF @@ -177,6 +179,7 @@ func (c *client) startGRPCProxy() (*grpc.Server, net.Listener, error) { if err := stream.SendMsg(data); err != nil { return err } + } })) go func() { @@ -213,16 +216,3 @@ 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 8dbb20ce7bc70..5540580c21f45 100644 --- a/pkg/apiclient/repository/repository.pb.go +++ b/pkg/apiclient/repository/repository.pb.go @@ -163,16 +163,12 @@ func (m *AppInfo) GetPath() string { // RepoAppDetailsQuery contains query information for app details request type RepoAppDetailsQuery struct { - Source *v1alpha1.ApplicationSource `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` - AppName string `protobuf:"bytes,2,opt,name=appName,proto3" json:"appName,omitempty"` - AppProject string `protobuf:"bytes,3,opt,name=appProject,proto3" json:"appProject,omitempty"` - // source index (for multi source apps) - SourceIndex int32 `protobuf:"varint,4,opt,name=sourceIndex,proto3" json:"sourceIndex,omitempty"` - // versionId from historical data (for multi source apps) - VersionId int32 `protobuf:"varint,5,opt,name=versionId,proto3" json:"versionId,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Source *v1alpha1.ApplicationSource `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` + AppName string `protobuf:"bytes,2,opt,name=appName,proto3" json:"appName,omitempty"` + AppProject string `protobuf:"bytes,3,opt,name=appProject,proto3" json:"appProject,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *RepoAppDetailsQuery) Reset() { *m = RepoAppDetailsQuery{} } @@ -229,20 +225,6 @@ func (m *RepoAppDetailsQuery) GetAppProject() string { return "" } -func (m *RepoAppDetailsQuery) GetSourceIndex() int32 { - if m != nil { - return m.SourceIndex - } - return 0 -} - -func (m *RepoAppDetailsQuery) GetVersionId() int32 { - if m != nil { - return m.VersionId - } - return 0 -} - // RepoAppsResponse contains applications of specified repository type RepoAppsResponse struct { Items []*AppInfo `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` @@ -296,9 +278,7 @@ type RepoQuery struct { // Repo URL for query Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` // Whether to force a cache refresh on repo's connection state - ForceRefresh bool `protobuf:"varint,2,opt,name=forceRefresh,proto3" json:"forceRefresh,omitempty"` - // App project for query - AppProject string `protobuf:"bytes,3,opt,name=appProject,proto3" json:"appProject,omitempty"` + ForceRefresh bool `protobuf:"varint,2,opt,name=forceRefresh,proto3" json:"forceRefresh,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -351,13 +331,6 @@ func (m *RepoQuery) GetForceRefresh() bool { return false } -func (m *RepoQuery) GetAppProject() string { - if m != nil { - return m.AppProject - } - return "" -} - // RepoAccessQuery is a query for checking access to a repo type RepoAccessQuery struct { // The URL to the repo @@ -730,81 +703,79 @@ func init() { } var fileDescriptor_8d38260443475705 = []byte{ - // 1178 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x5d, 0x6f, 0x1b, 0x45, - 0x17, 0xd6, 0x26, 0x8d, 0x93, 0x9c, 0x7c, 0xd4, 0x99, 0xe4, 0xed, 0xbb, 0xb8, 0x6e, 0x1a, 0x6d, - 0x4b, 0x15, 0xa2, 0xb2, 0x6e, 0x8c, 0x10, 0xa8, 0x08, 0x24, 0xe7, 0x43, 0x4d, 0x44, 0x44, 0xca, - 0x56, 0xe1, 0x02, 0x81, 0xd0, 0x64, 0x7d, 0x62, 0x6f, 0xbb, 0xde, 0x9d, 0xce, 0x8c, 0x4d, 0xad, - 0xaa, 0x37, 0x5c, 0x21, 0xc1, 0x0d, 0x42, 0x48, 0xdc, 0x21, 0x24, 0x24, 0x2e, 0xf8, 0x23, 0x5c, - 0x22, 0xf1, 0x07, 0x50, 0xc4, 0x8f, 0xe0, 0x0a, 0xa1, 0x99, 0x59, 0xef, 0xae, 0x13, 0xdb, 0x49, - 0x45, 0xc8, 0xdd, 0xcc, 0x73, 0xce, 0x9e, 0xf3, 0xcc, 0xb3, 0xe7, 0x9c, 0x9d, 0x05, 0x47, 0x20, - 0xef, 0x20, 0xaf, 0x70, 0x64, 0xb1, 0x08, 0x64, 0xcc, 0xbb, 0xb9, 0xa5, 0xcb, 0x78, 0x2c, 0x63, - 0x02, 0x19, 0x52, 0x2a, 0x37, 0xe2, 0xb8, 0x11, 0x62, 0x85, 0xb2, 0xa0, 0x42, 0xa3, 0x28, 0x96, - 0x54, 0x06, 0x71, 0x24, 0x8c, 0x67, 0x69, 0xaf, 0x11, 0xc8, 0x66, 0xfb, 0xd0, 0xf5, 0xe3, 0x56, - 0x85, 0xf2, 0x46, 0xcc, 0x78, 0xfc, 0x58, 0x2f, 0x5e, 0xf7, 0xeb, 0x95, 0x4e, 0xb5, 0xc2, 0x9e, - 0x34, 0xd4, 0x93, 0xa2, 0x42, 0x19, 0x0b, 0x03, 0x5f, 0x3f, 0x5b, 0xe9, 0xac, 0xd3, 0x90, 0x35, - 0xe9, 0x7a, 0xa5, 0x81, 0x11, 0x72, 0x2a, 0xb1, 0x9e, 0x44, 0xdb, 0x3e, 0x23, 0x9a, 0xa6, 0x75, - 0x26, 0x7d, 0xa7, 0x0b, 0x73, 0x1e, 0xb2, 0xb8, 0xc6, 0x98, 0xf8, 0xb0, 0x8d, 0xbc, 0x4b, 0x08, - 0x5c, 0x51, 0x4e, 0xb6, 0xb5, 0x62, 0xad, 0x4e, 0x7b, 0x7a, 0x4d, 0x4a, 0x30, 0xc5, 0xb1, 0x13, - 0x88, 0x20, 0x8e, 0xec, 0x31, 0x8d, 0xa7, 0x7b, 0x62, 0xc3, 0x24, 0x65, 0xec, 0x03, 0xda, 0x42, - 0x7b, 0x5c, 0x9b, 0x7a, 0x5b, 0xb2, 0x0c, 0x40, 0x19, 0x7b, 0xc8, 0xe3, 0xc7, 0xe8, 0x4b, 0xfb, - 0x8a, 0x36, 0xe6, 0x10, 0x67, 0x1d, 0x26, 0x6b, 0x8c, 0xed, 0x46, 0x47, 0xb1, 0x4a, 0x2a, 0xbb, - 0x0c, 0x7b, 0x49, 0xd5, 0x5a, 0x61, 0x8c, 0xca, 0x66, 0x92, 0x50, 0xaf, 0x9d, 0xbf, 0x2c, 0x58, - 0x4c, 0xe8, 0x6e, 0xa1, 0xa4, 0x41, 0x98, 0x90, 0x6e, 0x40, 0x41, 0xc4, 0x6d, 0xee, 0x9b, 0x08, - 0x33, 0xd5, 0x7d, 0x37, 0x53, 0xc7, 0xed, 0xa9, 0xa3, 0x17, 0x9f, 0xf9, 0x75, 0xb7, 0x53, 0x75, - 0xd9, 0x93, 0x86, 0xab, 0xb4, 0x76, 0x73, 0x5a, 0xbb, 0x3d, 0xad, 0xdd, 0x5a, 0x06, 0x3e, 0xd2, - 0x61, 0xbd, 0x24, 0x7c, 0xfe, 0xb4, 0x63, 0xa3, 0x4e, 0x3b, 0x7e, 0xf2, 0xb4, 0x64, 0x05, 0x66, - 0x4c, 0x8c, 0xdd, 0xa8, 0x8e, 0xcf, 0xb4, 0x1c, 0x13, 0x5e, 0x1e, 0x22, 0x65, 0x98, 0xee, 0x20, - 0x57, 0xa2, 0xee, 0xd6, 0xed, 0x09, 0x6d, 0xcf, 0x00, 0xe7, 0x5d, 0x28, 0xf6, 0x5e, 0x94, 0x87, - 0x82, 0xc5, 0x91, 0x40, 0xf2, 0x1a, 0x4c, 0x04, 0x12, 0x5b, 0xc2, 0xb6, 0x56, 0xc6, 0x57, 0x67, - 0xaa, 0x8b, 0x6e, 0xee, 0xf5, 0x26, 0xd2, 0x7a, 0xc6, 0xc3, 0xf1, 0x61, 0x5a, 0x3d, 0x3e, 0xfc, - 0x1d, 0x3b, 0x30, 0x7b, 0x14, 0xab, 0xa3, 0xe2, 0x11, 0x47, 0x61, 0x64, 0x9f, 0xf2, 0xfa, 0xb0, - 0xb3, 0xce, 0xe8, 0xfc, 0x38, 0x01, 0x57, 0x35, 0x49, 0xdf, 0x47, 0x31, 0xba, 0x9e, 0xda, 0x02, - 0x79, 0x94, 0xc9, 0x98, 0xee, 0x95, 0x8d, 0x51, 0x21, 0x3e, 0x8f, 0x79, 0x3d, 0xc9, 0x90, 0xee, - 0xc9, 0x6d, 0x98, 0x13, 0xa2, 0xf9, 0x90, 0x07, 0x1d, 0x2a, 0xf1, 0x7d, 0xec, 0x26, 0x45, 0xd5, - 0x0f, 0xaa, 0x08, 0x41, 0x24, 0xd0, 0x6f, 0x73, 0xd4, 0x32, 0x4e, 0x79, 0xe9, 0x9e, 0xdc, 0x85, - 0x05, 0x19, 0x8a, 0xcd, 0x30, 0xc0, 0x48, 0x6e, 0x22, 0x97, 0x5b, 0x54, 0x52, 0xbb, 0xa0, 0xa3, - 0x9c, 0x36, 0x90, 0x35, 0x28, 0xf6, 0x81, 0x2a, 0xe5, 0xa4, 0x76, 0x3e, 0x85, 0xa7, 0x25, 0x3c, - 0xdd, 0x5f, 0xc2, 0xfa, 0x8c, 0x60, 0x30, 0x7d, 0xbe, 0x32, 0x4c, 0x63, 0x44, 0x0f, 0x43, 0xdc, - 0xf7, 0x03, 0x7b, 0x46, 0xd3, 0xcb, 0x00, 0x72, 0x0f, 0x16, 0x4d, 0xe5, 0xd6, 0x94, 0xaa, 0xe9, - 0x39, 0x67, 0x75, 0x80, 0x41, 0x26, 0x55, 0x57, 0x29, 0xbc, 0xbb, 0x65, 0xcf, 0xad, 0x58, 0xab, - 0xe3, 0x5e, 0x1e, 0x22, 0x6f, 0xc3, 0xff, 0xb3, 0x6d, 0x24, 0x24, 0x0d, 0x43, 0x5d, 0xda, 0xbb, - 0x5b, 0xf6, 0xbc, 0xf6, 0x1e, 0x66, 0x26, 0xef, 0x41, 0x29, 0x35, 0x6d, 0x47, 0x12, 0x39, 0xe3, - 0x81, 0xc0, 0x0d, 0x2a, 0xf0, 0x80, 0x87, 0xf6, 0x55, 0x4d, 0x6a, 0x84, 0x07, 0x59, 0x82, 0x09, - 0xc6, 0xe3, 0x67, 0x5d, 0xbb, 0xa8, 0x5d, 0xcd, 0x46, 0xf5, 0x10, 0x4b, 0x4a, 0x68, 0xc1, 0xf4, - 0x50, 0xb2, 0x25, 0x55, 0x58, 0x6a, 0xf8, 0xec, 0x11, 0xf2, 0x4e, 0xe0, 0x63, 0xcd, 0xf7, 0xe3, - 0x76, 0xa4, 0x35, 0x27, 0xda, 0x6d, 0xa0, 0x8d, 0xb8, 0x40, 0x74, 0x8d, 0xee, 0x48, 0xc9, 0x36, - 0xa8, 0x08, 0xfc, 0x5a, 0x5b, 0x36, 0xed, 0x45, 0x2d, 0xec, 0x00, 0x8b, 0x33, 0x0f, 0xb3, 0xaa, - 0x44, 0x7b, 0x3d, 0xe4, 0xfc, 0x6c, 0xc1, 0x82, 0x02, 0x36, 0x39, 0x52, 0x89, 0x1e, 0x3e, 0x6d, - 0xa3, 0x90, 0xe4, 0x93, 0x5c, 0xd5, 0xce, 0x54, 0x77, 0xfe, 0xdd, 0x38, 0xf1, 0xd2, 0xae, 0x4c, - 0xea, 0xff, 0x1a, 0x14, 0xda, 0x4c, 0x20, 0x97, 0x49, 0x97, 0x25, 0x3b, 0x55, 0x1b, 0x3e, 0xc7, - 0xba, 0xd8, 0x8f, 0xc2, 0xae, 0x2e, 0xfe, 0x29, 0x2f, 0x03, 0x9c, 0xa7, 0x86, 0xe8, 0x01, 0xab, - 0x5f, 0x16, 0xd1, 0xea, 0xdf, 0xf3, 0x26, 0xa7, 0x01, 0x13, 0xf1, 0xc9, 0xd7, 0x16, 0x5c, 0xd9, - 0x0b, 0x84, 0x24, 0xff, 0xcb, 0x0f, 0x9c, 0x74, 0xbc, 0x94, 0xf6, 0x2e, 0x8a, 0x85, 0x4a, 0xe2, - 0xdc, 0xfc, 0xe2, 0xf7, 0x3f, 0xbf, 0x1d, 0xbb, 0x46, 0x96, 0xf4, 0x67, 0xb5, 0xb3, 0x9e, 0x7d, - 0xc3, 0x02, 0x14, 0x5f, 0x8e, 0x59, 0xe4, 0x2b, 0x0b, 0xc6, 0x1f, 0xe0, 0x50, 0x36, 0x17, 0xa6, - 0x89, 0x73, 0x4b, 0x33, 0xb9, 0x41, 0xae, 0x0f, 0x62, 0x52, 0x79, 0xae, 0x76, 0x2f, 0xc8, 0x77, - 0x16, 0x14, 0x15, 0x6f, 0x2f, 0x67, 0xbb, 0x1c, 0xa1, 0xca, 0xa3, 0x84, 0x22, 0x9f, 0xc2, 0x94, - 0xa1, 0x75, 0x34, 0x94, 0x4e, 0xb1, 0x1f, 0x3e, 0x12, 0xce, 0xaa, 0x0e, 0xe9, 0x90, 0x95, 0x11, - 0x27, 0xae, 0x70, 0x15, 0xb2, 0x65, 0xc2, 0xab, 0xcf, 0x13, 0x79, 0xe5, 0x64, 0xf8, 0xf4, 0x76, - 0x51, 0x2a, 0x0f, 0x32, 0xa5, 0xbd, 0x78, 0xae, 0x74, 0x54, 0xa5, 0xf8, 0xc6, 0x82, 0xb9, 0x07, - 0x28, 0xb3, 0x7b, 0x00, 0xb9, 0x39, 0x20, 0x72, 0xfe, 0x8e, 0x50, 0x72, 0x86, 0x3b, 0xa4, 0x04, - 0xde, 0xd1, 0x04, 0xde, 0x74, 0xee, 0x0d, 0x26, 0x60, 0xbe, 0xd6, 0x3a, 0xce, 0x81, 0xb7, 0xa7, - 0xa9, 0xd4, 0x4d, 0x84, 0xfb, 0xd6, 0x1a, 0xe9, 0x68, 0x4a, 0x3b, 0x18, 0xb6, 0x36, 0x9b, 0x94, - 0xcb, 0xa1, 0x32, 0x2f, 0xe7, 0xe1, 0xcc, 0x3d, 0x25, 0xe1, 0x6a, 0x12, 0xab, 0xe4, 0xce, 0x28, - 0x15, 0x9a, 0x18, 0xb6, 0x7c, 0x93, 0xe6, 0x7b, 0x0b, 0x0a, 0x66, 0x7a, 0x91, 0x1b, 0x27, 0x33, - 0xf6, 0x4d, 0xb5, 0x0b, 0x6c, 0x85, 0x57, 0x35, 0xc7, 0xb2, 0x33, 0xb0, 0xd6, 0xee, 0xeb, 0xe1, - 0xa1, 0x5a, 0xf3, 0x07, 0x0b, 0x8a, 0x3d, 0x0a, 0xbd, 0x67, 0x2f, 0x8f, 0xa4, 0x73, 0x36, 0x49, - 0xf2, 0x93, 0x05, 0x05, 0x33, 0x51, 0x4f, 0xf3, 0xea, 0x9b, 0xb4, 0x17, 0xc8, 0x6b, 0xdd, 0xbc, - 0xe0, 0xd2, 0x88, 0x32, 0xd7, 0x54, 0x5e, 0x64, 0x42, 0xfe, 0x62, 0x41, 0xb1, 0x47, 0x67, 0xb8, - 0x90, 0xff, 0x15, 0x61, 0xf7, 0xe5, 0x08, 0x13, 0x0a, 0x85, 0x2d, 0x0c, 0x51, 0xe2, 0xb0, 0x16, - 0xb0, 0x4f, 0xc2, 0x69, 0xf1, 0xdf, 0x31, 0x33, 0x76, 0x6d, 0xd4, 0x8c, 0x55, 0x82, 0x34, 0xa1, - 0x68, 0x52, 0xe4, 0xf4, 0x78, 0xe9, 0x64, 0xb7, 0xce, 0x91, 0x8c, 0x3c, 0x87, 0xf9, 0x8f, 0x68, - 0x18, 0x28, 0x65, 0xcd, 0xbd, 0x96, 0x5c, 0x3f, 0x35, 0x49, 0xb2, 0xfb, 0xee, 0x88, 0x6c, 0x55, - 0x9d, 0xed, 0xae, 0x73, 0x7b, 0x54, 0x5f, 0x77, 0x92, 0x54, 0x46, 0xc9, 0x8d, 0xed, 0x5f, 0x8f, - 0x97, 0xad, 0xdf, 0x8e, 0x97, 0xad, 0x3f, 0x8e, 0x97, 0xad, 0x8f, 0xdf, 0x3a, 0xdf, 0x1f, 0xa4, - 0xaf, 0x2f, 0xa6, 0xb9, 0x7f, 0xbd, 0xc3, 0x82, 0xfe, 0xd9, 0x7b, 0xe3, 0x9f, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x56, 0xc6, 0x8e, 0x59, 0xd1, 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. @@ -1505,16 +1476,6 @@ func (m *RepoAppDetailsQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.VersionId != 0 { - i = encodeVarintRepository(dAtA, i, uint64(m.VersionId)) - i-- - dAtA[i] = 0x28 - } - if m.SourceIndex != 0 { - i = encodeVarintRepository(dAtA, i, uint64(m.SourceIndex)) - i-- - dAtA[i] = 0x20 - } if len(m.AppProject) > 0 { i -= len(m.AppProject) copy(dAtA[i:], m.AppProject) @@ -1609,13 +1570,6 @@ func (m *RepoQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.AppProject) > 0 { - i -= len(m.AppProject) - copy(dAtA[i:], m.AppProject) - i = encodeVarintRepository(dAtA, i, uint64(len(m.AppProject))) - i-- - dAtA[i] = 0x1a - } if m.ForceRefresh { i-- if m.ForceRefresh { @@ -2004,12 +1958,6 @@ func (m *RepoAppDetailsQuery) Size() (n int) { if l > 0 { n += 1 + l + sovRepository(uint64(l)) } - if m.SourceIndex != 0 { - n += 1 + sovRepository(uint64(m.SourceIndex)) - } - if m.VersionId != 0 { - n += 1 + sovRepository(uint64(m.VersionId)) - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -2047,10 +1995,6 @@ func (m *RepoQuery) Size() (n int) { if m.ForceRefresh { n += 2 } - l = len(m.AppProject) - if l > 0 { - n += 1 + l + sovRepository(uint64(l)) - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -2615,44 +2559,6 @@ func (m *RepoAppDetailsQuery) Unmarshal(dAtA []byte) error { } m.AppProject = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field SourceIndex", wireType) - } - m.SourceIndex = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRepository - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.SourceIndex |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field VersionId", wireType) - } - m.VersionId = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRepository - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.VersionId |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) @@ -2841,38 +2747,6 @@ func (m *RepoQuery) Unmarshal(dAtA []byte) error { } } m.ForceRefresh = bool(v != 0) - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AppProject", 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.AppProject = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) diff --git a/pkg/apiclient/session/mocks/SessionServiceClient.go b/pkg/apiclient/session/mocks/SessionServiceClient.go index 9505a424619d9..16a3692d86600 100644 --- a/pkg/apiclient/session/mocks/SessionServiceClient.go +++ b/pkg/apiclient/session/mocks/SessionServiceClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v1.0.0. DO NOT EDIT. package mocks @@ -28,15 +28,7 @@ func (_m *SessionServiceClient) Create(ctx context.Context, in *session.SessionC _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for Create") - } - var r0 *session.SessionResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *session.SessionCreateRequest, ...grpc.CallOption) (*session.SessionResponse, error)); ok { - return rf(ctx, in, opts...) - } if rf, ok := ret.Get(0).(func(context.Context, *session.SessionCreateRequest, ...grpc.CallOption) *session.SessionResponse); ok { r0 = rf(ctx, in, opts...) } else { @@ -45,6 +37,7 @@ func (_m *SessionServiceClient) Create(ctx context.Context, in *session.SessionC } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, *session.SessionCreateRequest, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { @@ -65,15 +58,7 @@ func (_m *SessionServiceClient) Delete(ctx context.Context, in *session.SessionD _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for Delete") - } - var r0 *session.SessionResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *session.SessionDeleteRequest, ...grpc.CallOption) (*session.SessionResponse, error)); ok { - return rf(ctx, in, opts...) - } if rf, ok := ret.Get(0).(func(context.Context, *session.SessionDeleteRequest, ...grpc.CallOption) *session.SessionResponse); ok { r0 = rf(ctx, in, opts...) } else { @@ -82,44 +67,8 @@ func (_m *SessionServiceClient) Delete(ctx context.Context, in *session.SessionD } } - if rf, ok := ret.Get(1).(func(context.Context, *session.SessionDeleteRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetUserInfo provides a mock function with given fields: ctx, in, opts -func (_m *SessionServiceClient) GetUserInfo(ctx context.Context, in *session.GetUserInfoRequest, opts ...grpc.CallOption) (*session.GetUserInfoResponse, 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...) - - if len(ret) == 0 { - panic("no return value specified for GetUserInfo") - } - - var r0 *session.GetUserInfoResponse var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *session.GetUserInfoRequest, ...grpc.CallOption) (*session.GetUserInfoResponse, error)); ok { - return rf(ctx, in, opts...) - } - if rf, ok := ret.Get(0).(func(context.Context, *session.GetUserInfoRequest, ...grpc.CallOption) *session.GetUserInfoResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*session.GetUserInfoResponse) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *session.GetUserInfoRequest, ...grpc.CallOption) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *session.SessionDeleteRequest, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { r1 = ret.Error(1) @@ -127,17 +76,3 @@ func (_m *SessionServiceClient) GetUserInfo(ctx context.Context, in *session.Get return r0, r1 } - -// NewSessionServiceClient creates a new instance of SessionServiceClient. 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 NewSessionServiceClient(t interface { - mock.TestingT - Cleanup(func()) -}) *SessionServiceClient { - mock := &SessionServiceClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/apiclient/session/mocks/SessionServiceServer.go b/pkg/apiclient/session/mocks/SessionServiceServer.go index 710176a62ed23..f518fbc75a9f8 100644 --- a/pkg/apiclient/session/mocks/SessionServiceServer.go +++ b/pkg/apiclient/session/mocks/SessionServiceServer.go @@ -1,12 +1,13 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import ( context "context" - session "github.com/argoproj/argo-cd/v2/pkg/apiclient/session" mock "github.com/stretchr/testify/mock" + + session "github.com/argoproj/argo-cd/v2/pkg/apiclient/session" ) // SessionServiceServer is an autogenerated mock type for the SessionServiceServer type @@ -18,15 +19,7 @@ type SessionServiceServer struct { func (_m *SessionServiceServer) Create(_a0 context.Context, _a1 *session.SessionCreateRequest) (*session.SessionResponse, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for Create") - } - var r0 *session.SessionResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *session.SessionCreateRequest) (*session.SessionResponse, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, *session.SessionCreateRequest) *session.SessionResponse); ok { r0 = rf(_a0, _a1) } else { @@ -35,6 +28,7 @@ func (_m *SessionServiceServer) Create(_a0 context.Context, _a1 *session.Session } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, *session.SessionCreateRequest) error); ok { r1 = rf(_a0, _a1) } else { @@ -48,15 +42,7 @@ func (_m *SessionServiceServer) Create(_a0 context.Context, _a1 *session.Session func (_m *SessionServiceServer) Delete(_a0 context.Context, _a1 *session.SessionDeleteRequest) (*session.SessionResponse, error) { ret := _m.Called(_a0, _a1) - if len(ret) == 0 { - panic("no return value specified for Delete") - } - var r0 *session.SessionResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *session.SessionDeleteRequest) (*session.SessionResponse, error)); ok { - return rf(_a0, _a1) - } if rf, ok := ret.Get(0).(func(context.Context, *session.SessionDeleteRequest) *session.SessionResponse); ok { r0 = rf(_a0, _a1) } else { @@ -65,37 +51,8 @@ func (_m *SessionServiceServer) Delete(_a0 context.Context, _a1 *session.Session } } - if rf, ok := ret.Get(1).(func(context.Context, *session.SessionDeleteRequest) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetUserInfo provides a mock function with given fields: _a0, _a1 -func (_m *SessionServiceServer) GetUserInfo(_a0 context.Context, _a1 *session.GetUserInfoRequest) (*session.GetUserInfoResponse, error) { - ret := _m.Called(_a0, _a1) - - if len(ret) == 0 { - panic("no return value specified for GetUserInfo") - } - - var r0 *session.GetUserInfoResponse var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *session.GetUserInfoRequest) (*session.GetUserInfoResponse, error)); ok { - return rf(_a0, _a1) - } - if rf, ok := ret.Get(0).(func(context.Context, *session.GetUserInfoRequest) *session.GetUserInfoResponse); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*session.GetUserInfoResponse) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *session.GetUserInfoRequest) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *session.SessionDeleteRequest) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -103,17 +60,3 @@ func (_m *SessionServiceServer) GetUserInfo(_a0 context.Context, _a1 *session.Ge return r0, r1 } - -// NewSessionServiceServer creates a new instance of SessionServiceServer. 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 NewSessionServiceServer(t interface { - mock.TestingT - Cleanup(func()) -}) *SessionServiceServer { - mock := &SessionServiceServer{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/apiclient/settings/settings.pb.go b/pkg/apiclient/settings/settings.pb.go index 3345756bd0aad..b74110f9005d7 100644 --- a/pkg/apiclient/settings/settings.pb.go +++ b/pkg/apiclient/settings/settings.pb.go @@ -101,7 +101,6 @@ type Settings struct { ExecEnabled bool `protobuf:"varint,22,opt,name=execEnabled,proto3" json:"execEnabled,omitempty"` ControllerNamespace string `protobuf:"bytes,23,opt,name=controllerNamespace,proto3" json:"controllerNamespace,omitempty"` AppsInAnyNamespaceEnabled bool `protobuf:"varint,24,opt,name=appsInAnyNamespaceEnabled,proto3" json:"appsInAnyNamespaceEnabled,omitempty"` - ImpersonationEnabled bool `protobuf:"varint,25,opt,name=impersonationEnabled,proto3" json:"impersonationEnabled,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -308,13 +307,6 @@ func (m *Settings) GetAppsInAnyNamespaceEnabled() bool { return false } -func (m *Settings) GetImpersonationEnabled() bool { - if m != nil { - return m.ImpersonationEnabled - } - return false -} - type GoogleAnalyticsConfig struct { TrackingID string `protobuf:"bytes,1,opt,name=trackingID,proto3" json:"trackingID,omitempty"` AnonymizeUsers bool `protobuf:"varint,2,opt,name=anonymizeUsers,proto3" json:"anonymizeUsers,omitempty"` @@ -748,84 +740,83 @@ func init() { func init() { proto.RegisterFile("server/settings/settings.proto", fileDescriptor_a480d494da040caa) } var fileDescriptor_a480d494da040caa = []byte{ - // 1232 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, 0x97, 0xeb, 0x34, 0xb1, 0x9f, 0x9b, 0x3a, 0x99, 0xa6, 0xe9, 0xd6, 0x2a, 0x89, 0xf1, 0xa1, - 0x32, 0x08, 0xd6, 0x8d, 0x2b, 0x04, 0xaa, 0xa8, 0xa0, 0xb6, 0xab, 0xd6, 0x34, 0x6d, 0xc3, 0xb4, - 0xe9, 0x81, 0x4b, 0x35, 0x59, 0x3f, 0xd6, 0x4b, 0xd6, 0x33, 0xab, 0x99, 0x59, 0x13, 0xf7, 0xc8, - 0x07, 0xe0, 0x02, 0x9f, 0x86, 0x3b, 0x82, 0x23, 0x12, 0xf7, 0x08, 0x59, 0x9c, 0xf8, 0x14, 0x68, - 0x67, 0xff, 0x64, 0xb3, 0x76, 0x0a, 0x52, 0x6f, 0x33, 0xbf, 0xdf, 0xfb, 0x37, 0x6f, 0xde, 0x9b, - 0x79, 0xb0, 0xa3, 0x50, 0x4e, 0x51, 0x76, 0x14, 0x6a, 0xed, 0x71, 0x57, 0x65, 0x0b, 0x3b, 0x90, - 0x42, 0x0b, 0xb2, 0xe6, 0xf8, 0xa1, 0xd2, 0x28, 0x1b, 0x5b, 0xae, 0x70, 0x85, 0xc1, 0x3a, 0xd1, - 0x2a, 0xa6, 0x1b, 0xb7, 0x5c, 0x21, 0x5c, 0x1f, 0x3b, 0x2c, 0xf0, 0x3a, 0x8c, 0x73, 0xa1, 0x99, - 0xf6, 0x04, 0x4f, 0x94, 0x1b, 0xfb, 0xae, 0xa7, 0xc7, 0xe1, 0x91, 0xed, 0x88, 0x49, 0x87, 0x49, - 0xa3, 0xfe, 0x9d, 0x59, 0x7c, 0xec, 0x8c, 0x3a, 0xd3, 0x6e, 0x27, 0x38, 0x76, 0x23, 0x4d, 0xd5, - 0x61, 0x41, 0xe0, 0x7b, 0x8e, 0xd1, 0xed, 0x4c, 0xf7, 0x98, 0x1f, 0x8c, 0xd9, 0x5e, 0xc7, 0x45, - 0x8e, 0x92, 0x69, 0x1c, 0x25, 0xd6, 0xbe, 0xfc, 0x0f, 0x6b, 0xc5, 0x93, 0x08, 0x6f, 0xe4, 0x74, - 0x1c, 0x9f, 0x79, 0x93, 0x24, 0x9e, 0x56, 0x1d, 0xd6, 0x5f, 0x24, 0xec, 0xd7, 0x21, 0xca, 0x59, - 0xeb, 0x9f, 0x1a, 0x54, 0x52, 0x84, 0xdc, 0x84, 0x72, 0x28, 0x7d, 0xab, 0xd4, 0x2c, 0xb5, 0xab, - 0xbd, 0xb5, 0xf9, 0xe9, 0x6e, 0xf9, 0x90, 0xee, 0xd3, 0x08, 0x23, 0x77, 0xa0, 0x3a, 0xc2, 0x93, - 0xbe, 0xe0, 0xdf, 0x7a, 0xae, 0x75, 0xa9, 0x59, 0x6a, 0xd7, 0xba, 0xc4, 0x4e, 0x32, 0x63, 0x0f, - 0x52, 0x86, 0x9e, 0x09, 0x91, 0x3e, 0x40, 0xe4, 0x3f, 0x51, 0x29, 0x1b, 0x95, 0x6b, 0x99, 0xca, - 0xf3, 0xe1, 0xa0, 0x1f, 0x53, 0xbd, 0xab, 0xf3, 0xd3, 0x5d, 0x38, 0xdb, 0xd3, 0x9c, 0x1a, 0x69, - 0x42, 0x8d, 0x05, 0xc1, 0x3e, 0x3b, 0x42, 0xff, 0x09, 0xce, 0xac, 0x95, 0x28, 0x32, 0x9a, 0x87, - 0xc8, 0x2b, 0xd8, 0x94, 0xa8, 0x44, 0x28, 0x1d, 0x7c, 0x3e, 0x45, 0x29, 0xbd, 0x11, 0x2a, 0xeb, - 0x72, 0xb3, 0xdc, 0xae, 0x75, 0xdb, 0x99, 0xb7, 0xf4, 0x84, 0x36, 0x2d, 0x8a, 0x3e, 0xe4, 0x5a, - 0xce, 0xe8, 0xa2, 0x09, 0x62, 0x03, 0x51, 0x9a, 0xe9, 0x50, 0xf5, 0xd8, 0xc8, 0xc5, 0x87, 0x9c, - 0x1d, 0xf9, 0x38, 0xb2, 0x56, 0x9b, 0xa5, 0x76, 0x85, 0x2e, 0x61, 0xc8, 0x63, 0xa8, 0xc7, 0x95, - 0xf0, 0x80, 0x33, 0x7f, 0xa6, 0x3d, 0x47, 0x59, 0x6b, 0xe6, 0xcc, 0x3b, 0x59, 0x14, 0x8f, 0xce, - 0xf3, 0xc9, 0x71, 0x8b, 0x6a, 0xe4, 0x0d, 0x6c, 0x1c, 0x87, 0x4a, 0x8b, 0x89, 0xf7, 0x06, 0x9f, - 0x07, 0xa6, 0x9a, 0xac, 0x8a, 0x31, 0xf5, 0xcc, 0x3e, 0x2b, 0x00, 0x3b, 0x2d, 0x00, 0xb3, 0x78, - 0xed, 0x8c, 0xec, 0x69, 0xd7, 0x0e, 0x8e, 0x5d, 0x3b, 0x2a, 0x27, 0x3b, 0x57, 0x4e, 0x76, 0x5a, - 0x4e, 0xf6, 0x93, 0x82, 0x55, 0xba, 0xe0, 0x87, 0xbc, 0x0f, 0x2b, 0x63, 0xf4, 0x03, 0xab, 0x6a, - 0xfc, 0xad, 0x67, 0xa1, 0x3f, 0x46, 0x3f, 0xa0, 0x86, 0x22, 0x1f, 0xc0, 0x5a, 0xe0, 0x87, 0xae, - 0xc7, 0x95, 0x05, 0x26, 0xcd, 0xf5, 0x4c, 0xea, 0xc0, 0xe0, 0x34, 0xe5, 0xa3, 0x1c, 0x86, 0x0a, - 0xe5, 0xbe, 0x88, 0x76, 0x03, 0x4f, 0xc5, 0x39, 0xac, 0xc5, 0x39, 0x5c, 0x64, 0xc8, 0x8f, 0x25, - 0xb8, 0xe1, 0x98, 0xac, 0x3c, 0x65, 0x9c, 0xb9, 0x38, 0x41, 0xae, 0x0f, 0x12, 0x5f, 0x57, 0x8c, - 0xaf, 0x97, 0xef, 0x96, 0x81, 0xfe, 0x52, 0xe3, 0xf4, 0x22, 0xa7, 0xe4, 0x23, 0xd8, 0xcc, 0x52, - 0xf4, 0x0a, 0xa5, 0x32, 0x77, 0xb1, 0xde, 0x2c, 0xb7, 0xab, 0x74, 0x91, 0x20, 0x0d, 0xa8, 0x84, - 0x5e, 0x5f, 0xa9, 0x43, 0xba, 0x6f, 0x5d, 0x35, 0x95, 0x9a, 0xed, 0x49, 0x1b, 0xea, 0xa1, 0xd7, - 0x63, 0x9c, 0xa3, 0xec, 0x0b, 0xae, 0x91, 0x6b, 0xab, 0x6e, 0x44, 0x8a, 0x70, 0x54, 0xf2, 0x29, - 0x14, 0x19, 0xda, 0x88, 0x4b, 0x3e, 0x07, 0x45, 0xb6, 0x02, 0xa6, 0xd4, 0xf7, 0x42, 0x8e, 0x0e, - 0x98, 0xd6, 0x28, 0xb9, 0xb5, 0x19, 0xdb, 0x2a, 0xc0, 0xe4, 0x36, 0x5c, 0xd5, 0x92, 0x39, 0xc7, - 0x1e, 0x77, 0x9f, 0xa2, 0x1e, 0x8b, 0x91, 0x45, 0x8c, 0x60, 0x01, 0x8d, 0xce, 0x99, 0x3a, 0x38, - 0x40, 0x39, 0x61, 0x3c, 0x8a, 0xef, 0x9a, 0xb9, 0xa7, 0x45, 0x82, 0x7c, 0x08, 0x1b, 0x19, 0x28, - 0x94, 0x17, 0xa5, 0xd8, 0xda, 0x32, 0x76, 0x17, 0xf0, 0x42, 0x1b, 0x51, 0x21, 0xf4, 0xa1, 0xf4, - 0xad, 0xeb, 0x46, 0x7a, 0x09, 0x13, 0x9d, 0x1e, 0x4f, 0xd0, 0x49, 0xfb, 0x6d, 0xdb, 0xc4, 0x90, - 0x87, 0xc8, 0x1d, 0xb8, 0xe6, 0x08, 0xae, 0xa5, 0xf0, 0x7d, 0x94, 0xcf, 0xd8, 0x04, 0x55, 0xc0, - 0x1c, 0xb4, 0x6e, 0x18, 0x93, 0xcb, 0x28, 0xf2, 0x39, 0xdc, 0x64, 0x41, 0xa0, 0x86, 0xfc, 0x01, - 0x9f, 0x65, 0x68, 0xea, 0xc1, 0x32, 0x1e, 0x2e, 0x16, 0x20, 0x5d, 0xd8, 0xf2, 0x26, 0x01, 0x4a, - 0x25, 0xb8, 0xa9, 0xa6, 0x54, 0xf1, 0xa6, 0x51, 0x5c, 0xca, 0x35, 0x7e, 0x2e, 0xc1, 0xf6, 0xf2, - 0xa7, 0x86, 0x6c, 0x40, 0xf9, 0x18, 0x67, 0xf1, 0x1b, 0x4b, 0xa3, 0x25, 0x19, 0xc1, 0xe5, 0x29, - 0xf3, 0x43, 0x4c, 0x9e, 0xd5, 0x77, 0x6c, 0xf2, 0xa2, 0x5b, 0x1a, 0x1b, 0xbf, 0x77, 0xe9, 0xb3, - 0x52, 0xeb, 0x35, 0x5c, 0x5f, 0xfa, 0x06, 0x91, 0x1d, 0x80, 0xb4, 0x22, 0x86, 0x83, 0x24, 0xb6, - 0x1c, 0x12, 0xd5, 0x11, 0xe3, 0x82, 0xcf, 0xa2, 0x72, 0x3f, 0x54, 0x28, 0x95, 0x89, 0xb5, 0x42, - 0x0b, 0x68, 0x6b, 0x00, 0x37, 0xd2, 0xa7, 0x36, 0x69, 0x21, 0x8a, 0x2a, 0x10, 0x5c, 0x61, 0xfe, - 0xd9, 0x28, 0xbd, 0xfd, 0xd9, 0x68, 0xfd, 0x52, 0x82, 0x95, 0xe8, 0xc1, 0x21, 0x16, 0xac, 0x39, - 0x63, 0x66, 0x2a, 0x26, 0x8e, 0x29, 0xdd, 0x46, 0xad, 0x16, 0x2d, 0x5f, 0xe2, 0x89, 0x36, 0xa1, - 0x54, 0x69, 0xb6, 0x27, 0xf7, 0x01, 0x8e, 0x3c, 0xce, 0xe4, 0xec, 0x50, 0xfa, 0xca, 0x2a, 0x1b, - 0x67, 0xef, 0x9d, 0x7b, 0xc9, 0xec, 0x5e, 0xc6, 0xc7, 0xef, 0x7f, 0x4e, 0xa1, 0x71, 0x1f, 0xea, - 0x05, 0x7a, 0xc9, 0x9d, 0x6d, 0xe5, 0xef, 0xac, 0x9a, 0xcf, 0xf1, 0x2d, 0x58, 0x8d, 0xcf, 0x43, - 0x08, 0xac, 0x70, 0x36, 0xc1, 0x44, 0xcd, 0xac, 0x5b, 0x5f, 0x40, 0x35, 0xfb, 0x2c, 0x49, 0x17, - 0xc0, 0x11, 0x9c, 0xa3, 0xa3, 0x85, 0x4c, 0xb3, 0x72, 0xf6, 0xa9, 0xf6, 0x53, 0x8a, 0xe6, 0xa4, - 0x5a, 0x77, 0xa1, 0x9a, 0x11, 0xcb, 0x3c, 0x44, 0x98, 0x9e, 0x05, 0x69, 0x60, 0x66, 0xdd, 0xfa, - 0xb5, 0x0c, 0xb9, 0x0f, 0x76, 0xa9, 0xda, 0x36, 0xac, 0x7a, 0x4a, 0x85, 0x28, 0x13, 0xc5, 0x64, - 0x47, 0xda, 0x50, 0x71, 0x7c, 0x0f, 0xb9, 0x1e, 0x0e, 0xcc, 0x1f, 0x5e, 0xed, 0x5d, 0x99, 0x9f, - 0xee, 0x56, 0xfa, 0x09, 0x46, 0x33, 0x96, 0xec, 0x41, 0xcd, 0xf1, 0xbd, 0x94, 0x88, 0xbf, 0xea, - 0x5e, 0x7d, 0x7e, 0xba, 0x5b, 0xeb, 0xef, 0x0f, 0x33, 0xf9, 0xbc, 0x4c, 0xe4, 0x54, 0x39, 0x22, - 0x48, 0x3e, 0xec, 0x2a, 0x4d, 0x76, 0xe4, 0x35, 0xac, 0x7b, 0xa3, 0x97, 0xe2, 0x18, 0x79, 0xdf, - 0x0c, 0x2f, 0xd6, 0xaa, 0xc9, 0xcd, 0xed, 0x25, 0xd3, 0x83, 0x3d, 0xcc, 0x0b, 0x9a, 0xeb, 0xea, - 0x6d, 0xce, 0x4f, 0x77, 0xd7, 0x87, 0x83, 0x1c, 0x4e, 0xcf, 0xdb, 0x23, 0xf7, 0xc0, 0x42, 0xd3, - 0xaa, 0x07, 0x4f, 0xfa, 0x0f, 0x1f, 0x84, 0x7a, 0x8c, 0x5c, 0x27, 0x9d, 0x64, 0x7e, 0xed, 0x0a, - 0xbd, 0x90, 0x6f, 0xcc, 0x80, 0x2c, 0xfa, 0x5c, 0x52, 0x22, 0x4f, 0xcf, 0xb7, 0xf5, 0xa7, 0x6f, - 0x6d, 0xeb, 0x78, 0x72, 0xb3, 0xb3, 0xd1, 0x33, 0x1a, 0x81, 0x6c, 0x63, 0x3f, 0x57, 0x5b, 0xdd, - 0xdf, 0x4a, 0x50, 0x4f, 0xfb, 0xeb, 0x05, 0xca, 0xa9, 0xe7, 0x20, 0xf9, 0x0a, 0xca, 0x8f, 0x50, - 0x93, 0xed, 0x85, 0x59, 0xc7, 0xcc, 0x77, 0x8d, 0xcd, 0x05, 0xbc, 0x65, 0xfd, 0xf0, 0xe7, 0xdf, - 0x3f, 0x5d, 0x22, 0x64, 0xc3, 0xcc, 0xac, 0xd3, 0xbd, 0x6c, 0x5e, 0x24, 0x63, 0x80, 0x47, 0x98, - 0x7d, 0x7e, 0x17, 0x99, 0x6c, 0x2e, 0xe0, 0x85, 0x5e, 0x6f, 0x35, 0x8d, 0x87, 0x06, 0xb1, 0x8a, - 0x1e, 0x3a, 0x49, 0x8b, 0xf7, 0xfa, 0xbf, 0xcf, 0x77, 0x4a, 0x7f, 0xcc, 0x77, 0x4a, 0x7f, 0xcd, - 0x77, 0x4a, 0xdf, 0x7c, 0xf2, 0xff, 0xa6, 0xe4, 0xb8, 0xd4, 0x32, 0x63, 0x47, 0xab, 0x66, 0xa6, - 0xbd, 0xfb, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8d, 0xd6, 0x25, 0xb7, 0xc2, 0x0b, 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. @@ -999,18 +990,6 @@ func (m *Settings) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.ImpersonationEnabled { - i-- - if m.ImpersonationEnabled { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x1 - i-- - dAtA[i] = 0xc8 - } if m.AppsInAnyNamespaceEnabled { i-- if m.AppsInAnyNamespaceEnabled { @@ -1771,9 +1750,6 @@ func (m *Settings) Size() (n int) { if m.AppsInAnyNamespaceEnabled { n += 3 } - if m.ImpersonationEnabled { - n += 3 - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -2864,26 +2840,6 @@ func (m *Settings) Unmarshal(dAtA []byte) error { } } m.AppsInAnyNamespaceEnabled = bool(v != 0) - case 25: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ImpersonationEnabled", 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.ImpersonationEnabled = bool(v != 0) default: iNdEx = preIndex skippy, err := skipSettings(dAtA[iNdEx:]) diff --git a/pkg/apis/api-rules/violation_exceptions.list b/pkg/apis/api-rules/violation_exceptions.list index e69de29bb2d1d..5630d8d4bceb2 100644 --- a/pkg/apis/api-rules/violation_exceptions.list +++ b/pkg/apis/api-rules/violation_exceptions.list @@ -0,0 +1,132 @@ +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,ClusterResourceBlacklist +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,ClusterResourceWhitelist +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,Destinations +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,NamespaceResourceBlacklist +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,NamespaceResourceWhitelist +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,Roles +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,SignatureKeys +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 +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceHelm,FileParameters +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceHelm,Parameters +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceHelm,ValueFiles +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,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 +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationStatus,SourceTypes +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSummary,ExternalURLs +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSummary,Images +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 +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Command,Command +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ExecProviderConfig,Args +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,GitGenerator,Directories +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,GitGenerator,Files +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,HelmOptions,ValuesFileSchemes +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,HostInfo,ResourcesInfo +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,JWTTokens,Items +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ListGenerator,Elements +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,MatrixGenerator,Generators +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,MergeGenerator,Generators +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 +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,OverrideIgnoreDiff,ManagedFieldsManagers +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ProjectRole,Groups +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 +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceAction,Params +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceActions,Definitions +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceIgnoreDifferences,JQPathExpressions +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceIgnoreDifferences,JSONPointers +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceIgnoreDifferences,ManagedFieldsManagers +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceNetworkingInfo,ExternalURLs +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceNetworkingInfo,Ingress +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceNetworkingInfo,TargetRefs +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceNode,Images +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceNode,Info +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceNode,ParentRefs +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceOverride,KnownTypeFields +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 +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SyncOperation,Resources +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SyncOperation,Revisions +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SyncOperationResult,Revisions +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SyncStatus,Revisions +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SyncWindow,Applications +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SyncWindow,Clusters +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SyncWindow,Namespaces +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,TLSClientConfig,CAData +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,TLSClientConfig,CertData +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,TLSClientConfig,KeyData +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,rawResourceOverride,KnownTypeFields +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceJsonnet,TLAs +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourcePluginParameter,String_ +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ClusterCacheInfo,APIsCount +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ConnectionState,ModifiedAt +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ErrApplicationNotAllowedToUseProject,application +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ErrApplicationNotAllowedToUseProject,namespace +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ErrApplicationNotAllowedToUseProject,project +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,HelmOptions,ValuesFileSchemes +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,JWTToken,ExpiresAt +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 +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,RefTarget,TargetRevision +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,RepoCreds,GitHubAppEnterpriseBaseURL +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,RepoCreds,GithubAppId +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,RepoCreds,GithubAppInstallationId +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Repository,EnableLFS +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Repository,GitHubAppEnterpriseBaseURL +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Repository,GithubAppId +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Repository,GithubAppInstallationId +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceActionDefinition,ActionLua +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceActions,ActionDiscoveryLua +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 +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,rawResourceOverride,HealthLua +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,rawResourceOverride,UseOpenLibs diff --git a/pkg/apis/application/v1alpha1/app_project_types.go b/pkg/apis/application/v1alpha1/app_project_types.go index b888cd37391b3..81f95ab624a0d 100644 --- a/pkg/apis/application/v1alpha1/app_project_types.go +++ b/pkg/apis/application/v1alpha1/app_project_types.go @@ -112,6 +112,7 @@ func (p *AppProject) GetJWTToken(roleName string, issuedAt int64, id string) (*J return &token, i, nil } } + } if issuedAt != -1 { @@ -143,10 +144,10 @@ func (p AppProject) RemoveJWTToken(roleIndex int, issuedAt int64, id string) err } if err1 == nil || err2 == nil { - // If we find this token from either places, we can say there are no error + //If we find this token from either places, we can say there are no error return nil } else { - // If we could not locate this taken from either places, we can return any of the errors + //If we could not locate this taken from either places, we can return any of the errors return err2 } } @@ -265,23 +266,6 @@ func (p *AppProject) ValidateProject() error { } } - destServiceAccts := make(map[string]bool) - for _, destServiceAcct := range p.Spec.DestinationServiceAccounts { - if destServiceAcct.Server == "!*" { - return status.Errorf(codes.InvalidArgument, "server has an invalid format, '!*'") - } - - if destServiceAcct.Namespace == "!*" { - return status.Errorf(codes.InvalidArgument, "namespace has an invalid format, '!*'") - } - - key := fmt.Sprintf("%s/%s", destServiceAcct.Server, destServiceAcct.Namespace) - if _, ok := destServiceAccts[key]; ok { - return status.Errorf(codes.InvalidArgument, "destinationServiceAccount '%s' already added", key) - } - destServiceAccts[key] = true - } - return nil } @@ -444,7 +428,7 @@ func (proj AppProject) IsDestinationPermitted(dst ApplicationDestination, projec if destinationMatched && proj.Spec.PermitOnlyProjectScopedClusters { clusters, err := projectClusters(proj.Name) if err != nil { - return false, fmt.Errorf("could not retrieve project clusters: %w", err) + return false, fmt.Errorf("could not retrieve project clusters: %s", err) } for _, cluster := range clusters { @@ -579,5 +563,5 @@ func (p AppProject) IsAppNamespacePermitted(app *Application, controllerNs strin return true } - return glob.MatchStringInList(p.Spec.SourceNamespaces, app.Namespace, glob.REGEXP) + return glob.MatchStringInList(p.Spec.SourceNamespaces, app.Namespace, false) } diff --git a/pkg/apis/application/v1alpha1/applicationset_types.go b/pkg/apis/application/v1alpha1/applicationset_types.go index d4446130c7026..389f421fed400 100644 --- a/pkg/apis/application/v1alpha1/applicationset_types.go +++ b/pkg/apis/application/v1alpha1/applicationset_types.go @@ -35,12 +35,6 @@ type SecretRef struct { Key string `json:"key" protobuf:"bytes,2,opt,name=key"` } -// Utility struct for a reference to a configmap key. -type ConfigMapKeyRef struct { - ConfigMapName string `json:"configMapName" protobuf:"bytes,1,opt,name=configMapName"` - Key string `json:"key" protobuf:"bytes,2,opt,name=key"` -} - // ApplicationSet is a set of Application resources // +genclient // +genclient:noStatus @@ -504,8 +498,6 @@ type SCMProviderGeneratorGitlab struct { 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"` - // ConfigMap key holding the trusted certificates - CARef *ConfigMapKeyRef `json:"caRef,omitempty" protobuf:"bytes,9,opt,name=caRef"` } func (s *SCMProviderGeneratorGitlab) WillIncludeSharedProjects() bool { @@ -534,12 +526,6 @@ type SCMProviderGeneratorBitbucketServer struct { BasicAuth *BasicAuthBitbucketServer `json:"basicAuth,omitempty" protobuf:"bytes,3,opt,name=basicAuth"` // Scan all branches instead of just the default branch. AllBranches bool `json:"allBranches,omitempty" protobuf:"varint,4,opt,name=allBranches"` - // Credentials for AccessToken (Bearer auth) - BearerToken *BearerTokenBitbucket `json:"bearerToken,omitempty" protobuf:"bytes,5,opt,name=bearerToken"` - // Allow self-signed TLS / Certificates; default: false - Insecure bool `json:"insecure,omitempty" protobuf:"varint,6,opt,name=insecure"` - // ConfigMap key holding the trusted certificates - CARef *ConfigMapKeyRef `json:"caRef,omitempty" protobuf:"bytes,7,opt,name=caRef"` } // SCMProviderGeneratorAzureDevOps defines connection info specific to Azure DevOps. @@ -691,8 +677,6 @@ type PullRequestGeneratorGitLab struct { 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"` - // ConfigMap key holding the trusted certificates - CARef *ConfigMapKeyRef `json:"caRef,omitempty" protobuf:"bytes,7,opt,name=caRef"` } // PullRequestGeneratorBitbucketServer defines connection info specific to BitbucketServer. @@ -705,12 +689,6 @@ type PullRequestGeneratorBitbucketServer struct { API string `json:"api" protobuf:"bytes,3,opt,name=api"` // Credentials for Basic auth BasicAuth *BasicAuthBitbucketServer `json:"basicAuth,omitempty" protobuf:"bytes,4,opt,name=basicAuth"` - // Credentials for AccessToken (Bearer auth) - BearerToken *BearerTokenBitbucket `json:"bearerToken,omitempty" protobuf:"bytes,5,opt,name=bearerToken"` - // Allow self-signed TLS / Certificates; default: false - Insecure bool `json:"insecure,omitempty" protobuf:"varint,6,opt,name=insecure"` - // ConfigMap key holding the trusted certificates - CARef *ConfigMapKeyRef `json:"caRef,omitempty" protobuf:"bytes,7,opt,name=caRef"` } // PullRequestGeneratorBitbucket defines connection info specific to Bitbucket. @@ -727,12 +705,6 @@ type PullRequestGeneratorBitbucket struct { BearerToken *BearerTokenBitbucketCloud `json:"bearerToken,omitempty" protobuf:"bytes,5,opt,name=bearerToken"` } -// BearerTokenBitbucket defines the Bearer token for BitBucket AppToken auth. -type BearerTokenBitbucket struct { - // Password (or personal access token) reference. - TokenRef *SecretRef `json:"tokenRef" protobuf:"bytes,1,opt,name=tokenRef"` -} - // BearerTokenBitbucketCloud defines the Bearer token for BitBucket AppToken auth. type BearerTokenBitbucketCloud struct { // Password (or personal access token) reference. @@ -787,11 +759,9 @@ type ApplicationSetStatus struct { // Important: Run "make" to regenerate code after modifying this file Conditions []ApplicationSetCondition `json:"conditions,omitempty" protobuf:"bytes,1,name=conditions"` ApplicationStatus []ApplicationSetApplicationStatus `json:"applicationStatus,omitempty" protobuf:"bytes,2,name=applicationStatus"` - // Resources is a list of Applications resources managed by this application set. - Resources []ResourceStatus `json:"resources,omitempty" protobuf:"bytes,3,opt,name=resources"` } -// ApplicationSetCondition contains details about an applicationset condition, which is usually an error or warning +// ApplicationSetCondition contains details about an applicationset condition, which is usally an error or warning type ApplicationSetCondition struct { // Type is an applicationset condition type Type ApplicationSetConditionType `json:"type" protobuf:"bytes,1,opt,name=type"` @@ -801,7 +771,7 @@ type ApplicationSetCondition struct { LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,3,opt,name=lastTransitionTime"` // True/False/Unknown Status ApplicationSetConditionStatus `json:"status" protobuf:"bytes,4,opt,name=status"` - // Single word camelcase representing the reason for the status eg ErrorOccurred + //Single word camelcase representing the reason for the status eg ErrorOccurred Reason string `json:"reason" protobuf:"bytes,5,opt,name=reason"` } @@ -863,8 +833,6 @@ type ApplicationSetApplicationStatus struct { 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"` - // TargetRevision tracks the desired revisions the Application should be synced to. - TargetRevisions []string `json:"targetRevisions" protobuf:"bytes,6,opt,name=targetrevisions"` } // ApplicationSetList contains a list of ApplicationSet @@ -876,21 +844,6 @@ type ApplicationSetList struct { Items []ApplicationSet `json:"items" protobuf:"bytes,2,rep,name=items"` } -// ApplicationSetTree holds nodes which belongs to the application -// Used to build a tree of an ApplicationSet and its children -type ApplicationSetTree struct { - // Nodes contains list of nodes which are directly managed by the applicationset - Nodes []ResourceNode `json:"nodes,omitempty" protobuf:"bytes,1,rep,name=nodes"` -} - -// Normalize sorts applicationset tree nodes. The persistent order allows to -// effectively compare previously cached app tree and allows to unnecessary Redis requests. -func (t *ApplicationSetTree) Normalize() { - sort.Slice(t.Nodes, func(i, j int) bool { - return t.Nodes[i].FullName() < t.Nodes[j].FullName() - }) -} - // func init() { // SchemeBuilder.Register(&ApplicationSet{}, &ApplicationSetList{}) // } diff --git a/pkg/apis/application/v1alpha1/applicationset_types_test.go b/pkg/apis/application/v1alpha1/applicationset_types_test.go index 867024578f76e..282cc1ca9a423 100644 --- a/pkg/apis/application/v1alpha1/applicationset_types_test.go +++ b/pkg/apis/application/v1alpha1/applicationset_types_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" ) func testAppSetCond(t ApplicationSetConditionType, msg string, lastTransitionTime *metav1.Time, status ApplicationSetConditionStatus, reason string) ApplicationSetCondition { @@ -72,6 +72,7 @@ func TestApplicationSetRBACName(t *testing.T) { a.Spec.Template.Spec.Project = "test" assert.Equal(t, "test/test-appset", a.RBACName("argocd")) }) + } func TestApplicationSetSetConditions(t *testing.T) { @@ -172,9 +173,9 @@ func TestSCMProviderGeneratorGitlab_WillIncludeSharedProjects(t *testing.T) { settings := SCMProviderGeneratorGitlab{} assert.True(t, settings.WillIncludeSharedProjects()) - settings.IncludeSharedProjects = ptr.To(false) + settings.IncludeSharedProjects = pointer.Bool(false) assert.False(t, settings.WillIncludeSharedProjects()) - settings.IncludeSharedProjects = ptr.To(true) + 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 5fc96609621ca..1db2f05f0b6bd 100644 --- a/pkg/apis/application/v1alpha1/generated.pb.go +++ b/pkg/apis/application/v1alpha1/generated.pb.go @@ -263,38 +263,10 @@ func (m *ApplicationDestination) XXX_DiscardUnknown() { var xxx_messageInfo_ApplicationDestination proto.InternalMessageInfo -func (m *ApplicationDestinationServiceAccount) Reset() { *m = ApplicationDestinationServiceAccount{} } -func (*ApplicationDestinationServiceAccount) ProtoMessage() {} -func (*ApplicationDestinationServiceAccount) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{8} -} -func (m *ApplicationDestinationServiceAccount) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ApplicationDestinationServiceAccount) 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 *ApplicationDestinationServiceAccount) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplicationDestinationServiceAccount.Merge(m, src) -} -func (m *ApplicationDestinationServiceAccount) XXX_Size() int { - return m.Size() -} -func (m *ApplicationDestinationServiceAccount) XXX_DiscardUnknown() { - xxx_messageInfo_ApplicationDestinationServiceAccount.DiscardUnknown(m) -} - -var xxx_messageInfo_ApplicationDestinationServiceAccount proto.InternalMessageInfo - func (m *ApplicationList) Reset() { *m = ApplicationList{} } func (*ApplicationList) ProtoMessage() {} func (*ApplicationList) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{9} + return fileDescriptor_030104ce3b95bcac, []int{8} } func (m *ApplicationList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -322,7 +294,7 @@ var xxx_messageInfo_ApplicationList proto.InternalMessageInfo func (m *ApplicationMatchExpression) Reset() { *m = ApplicationMatchExpression{} } func (*ApplicationMatchExpression) ProtoMessage() {} func (*ApplicationMatchExpression) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{10} + return fileDescriptor_030104ce3b95bcac, []int{9} } func (m *ApplicationMatchExpression) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -350,7 +322,7 @@ var xxx_messageInfo_ApplicationMatchExpression proto.InternalMessageInfo func (m *ApplicationPreservedFields) Reset() { *m = ApplicationPreservedFields{} } func (*ApplicationPreservedFields) ProtoMessage() {} func (*ApplicationPreservedFields) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{11} + return fileDescriptor_030104ce3b95bcac, []int{10} } func (m *ApplicationPreservedFields) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -378,7 +350,7 @@ var xxx_messageInfo_ApplicationPreservedFields proto.InternalMessageInfo func (m *ApplicationSet) Reset() { *m = ApplicationSet{} } func (*ApplicationSet) ProtoMessage() {} func (*ApplicationSet) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{12} + return fileDescriptor_030104ce3b95bcac, []int{11} } func (m *ApplicationSet) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -406,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{13} + return fileDescriptor_030104ce3b95bcac, []int{12} } func (m *ApplicationSetApplicationStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -434,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{14} + return fileDescriptor_030104ce3b95bcac, []int{13} } func (m *ApplicationSetCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -462,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{15} + return fileDescriptor_030104ce3b95bcac, []int{14} } func (m *ApplicationSetGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -490,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{16} + return fileDescriptor_030104ce3b95bcac, []int{15} } func (m *ApplicationSetList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -518,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{17} + return fileDescriptor_030104ce3b95bcac, []int{16} } func (m *ApplicationSetNestedGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -548,7 +520,7 @@ func (m *ApplicationSetResourceIgnoreDifferences) Reset() { } func (*ApplicationSetResourceIgnoreDifferences) ProtoMessage() {} func (*ApplicationSetResourceIgnoreDifferences) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{18} + return fileDescriptor_030104ce3b95bcac, []int{17} } func (m *ApplicationSetResourceIgnoreDifferences) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -576,7 +548,7 @@ var xxx_messageInfo_ApplicationSetResourceIgnoreDifferences proto.InternalMessag func (m *ApplicationSetRolloutStep) Reset() { *m = ApplicationSetRolloutStep{} } func (*ApplicationSetRolloutStep) ProtoMessage() {} func (*ApplicationSetRolloutStep) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{19} + return fileDescriptor_030104ce3b95bcac, []int{18} } func (m *ApplicationSetRolloutStep) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -604,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{20} + return fileDescriptor_030104ce3b95bcac, []int{19} } func (m *ApplicationSetRolloutStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -632,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{21} + return fileDescriptor_030104ce3b95bcac, []int{20} } func (m *ApplicationSetSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -660,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{22} + return fileDescriptor_030104ce3b95bcac, []int{21} } func (m *ApplicationSetStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -688,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{23} + return fileDescriptor_030104ce3b95bcac, []int{22} } func (m *ApplicationSetStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -716,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{24} + return fileDescriptor_030104ce3b95bcac, []int{23} } func (m *ApplicationSetSyncPolicy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -744,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{25} + return fileDescriptor_030104ce3b95bcac, []int{24} } func (m *ApplicationSetTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -772,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{26} + return fileDescriptor_030104ce3b95bcac, []int{25} } func (m *ApplicationSetTemplateMeta) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -800,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{27} + return fileDescriptor_030104ce3b95bcac, []int{26} } func (m *ApplicationSetTerminalGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -825,38 +797,10 @@ func (m *ApplicationSetTerminalGenerator) XXX_DiscardUnknown() { var xxx_messageInfo_ApplicationSetTerminalGenerator proto.InternalMessageInfo -func (m *ApplicationSetTree) Reset() { *m = ApplicationSetTree{} } -func (*ApplicationSetTree) ProtoMessage() {} -func (*ApplicationSetTree) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{28} -} -func (m *ApplicationSetTree) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ApplicationSetTree) 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 *ApplicationSetTree) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplicationSetTree.Merge(m, src) -} -func (m *ApplicationSetTree) XXX_Size() int { - return m.Size() -} -func (m *ApplicationSetTree) XXX_DiscardUnknown() { - xxx_messageInfo_ApplicationSetTree.DiscardUnknown(m) -} - -var xxx_messageInfo_ApplicationSetTree proto.InternalMessageInfo - func (m *ApplicationSource) Reset() { *m = ApplicationSource{} } func (*ApplicationSource) ProtoMessage() {} func (*ApplicationSource) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{29} + return fileDescriptor_030104ce3b95bcac, []int{27} } func (m *ApplicationSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -884,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{30} + return fileDescriptor_030104ce3b95bcac, []int{28} } func (m *ApplicationSourceDirectory) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -912,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{31} + return fileDescriptor_030104ce3b95bcac, []int{29} } func (m *ApplicationSourceHelm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -940,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{32} + return fileDescriptor_030104ce3b95bcac, []int{30} } func (m *ApplicationSourceJsonnet) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -968,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{33} + return fileDescriptor_030104ce3b95bcac, []int{31} } func (m *ApplicationSourceKustomize) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -996,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{34} + return fileDescriptor_030104ce3b95bcac, []int{32} } func (m *ApplicationSourcePlugin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1024,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{35} + return fileDescriptor_030104ce3b95bcac, []int{33} } func (m *ApplicationSourcePluginParameter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1052,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{36} + return fileDescriptor_030104ce3b95bcac, []int{34} } func (m *ApplicationSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1080,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{37} + return fileDescriptor_030104ce3b95bcac, []int{35} } func (m *ApplicationStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1108,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{38} + return fileDescriptor_030104ce3b95bcac, []int{36} } func (m *ApplicationSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1136,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{39} + return fileDescriptor_030104ce3b95bcac, []int{37} } func (m *ApplicationTree) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1164,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{40} + return fileDescriptor_030104ce3b95bcac, []int{38} } func (m *ApplicationWatchEvent) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1192,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{41} + return fileDescriptor_030104ce3b95bcac, []int{39} } func (m *Backoff) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1220,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{42} + return fileDescriptor_030104ce3b95bcac, []int{40} } func (m *BasicAuthBitbucketServer) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1245,38 +1189,10 @@ func (m *BasicAuthBitbucketServer) XXX_DiscardUnknown() { var xxx_messageInfo_BasicAuthBitbucketServer proto.InternalMessageInfo -func (m *BearerTokenBitbucket) Reset() { *m = BearerTokenBitbucket{} } -func (*BearerTokenBitbucket) ProtoMessage() {} -func (*BearerTokenBitbucket) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{43} -} -func (m *BearerTokenBitbucket) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *BearerTokenBitbucket) 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 *BearerTokenBitbucket) XXX_Merge(src proto.Message) { - xxx_messageInfo_BearerTokenBitbucket.Merge(m, src) -} -func (m *BearerTokenBitbucket) XXX_Size() int { - return m.Size() -} -func (m *BearerTokenBitbucket) XXX_DiscardUnknown() { - xxx_messageInfo_BearerTokenBitbucket.DiscardUnknown(m) -} - -var xxx_messageInfo_BearerTokenBitbucket proto.InternalMessageInfo - func (m *BearerTokenBitbucketCloud) Reset() { *m = BearerTokenBitbucketCloud{} } func (*BearerTokenBitbucketCloud) ProtoMessage() {} func (*BearerTokenBitbucketCloud) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{44} + return fileDescriptor_030104ce3b95bcac, []int{41} } func (m *BearerTokenBitbucketCloud) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1304,7 +1220,7 @@ var xxx_messageInfo_BearerTokenBitbucketCloud proto.InternalMessageInfo func (m *ChartDetails) Reset() { *m = ChartDetails{} } func (*ChartDetails) ProtoMessage() {} func (*ChartDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{45} + return fileDescriptor_030104ce3b95bcac, []int{42} } func (m *ChartDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1332,7 +1248,7 @@ var xxx_messageInfo_ChartDetails proto.InternalMessageInfo func (m *Cluster) Reset() { *m = Cluster{} } func (*Cluster) ProtoMessage() {} func (*Cluster) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{46} + return fileDescriptor_030104ce3b95bcac, []int{43} } func (m *Cluster) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1360,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{47} + return fileDescriptor_030104ce3b95bcac, []int{44} } func (m *ClusterCacheInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1388,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{48} + return fileDescriptor_030104ce3b95bcac, []int{45} } func (m *ClusterConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1416,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{49} + return fileDescriptor_030104ce3b95bcac, []int{46} } func (m *ClusterGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1444,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{50} + return fileDescriptor_030104ce3b95bcac, []int{47} } func (m *ClusterInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1472,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{51} + return fileDescriptor_030104ce3b95bcac, []int{48} } func (m *ClusterList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1500,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{52} + return fileDescriptor_030104ce3b95bcac, []int{49} } func (m *Command) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1528,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{53} + return fileDescriptor_030104ce3b95bcac, []int{50} } func (m *ComparedTo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1556,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{54} + return fileDescriptor_030104ce3b95bcac, []int{51} } func (m *ComponentParameter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1584,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{55} + return fileDescriptor_030104ce3b95bcac, []int{52} } func (m *ConfigManagementPlugin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1609,38 +1525,10 @@ func (m *ConfigManagementPlugin) XXX_DiscardUnknown() { var xxx_messageInfo_ConfigManagementPlugin proto.InternalMessageInfo -func (m *ConfigMapKeyRef) Reset() { *m = ConfigMapKeyRef{} } -func (*ConfigMapKeyRef) ProtoMessage() {} -func (*ConfigMapKeyRef) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{56} -} -func (m *ConfigMapKeyRef) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ConfigMapKeyRef) 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 *ConfigMapKeyRef) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConfigMapKeyRef.Merge(m, src) -} -func (m *ConfigMapKeyRef) XXX_Size() int { - return m.Size() -} -func (m *ConfigMapKeyRef) XXX_DiscardUnknown() { - xxx_messageInfo_ConfigMapKeyRef.DiscardUnknown(m) -} - -var xxx_messageInfo_ConfigMapKeyRef proto.InternalMessageInfo - func (m *ConnectionState) Reset() { *m = ConnectionState{} } func (*ConnectionState) ProtoMessage() {} func (*ConnectionState) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{57} + return fileDescriptor_030104ce3b95bcac, []int{53} } func (m *ConnectionState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1668,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{58} + return fileDescriptor_030104ce3b95bcac, []int{54} } func (m *DuckTypeGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1696,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{59} + return fileDescriptor_030104ce3b95bcac, []int{55} } func (m *EnvEntry) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1724,7 +1612,7 @@ var xxx_messageInfo_EnvEntry proto.InternalMessageInfo func (m *ErrApplicationNotAllowedToUseProject) Reset() { *m = ErrApplicationNotAllowedToUseProject{} } func (*ErrApplicationNotAllowedToUseProject) ProtoMessage() {} func (*ErrApplicationNotAllowedToUseProject) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{60} + return fileDescriptor_030104ce3b95bcac, []int{56} } func (m *ErrApplicationNotAllowedToUseProject) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1752,7 +1640,7 @@ var xxx_messageInfo_ErrApplicationNotAllowedToUseProject proto.InternalMessageIn func (m *ExecProviderConfig) Reset() { *m = ExecProviderConfig{} } func (*ExecProviderConfig) ProtoMessage() {} func (*ExecProviderConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{61} + return fileDescriptor_030104ce3b95bcac, []int{57} } func (m *ExecProviderConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1780,7 +1668,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{62} + return fileDescriptor_030104ce3b95bcac, []int{58} } func (m *GitDirectoryGeneratorItem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1808,7 +1696,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{63} + return fileDescriptor_030104ce3b95bcac, []int{59} } func (m *GitFileGeneratorItem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1836,7 +1724,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{64} + return fileDescriptor_030104ce3b95bcac, []int{60} } func (m *GitGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1864,7 +1752,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{65} + return fileDescriptor_030104ce3b95bcac, []int{61} } func (m *GnuPGPublicKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1892,7 +1780,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{66} + return fileDescriptor_030104ce3b95bcac, []int{62} } func (m *GnuPGPublicKeyList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1920,7 +1808,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{67} + return fileDescriptor_030104ce3b95bcac, []int{63} } func (m *HealthStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1948,7 +1836,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{68} + return fileDescriptor_030104ce3b95bcac, []int{64} } func (m *HelmFileParameter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1976,7 +1864,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{69} + return fileDescriptor_030104ce3b95bcac, []int{65} } func (m *HelmOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2004,7 +1892,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{70} + return fileDescriptor_030104ce3b95bcac, []int{66} } func (m *HelmParameter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2032,7 +1920,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{71} + return fileDescriptor_030104ce3b95bcac, []int{67} } func (m *HostInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2060,7 +1948,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{72} + return fileDescriptor_030104ce3b95bcac, []int{68} } func (m *HostResourceInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2088,7 +1976,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{73} + return fileDescriptor_030104ce3b95bcac, []int{69} } func (m *Info) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2116,7 +2004,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{74} + return fileDescriptor_030104ce3b95bcac, []int{70} } func (m *InfoItem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2144,7 +2032,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{75} + return fileDescriptor_030104ce3b95bcac, []int{71} } func (m *JWTToken) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2172,7 +2060,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{76} + return fileDescriptor_030104ce3b95bcac, []int{72} } func (m *JWTTokens) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2200,7 +2088,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{77} + return fileDescriptor_030104ce3b95bcac, []int{73} } func (m *JsonnetVar) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2228,7 +2116,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{78} + return fileDescriptor_030104ce3b95bcac, []int{74} } func (m *KnownTypeField) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2256,7 +2144,7 @@ var xxx_messageInfo_KnownTypeField proto.InternalMessageInfo func (m *KustomizeGvk) Reset() { *m = KustomizeGvk{} } func (*KustomizeGvk) ProtoMessage() {} func (*KustomizeGvk) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{79} + return fileDescriptor_030104ce3b95bcac, []int{75} } func (m *KustomizeGvk) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2284,7 +2172,7 @@ var xxx_messageInfo_KustomizeGvk proto.InternalMessageInfo func (m *KustomizeOptions) Reset() { *m = KustomizeOptions{} } func (*KustomizeOptions) ProtoMessage() {} func (*KustomizeOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{80} + return fileDescriptor_030104ce3b95bcac, []int{76} } func (m *KustomizeOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2312,7 +2200,7 @@ var xxx_messageInfo_KustomizeOptions proto.InternalMessageInfo func (m *KustomizePatch) Reset() { *m = KustomizePatch{} } func (*KustomizePatch) ProtoMessage() {} func (*KustomizePatch) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{81} + return fileDescriptor_030104ce3b95bcac, []int{77} } func (m *KustomizePatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2340,7 +2228,7 @@ var xxx_messageInfo_KustomizePatch proto.InternalMessageInfo func (m *KustomizeReplica) Reset() { *m = KustomizeReplica{} } func (*KustomizeReplica) ProtoMessage() {} func (*KustomizeReplica) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{82} + return fileDescriptor_030104ce3b95bcac, []int{78} } func (m *KustomizeReplica) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2368,7 +2256,7 @@ var xxx_messageInfo_KustomizeReplica proto.InternalMessageInfo func (m *KustomizeResId) Reset() { *m = KustomizeResId{} } func (*KustomizeResId) ProtoMessage() {} func (*KustomizeResId) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{83} + return fileDescriptor_030104ce3b95bcac, []int{79} } func (m *KustomizeResId) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2396,7 +2284,7 @@ var xxx_messageInfo_KustomizeResId proto.InternalMessageInfo func (m *KustomizeSelector) Reset() { *m = KustomizeSelector{} } func (*KustomizeSelector) ProtoMessage() {} func (*KustomizeSelector) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{84} + return fileDescriptor_030104ce3b95bcac, []int{80} } func (m *KustomizeSelector) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2424,7 +2312,7 @@ var xxx_messageInfo_KustomizeSelector proto.InternalMessageInfo func (m *ListGenerator) Reset() { *m = ListGenerator{} } func (*ListGenerator) ProtoMessage() {} func (*ListGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{85} + return fileDescriptor_030104ce3b95bcac, []int{81} } func (m *ListGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2452,7 +2340,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{86} + return fileDescriptor_030104ce3b95bcac, []int{82} } func (m *ManagedNamespaceMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2480,7 +2368,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{87} + return fileDescriptor_030104ce3b95bcac, []int{83} } func (m *MatrixGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2508,7 +2396,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{88} + return fileDescriptor_030104ce3b95bcac, []int{84} } func (m *MergeGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2536,7 +2424,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{89} + return fileDescriptor_030104ce3b95bcac, []int{85} } func (m *NestedMatrixGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2564,7 +2452,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{90} + return fileDescriptor_030104ce3b95bcac, []int{86} } func (m *NestedMergeGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2592,7 +2480,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{91} + return fileDescriptor_030104ce3b95bcac, []int{87} } func (m *Operation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2620,7 +2508,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{92} + return fileDescriptor_030104ce3b95bcac, []int{88} } func (m *OperationInitiator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2648,7 +2536,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{93} + return fileDescriptor_030104ce3b95bcac, []int{89} } func (m *OperationState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2676,7 +2564,7 @@ var xxx_messageInfo_OperationState proto.InternalMessageInfo func (m *OptionalArray) Reset() { *m = OptionalArray{} } func (*OptionalArray) ProtoMessage() {} func (*OptionalArray) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{94} + return fileDescriptor_030104ce3b95bcac, []int{90} } func (m *OptionalArray) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2704,7 +2592,7 @@ var xxx_messageInfo_OptionalArray proto.InternalMessageInfo func (m *OptionalMap) Reset() { *m = OptionalMap{} } func (*OptionalMap) ProtoMessage() {} func (*OptionalMap) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{95} + return fileDescriptor_030104ce3b95bcac, []int{91} } func (m *OptionalMap) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2732,7 +2620,7 @@ var xxx_messageInfo_OptionalMap proto.InternalMessageInfo func (m *OrphanedResourceKey) Reset() { *m = OrphanedResourceKey{} } func (*OrphanedResourceKey) ProtoMessage() {} func (*OrphanedResourceKey) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{96} + return fileDescriptor_030104ce3b95bcac, []int{92} } func (m *OrphanedResourceKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2760,7 +2648,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{97} + return fileDescriptor_030104ce3b95bcac, []int{93} } func (m *OrphanedResourcesMonitorSettings) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2788,7 +2676,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{98} + return fileDescriptor_030104ce3b95bcac, []int{94} } func (m *OverrideIgnoreDiff) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2816,7 +2704,7 @@ var xxx_messageInfo_OverrideIgnoreDiff proto.InternalMessageInfo func (m *PluginConfigMapRef) Reset() { *m = PluginConfigMapRef{} } func (*PluginConfigMapRef) ProtoMessage() {} func (*PluginConfigMapRef) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{99} + return fileDescriptor_030104ce3b95bcac, []int{95} } func (m *PluginConfigMapRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2844,7 +2732,7 @@ var xxx_messageInfo_PluginConfigMapRef proto.InternalMessageInfo func (m *PluginGenerator) Reset() { *m = PluginGenerator{} } func (*PluginGenerator) ProtoMessage() {} func (*PluginGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{100} + return fileDescriptor_030104ce3b95bcac, []int{96} } func (m *PluginGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2872,7 +2760,7 @@ var xxx_messageInfo_PluginGenerator proto.InternalMessageInfo func (m *PluginInput) Reset() { *m = PluginInput{} } func (*PluginInput) ProtoMessage() {} func (*PluginInput) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{101} + return fileDescriptor_030104ce3b95bcac, []int{97} } func (m *PluginInput) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2900,7 +2788,7 @@ var xxx_messageInfo_PluginInput proto.InternalMessageInfo func (m *ProjectRole) Reset() { *m = ProjectRole{} } func (*ProjectRole) ProtoMessage() {} func (*ProjectRole) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{102} + return fileDescriptor_030104ce3b95bcac, []int{98} } func (m *ProjectRole) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2928,7 +2816,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{103} + return fileDescriptor_030104ce3b95bcac, []int{99} } func (m *PullRequestGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2956,7 +2844,7 @@ var xxx_messageInfo_PullRequestGenerator proto.InternalMessageInfo func (m *PullRequestGeneratorAzureDevOps) Reset() { *m = PullRequestGeneratorAzureDevOps{} } func (*PullRequestGeneratorAzureDevOps) ProtoMessage() {} func (*PullRequestGeneratorAzureDevOps) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{104} + return fileDescriptor_030104ce3b95bcac, []int{100} } func (m *PullRequestGeneratorAzureDevOps) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2984,7 +2872,7 @@ var xxx_messageInfo_PullRequestGeneratorAzureDevOps proto.InternalMessageInfo func (m *PullRequestGeneratorBitbucket) Reset() { *m = PullRequestGeneratorBitbucket{} } func (*PullRequestGeneratorBitbucket) ProtoMessage() {} func (*PullRequestGeneratorBitbucket) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{105} + return fileDescriptor_030104ce3b95bcac, []int{101} } func (m *PullRequestGeneratorBitbucket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3012,7 +2900,7 @@ var xxx_messageInfo_PullRequestGeneratorBitbucket proto.InternalMessageInfo func (m *PullRequestGeneratorBitbucketServer) Reset() { *m = PullRequestGeneratorBitbucketServer{} } func (*PullRequestGeneratorBitbucketServer) ProtoMessage() {} func (*PullRequestGeneratorBitbucketServer) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{106} + return fileDescriptor_030104ce3b95bcac, []int{102} } func (m *PullRequestGeneratorBitbucketServer) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3040,7 +2928,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{107} + return fileDescriptor_030104ce3b95bcac, []int{103} } func (m *PullRequestGeneratorFilter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3068,7 +2956,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{108} + return fileDescriptor_030104ce3b95bcac, []int{104} } func (m *PullRequestGeneratorGitLab) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3096,7 +2984,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{109} + return fileDescriptor_030104ce3b95bcac, []int{105} } func (m *PullRequestGeneratorGitea) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3124,7 +3012,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{110} + return fileDescriptor_030104ce3b95bcac, []int{106} } func (m *PullRequestGeneratorGithub) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3152,7 +3040,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{111} + return fileDescriptor_030104ce3b95bcac, []int{107} } func (m *RefTarget) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3180,7 +3068,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{112} + return fileDescriptor_030104ce3b95bcac, []int{108} } func (m *RepoCreds) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3208,7 +3096,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{113} + return fileDescriptor_030104ce3b95bcac, []int{109} } func (m *RepoCredsList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3236,7 +3124,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{114} + return fileDescriptor_030104ce3b95bcac, []int{110} } func (m *Repository) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3264,7 +3152,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{115} + return fileDescriptor_030104ce3b95bcac, []int{111} } func (m *RepositoryCertificate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3292,7 +3180,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{116} + return fileDescriptor_030104ce3b95bcac, []int{112} } func (m *RepositoryCertificateList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3320,7 +3208,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{117} + return fileDescriptor_030104ce3b95bcac, []int{113} } func (m *RepositoryList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3348,7 +3236,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{118} + return fileDescriptor_030104ce3b95bcac, []int{114} } func (m *ResourceAction) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3376,7 +3264,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{119} + return fileDescriptor_030104ce3b95bcac, []int{115} } func (m *ResourceActionDefinition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3404,7 +3292,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{120} + return fileDescriptor_030104ce3b95bcac, []int{116} } func (m *ResourceActionParam) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3432,7 +3320,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{121} + return fileDescriptor_030104ce3b95bcac, []int{117} } func (m *ResourceActions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3460,7 +3348,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{122} + return fileDescriptor_030104ce3b95bcac, []int{118} } func (m *ResourceDiff) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3488,7 +3376,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{123} + return fileDescriptor_030104ce3b95bcac, []int{119} } func (m *ResourceIgnoreDifferences) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3516,7 +3404,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{124} + return fileDescriptor_030104ce3b95bcac, []int{120} } func (m *ResourceNetworkingInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3544,7 +3432,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{125} + return fileDescriptor_030104ce3b95bcac, []int{121} } func (m *ResourceNode) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3572,7 +3460,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{126} + return fileDescriptor_030104ce3b95bcac, []int{122} } func (m *ResourceOverride) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3600,7 +3488,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{127} + return fileDescriptor_030104ce3b95bcac, []int{123} } func (m *ResourceRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3628,7 +3516,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{128} + return fileDescriptor_030104ce3b95bcac, []int{124} } func (m *ResourceResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3656,7 +3544,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{129} + return fileDescriptor_030104ce3b95bcac, []int{125} } func (m *ResourceStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3684,7 +3572,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{130} + return fileDescriptor_030104ce3b95bcac, []int{126} } func (m *RetryStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3712,7 +3600,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{131} + return fileDescriptor_030104ce3b95bcac, []int{127} } func (m *RevisionHistory) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3740,7 +3628,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{132} + return fileDescriptor_030104ce3b95bcac, []int{128} } func (m *RevisionMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3768,7 +3656,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{133} + return fileDescriptor_030104ce3b95bcac, []int{129} } func (m *SCMProviderGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3796,7 +3684,7 @@ var xxx_messageInfo_SCMProviderGenerator proto.InternalMessageInfo func (m *SCMProviderGeneratorAWSCodeCommit) Reset() { *m = SCMProviderGeneratorAWSCodeCommit{} } func (*SCMProviderGeneratorAWSCodeCommit) ProtoMessage() {} func (*SCMProviderGeneratorAWSCodeCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{134} + return fileDescriptor_030104ce3b95bcac, []int{130} } func (m *SCMProviderGeneratorAWSCodeCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3824,7 +3712,7 @@ var xxx_messageInfo_SCMProviderGeneratorAWSCodeCommit proto.InternalMessageInfo func (m *SCMProviderGeneratorAzureDevOps) Reset() { *m = SCMProviderGeneratorAzureDevOps{} } func (*SCMProviderGeneratorAzureDevOps) ProtoMessage() {} func (*SCMProviderGeneratorAzureDevOps) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{135} + return fileDescriptor_030104ce3b95bcac, []int{131} } func (m *SCMProviderGeneratorAzureDevOps) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3852,7 +3740,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{136} + return fileDescriptor_030104ce3b95bcac, []int{132} } func (m *SCMProviderGeneratorBitbucket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3880,7 +3768,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{137} + return fileDescriptor_030104ce3b95bcac, []int{133} } func (m *SCMProviderGeneratorBitbucketServer) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3908,7 +3796,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{138} + return fileDescriptor_030104ce3b95bcac, []int{134} } func (m *SCMProviderGeneratorFilter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3936,7 +3824,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{139} + return fileDescriptor_030104ce3b95bcac, []int{135} } func (m *SCMProviderGeneratorGitea) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3964,7 +3852,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{140} + return fileDescriptor_030104ce3b95bcac, []int{136} } func (m *SCMProviderGeneratorGithub) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3992,7 +3880,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{141} + return fileDescriptor_030104ce3b95bcac, []int{137} } func (m *SCMProviderGeneratorGitlab) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4020,7 +3908,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{142} + return fileDescriptor_030104ce3b95bcac, []int{138} } func (m *SecretRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4048,7 +3936,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{143} + return fileDescriptor_030104ce3b95bcac, []int{139} } func (m *SignatureKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4076,7 +3964,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{144} + return fileDescriptor_030104ce3b95bcac, []int{140} } func (m *SyncOperation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4104,7 +3992,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{145} + return fileDescriptor_030104ce3b95bcac, []int{141} } func (m *SyncOperationResource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4132,7 +4020,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{146} + return fileDescriptor_030104ce3b95bcac, []int{142} } func (m *SyncOperationResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4160,7 +4048,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{147} + return fileDescriptor_030104ce3b95bcac, []int{143} } func (m *SyncPolicy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4188,7 +4076,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{148} + return fileDescriptor_030104ce3b95bcac, []int{144} } func (m *SyncPolicyAutomated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4216,7 +4104,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{149} + return fileDescriptor_030104ce3b95bcac, []int{145} } func (m *SyncStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4244,7 +4132,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{150} + return fileDescriptor_030104ce3b95bcac, []int{146} } func (m *SyncStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4272,7 +4160,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{151} + return fileDescriptor_030104ce3b95bcac, []int{147} } func (m *SyncStrategyApply) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4300,7 +4188,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{152} + return fileDescriptor_030104ce3b95bcac, []int{148} } func (m *SyncStrategyHook) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4328,7 +4216,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{153} + return fileDescriptor_030104ce3b95bcac, []int{149} } func (m *SyncWindow) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4356,7 +4244,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{154} + return fileDescriptor_030104ce3b95bcac, []int{150} } func (m *TLSClientConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4384,7 +4272,7 @@ var xxx_messageInfo_TLSClientConfig proto.InternalMessageInfo func (m *TagFilter) Reset() { *m = TagFilter{} } func (*TagFilter) ProtoMessage() {} func (*TagFilter) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{155} + return fileDescriptor_030104ce3b95bcac, []int{151} } func (m *TagFilter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4419,7 +4307,6 @@ func init() { proto.RegisterType((*Application)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Application") proto.RegisterType((*ApplicationCondition)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationCondition") proto.RegisterType((*ApplicationDestination)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationDestination") - proto.RegisterType((*ApplicationDestinationServiceAccount)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationDestinationServiceAccount") 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") @@ -4441,7 +4328,6 @@ func init() { proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetTemplateMeta.AnnotationsEntry") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetTemplateMeta.LabelsEntry") proto.RegisterType((*ApplicationSetTerminalGenerator)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetTerminalGenerator") - proto.RegisterType((*ApplicationSetTree)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetTree") proto.RegisterType((*ApplicationSource)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSource") proto.RegisterType((*ApplicationSourceDirectory)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSourceDirectory") proto.RegisterType((*ApplicationSourceHelm)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSourceHelm") @@ -4458,7 +4344,6 @@ 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((*BearerTokenBitbucket)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.BearerTokenBitbucket") 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") @@ -4474,7 +4359,6 @@ func init() { proto.RegisterType((*ComparedTo)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ComparedTo") proto.RegisterType((*ComponentParameter)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ComponentParameter") proto.RegisterType((*ConfigManagementPlugin)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ConfigManagementPlugin") - proto.RegisterType((*ConfigMapKeyRef)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ConfigMapKeyRef") proto.RegisterType((*ConnectionState)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ConnectionState") proto.RegisterType((*DuckTypeGenerator)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.DuckTypeGenerator") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.DuckTypeGenerator.ValuesEntry") @@ -4593,717 +4477,695 @@ func init() { } var fileDescriptor_030104ce3b95bcac = []byte{ - // 11357 bytes of a gzipped FileDescriptorProto + // 11004 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, 0x11, 0x4d, 0xf2, 0x0e, 0xa4, 0xee, 0x0e, 0xf4, - 0x9c, 0x7d, 0x3a, 0x45, 0x77, 0x80, 0x8f, 0xba, 0x93, 0x2f, 0x3a, 0x4b, 0x32, 0x3e, 0x48, 0x10, - 0x24, 0x40, 0xe0, 0x1a, 0x20, 0x29, 0x9d, 0x7c, 0x3a, 0x0d, 0x66, 0x1b, 0x8b, 0x21, 0x66, 0x67, - 0xf6, 0x66, 0x66, 0x41, 0xe2, 0x2c, 0xc9, 0x92, 0x25, 0xd9, 0x72, 0xf4, 0x71, 0x8a, 0x94, 0xaa, - 0x9c, 0x13, 0x4b, 0x91, 0x2d, 0x27, 0x95, 0x54, 0x4a, 0x15, 0x25, 0xf9, 0x11, 0x27, 0xb6, 0xcb, - 0x15, 0x3b, 0xe5, 0x52, 0xe2, 0xa4, 0xec, 0x52, 0xa9, 0x2c, 0x25, 0xb1, 0x11, 0x89, 0x71, 0x2a, - 0xa9, 0xfc, 0x70, 0x55, 0x9c, 0xfc, 0x48, 0x31, 0xf9, 0x91, 0xea, 0xef, 0x9e, 0xd9, 0x59, 0x60, - 0x41, 0x0c, 0x40, 0x4a, 0xb9, 0x7f, 0xbb, 0xfd, 0x5e, 0xf7, 0xeb, 0xe9, 0x8f, 0xf7, 0x5e, 0xbf, - 0x7e, 0xef, 0x35, 0x2c, 0x36, 0xbc, 0x64, 0xb3, 0xbd, 0x3e, 0xe9, 0x86, 0xcd, 0x29, 0x27, 0x6a, - 0x84, 0xad, 0x28, 0xbc, 0xc9, 0x7e, 0x3c, 0xed, 0xd6, 0xa7, 0xb6, 0xcf, 0x4f, 0xb5, 0xb6, 0x1a, - 0x53, 0x4e, 0xcb, 0x8b, 0xa7, 0x9c, 0x56, 0xcb, 0xf7, 0x5c, 0x27, 0xf1, 0xc2, 0x60, 0x6a, 0xfb, - 0x19, 0xc7, 0x6f, 0x6d, 0x3a, 0xcf, 0x4c, 0x35, 0x48, 0x40, 0x22, 0x27, 0x21, 0xf5, 0xc9, 0x56, - 0x14, 0x26, 0x21, 0xfa, 0x69, 0xdd, 0xda, 0xa4, 0x6c, 0x8d, 0xfd, 0x78, 0xc5, 0xad, 0x4f, 0x6e, - 0x9f, 0x9f, 0x6c, 0x6d, 0x35, 0x26, 0x69, 0x6b, 0x93, 0x46, 0x6b, 0x93, 0xb2, 0xb5, 0xb3, 0x4f, - 0x1b, 0x7d, 0x69, 0x84, 0x8d, 0x70, 0x8a, 0x35, 0xba, 0xde, 0xde, 0x60, 0xff, 0xd8, 0x1f, 0xf6, - 0x8b, 0x13, 0x3b, 0x6b, 0x6f, 0x3d, 0x1f, 0x4f, 0x7a, 0x21, 0xed, 0xde, 0x94, 0x1b, 0x46, 0x64, - 0x6a, 0xbb, 0xa3, 0x43, 0x67, 0x2f, 0x69, 0x1c, 0x72, 0x3b, 0x21, 0x41, 0xec, 0x85, 0x41, 0xfc, - 0x34, 0xed, 0x02, 0x89, 0xb6, 0x49, 0x64, 0x7e, 0x9e, 0x81, 0x90, 0xd7, 0xd2, 0xb3, 0xba, 0xa5, - 0xa6, 0xe3, 0x6e, 0x7a, 0x01, 0x89, 0x76, 0x74, 0xf5, 0x26, 0x49, 0x9c, 0xbc, 0x5a, 0x53, 0xdd, - 0x6a, 0x45, 0xed, 0x20, 0xf1, 0x9a, 0xa4, 0xa3, 0xc2, 0xbb, 0xf6, 0xab, 0x10, 0xbb, 0x9b, 0xa4, - 0xe9, 0x74, 0xd4, 0x7b, 0x67, 0xb7, 0x7a, 0xed, 0xc4, 0xf3, 0xa7, 0xbc, 0x20, 0x89, 0x93, 0x28, - 0x5b, 0xc9, 0xfe, 0x55, 0x0b, 0x86, 0xa7, 0x6f, 0xac, 0x4e, 0xb7, 0x93, 0xcd, 0xd9, 0x30, 0xd8, - 0xf0, 0x1a, 0xe8, 0x39, 0x18, 0x74, 0xfd, 0x76, 0x9c, 0x90, 0xe8, 0xaa, 0xd3, 0x24, 0xe3, 0xd6, - 0x39, 0xeb, 0xc9, 0xda, 0xcc, 0xc9, 0x6f, 0xed, 0x4e, 0xbc, 0xe5, 0xce, 0xee, 0xc4, 0xe0, 0xac, - 0x06, 0x61, 0x13, 0x0f, 0xbd, 0x1d, 0x06, 0xa2, 0xd0, 0x27, 0xd3, 0xf8, 0xea, 0x78, 0x89, 0x55, - 0x19, 0x15, 0x55, 0x06, 0x30, 0x2f, 0xc6, 0x12, 0x4e, 0x51, 0x5b, 0x51, 0xb8, 0xe1, 0xf9, 0x64, - 0xbc, 0x9c, 0x46, 0x5d, 0xe1, 0xc5, 0x58, 0xc2, 0xed, 0x3f, 0x29, 0x01, 0x4c, 0xb7, 0x5a, 0x2b, - 0x51, 0x78, 0x93, 0xb8, 0x09, 0xfa, 0x30, 0x54, 0xe9, 0x30, 0xd7, 0x9d, 0xc4, 0x61, 0x1d, 0x1b, - 0x3c, 0xff, 0x93, 0x93, 0xfc, 0xab, 0x27, 0xcd, 0xaf, 0xd6, 0x8b, 0x8c, 0x62, 0x4f, 0x6e, 0x3f, - 0x33, 0xb9, 0xbc, 0x4e, 0xeb, 0x2f, 0x91, 0xc4, 0x99, 0x41, 0x82, 0x18, 0xe8, 0x32, 0xac, 0x5a, - 0x45, 0x01, 0xf4, 0xc5, 0x2d, 0xe2, 0xb2, 0x6f, 0x18, 0x3c, 0xbf, 0x38, 0x79, 0x98, 0xd5, 0x3c, - 0xa9, 0x7b, 0xbe, 0xda, 0x22, 0xee, 0xcc, 0x90, 0xa0, 0xdc, 0x47, 0xff, 0x61, 0x46, 0x07, 0x6d, - 0x43, 0x7f, 0x9c, 0x38, 0x49, 0x3b, 0x66, 0x43, 0x31, 0x78, 0xfe, 0x6a, 0x61, 0x14, 0x59, 0xab, - 0x33, 0x23, 0x82, 0x66, 0x3f, 0xff, 0x8f, 0x05, 0x35, 0xfb, 0xcf, 0x2c, 0x18, 0xd1, 0xc8, 0x8b, - 0x5e, 0x9c, 0xa0, 0x9f, 0xed, 0x18, 0xdc, 0xc9, 0xde, 0x06, 0x97, 0xd6, 0x66, 0x43, 0x7b, 0x42, - 0x10, 0xab, 0xca, 0x12, 0x63, 0x60, 0x9b, 0x50, 0xf1, 0x12, 0xd2, 0x8c, 0xc7, 0x4b, 0xe7, 0xca, - 0x4f, 0x0e, 0x9e, 0xbf, 0x54, 0xd4, 0x77, 0xce, 0x0c, 0x0b, 0xa2, 0x95, 0x05, 0xda, 0x3c, 0xe6, - 0x54, 0xec, 0xbf, 0x1c, 0x36, 0xbf, 0x8f, 0x0e, 0x38, 0x7a, 0x06, 0x06, 0xe3, 0xb0, 0x1d, 0xb9, - 0x04, 0x93, 0x56, 0x18, 0x8f, 0x5b, 0xe7, 0xca, 0x74, 0xe9, 0xd1, 0x45, 0xbd, 0xaa, 0x8b, 0xb1, - 0x89, 0x83, 0xbe, 0x60, 0xc1, 0x50, 0x9d, 0xc4, 0x89, 0x17, 0x30, 0xfa, 0xb2, 0xf3, 0x6b, 0x87, - 0xee, 0xbc, 0x2c, 0x9c, 0xd3, 0x8d, 0xcf, 0x9c, 0x12, 0x1f, 0x32, 0x64, 0x14, 0xc6, 0x38, 0x45, - 0x9f, 0x6e, 0xce, 0x3a, 0x89, 0xdd, 0xc8, 0x6b, 0xd1, 0xff, 0x62, 0xfb, 0xa8, 0xcd, 0x39, 0xa7, - 0x41, 0xd8, 0xc4, 0x43, 0x01, 0x54, 0xe8, 0xe6, 0x8b, 0xc7, 0xfb, 0x58, 0xff, 0x17, 0x0e, 0xd7, - 0x7f, 0x31, 0xa8, 0x74, 0x5f, 0xeb, 0xd1, 0xa7, 0xff, 0x62, 0xcc, 0xc9, 0xa0, 0xcf, 0x5b, 0x30, - 0x2e, 0x98, 0x03, 0x26, 0x7c, 0x40, 0x6f, 0x6c, 0x7a, 0x09, 0xf1, 0xbd, 0x38, 0x19, 0xaf, 0xb0, - 0x3e, 0x4c, 0xf5, 0xb6, 0xb6, 0xe6, 0xa3, 0xb0, 0xdd, 0xba, 0xe2, 0x05, 0xf5, 0x99, 0x73, 0x82, - 0xd2, 0xf8, 0x6c, 0x97, 0x86, 0x71, 0x57, 0x92, 0xe8, 0xcb, 0x16, 0x9c, 0x0d, 0x9c, 0x26, 0x89, - 0x5b, 0x0e, 0x9d, 0x5a, 0x0e, 0x9e, 0xf1, 0x1d, 0x77, 0x8b, 0xf5, 0xa8, 0xff, 0xde, 0x7a, 0x64, - 0x8b, 0x1e, 0x9d, 0xbd, 0xda, 0xb5, 0x69, 0xbc, 0x07, 0x59, 0xf4, 0x75, 0x0b, 0xc6, 0xc2, 0xa8, - 0xb5, 0xe9, 0x04, 0xa4, 0x2e, 0xa1, 0xf1, 0xf8, 0x00, 0xdb, 0x7a, 0x1f, 0x3a, 0xdc, 0x14, 0x2d, - 0x67, 0x9b, 0x5d, 0x0a, 0x03, 0x2f, 0x09, 0xa3, 0x55, 0x92, 0x24, 0x5e, 0xd0, 0x88, 0x67, 0x4e, - 0xdf, 0xd9, 0x9d, 0x18, 0xeb, 0xc0, 0xc2, 0x9d, 0xfd, 0x41, 0x3f, 0x07, 0x83, 0xf1, 0x4e, 0xe0, - 0xde, 0xf0, 0x82, 0x7a, 0x78, 0x2b, 0x1e, 0xaf, 0x16, 0xb1, 0x7d, 0x57, 0x55, 0x83, 0x62, 0x03, - 0x6a, 0x02, 0xd8, 0xa4, 0x96, 0x3f, 0x71, 0x7a, 0x29, 0xd5, 0x8a, 0x9e, 0x38, 0xbd, 0x98, 0xf6, - 0x20, 0x8b, 0x7e, 0xc9, 0x82, 0xe1, 0xd8, 0x6b, 0x04, 0x4e, 0xd2, 0x8e, 0xc8, 0x15, 0xb2, 0x13, - 0x8f, 0x03, 0xeb, 0xc8, 0xe5, 0x43, 0x8e, 0x8a, 0xd1, 0xe4, 0xcc, 0x69, 0xd1, 0xc7, 0x61, 0xb3, - 0x34, 0xc6, 0x69, 0xba, 0x79, 0x1b, 0x4d, 0x2f, 0xeb, 0xc1, 0x62, 0x37, 0x9a, 0x5e, 0xd4, 0x5d, - 0x49, 0xa2, 0x9f, 0x81, 0x13, 0xbc, 0x48, 0x8d, 0x6c, 0x3c, 0x3e, 0xc4, 0x18, 0xed, 0xa9, 0x3b, - 0xbb, 0x13, 0x27, 0x56, 0x33, 0x30, 0xdc, 0x81, 0x8d, 0x5e, 0x85, 0x89, 0x16, 0x89, 0x9a, 0x5e, - 0xb2, 0x1c, 0xf8, 0x3b, 0x92, 0x7d, 0xbb, 0x61, 0x8b, 0xd4, 0x45, 0x77, 0xe2, 0xf1, 0xe1, 0x73, - 0xd6, 0x93, 0xd5, 0x99, 0xb7, 0x89, 0x6e, 0x4e, 0xac, 0xec, 0x8d, 0x8e, 0xf7, 0x6b, 0x0f, 0xfd, - 0x81, 0x05, 0x67, 0x0d, 0x2e, 0xbb, 0x4a, 0xa2, 0x6d, 0xcf, 0x25, 0xd3, 0xae, 0x1b, 0xb6, 0x83, - 0x24, 0x1e, 0x1f, 0x61, 0xc3, 0xb8, 0x7e, 0x14, 0x3c, 0x3f, 0x4d, 0x4a, 0xaf, 0xcb, 0xae, 0x28, - 0x31, 0xde, 0xa3, 0xa7, 0xf6, 0xbf, 0x2e, 0xc1, 0x89, 0xac, 0x06, 0x80, 0xfe, 0x9e, 0x05, 0xa3, - 0x37, 0x6f, 0x25, 0x6b, 0xe1, 0x16, 0x09, 0xe2, 0x99, 0x1d, 0xca, 0xa7, 0x99, 0xec, 0x1b, 0x3c, - 0xef, 0x16, 0xab, 0x6b, 0x4c, 0x5e, 0x4e, 0x53, 0xb9, 0x10, 0x24, 0xd1, 0xce, 0xcc, 0xc3, 0xe2, - 0x9b, 0x46, 0x2f, 0xdf, 0x58, 0x33, 0xa1, 0x38, 0xdb, 0xa9, 0xb3, 0x9f, 0xb5, 0xe0, 0x54, 0x5e, - 0x13, 0xe8, 0x04, 0x94, 0xb7, 0xc8, 0x0e, 0xd7, 0x44, 0x31, 0xfd, 0x89, 0x5e, 0x86, 0xca, 0xb6, - 0xe3, 0xb7, 0x89, 0x50, 0xd3, 0xe6, 0x0f, 0xf7, 0x21, 0xaa, 0x67, 0x98, 0xb7, 0xfa, 0xee, 0xd2, - 0xf3, 0x96, 0xfd, 0x47, 0x65, 0x18, 0x34, 0x26, 0xed, 0x18, 0x54, 0xcf, 0x30, 0xa5, 0x7a, 0x2e, - 0x15, 0xb6, 0xde, 0xba, 0xea, 0x9e, 0xb7, 0x32, 0xba, 0xe7, 0x72, 0x71, 0x24, 0xf7, 0x54, 0x3e, - 0x51, 0x02, 0xb5, 0xb0, 0x45, 0x8f, 0x21, 0x54, 0x87, 0xe9, 0x2b, 0x62, 0x0a, 0x97, 0x65, 0x73, - 0x33, 0xc3, 0x77, 0x76, 0x27, 0x6a, 0xea, 0x2f, 0xd6, 0x84, 0xec, 0xef, 0x5a, 0x70, 0xca, 0xe8, - 0xe3, 0x6c, 0x18, 0xd4, 0x3d, 0x36, 0xb5, 0xe7, 0xa0, 0x2f, 0xd9, 0x69, 0xc9, 0xa3, 0x8e, 0x1a, - 0xa9, 0xb5, 0x9d, 0x16, 0xc1, 0x0c, 0x42, 0x4f, 0x2c, 0x4d, 0x12, 0xc7, 0x4e, 0x83, 0x64, 0x0f, - 0x37, 0x4b, 0xbc, 0x18, 0x4b, 0x38, 0x8a, 0x00, 0xf9, 0x4e, 0x9c, 0xac, 0x45, 0x4e, 0x10, 0xb3, - 0xe6, 0xd7, 0xbc, 0x26, 0x11, 0x03, 0xfc, 0x57, 0x7a, 0x5b, 0x31, 0xb4, 0xc6, 0xcc, 0x43, 0x77, - 0x76, 0x27, 0xd0, 0x62, 0x47, 0x4b, 0x38, 0xa7, 0x75, 0xfb, 0xcb, 0x16, 0x3c, 0x94, 0xcf, 0x60, - 0xd0, 0x13, 0xd0, 0xcf, 0xcf, 0xb9, 0xe2, 0xeb, 0xf4, 0x94, 0xb0, 0x52, 0x2c, 0xa0, 0x68, 0x0a, - 0x6a, 0x4a, 0xe0, 0x89, 0x6f, 0x1c, 0x13, 0xa8, 0x35, 0x2d, 0x25, 0x35, 0x0e, 0x1d, 0x34, 0xfa, - 0x47, 0xa8, 0xa0, 0x6a, 0xd0, 0xd8, 0xc1, 0x90, 0x41, 0xec, 0xef, 0x58, 0xf0, 0xe3, 0xbd, 0xb0, - 0xbd, 0xa3, 0xeb, 0xe3, 0x2a, 0x9c, 0xae, 0x93, 0x0d, 0xa7, 0xed, 0x27, 0x69, 0x8a, 0xa2, 0xd3, - 0x8f, 0x8a, 0xca, 0xa7, 0xe7, 0xf2, 0x90, 0x70, 0x7e, 0x5d, 0xfb, 0x3f, 0x59, 0x30, 0x6a, 0x7c, - 0xd6, 0x31, 0x1c, 0x9d, 0x82, 0xf4, 0xd1, 0x69, 0xa1, 0xb0, 0x6d, 0xda, 0xe5, 0xec, 0xf4, 0x79, - 0x0b, 0xce, 0x1a, 0x58, 0x4b, 0x4e, 0xe2, 0x6e, 0x5e, 0xb8, 0xdd, 0x8a, 0x48, 0x1c, 0xd3, 0x25, - 0xf5, 0xa8, 0xc1, 0x8e, 0x67, 0x06, 0x45, 0x0b, 0xe5, 0x2b, 0x64, 0x87, 0xf3, 0xe6, 0xa7, 0xa0, - 0xca, 0xf7, 0x5c, 0x18, 0x89, 0x49, 0x52, 0xdf, 0xb6, 0x2c, 0xca, 0xb1, 0xc2, 0x40, 0x36, 0xf4, - 0x33, 0x9e, 0x4b, 0x79, 0x10, 0x55, 0x13, 0x80, 0xce, 0xfb, 0x75, 0x56, 0x82, 0x05, 0xc4, 0x8e, - 0x53, 0xdd, 0x59, 0x89, 0x08, 0x5b, 0x0f, 0xf5, 0x8b, 0x1e, 0xf1, 0xeb, 0x31, 0x3d, 0xd6, 0x39, - 0x41, 0x10, 0x26, 0xe2, 0x84, 0x66, 0x1c, 0xeb, 0xa6, 0x75, 0x31, 0x36, 0x71, 0x28, 0x51, 0xdf, - 0x59, 0x27, 0x3e, 0x1f, 0x51, 0x41, 0x74, 0x91, 0x95, 0x60, 0x01, 0xb1, 0xef, 0x94, 0xd8, 0x01, - 0x52, 0x71, 0x34, 0x72, 0x1c, 0xd6, 0x87, 0x28, 0x25, 0x02, 0x56, 0x8a, 0xe3, 0xc7, 0xa4, 0xbb, - 0x05, 0xe2, 0xb5, 0x8c, 0x14, 0xc0, 0x85, 0x52, 0xdd, 0xdb, 0x0a, 0xf1, 0xf1, 0x32, 0x4c, 0xa4, - 0x2b, 0x74, 0x08, 0x11, 0x7a, 0xe4, 0x35, 0x08, 0x65, 0xed, 0x51, 0x06, 0x3e, 0x36, 0xf1, 0xba, - 0xf0, 0xe1, 0xd2, 0x51, 0xf2, 0x61, 0x53, 0x4c, 0x94, 0xf7, 0x11, 0x13, 0x4f, 0xa8, 0x51, 0xef, - 0xcb, 0xf0, 0xbc, 0xb4, 0xa8, 0x3c, 0x07, 0x7d, 0x71, 0x42, 0x5a, 0xe3, 0x95, 0x34, 0x9b, 0x5d, - 0x4d, 0x48, 0x0b, 0x33, 0x08, 0x7a, 0x0f, 0x8c, 0x26, 0x4e, 0xd4, 0x20, 0x49, 0x44, 0xb6, 0x3d, - 0x66, 0xbb, 0x64, 0xe7, 0xd9, 0xda, 0xcc, 0x49, 0xaa, 0x75, 0xad, 0x31, 0x10, 0x96, 0x20, 0x9c, - 0xc5, 0xb5, 0xff, 0x7b, 0x09, 0x1e, 0x4e, 0x4f, 0x81, 0x16, 0x8c, 0xef, 0x4b, 0x09, 0xc6, 0x77, - 0x98, 0x82, 0xf1, 0xee, 0xee, 0xc4, 0x5b, 0xbb, 0x54, 0xfb, 0xa1, 0x91, 0x9b, 0x68, 0x3e, 0x33, - 0x09, 0x53, 0xe9, 0x49, 0xb8, 0xbb, 0x3b, 0xf1, 0x68, 0x97, 0x6f, 0xcc, 0xcc, 0xd2, 0x13, 0xd0, - 0x1f, 0x11, 0x27, 0x0e, 0x03, 0x31, 0x4f, 0x6a, 0x36, 0x31, 0x2b, 0xc5, 0x02, 0x6a, 0x7f, 0xbb, - 0x96, 0x1d, 0xec, 0x79, 0x6e, 0x8f, 0x0d, 0x23, 0xe4, 0x41, 0x1f, 0x3b, 0xb5, 0x71, 0xce, 0x72, - 0xe5, 0x70, 0xbb, 0x90, 0x4a, 0x11, 0xd5, 0xf4, 0x4c, 0x95, 0xce, 0x1a, 0x2d, 0xc2, 0x8c, 0x04, - 0xba, 0x0d, 0x55, 0x57, 0x1e, 0xa6, 0x4a, 0x45, 0x98, 0x1d, 0xc5, 0x51, 0x4a, 0x53, 0x1c, 0xa2, - 0xec, 0x5e, 0x9d, 0xc0, 0x14, 0x35, 0x44, 0xa0, 0xdc, 0xf0, 0x12, 0x31, 0xad, 0x87, 0x3c, 0x2e, - 0xcf, 0x7b, 0xc6, 0x27, 0x0e, 0x50, 0x19, 0x34, 0xef, 0x25, 0x98, 0xb6, 0x8f, 0x3e, 0x6d, 0xc1, - 0x60, 0xec, 0x36, 0x57, 0xa2, 0x70, 0xdb, 0xab, 0x93, 0x48, 0xe8, 0x98, 0x87, 0xe4, 0x6c, 0xab, - 0xb3, 0x4b, 0xb2, 0x41, 0x4d, 0x97, 0x9b, 0x2f, 0x34, 0x04, 0x9b, 0x74, 0xe9, 0xd9, 0xeb, 0x61, - 0xf1, 0xed, 0x73, 0xc4, 0x65, 0x3b, 0x4e, 0x9e, 0x99, 0xd9, 0x4a, 0x39, 0xb4, 0xce, 0x3d, 0xd7, - 0x76, 0xb7, 0xe8, 0x7e, 0xd3, 0x1d, 0x7a, 0xeb, 0x9d, 0xdd, 0x89, 0x87, 0x67, 0xf3, 0x69, 0xe2, - 0x6e, 0x9d, 0x61, 0x03, 0xd6, 0x6a, 0xfb, 0x3e, 0x26, 0xaf, 0xb6, 0x09, 0xb3, 0x88, 0x15, 0x30, - 0x60, 0x2b, 0xba, 0xc1, 0xcc, 0x80, 0x19, 0x10, 0x6c, 0xd2, 0x45, 0xaf, 0x42, 0x7f, 0xd3, 0x49, - 0x22, 0xef, 0xb6, 0x30, 0x83, 0x1d, 0xf2, 0x14, 0xb4, 0xc4, 0xda, 0xd2, 0xc4, 0x99, 0xa0, 0xe7, - 0x85, 0x58, 0x10, 0x42, 0x4d, 0xa8, 0x34, 0x49, 0xd4, 0x20, 0xe3, 0xd5, 0x22, 0x4c, 0xfe, 0x4b, - 0xb4, 0x29, 0x4d, 0xb0, 0x46, 0x95, 0x2b, 0x56, 0x86, 0x39, 0x15, 0xf4, 0x32, 0x54, 0x63, 0xe2, - 0x13, 0x97, 0xaa, 0x47, 0x35, 0x46, 0xf1, 0x9d, 0x3d, 0xaa, 0x8a, 0x54, 0x2f, 0x59, 0x15, 0x55, - 0xf9, 0x06, 0x93, 0xff, 0xb0, 0x6a, 0x92, 0x0e, 0x60, 0xcb, 0x6f, 0x37, 0xbc, 0x60, 0x1c, 0x8a, - 0x18, 0xc0, 0x15, 0xd6, 0x56, 0x66, 0x00, 0x79, 0x21, 0x16, 0x84, 0xec, 0xff, 0x62, 0x01, 0x4a, - 0x33, 0xb5, 0x63, 0xd0, 0x89, 0x5f, 0x4d, 0xeb, 0xc4, 0x8b, 0x45, 0x2a, 0x2d, 0x5d, 0xd4, 0xe2, - 0xdf, 0xaa, 0x41, 0x46, 0x1c, 0x5c, 0x25, 0x71, 0x42, 0xea, 0x6f, 0xb2, 0xf0, 0x37, 0x59, 0xf8, - 0x9b, 0x2c, 0x5c, 0xb1, 0xf0, 0xf5, 0x0c, 0x0b, 0x7f, 0xaf, 0xb1, 0xeb, 0xf5, 0xfd, 0xfa, 0x2b, - 0xea, 0x02, 0xde, 0xec, 0x81, 0x81, 0x40, 0x39, 0xc1, 0xe5, 0xd5, 0xe5, 0xab, 0xb9, 0x3c, 0xfb, - 0x95, 0x34, 0xcf, 0x3e, 0x2c, 0x89, 0xff, 0x1f, 0xb8, 0xf4, 0x1f, 0x58, 0xf0, 0xb6, 0x34, 0xf7, - 0x92, 0x2b, 0x67, 0xa1, 0x11, 0x84, 0x11, 0x99, 0xf3, 0x36, 0x36, 0x48, 0x44, 0x02, 0x97, 0xc4, - 0xca, 0xb6, 0x63, 0x75, 0xb3, 0xed, 0xa0, 0x67, 0x61, 0xe8, 0x66, 0x1c, 0x06, 0x2b, 0xa1, 0x17, - 0x08, 0x16, 0x44, 0x4f, 0x1c, 0x27, 0xee, 0xec, 0x4e, 0x0c, 0xd1, 0x11, 0x95, 0xe5, 0x38, 0x85, - 0x85, 0x66, 0x61, 0xec, 0xe6, 0xab, 0x2b, 0x4e, 0x62, 0x58, 0x13, 0xe4, 0xb9, 0x9f, 0xdd, 0x47, - 0x5d, 0x7e, 0x31, 0x03, 0xc4, 0x9d, 0xf8, 0xf6, 0xdf, 0x2e, 0xc1, 0x99, 0xcc, 0x87, 0x84, 0xbe, - 0x1f, 0xb6, 0x13, 0x7a, 0x26, 0x42, 0x5f, 0xb5, 0xe0, 0x44, 0x33, 0x6d, 0xb0, 0x88, 0x85, 0xb9, - 0xfb, 0xfd, 0x85, 0xc9, 0x88, 0x8c, 0x45, 0x64, 0x66, 0x5c, 0x8c, 0xd0, 0x89, 0x0c, 0x20, 0xc6, - 0x1d, 0x7d, 0x41, 0x2f, 0x43, 0xad, 0xe9, 0xdc, 0xbe, 0xd6, 0xaa, 0x3b, 0x89, 0x3c, 0x8e, 0x76, - 0xb7, 0x22, 0xb4, 0x13, 0xcf, 0x9f, 0xe4, 0x9e, 0x1b, 0x93, 0x0b, 0x41, 0xb2, 0x1c, 0xad, 0x26, - 0x91, 0x17, 0x34, 0xb8, 0x91, 0x73, 0x49, 0x36, 0x83, 0x75, 0x8b, 0xf6, 0x57, 0xac, 0xac, 0x90, - 0x52, 0xa3, 0x13, 0x39, 0x09, 0x69, 0xec, 0xa0, 0x8f, 0x40, 0x85, 0x9e, 0x1b, 0xe5, 0xa8, 0xdc, - 0x28, 0x52, 0x72, 0x1a, 0x33, 0xa1, 0x85, 0x28, 0xfd, 0x17, 0x63, 0x4e, 0xd4, 0xfe, 0x6a, 0x2d, - 0xab, 0x2c, 0xb0, 0xbb, 0xf9, 0xf3, 0x00, 0x8d, 0x70, 0x8d, 0x34, 0x5b, 0x3e, 0x1d, 0x16, 0x8b, - 0x5d, 0xf0, 0x28, 0x53, 0xc9, 0xbc, 0x82, 0x60, 0x03, 0x0b, 0xfd, 0xb2, 0x05, 0xd0, 0x90, 0x6b, - 0x5e, 0x2a, 0x02, 0xd7, 0x8a, 0xfc, 0x1c, 0xbd, 0xa3, 0x74, 0x5f, 0x14, 0x41, 0x6c, 0x10, 0x47, - 0xbf, 0x60, 0x41, 0x35, 0x91, 0xdd, 0xe7, 0xa2, 0x71, 0xad, 0xc8, 0x9e, 0xc8, 0x8f, 0xd6, 0x3a, - 0x91, 0x1a, 0x12, 0x45, 0x17, 0xfd, 0xa2, 0x05, 0x10, 0xef, 0x04, 0xee, 0x4a, 0xe8, 0x7b, 0xee, - 0x8e, 0x90, 0x98, 0xd7, 0x0b, 0x35, 0xe7, 0xa8, 0xd6, 0x67, 0x46, 0xe8, 0x68, 0xe8, 0xff, 0xd8, - 0xa0, 0x8c, 0x3e, 0x06, 0xd5, 0x58, 0x2c, 0x37, 0x21, 0x23, 0xd7, 0x8a, 0x35, 0x2a, 0xf1, 0xb6, - 0x05, 0x7b, 0x15, 0xff, 0xb0, 0xa2, 0x89, 0xfe, 0xa6, 0x05, 0xa3, 0xad, 0xb4, 0x99, 0x50, 0x88, - 0xc3, 0xe2, 0x78, 0x40, 0xc6, 0x0c, 0xc9, 0xad, 0x2d, 0x99, 0x42, 0x9c, 0xed, 0x05, 0xe5, 0x80, - 0x7a, 0x05, 0x2f, 0xb7, 0xb8, 0xc9, 0x72, 0x40, 0x73, 0xc0, 0xf9, 0x2c, 0x10, 0x77, 0xe2, 0xa3, - 0x15, 0x38, 0x45, 0x7b, 0xb7, 0xc3, 0xd5, 0x4f, 0x29, 0x5e, 0x62, 0x26, 0x0c, 0xab, 0x33, 0x8f, - 0x88, 0x15, 0xc2, 0xee, 0x3a, 0xb2, 0x38, 0x38, 0xb7, 0x26, 0xfa, 0x23, 0x0b, 0x1e, 0xf1, 0x98, - 0x18, 0x30, 0x0d, 0xf6, 0x5a, 0x22, 0x88, 0x8b, 0x76, 0x52, 0x28, 0xaf, 0xe8, 0x26, 0x7e, 0x66, - 0x7e, 0x5c, 0x7c, 0xc1, 0x23, 0x0b, 0x7b, 0x74, 0x09, 0xef, 0xd9, 0x61, 0xf4, 0x53, 0x30, 0x2c, - 0xf7, 0xc5, 0x0a, 0x65, 0xc1, 0x4c, 0xd0, 0xd6, 0x66, 0xc6, 0xee, 0xec, 0x4e, 0x0c, 0xaf, 0x99, - 0x00, 0x9c, 0xc6, 0xb3, 0xff, 0x4d, 0x39, 0x75, 0x4b, 0xa4, 0x6c, 0x98, 0x8c, 0xdd, 0xb8, 0xd2, - 0xfe, 0x23, 0xb9, 0x67, 0xa1, 0xec, 0x46, 0x59, 0x97, 0x34, 0xbb, 0x51, 0x45, 0x31, 0x36, 0x88, - 0x53, 0xa5, 0x74, 0xcc, 0xc9, 0x5a, 0x4a, 0x05, 0x07, 0x7c, 0xb9, 0xc8, 0x2e, 0x75, 0xde, 0xe9, - 0x9d, 0x11, 0x5d, 0x1b, 0xeb, 0x00, 0xe1, 0xce, 0x2e, 0xa1, 0x8f, 0x42, 0x2d, 0x52, 0x9e, 0x2d, - 0xe5, 0x22, 0x8e, 0x6a, 0x72, 0xd9, 0x88, 0xee, 0xa8, 0x0b, 0x20, 0xed, 0xc3, 0xa2, 0x29, 0xda, - 0x7f, 0x98, 0xbe, 0x18, 0x33, 0x78, 0x47, 0x0f, 0x97, 0x7e, 0x5f, 0xb0, 0x60, 0x30, 0x0a, 0x7d, - 0xdf, 0x0b, 0x1a, 0x94, 0xcf, 0x09, 0x61, 0xfd, 0xc1, 0x23, 0x91, 0x97, 0x82, 0xa1, 0x31, 0xcd, - 0x1a, 0x6b, 0x9a, 0xd8, 0xec, 0x80, 0xfd, 0x67, 0x16, 0x8c, 0x77, 0xe3, 0xc7, 0x88, 0xc0, 0x5b, - 0x25, 0xb3, 0x51, 0x43, 0xb1, 0x1c, 0xcc, 0x11, 0x9f, 0x28, 0xb3, 0x79, 0x75, 0xe6, 0x71, 0xf1, - 0x99, 0x6f, 0x5d, 0xe9, 0x8e, 0x8a, 0xf7, 0x6a, 0x07, 0xbd, 0x04, 0x27, 0x8c, 0xef, 0x8a, 0xd5, - 0xc0, 0xd4, 0x66, 0x26, 0xa9, 0x02, 0x34, 0x9d, 0x81, 0xdd, 0xdd, 0x9d, 0x78, 0x28, 0x5b, 0x26, - 0x04, 0x46, 0x47, 0x3b, 0xf6, 0x6f, 0x94, 0xb2, 0xb3, 0xa5, 0x64, 0xfd, 0x1b, 0x56, 0x87, 0x35, - 0xe1, 0xfd, 0x47, 0x21, 0x5f, 0x99, 0xdd, 0x41, 0xb9, 0x61, 0x74, 0xc7, 0xb9, 0x8f, 0xd7, 0xf6, - 0xf6, 0xbf, 0xed, 0x83, 0x3d, 0x7a, 0xd6, 0x83, 0xf2, 0x7e, 0xe0, 0x7b, 0xd4, 0xcf, 0x59, 0xea, - 0xc2, 0x8c, 0xef, 0xe1, 0xfa, 0x51, 0x8d, 0x3d, 0x3f, 0x3f, 0xc5, 0xdc, 0x75, 0x44, 0x59, 0xd1, - 0xd3, 0x57, 0x73, 0xe8, 0x6b, 0x56, 0xfa, 0xca, 0x8f, 0x3b, 0x35, 0x7a, 0x47, 0xd6, 0x27, 0xe3, - 0x1e, 0x91, 0x77, 0x4c, 0xdf, 0x3e, 0x75, 0xbb, 0x61, 0x9c, 0x04, 0xd8, 0xf0, 0x02, 0xc7, 0xf7, - 0x5e, 0xa3, 0xa7, 0xa3, 0x0a, 0x13, 0xf0, 0x4c, 0x63, 0xba, 0xa8, 0x4a, 0xb1, 0x81, 0x71, 0xf6, - 0xaf, 0xc2, 0xa0, 0xf1, 0xe5, 0x39, 0x1e, 0x2f, 0xa7, 0x4c, 0x8f, 0x97, 0x9a, 0xe1, 0xa8, 0x72, - 0xf6, 0xbd, 0x70, 0x22, 0xdb, 0xc1, 0x83, 0xd4, 0xb7, 0xff, 0xf7, 0x40, 0xf6, 0x0e, 0x6e, 0x8d, - 0x44, 0x4d, 0xda, 0xb5, 0x37, 0x0d, 0x5b, 0x6f, 0x1a, 0xb6, 0xde, 0x34, 0x6c, 0x99, 0x77, 0x13, - 0xc2, 0x68, 0x33, 0x70, 0x4c, 0x46, 0x9b, 0x94, 0x19, 0xaa, 0x5a, 0xb8, 0x19, 0xca, 0xfe, 0x74, - 0x87, 0xe5, 0x7e, 0x2d, 0x22, 0x04, 0x85, 0x50, 0x09, 0xc2, 0x3a, 0x91, 0x3a, 0xee, 0xe5, 0x62, - 0x14, 0xb6, 0xab, 0x61, 0xdd, 0x70, 0x17, 0xa7, 0xff, 0x62, 0xcc, 0xe9, 0xd8, 0x77, 0x2a, 0x90, - 0x52, 0x27, 0xf9, 0xbc, 0xbf, 0x1d, 0x06, 0x22, 0xd2, 0x0a, 0xaf, 0xe1, 0x45, 0x21, 0xcb, 0x74, - 0x44, 0x09, 0x2f, 0xc6, 0x12, 0x4e, 0x65, 0x5e, 0xcb, 0x49, 0x36, 0x85, 0x30, 0x53, 0x32, 0x6f, - 0xc5, 0x49, 0x36, 0x31, 0x83, 0xa0, 0xf7, 0xc2, 0x48, 0x92, 0xba, 0x0a, 0x17, 0x57, 0xbe, 0x0f, - 0x09, 0xdc, 0x91, 0xf4, 0x45, 0x39, 0xce, 0x60, 0xa3, 0x57, 0xa1, 0x6f, 0x93, 0xf8, 0x4d, 0x31, - 0xf5, 0xab, 0xc5, 0xc9, 0x1a, 0xf6, 0xad, 0x97, 0x88, 0xdf, 0xe4, 0x9c, 0x90, 0xfe, 0xc2, 0x8c, - 0x14, 0x5d, 0xf7, 0xb5, 0xad, 0x76, 0x9c, 0x84, 0x4d, 0xef, 0x35, 0x69, 0xe9, 0x7c, 0x7f, 0xc1, - 0x84, 0xaf, 0xc8, 0xf6, 0xb9, 0x49, 0x49, 0xfd, 0xc5, 0x9a, 0x32, 0xeb, 0x47, 0xdd, 0x8b, 0xd8, - 0x92, 0xd9, 0x11, 0x06, 0xcb, 0xa2, 0xfb, 0x31, 0x27, 0xdb, 0xe7, 0xfd, 0x50, 0x7f, 0xb1, 0xa6, - 0x8c, 0x76, 0xd4, 0xfe, 0x1b, 0x64, 0x7d, 0xb8, 0x56, 0x70, 0x1f, 0xf8, 0xde, 0xcb, 0xdd, 0x87, - 0x8f, 0x43, 0xc5, 0xdd, 0x74, 0xa2, 0x64, 0x7c, 0x88, 0x2d, 0x1a, 0xb5, 0x8a, 0x67, 0x69, 0x21, - 0xe6, 0x30, 0xf4, 0x28, 0x94, 0x23, 0xb2, 0xc1, 0xbc, 0x93, 0x0d, 0xbf, 0x28, 0x4c, 0x36, 0x30, - 0x2d, 0xb7, 0x7f, 0xad, 0x94, 0x56, 0xdb, 0xd2, 0xdf, 0xcd, 0x57, 0xbb, 0xdb, 0x8e, 0x62, 0x69, - 0xfe, 0x32, 0x56, 0x3b, 0x2b, 0xc6, 0x12, 0x8e, 0x3e, 0x61, 0xc1, 0xc0, 0xcd, 0x38, 0x0c, 0x02, - 0x92, 0x08, 0x11, 0x79, 0xbd, 0xe0, 0xa1, 0xb8, 0xcc, 0x5b, 0xd7, 0x7d, 0x10, 0x05, 0x58, 0xd2, - 0xa5, 0xdd, 0x25, 0xb7, 0x5d, 0xbf, 0x5d, 0xef, 0x70, 0x75, 0xb9, 0xc0, 0x8b, 0xb1, 0x84, 0x53, - 0x54, 0x2f, 0xe0, 0xa8, 0x7d, 0x69, 0xd4, 0x85, 0x40, 0xa0, 0x0a, 0xb8, 0xfd, 0xcd, 0x01, 0x38, - 0x9d, 0xbb, 0x39, 0xa8, 0x42, 0xc5, 0x54, 0x96, 0x8b, 0x9e, 0x4f, 0xa4, 0x93, 0x17, 0x53, 0xa8, - 0xae, 0xab, 0x52, 0x6c, 0x60, 0xa0, 0x9f, 0x07, 0x68, 0x39, 0x91, 0xd3, 0x24, 0xca, 0x3c, 0x7d, - 0x68, 0xbd, 0x85, 0xf6, 0x63, 0x45, 0xb6, 0xa9, 0x8f, 0xe8, 0xaa, 0x28, 0xc6, 0x06, 0x49, 0xf4, - 0x1c, 0x0c, 0x46, 0xc4, 0x27, 0x4e, 0xcc, 0x9c, 0xdb, 0xb3, 0x91, 0x3a, 0x58, 0x83, 0xb0, 0x89, - 0x87, 0x9e, 0x50, 0xfe, 0x70, 0x19, 0xbf, 0xa0, 0xb4, 0x4f, 0x1c, 0x7a, 0xdd, 0x82, 0x91, 0x0d, - 0xcf, 0x27, 0x9a, 0xba, 0x88, 0xab, 0x59, 0x3e, 0xfc, 0x47, 0x5e, 0x34, 0xdb, 0xd5, 0x1c, 0x32, - 0x55, 0x1c, 0xe3, 0x0c, 0x79, 0x3a, 0xcd, 0xdb, 0x24, 0x62, 0xac, 0xb5, 0x3f, 0x3d, 0xcd, 0xd7, - 0x79, 0x31, 0x96, 0x70, 0x34, 0x0d, 0xa3, 0x2d, 0x27, 0x8e, 0x67, 0x23, 0x52, 0x27, 0x41, 0xe2, - 0x39, 0x3e, 0x8f, 0x7a, 0xa9, 0x6a, 0x67, 0xf1, 0x95, 0x34, 0x18, 0x67, 0xf1, 0xd1, 0x07, 0xe0, - 0x61, 0x6e, 0xff, 0x59, 0xf2, 0xe2, 0xd8, 0x0b, 0x1a, 0x7a, 0x19, 0x08, 0x33, 0xd8, 0x84, 0x68, - 0xea, 0xe1, 0x85, 0x7c, 0x34, 0xdc, 0xad, 0x3e, 0x7a, 0x0a, 0xaa, 0xf1, 0x96, 0xd7, 0x9a, 0x8d, - 0xea, 0x31, 0xbb, 0xfb, 0xa9, 0x6a, 0xa3, 0xeb, 0xaa, 0x28, 0xc7, 0x0a, 0x03, 0xb9, 0x30, 0xc4, - 0xa7, 0x84, 0x3b, 0xf4, 0x09, 0xfe, 0xf8, 0x74, 0x57, 0x31, 0x2d, 0x82, 0x38, 0x27, 0xb1, 0x73, - 0xeb, 0x82, 0xbc, 0x89, 0xe2, 0x17, 0x27, 0xd7, 0x8d, 0x66, 0x70, 0xaa, 0xd1, 0xf4, 0x89, 0x6d, - 0xb0, 0x87, 0x13, 0xdb, 0x73, 0x30, 0xb8, 0xd5, 0x5e, 0x27, 0x62, 0xe4, 0x05, 0xdb, 0x52, 0xab, - 0xef, 0x8a, 0x06, 0x61, 0x13, 0x8f, 0xf9, 0x52, 0xb6, 0x3c, 0xf1, 0x2f, 0x1e, 0x1f, 0x36, 0x7c, - 0x29, 0x57, 0x16, 0x64, 0x31, 0x36, 0x71, 0xec, 0x5f, 0x29, 0xa5, 0x8d, 0x12, 0x26, 0xff, 0x40, - 0x31, 0xe5, 0x12, 0xc9, 0x75, 0x27, 0x92, 0xba, 0xc4, 0x21, 0xe3, 0x86, 0x44, 0xbb, 0xd7, 0x9d, - 0xc8, 0xe4, 0x37, 0x8c, 0x00, 0x96, 0x94, 0xd0, 0x4d, 0xe8, 0x4b, 0x7c, 0xa7, 0xa0, 0x40, 0x43, - 0x83, 0xa2, 0xb6, 0x11, 0x2d, 0x4e, 0xc7, 0x98, 0xd1, 0x40, 0x8f, 0xd0, 0x83, 0xd1, 0xba, 0xbc, - 0xc4, 0x12, 0x67, 0x99, 0xf5, 0x18, 0xb3, 0x52, 0xfb, 0xcf, 0x07, 0x73, 0x58, 0xbe, 0x92, 0xb1, - 0xe8, 0x3c, 0x00, 0x9d, 0xb1, 0x95, 0x88, 0x6c, 0x78, 0xb7, 0x85, 0x8e, 0xa3, 0xd8, 0xca, 0x55, - 0x05, 0xc1, 0x06, 0x96, 0xac, 0xb3, 0xda, 0xde, 0xa0, 0x75, 0x4a, 0x9d, 0x75, 0x38, 0x04, 0x1b, - 0x58, 0xe8, 0x59, 0xe8, 0xf7, 0x9a, 0x4e, 0x43, 0xf9, 0xd8, 0x3e, 0x42, 0xf9, 0xc9, 0x02, 0x2b, - 0xb9, 0xbb, 0x3b, 0x31, 0xa2, 0x3a, 0xc4, 0x8a, 0xb0, 0xc0, 0x45, 0xbf, 0x61, 0xc1, 0x90, 0x1b, - 0x36, 0x9b, 0x61, 0xc0, 0x4f, 0xa6, 0xe2, 0x98, 0x7d, 0xf3, 0xa8, 0x34, 0x90, 0xc9, 0x59, 0x83, - 0x18, 0x3f, 0x67, 0xab, 0x88, 0x48, 0x13, 0x84, 0x53, 0xbd, 0x32, 0xd9, 0x4e, 0x65, 0x1f, 0xb6, - 0xf3, 0x9b, 0x16, 0x8c, 0xf1, 0xba, 0xc6, 0x81, 0x59, 0x04, 0xff, 0x85, 0x47, 0xfc, 0x59, 0x1d, - 0x36, 0x04, 0x65, 0x47, 0xed, 0x80, 0xe3, 0xce, 0x4e, 0xa2, 0x79, 0x18, 0xdb, 0x08, 0x23, 0x97, - 0x98, 0x03, 0x21, 0x78, 0xa6, 0x6a, 0xe8, 0x62, 0x16, 0x01, 0x77, 0xd6, 0x41, 0xd7, 0xe1, 0x21, - 0xa3, 0xd0, 0x1c, 0x07, 0xce, 0x36, 0x1f, 0x13, 0xad, 0x3d, 0x74, 0x31, 0x17, 0x0b, 0x77, 0xa9, - 0x9d, 0xe6, 0x50, 0xb5, 0x1e, 0x38, 0xd4, 0x2b, 0x70, 0xc6, 0xed, 0x1c, 0x99, 0xed, 0xb8, 0xbd, - 0x1e, 0x73, 0x26, 0x5a, 0x9d, 0xf9, 0x31, 0xd1, 0xc0, 0x99, 0xd9, 0x6e, 0x88, 0xb8, 0x7b, 0x1b, - 0xe8, 0x23, 0x50, 0x8d, 0x08, 0x9b, 0x95, 0x58, 0x44, 0xc2, 0x1d, 0xd2, 0x90, 0xa0, 0x95, 0x63, - 0xde, 0xac, 0x16, 0x0b, 0xa2, 0x20, 0xc6, 0x8a, 0x22, 0xba, 0x05, 0x03, 0x2d, 0x27, 0x71, 0x37, - 0x45, 0xfc, 0xdb, 0xa1, 0xcd, 0xde, 0x8a, 0x38, 0xbb, 0xa5, 0x30, 0x22, 0xe6, 0x39, 0x11, 0x2c, - 0xa9, 0x51, 0x45, 0xc9, 0x0d, 0x9b, 0xad, 0x30, 0x20, 0x41, 0x22, 0x39, 0xf8, 0x08, 0xbf, 0x4a, - 0x90, 0xa5, 0xd8, 0xc0, 0x40, 0x2b, 0x70, 0x8a, 0x99, 0xd5, 0x6e, 0x78, 0xc9, 0x66, 0xd8, 0x4e, - 0xe4, 0x29, 0x71, 0x7c, 0x24, 0x7d, 0x99, 0xb4, 0x98, 0x83, 0x83, 0x73, 0x6b, 0x66, 0x65, 0xcf, - 0xe8, 0xbd, 0xc9, 0x9e, 0x13, 0xfb, 0xcb, 0x9e, 0xb3, 0xef, 0x83, 0xb1, 0x0e, 0xa6, 0x71, 0x20, - 0xdb, 0xd9, 0x1c, 0x3c, 0x94, 0xbf, 0x3d, 0x0f, 0x64, 0x41, 0xfb, 0xa7, 0x19, 0x17, 0x6a, 0xe3, - 0x34, 0xd1, 0x83, 0x35, 0xd6, 0x81, 0x32, 0x09, 0xb6, 0x85, 0xb4, 0xba, 0x78, 0xb8, 0x55, 0x72, - 0x21, 0xd8, 0xe6, 0xdc, 0x85, 0x99, 0x9c, 0x2e, 0x04, 0xdb, 0x98, 0xb6, 0x8d, 0xbe, 0x64, 0xa5, - 0xb4, 0x61, 0x6e, 0xc3, 0xfd, 0xd0, 0x91, 0x1c, 0x9f, 0x7a, 0x56, 0x90, 0xed, 0x7f, 0x57, 0x82, - 0x73, 0xfb, 0x35, 0xd2, 0xc3, 0xf0, 0x3d, 0x0e, 0xfd, 0x31, 0x73, 0x8a, 0x10, 0xec, 0x7f, 0x90, - 0xee, 0x0a, 0xee, 0x26, 0xf1, 0x0a, 0x16, 0x20, 0xe4, 0x43, 0xb9, 0xe9, 0xb4, 0x84, 0x69, 0x6f, - 0xe1, 0xb0, 0xa1, 0x66, 0xf4, 0xbf, 0xe3, 0x2f, 0x39, 0x2d, 0xbe, 0x3c, 0x8d, 0x02, 0x4c, 0xc9, - 0xa0, 0x04, 0x2a, 0x4e, 0x14, 0x39, 0xf2, 0x06, 0xfe, 0x4a, 0x31, 0xf4, 0xa6, 0x69, 0x93, 0xfc, - 0x02, 0x33, 0x55, 0x84, 0x39, 0x31, 0xfb, 0x73, 0x03, 0xa9, 0xb8, 0x24, 0xe6, 0x56, 0x11, 0x43, - 0xbf, 0xb0, 0xe8, 0x59, 0x45, 0x47, 0xf8, 0xf1, 0xc0, 0x5f, 0x76, 0x58, 0x16, 0xe9, 0x13, 0x04, - 0x29, 0xf4, 0x59, 0x8b, 0x25, 0x29, 0x90, 0xc1, 0x5e, 0xe2, 0x88, 0x7a, 0x34, 0x39, 0x13, 0xcc, - 0xd4, 0x07, 0xb2, 0x10, 0x9b, 0xd4, 0x45, 0xb2, 0x11, 0xa6, 0x9a, 0x77, 0x26, 0x1b, 0x61, 0xaa, - 0xb6, 0x84, 0xa3, 0xdb, 0x39, 0xee, 0x13, 0x05, 0x04, 0xba, 0xf7, 0xe0, 0x30, 0xf1, 0x35, 0x0b, - 0xc6, 0xbc, 0xec, 0x3d, 0xb8, 0x38, 0xd0, 0xdd, 0x28, 0xc6, 0xfc, 0xd6, 0x79, 0xcd, 0xae, 0x14, - 0x87, 0x0e, 0x10, 0xee, 0xec, 0x0c, 0xaa, 0x43, 0x9f, 0x17, 0x6c, 0x84, 0x42, 0x5d, 0x9a, 0x39, - 0x5c, 0xa7, 0x16, 0x82, 0x8d, 0x50, 0xef, 0x66, 0xfa, 0x0f, 0xb3, 0xd6, 0xd1, 0x22, 0x9c, 0x92, - 0xa1, 0x29, 0x97, 0xbc, 0x38, 0x09, 0xa3, 0x9d, 0x45, 0xaf, 0xe9, 0x25, 0x4c, 0xd5, 0x29, 0xcf, - 0x8c, 0x53, 0x49, 0x84, 0x73, 0xe0, 0x38, 0xb7, 0x16, 0x7a, 0x0d, 0x06, 0xe4, 0xdd, 0x73, 0xb5, - 0x88, 0xc3, 0x71, 0xe7, 0xfa, 0x57, 0x8b, 0x69, 0x55, 0x5c, 0x3e, 0x4b, 0x82, 0xf6, 0xeb, 0x83, - 0xd0, 0x79, 0x45, 0x9e, 0xbe, 0x0f, 0xb7, 0x8e, 0xfb, 0x3e, 0x9c, 0x1e, 0x8d, 0x62, 0x7d, 0x95, - 0x5d, 0xc0, 0xda, 0x16, 0x54, 0xf5, 0x35, 0xe5, 0x4e, 0xe0, 0x62, 0x46, 0x03, 0x45, 0xd0, 0xbf, - 0x49, 0x1c, 0x3f, 0xd9, 0x2c, 0xe6, 0x46, 0xe5, 0x12, 0x6b, 0x2b, 0x1b, 0x4f, 0xc6, 0x4b, 0xb1, - 0xa0, 0x84, 0x6e, 0xc3, 0xc0, 0x26, 0x5f, 0x00, 0xe2, 0xb4, 0xb2, 0x74, 0xd8, 0xc1, 0x4d, 0xad, - 0x2a, 0x3d, 0xdd, 0xa2, 0x00, 0x4b, 0x72, 0xcc, 0xf7, 0xca, 0xf0, 0x0e, 0xe1, 0x5b, 0xb7, 0xb8, - 0x50, 0xba, 0xde, 0x5d, 0x43, 0x3e, 0x0c, 0x43, 0x11, 0x71, 0xc3, 0xc0, 0xf5, 0x7c, 0x52, 0x9f, - 0x96, 0xb7, 0x25, 0x07, 0x89, 0xa0, 0x62, 0xc6, 0x08, 0x6c, 0xb4, 0x81, 0x53, 0x2d, 0xa2, 0xcf, - 0x58, 0x30, 0xa2, 0xa2, 0xaa, 0xe9, 0x84, 0x10, 0x61, 0x15, 0x5f, 0x2c, 0x28, 0x86, 0x9b, 0xb5, - 0x39, 0x83, 0xee, 0xec, 0x4e, 0x8c, 0xa4, 0xcb, 0x70, 0x86, 0x2e, 0x7a, 0x09, 0x20, 0x5c, 0xe7, - 0x0e, 0x56, 0xd3, 0x89, 0x30, 0x91, 0x1f, 0xe4, 0x53, 0x47, 0x78, 0x24, 0xa6, 0x6c, 0x01, 0x1b, - 0xad, 0xa1, 0x2b, 0x00, 0x7c, 0xdb, 0xac, 0xed, 0xb4, 0xe4, 0x91, 0x46, 0x86, 0xc0, 0xc1, 0xaa, - 0x82, 0xdc, 0xdd, 0x9d, 0xe8, 0x34, 0x59, 0x32, 0x2f, 0x12, 0xa3, 0x3a, 0xfa, 0x39, 0x18, 0x88, - 0xdb, 0xcd, 0xa6, 0xa3, 0x0c, 0xe8, 0x05, 0xc6, 0x76, 0xf2, 0x76, 0x0d, 0x56, 0xc4, 0x0b, 0xb0, - 0xa4, 0x88, 0x6e, 0x52, 0xa6, 0x1a, 0x0b, 0x5b, 0x2a, 0xdb, 0x45, 0x5c, 0x27, 0xe0, 0x86, 0xa4, - 0x77, 0x49, 0x15, 0x1f, 0xe7, 0xe0, 0xdc, 0xdd, 0x9d, 0x78, 0x28, 0x5d, 0xbe, 0x18, 0x8a, 0x68, - 0xcb, 0xdc, 0x36, 0xd1, 0x65, 0x99, 0x64, 0x89, 0x7e, 0xb6, 0xcc, 0xfd, 0xf1, 0xa4, 0x4e, 0xb2, - 0xc4, 0x8a, 0xbb, 0x8f, 0x99, 0x59, 0x19, 0x2d, 0xc1, 0x49, 0x37, 0x0c, 0x92, 0x28, 0xf4, 0x7d, - 0x9e, 0x64, 0x8c, 0x9f, 0x2e, 0xb9, 0x81, 0xfd, 0xad, 0xa2, 0xdb, 0x27, 0x67, 0x3b, 0x51, 0x70, - 0x5e, 0x3d, 0x3b, 0x48, 0x5f, 0x76, 0x89, 0xc1, 0x79, 0x16, 0x86, 0xc8, 0xed, 0x84, 0x44, 0x81, - 0xe3, 0x5f, 0xc3, 0x8b, 0xd2, 0xb4, 0xcc, 0xf6, 0xc0, 0x05, 0xa3, 0x1c, 0xa7, 0xb0, 0x90, 0xad, - 0x4c, 0x2a, 0x46, 0x04, 0x31, 0x37, 0xa9, 0x48, 0x03, 0x8a, 0xfd, 0xcd, 0x72, 0x4a, 0x21, 0xbb, - 0x2f, 0x57, 0x6b, 0x2c, 0x55, 0x8d, 0xcc, 0xe9, 0xc3, 0x00, 0xe2, 0xa0, 0x51, 0x24, 0x65, 0x95, - 0xaa, 0x66, 0xd9, 0x24, 0x84, 0xd3, 0x74, 0xd1, 0x16, 0x54, 0x36, 0xc3, 0x38, 0x91, 0xc7, 0x8f, - 0x43, 0x9e, 0x74, 0x2e, 0x85, 0x71, 0xc2, 0xb4, 0x08, 0xf5, 0xd9, 0xb4, 0x24, 0xc6, 0x9c, 0x06, - 0x3d, 0x83, 0xc6, 0x9b, 0x4e, 0x54, 0x8f, 0x67, 0x59, 0xbc, 0x7f, 0x1f, 0x53, 0x1f, 0x94, 0xb2, - 0xb8, 0xaa, 0x41, 0xd8, 0xc4, 0xb3, 0xff, 0xab, 0x95, 0xba, 0x7f, 0xb8, 0xc1, 0x9c, 0xb7, 0xb7, - 0x49, 0x40, 0xb9, 0x81, 0xe9, 0x2e, 0xf6, 0x53, 0x99, 0x50, 0xd8, 0xb7, 0x75, 0x4b, 0xbd, 0x77, - 0x8b, 0xb6, 0x30, 0xc9, 0x9a, 0x30, 0x3c, 0xcb, 0x3e, 0x6e, 0xa5, 0x63, 0x9a, 0x4b, 0x45, 0x9c, - 0x4b, 0xcc, 0xb8, 0xfe, 0x7d, 0xc3, 0xa3, 0xed, 0x2f, 0x59, 0x30, 0x30, 0xe3, 0xb8, 0x5b, 0xe1, - 0xc6, 0x06, 0x7a, 0x0a, 0xaa, 0xf5, 0x76, 0x64, 0x86, 0x57, 0x2b, 0xcb, 0xc6, 0x9c, 0x28, 0xc7, - 0x0a, 0x83, 0x2e, 0xfd, 0x0d, 0xc7, 0x95, 0xd1, 0xfd, 0x65, 0xbe, 0xf4, 0x2f, 0xb2, 0x12, 0x2c, - 0x20, 0x74, 0xf8, 0x9b, 0xce, 0x6d, 0x59, 0x39, 0x7b, 0xf9, 0xb1, 0xa4, 0x41, 0xd8, 0xc4, 0xb3, - 0xff, 0x95, 0x05, 0xe3, 0x33, 0x4e, 0xec, 0xb9, 0xd3, 0xed, 0x64, 0x73, 0xc6, 0x4b, 0xd6, 0xdb, - 0xee, 0x16, 0x49, 0x78, 0x16, 0x08, 0xda, 0xcb, 0x76, 0x4c, 0x77, 0xa0, 0x3a, 0x0e, 0xaa, 0x5e, - 0x5e, 0x13, 0xe5, 0x58, 0x61, 0xa0, 0xd7, 0x60, 0xb0, 0xe5, 0xc4, 0xf1, 0xad, 0x30, 0xaa, 0x63, - 0xb2, 0x51, 0x4c, 0x9e, 0x98, 0x55, 0xe2, 0x46, 0x24, 0xc1, 0x64, 0x43, 0x38, 0x0a, 0xe8, 0xf6, - 0xb1, 0x49, 0xcc, 0xfe, 0x65, 0x0b, 0x4e, 0xcd, 0x10, 0x27, 0x22, 0x11, 0x4b, 0x2b, 0xa3, 0x3e, - 0x04, 0xbd, 0x0a, 0xd5, 0x84, 0x96, 0xd0, 0x1e, 0x59, 0xc5, 0xf6, 0x88, 0x5d, 0xf1, 0xaf, 0x89, - 0xc6, 0xb1, 0x22, 0x63, 0x7f, 0xc1, 0x82, 0x33, 0x79, 0x7d, 0x99, 0xf5, 0xc3, 0x76, 0xfd, 0x7e, - 0x74, 0xe8, 0x6f, 0x59, 0x30, 0xc4, 0xae, 0x4d, 0xe7, 0x48, 0xe2, 0x78, 0x7e, 0x47, 0x4a, 0x3b, - 0xab, 0xc7, 0x94, 0x76, 0xe7, 0xa0, 0x6f, 0x33, 0x6c, 0x92, 0xec, 0x95, 0xff, 0xa5, 0xb0, 0x49, - 0x30, 0x83, 0xa0, 0x67, 0xe8, 0x22, 0xf4, 0x82, 0xc4, 0xa1, 0xdb, 0x51, 0xda, 0xbe, 0x47, 0xf9, - 0x02, 0x54, 0xc5, 0xd8, 0xc4, 0xb1, 0xff, 0x65, 0x0d, 0x06, 0x84, 0x7f, 0x4a, 0xcf, 0x59, 0x49, - 0xa4, 0x89, 0xa2, 0xd4, 0xd5, 0x44, 0x11, 0x43, 0xbf, 0xcb, 0x72, 0x6b, 0x0a, 0x4d, 0xf8, 0x4a, - 0x21, 0x0e, 0x4d, 0x3c, 0x5d, 0xa7, 0xee, 0x16, 0xff, 0x8f, 0x05, 0x29, 0xf4, 0x45, 0x0b, 0x46, - 0xdd, 0x30, 0x08, 0x88, 0xab, 0xd5, 0xb4, 0xbe, 0x22, 0xfc, 0x56, 0x66, 0xd3, 0x8d, 0xea, 0x3b, - 0xbb, 0x0c, 0x00, 0x67, 0xc9, 0xa3, 0x17, 0x60, 0x98, 0x8f, 0xd9, 0xf5, 0x94, 0xc1, 0x5e, 0x67, - 0x3a, 0x33, 0x81, 0x38, 0x8d, 0x8b, 0x26, 0xf9, 0xc5, 0x87, 0xc8, 0x29, 0xd6, 0xaf, 0xed, 0x9a, - 0x46, 0x36, 0x31, 0x03, 0x03, 0x45, 0x80, 0x22, 0xb2, 0x11, 0x91, 0x78, 0x53, 0xf8, 0xef, 0x30, - 0x15, 0x71, 0xe0, 0xde, 0xf2, 0x09, 0xe0, 0x8e, 0x96, 0x70, 0x4e, 0xeb, 0x68, 0x4b, 0x9c, 0x91, - 0xab, 0x45, 0xf0, 0x73, 0x31, 0xcd, 0x5d, 0x8f, 0xca, 0x13, 0x50, 0x61, 0xa2, 0x8b, 0xa9, 0xa6, - 0x65, 0x1e, 0xc3, 0xc6, 0x04, 0x1b, 0xe6, 0xe5, 0x68, 0x0e, 0x4e, 0x64, 0xf2, 0xb4, 0xc5, 0xc2, - 0xb0, 0xae, 0xe2, 0x95, 0x32, 0x19, 0xde, 0x62, 0xdc, 0x51, 0xc3, 0xb4, 0x9f, 0x0c, 0xee, 0x63, - 0x3f, 0xd9, 0x51, 0x5e, 0xa2, 0xdc, 0xe4, 0xfd, 0x62, 0x21, 0x03, 0xd0, 0x93, 0x4b, 0xe8, 0xe7, - 0x33, 0x2e, 0xa1, 0xc3, 0xac, 0x03, 0xd7, 0x8b, 0xe9, 0xc0, 0xc1, 0xfd, 0x3f, 0xef, 0xa7, 0x3f, - 0xe7, 0xff, 0xb2, 0x40, 0xce, 0xeb, 0xac, 0xe3, 0x6e, 0x12, 0xba, 0x64, 0xd0, 0x7b, 0x61, 0x44, - 0x59, 0x01, 0xb8, 0x4a, 0x64, 0xb1, 0x55, 0xa3, 0x2e, 0xf7, 0x71, 0x0a, 0x8a, 0x33, 0xd8, 0x68, - 0x0a, 0x6a, 0x74, 0x9c, 0x78, 0x55, 0x2e, 0xf7, 0x95, 0xa5, 0x61, 0x7a, 0x65, 0x41, 0xd4, 0xd2, - 0x38, 0x28, 0x84, 0x31, 0xdf, 0x89, 0x13, 0xd6, 0x83, 0xd5, 0x9d, 0xc0, 0xbd, 0xc7, 0x6c, 0x1e, - 0x2c, 0x28, 0x66, 0x31, 0xdb, 0x10, 0xee, 0x6c, 0xdb, 0xfe, 0x6e, 0x1f, 0x0c, 0xa7, 0x38, 0xe3, - 0x01, 0x15, 0x86, 0xa7, 0xa0, 0x2a, 0x65, 0x78, 0x36, 0x6d, 0x91, 0x12, 0xf4, 0x0a, 0x83, 0x0a, - 0xad, 0x75, 0x2d, 0x55, 0xb3, 0x0a, 0x8e, 0x21, 0x70, 0xb1, 0x89, 0xc7, 0x98, 0x72, 0xe2, 0xc7, - 0xb3, 0xbe, 0x47, 0x82, 0x84, 0x77, 0xb3, 0x18, 0xa6, 0xbc, 0xb6, 0xb8, 0x6a, 0x36, 0xaa, 0x99, - 0x72, 0x06, 0x80, 0xb3, 0xe4, 0xd1, 0xa7, 0x2c, 0x18, 0x76, 0x6e, 0xc5, 0x3a, 0x01, 0xb4, 0x70, - 0xfe, 0x3c, 0xa4, 0x90, 0x4a, 0xe5, 0x94, 0xe6, 0x56, 0xeb, 0x54, 0x11, 0x4e, 0x13, 0x45, 0x6f, - 0x58, 0x80, 0xc8, 0x6d, 0xe2, 0x4a, 0xf7, 0x54, 0xd1, 0x97, 0xfe, 0x22, 0x0e, 0xcb, 0x17, 0x3a, - 0xda, 0xe5, 0x5c, 0xbd, 0xb3, 0x1c, 0xe7, 0xf4, 0xc1, 0xfe, 0x17, 0x65, 0xb5, 0xa1, 0xb4, 0x47, - 0xb4, 0x63, 0x78, 0x66, 0x5a, 0xf7, 0xee, 0x99, 0xa9, 0x3d, 0x4b, 0x3a, 0x83, 0x84, 0x53, 0x31, - 0x85, 0xa5, 0xfb, 0x14, 0x53, 0xf8, 0x0b, 0x56, 0x2a, 0x41, 0xd7, 0xe0, 0xf9, 0x97, 0x8a, 0xf5, - 0xc6, 0x9e, 0xe4, 0x5e, 0x2f, 0x19, 0xee, 0x9e, 0x76, 0x76, 0xa2, 0xdc, 0xd4, 0x40, 0x3b, 0x10, - 0x37, 0xfc, 0x0f, 0x65, 0x18, 0x34, 0x24, 0x69, 0xae, 0x5a, 0x64, 0x3d, 0x60, 0x6a, 0x51, 0xe9, - 0x00, 0x6a, 0xd1, 0xcf, 0x43, 0xcd, 0x95, 0x5c, 0xbe, 0x98, 0x14, 0xe2, 0x59, 0xd9, 0xa1, 0x19, - 0xbd, 0x2a, 0xc2, 0x9a, 0x26, 0x9a, 0x4f, 0x45, 0xa2, 0xa5, 0xce, 0xdb, 0x79, 0xa1, 0x62, 0x42, - 0x52, 0x74, 0xd6, 0xc9, 0xde, 0xff, 0x56, 0x7a, 0xf0, 0x3d, 0xfa, 0xae, 0xa5, 0x26, 0xf7, 0x18, - 0x52, 0x8e, 0xdc, 0x4c, 0xa7, 0x1c, 0xb9, 0x50, 0xc8, 0x30, 0x77, 0xc9, 0x35, 0x72, 0x15, 0x06, - 0x66, 0xc3, 0x66, 0xd3, 0x09, 0xea, 0xe8, 0x27, 0x60, 0xc0, 0xe5, 0x3f, 0x85, 0x6d, 0x8a, 0xdd, - 0x70, 0x0a, 0x28, 0x96, 0x30, 0xf4, 0x08, 0xf4, 0x39, 0x51, 0x43, 0xda, 0xa3, 0x98, 0x27, 0xd2, - 0x74, 0xd4, 0x88, 0x31, 0x2b, 0xb5, 0xff, 0x49, 0x1f, 0x30, 0x07, 0x00, 0x27, 0x22, 0xf5, 0xb5, - 0x90, 0x65, 0xfe, 0x3c, 0xd2, 0x7b, 0x41, 0x7d, 0x58, 0x7a, 0x90, 0xef, 0x06, 0x8d, 0xfb, 0xa1, - 0xf2, 0x31, 0xdf, 0x0f, 0x75, 0xb9, 0xf2, 0xeb, 0x7b, 0x80, 0xae, 0xfc, 0xec, 0xcf, 0x59, 0x80, - 0x94, 0xd7, 0x88, 0xbe, 0x93, 0x9f, 0x82, 0x9a, 0xf2, 0x1f, 0x11, 0x8a, 0x95, 0x66, 0x11, 0x12, - 0x80, 0x35, 0x4e, 0x0f, 0x27, 0xe4, 0xc7, 0x25, 0xff, 0x2e, 0xa7, 0xfd, 0xab, 0x19, 0xd7, 0x17, - 0xec, 0xdc, 0xfe, 0xbd, 0x12, 0x3c, 0xc4, 0x45, 0xf2, 0x92, 0x13, 0x38, 0x0d, 0xd2, 0xa4, 0xbd, - 0xea, 0xd5, 0xcb, 0xc2, 0xa5, 0x47, 0x33, 0x4f, 0xfa, 0x4b, 0x1f, 0x76, 0xef, 0xf2, 0x3d, 0xc7, - 0x77, 0xd9, 0x42, 0xe0, 0x25, 0x98, 0x35, 0x8e, 0x62, 0xa8, 0xca, 0xf7, 0x35, 0x04, 0x2f, 0x2e, - 0x88, 0x90, 0x62, 0x4b, 0x42, 0x6e, 0x12, 0xac, 0x08, 0x51, 0xc5, 0xd5, 0x0f, 0xdd, 0x2d, 0x4c, - 0x5a, 0x21, 0xe3, 0xbb, 0x86, 0xbb, 0xea, 0xa2, 0x28, 0xc7, 0x0a, 0xc3, 0x6e, 0xc2, 0xa8, 0x1c, - 0xc3, 0xd6, 0x15, 0xb2, 0x83, 0xc9, 0x06, 0x95, 0x3f, 0xae, 0x2c, 0x32, 0x9e, 0xfc, 0x50, 0xf2, - 0x67, 0xd6, 0x04, 0xe2, 0x34, 0xae, 0x4c, 0x06, 0x5a, 0xca, 0x4f, 0x06, 0x6a, 0xff, 0x9e, 0x05, - 0x59, 0x01, 0x68, 0xa4, 0x3e, 0xb4, 0xf6, 0x4c, 0x7d, 0x78, 0x80, 0xe4, 0x81, 0x3f, 0x0b, 0x83, - 0x4e, 0x42, 0x75, 0x16, 0x7e, 0xca, 0x2f, 0xdf, 0xdb, 0x45, 0xd0, 0x52, 0x58, 0xf7, 0x36, 0x3c, - 0x76, 0xba, 0x37, 0x9b, 0xb3, 0xff, 0xb2, 0x0f, 0xc6, 0x3a, 0x82, 0x99, 0xd0, 0xf3, 0x30, 0xa4, - 0x86, 0x42, 0xda, 0xcf, 0x6a, 0xa6, 0xcb, 0xa2, 0x86, 0xe1, 0x14, 0x66, 0x0f, 0xfb, 0x61, 0x01, - 0x4e, 0x46, 0xe4, 0xd5, 0x36, 0x69, 0x93, 0xe9, 0x8d, 0x84, 0x44, 0xab, 0xc4, 0x0d, 0x83, 0x3a, - 0x4f, 0xd0, 0x59, 0x9e, 0x79, 0xf8, 0xce, 0xee, 0xc4, 0x49, 0xdc, 0x09, 0xc6, 0x79, 0x75, 0x50, - 0x0b, 0x86, 0x7d, 0x53, 0xe5, 0x14, 0xe7, 0x8d, 0x7b, 0xd2, 0x56, 0xd5, 0x92, 0x48, 0x15, 0xe3, - 0x34, 0x81, 0xb4, 0xde, 0x5a, 0xb9, 0x4f, 0x7a, 0xeb, 0x27, 0xb5, 0xde, 0xca, 0x3d, 0x16, 0x3e, - 0x58, 0x70, 0x30, 0xdb, 0x51, 0x2b, 0xae, 0x2f, 0x42, 0x55, 0x7a, 0x73, 0xf5, 0xe4, 0x05, 0x65, - 0xb6, 0xd3, 0x85, 0x81, 0x3e, 0x01, 0x3f, 0x7e, 0x21, 0x8a, 0x8c, 0xc1, 0xbc, 0x1a, 0x26, 0xd3, - 0xbe, 0x1f, 0xde, 0xa2, 0x3a, 0xc1, 0xb5, 0x98, 0x08, 0x83, 0x8e, 0x7d, 0xb7, 0x04, 0x39, 0x67, - 0x23, 0xba, 0x1f, 0xb5, 0x22, 0x92, 0xda, 0x8f, 0x07, 0x53, 0x46, 0xd0, 0x6d, 0xee, 0xf1, 0xc6, - 0x45, 0xee, 0x07, 0x8a, 0x3e, 0xdb, 0x69, 0x27, 0x38, 0xc5, 0x8e, 0x94, 0x23, 0xdc, 0x79, 0x00, - 0xad, 0x3f, 0x8a, 0x08, 0x0b, 0x75, 0xa1, 0xae, 0xd5, 0x4c, 0x6c, 0x60, 0xd1, 0xa3, 0xbe, 0x17, - 0xc4, 0x89, 0xe3, 0xfb, 0x97, 0xbc, 0x20, 0x11, 0x36, 0x4b, 0xa5, 0x5b, 0x2c, 0x68, 0x10, 0x36, - 0xf1, 0xce, 0xbe, 0xcb, 0x98, 0xbf, 0x83, 0xcc, 0xfb, 0x26, 0x9c, 0x99, 0xf7, 0x12, 0x15, 0x17, - 0xa4, 0xd6, 0x1b, 0x55, 0x0f, 0x55, 0x9c, 0x9b, 0xd5, 0x35, 0xce, 0xcd, 0x88, 0xcb, 0x29, 0xa5, - 0xc3, 0x88, 0xb2, 0x71, 0x39, 0xf6, 0xf3, 0x70, 0x6a, 0xde, 0x4b, 0x2e, 0x7a, 0x3e, 0x39, 0x20, - 0x11, 0xfb, 0x77, 0xfb, 0x61, 0xc8, 0x8c, 0x70, 0x3d, 0x48, 0xa8, 0xde, 0x17, 0xa8, 0x06, 0x28, - 0xbe, 0xce, 0x53, 0xd7, 0x91, 0x37, 0x0e, 0x1d, 0x6e, 0x9b, 0x3f, 0x62, 0x86, 0x12, 0xa8, 0x69, - 0x62, 0xb3, 0x03, 0xe8, 0x16, 0x54, 0x36, 0x58, 0xdc, 0x48, 0xb9, 0x08, 0x9f, 0x8d, 0xbc, 0x11, - 0xd5, 0xdb, 0x91, 0x47, 0x9e, 0x70, 0x7a, 0x54, 0x70, 0x47, 0xe9, 0x60, 0x44, 0xc3, 0xa1, 0x58, - 0x84, 0x21, 0x2a, 0x8c, 0x6e, 0x22, 0xa1, 0x72, 0x0f, 0x22, 0x21, 0xc5, 0xa0, 0xfb, 0xef, 0x13, - 0x83, 0x66, 0x31, 0x40, 0xc9, 0x26, 0x53, 0x2b, 0x45, 0x04, 0xc4, 0x00, 0x1b, 0x04, 0x23, 0x06, - 0x28, 0x05, 0xc6, 0x59, 0x7c, 0xf4, 0x31, 0xc5, 0xe2, 0xab, 0x45, 0x98, 0x7b, 0xcd, 0x15, 0x7d, - 0xd4, 0xdc, 0xfd, 0x73, 0x25, 0x18, 0x99, 0x0f, 0xda, 0x2b, 0xf3, 0x2b, 0xed, 0x75, 0xdf, 0x73, - 0xaf, 0x90, 0x1d, 0xca, 0xc2, 0xb7, 0xc8, 0xce, 0xc2, 0x9c, 0xd8, 0x41, 0x6a, 0xcd, 0x5c, 0xa1, - 0x85, 0x98, 0xc3, 0x28, 0x33, 0xda, 0xf0, 0x82, 0x06, 0x89, 0x5a, 0x91, 0x27, 0x2c, 0xb1, 0x06, - 0x33, 0xba, 0xa8, 0x41, 0xd8, 0xc4, 0xa3, 0x6d, 0x87, 0xb7, 0x02, 0x12, 0x65, 0xf5, 0xeb, 0x65, - 0x5a, 0x88, 0x39, 0x8c, 0x22, 0x25, 0x51, 0x3b, 0x4e, 0xc4, 0x62, 0x54, 0x48, 0x6b, 0xb4, 0x10, - 0x73, 0x18, 0xdd, 0xe9, 0x71, 0x7b, 0x9d, 0xb9, 0xc4, 0x64, 0xc2, 0x2d, 0x56, 0x79, 0x31, 0x96, - 0x70, 0x8a, 0xba, 0x45, 0x76, 0xe6, 0xe8, 0x61, 0x3c, 0x13, 0x10, 0x76, 0x85, 0x17, 0x63, 0x09, - 0x67, 0x29, 0x44, 0xd3, 0xc3, 0xf1, 0x43, 0x97, 0x42, 0x34, 0xdd, 0xfd, 0x2e, 0xc7, 0xfa, 0x5f, - 0xb7, 0x60, 0xc8, 0x74, 0x64, 0x43, 0x8d, 0x8c, 0x2e, 0xbc, 0xdc, 0x91, 0x81, 0xfa, 0x3d, 0x79, - 0xaf, 0x33, 0x36, 0xbc, 0x24, 0x6c, 0xc5, 0x4f, 0x93, 0xa0, 0xe1, 0x05, 0x84, 0x39, 0x1a, 0x70, - 0x07, 0xb8, 0x94, 0x97, 0xdc, 0x6c, 0x58, 0x27, 0xf7, 0xa0, 0x4c, 0xdb, 0x37, 0x60, 0xac, 0x23, - 0x0a, 0xb0, 0x07, 0x15, 0x64, 0xdf, 0x18, 0x6c, 0x1b, 0xc3, 0x20, 0x6d, 0x58, 0xa6, 0xb1, 0x9a, - 0x85, 0x31, 0xbe, 0x91, 0x28, 0xa5, 0x55, 0x77, 0x93, 0x34, 0x55, 0x64, 0x27, 0x33, 0xfb, 0x5f, - 0xcf, 0x02, 0x71, 0x27, 0xbe, 0xfd, 0x79, 0x0b, 0x86, 0x53, 0x81, 0x99, 0x05, 0x29, 0x4b, 0x6c, - 0xa7, 0x85, 0xcc, 0xaf, 0x92, 0x39, 0x97, 0x97, 0x99, 0x30, 0xd5, 0x3b, 0x4d, 0x83, 0xb0, 0x89, - 0x67, 0x7f, 0xa9, 0x04, 0x55, 0xe9, 0x9b, 0xd2, 0x43, 0x57, 0x3e, 0x6b, 0xc1, 0xb0, 0xba, 0x6a, - 0x61, 0x36, 0xbc, 0x52, 0x11, 0xa1, 0x2a, 0xb4, 0x07, 0xca, 0x0a, 0x10, 0x6c, 0x84, 0x5a, 0x73, - 0xc7, 0x26, 0x31, 0x9c, 0xa6, 0x8d, 0xae, 0x03, 0xc4, 0x3b, 0x71, 0x42, 0x9a, 0x86, 0x35, 0xd1, - 0x36, 0x76, 0xdc, 0xa4, 0x1b, 0x46, 0x84, 0xee, 0xaf, 0xab, 0x61, 0x9d, 0xac, 0x2a, 0x4c, 0xad, - 0x42, 0xe9, 0x32, 0x6c, 0xb4, 0x64, 0xff, 0xa3, 0x12, 0x9c, 0xc8, 0x76, 0x09, 0x7d, 0x10, 0x86, - 0x24, 0x75, 0xe3, 0xd4, 0x29, 0x3d, 0x6b, 0x86, 0xb0, 0x01, 0xbb, 0xbb, 0x3b, 0x31, 0xd1, 0xf9, - 0xd2, 0xe7, 0xa4, 0x89, 0x82, 0x53, 0x8d, 0xf1, 0xfb, 0x2e, 0x71, 0x31, 0x3b, 0xb3, 0x33, 0xdd, - 0x6a, 0x89, 0x4b, 0x2b, 0xe3, 0xbe, 0xcb, 0x84, 0xe2, 0x0c, 0x36, 0x5a, 0x81, 0x53, 0x46, 0xc9, - 0x55, 0xe2, 0x35, 0x36, 0xd7, 0xc3, 0x48, 0x9e, 0xc0, 0x1e, 0xd1, 0x2e, 0x73, 0x9d, 0x38, 0x38, - 0xb7, 0x26, 0x95, 0xf6, 0xae, 0xd3, 0x72, 0x5c, 0x2f, 0xd9, 0x11, 0xe6, 0x51, 0xc5, 0x9b, 0x66, - 0x45, 0x39, 0x56, 0x18, 0xf6, 0x12, 0xf4, 0xf5, 0xb8, 0x82, 0x7a, 0xd2, 0xfc, 0x5f, 0x84, 0x2a, - 0x6d, 0x4e, 0xaa, 0x77, 0x45, 0x34, 0x19, 0x42, 0x55, 0xbe, 0x9b, 0x84, 0x6c, 0x28, 0x7b, 0x8e, - 0xbc, 0x52, 0x54, 0x9f, 0xb5, 0x10, 0xc7, 0x6d, 0x76, 0x98, 0xa6, 0x40, 0xf4, 0x38, 0x94, 0xc9, - 0xed, 0x56, 0xf6, 0xee, 0xf0, 0xc2, 0xed, 0x96, 0x17, 0x91, 0x98, 0x22, 0x91, 0xdb, 0x2d, 0x74, - 0x16, 0x4a, 0x5e, 0x5d, 0x08, 0x29, 0x10, 0x38, 0xa5, 0x85, 0x39, 0x5c, 0xf2, 0xea, 0xf6, 0x6d, - 0xa8, 0xa9, 0x87, 0x9a, 0xd0, 0x96, 0xe4, 0xdd, 0x56, 0x11, 0xce, 0x64, 0xb2, 0xdd, 0x2e, 0x5c, - 0xbb, 0x0d, 0xa0, 0xc3, 0x40, 0x8b, 0xe2, 0x2f, 0xe7, 0xa0, 0xcf, 0x0d, 0x45, 0xf4, 0x7c, 0x55, - 0x37, 0xc3, 0x98, 0x36, 0x83, 0xd8, 0x37, 0x60, 0xe4, 0x4a, 0x10, 0xde, 0x62, 0xef, 0x29, 0xb0, - 0xf4, 0x81, 0xb4, 0xe1, 0x0d, 0xfa, 0x23, 0xab, 0x22, 0x30, 0x28, 0xe6, 0x30, 0x95, 0xd8, 0xac, - 0xd4, 0x2d, 0xb1, 0x99, 0xfd, 0x71, 0x0b, 0x86, 0x54, 0x3c, 0xd9, 0xfc, 0xf6, 0x16, 0x6d, 0xb7, - 0x11, 0x85, 0xed, 0x56, 0xb6, 0x5d, 0xf6, 0x26, 0x1c, 0xe6, 0x30, 0x33, 0xd0, 0xb2, 0xb4, 0x4f, - 0xa0, 0xe5, 0x39, 0xe8, 0xdb, 0xf2, 0x82, 0x7a, 0xf6, 0x6d, 0xa0, 0x2b, 0x5e, 0x50, 0xc7, 0x0c, - 0x42, 0xbb, 0x70, 0x42, 0x75, 0x41, 0x0a, 0x84, 0xe7, 0x61, 0x68, 0xbd, 0xed, 0xf9, 0x75, 0x99, - 0x17, 0x31, 0x63, 0x51, 0x99, 0x31, 0x60, 0x38, 0x85, 0x49, 0xcf, 0x75, 0xeb, 0x5e, 0xe0, 0x44, - 0x3b, 0x2b, 0x5a, 0x02, 0x29, 0xa6, 0x34, 0xa3, 0x20, 0xd8, 0xc0, 0xb2, 0x5f, 0x2f, 0xc3, 0x48, - 0x3a, 0xaa, 0xae, 0x87, 0xe3, 0xd5, 0xe3, 0x50, 0x61, 0x81, 0x76, 0xd9, 0xa9, 0xe5, 0xa9, 0x04, - 0x39, 0x0c, 0xc5, 0xd0, 0xcf, 0xb3, 0x87, 0x14, 0xf3, 0xae, 0x96, 0xea, 0xa4, 0xb2, 0xc3, 0x30, - 0x97, 0x3b, 0x91, 0xb0, 0x44, 0x90, 0x42, 0x9f, 0xb2, 0x60, 0x20, 0x6c, 0x99, 0x09, 0xb1, 0x3e, - 0x50, 0x64, 0xc4, 0xa1, 0x08, 0x43, 0x12, 0x1a, 0xb1, 0x9a, 0x7a, 0x39, 0x1d, 0x92, 0xf4, 0xd9, - 0x77, 0xc3, 0x90, 0x89, 0xb9, 0x9f, 0x52, 0x5c, 0x35, 0x95, 0xe2, 0xcf, 0x9a, 0x8b, 0x42, 0xc4, - 0x54, 0xf6, 0xb0, 0xdd, 0xae, 0x41, 0xc5, 0x55, 0x7e, 0x09, 0xf7, 0x94, 0x4d, 0x57, 0xa5, 0xf3, - 0x60, 0x77, 0x53, 0xbc, 0x35, 0xfb, 0xbb, 0x96, 0xb1, 0x3e, 0x30, 0x89, 0x17, 0xea, 0x28, 0x82, - 0x72, 0x63, 0x7b, 0x4b, 0xa8, 0xa2, 0x97, 0x0b, 0x1a, 0xde, 0xf9, 0xed, 0x2d, 0xbd, 0xc6, 0xcd, - 0x52, 0x4c, 0x89, 0xf5, 0x60, 0x2c, 0x4c, 0x85, 0xde, 0x96, 0xf7, 0x0f, 0xbd, 0xb5, 0xdf, 0x28, - 0xc1, 0x58, 0xc7, 0xa2, 0x42, 0xaf, 0x41, 0x25, 0xa2, 0x5f, 0x29, 0x3e, 0x6f, 0xb1, 0xb0, 0x60, - 0xd9, 0x78, 0xa1, 0xae, 0xe5, 0x6e, 0xba, 0x1c, 0x73, 0x92, 0xe8, 0x32, 0x20, 0xed, 0x3d, 0xa3, - 0x2c, 0x95, 0xfc, 0x93, 0xcf, 0x8a, 0xaa, 0x68, 0xba, 0x03, 0x03, 0xe7, 0xd4, 0x42, 0x2f, 0x64, - 0x0d, 0x9e, 0xe5, 0xb4, 0x39, 0x7b, 0x2f, 0xdb, 0xa5, 0xfd, 0xdb, 0x25, 0x18, 0x4e, 0xe5, 0x27, - 0x43, 0x3e, 0x54, 0x89, 0xcf, 0xee, 0x1a, 0xa4, 0xb0, 0x39, 0x6c, 0xb6, 0x71, 0x25, 0x20, 0x2f, - 0x88, 0x76, 0xb1, 0xa2, 0xf0, 0x60, 0xdc, 0xf9, 0x3f, 0x0f, 0x43, 0xb2, 0x43, 0x1f, 0x70, 0x9a, - 0xbe, 0x18, 0x40, 0xb5, 0x46, 0x2f, 0x18, 0x30, 0x9c, 0xc2, 0xb4, 0x7f, 0xbf, 0x0c, 0xe3, 0xfc, - 0x72, 0xa6, 0xae, 0x56, 0xde, 0x92, 0x3c, 0x6f, 0xfd, 0x35, 0x9d, 0x45, 0xd0, 0x2a, 0xe2, 0x49, - 0xcd, 0x6e, 0x84, 0x7a, 0x72, 0x18, 0xfb, 0x6a, 0xc6, 0x61, 0x8c, 0xab, 0xdd, 0x8d, 0x23, 0xea, - 0xd1, 0x0f, 0x97, 0x07, 0xd9, 0xdf, 0x2f, 0xc1, 0x68, 0xe6, 0xe5, 0x14, 0xf4, 0x7a, 0x3a, 0xd9, - 0xb6, 0x55, 0x84, 0x4d, 0x7d, 0xcf, 0xc7, 0x34, 0x0e, 0x96, 0x72, 0xfb, 0x3e, 0x6d, 0x15, 0xfb, - 0x3b, 0x25, 0x18, 0x49, 0x3f, 0xf9, 0xf2, 0x00, 0x8e, 0xd4, 0x3b, 0xa0, 0xc6, 0x5e, 0x35, 0x60, - 0x2f, 0x15, 0x73, 0x93, 0x3c, 0x4f, 0x20, 0x2f, 0x0b, 0xb1, 0x86, 0x3f, 0x10, 0x99, 0xcc, 0xed, - 0x7f, 0x68, 0xc1, 0x69, 0xfe, 0x95, 0xd9, 0x75, 0xf8, 0xd7, 0xf3, 0x46, 0xf7, 0xe5, 0x62, 0x3b, - 0x98, 0xc9, 0x7e, 0xb9, 0xdf, 0xf8, 0xb2, 0x87, 0x45, 0x45, 0x6f, 0xd3, 0x4b, 0xe1, 0x01, 0xec, - 0xec, 0x81, 0x16, 0x83, 0xfd, 0x9d, 0x32, 0xe8, 0xb7, 0x54, 0x91, 0x27, 0xa2, 0x47, 0x0b, 0xc9, - 0x02, 0xba, 0xba, 0x13, 0xb8, 0xfa, 0xd5, 0xd6, 0x6a, 0x26, 0x78, 0xf4, 0x97, 0x2c, 0x18, 0xf4, - 0x02, 0x2f, 0xf1, 0x1c, 0x76, 0x8c, 0x2e, 0xe6, 0x41, 0x44, 0x45, 0x6e, 0x81, 0xb7, 0x1c, 0x46, - 0xe6, 0x3d, 0x8e, 0x22, 0x86, 0x4d, 0xca, 0xe8, 0xc3, 0xc2, 0xa7, 0xbb, 0x5c, 0x58, 0xdc, 0x73, - 0x35, 0xe3, 0xc8, 0xdd, 0xa2, 0x8a, 0x57, 0x12, 0x15, 0x94, 0x2e, 0x00, 0xd3, 0xa6, 0x54, 0x42, - 0x69, 0xfd, 0x3c, 0x3f, 0x2d, 0xc6, 0x9c, 0x90, 0x1d, 0x03, 0xea, 0x1c, 0x8b, 0x03, 0xfa, 0xcb, - 0x4e, 0x41, 0xcd, 0x69, 0x27, 0x61, 0x93, 0x0e, 0x93, 0xb8, 0x6a, 0xd2, 0x1e, 0xc1, 0x12, 0x80, - 0x35, 0x8e, 0xfd, 0x7a, 0x05, 0x32, 0xe1, 0x9c, 0xe8, 0xb6, 0xf9, 0x0e, 0xb0, 0x55, 0xec, 0x3b, - 0xc0, 0xaa, 0x33, 0x79, 0x6f, 0x01, 0xa3, 0x06, 0x54, 0x5a, 0x9b, 0x4e, 0x2c, 0xd5, 0xea, 0x17, - 0xd5, 0x39, 0x8e, 0x16, 0xde, 0xdd, 0x9d, 0xf8, 0x99, 0xde, 0xac, 0xae, 0x74, 0xad, 0x4e, 0xf1, - 0x14, 0x34, 0x9a, 0x34, 0x6b, 0x03, 0xf3, 0xf6, 0x0f, 0xf2, 0x24, 0xe4, 0x27, 0xc4, 0xf3, 0x0d, - 0x98, 0xc4, 0x6d, 0x3f, 0x11, 0xab, 0xe1, 0xc5, 0x02, 0x77, 0x19, 0x6f, 0x58, 0x27, 0x22, 0xe0, - 0xff, 0xb1, 0x41, 0x14, 0x7d, 0x10, 0x6a, 0x71, 0xe2, 0x44, 0xc9, 0x3d, 0x86, 0x0e, 0xab, 0x41, - 0x5f, 0x95, 0x8d, 0x60, 0xdd, 0x1e, 0x7a, 0x89, 0x25, 0x45, 0xf6, 0xe2, 0xcd, 0x7b, 0x0c, 0xc5, - 0x90, 0x09, 0x94, 0x45, 0x0b, 0xd8, 0x68, 0x0d, 0x9d, 0x07, 0x60, 0x6b, 0x9b, 0xfb, 0x1f, 0x56, - 0x99, 0x95, 0x49, 0xb1, 0x42, 0xac, 0x20, 0xd8, 0xc0, 0xb2, 0x7f, 0x12, 0xd2, 0x99, 0x34, 0xd0, - 0x84, 0x4c, 0xdc, 0xc1, 0xad, 0xd0, 0x2c, 0xa4, 0x22, 0x95, 0x63, 0xe3, 0x37, 0x2d, 0x30, 0xd3, - 0x7d, 0xa0, 0x57, 0x79, 0x5e, 0x11, 0xab, 0x88, 0x9b, 0x43, 0xa3, 0xdd, 0xc9, 0x25, 0xa7, 0x95, - 0xb9, 0xc2, 0x96, 0xc9, 0x45, 0xce, 0xbe, 0x0b, 0xaa, 0x12, 0x7a, 0x20, 0xa5, 0xee, 0x63, 0x70, - 0x52, 0x86, 0x67, 0x4a, 0xbb, 0xa9, 0xb8, 0x75, 0xda, 0xdf, 0xf4, 0x23, 0xed, 0x39, 0xa5, 0x6e, - 0xf6, 0x9c, 0x1e, 0x5e, 0x83, 0xfe, 0x2d, 0x0b, 0xce, 0x65, 0x3b, 0x10, 0x2f, 0x85, 0x81, 0x97, - 0x84, 0xd1, 0x2a, 0x49, 0x12, 0x2f, 0x68, 0xb0, 0x74, 0x6a, 0xb7, 0x9c, 0x48, 0x66, 0xab, 0x67, - 0x8c, 0xf2, 0x86, 0x13, 0x05, 0x98, 0x95, 0xa2, 0x1d, 0xe8, 0xe7, 0x4e, 0x6a, 0x42, 0x5b, 0x3f, - 0xe4, 0xde, 0xc8, 0x19, 0x0e, 0x7d, 0x5c, 0xe0, 0x0e, 0x72, 0x58, 0x10, 0xb4, 0xbf, 0x6f, 0x01, - 0x5a, 0xde, 0x26, 0x51, 0xe4, 0xd5, 0x0d, 0xb7, 0x3a, 0xf6, 0x0c, 0x92, 0xf1, 0xdc, 0x91, 0x19, - 0x3c, 0x9c, 0x79, 0x06, 0xc9, 0xf8, 0x97, 0xff, 0x0c, 0x52, 0xe9, 0x60, 0xcf, 0x20, 0xa1, 0x65, - 0x38, 0xdd, 0xe4, 0xc7, 0x0d, 0xfe, 0xb4, 0x08, 0x3f, 0x7b, 0xa8, 0x38, 0xb7, 0x33, 0x77, 0x76, - 0x27, 0x4e, 0x2f, 0xe5, 0x21, 0xe0, 0xfc, 0x7a, 0xf6, 0xbb, 0x00, 0x71, 0x6f, 0xba, 0xd9, 0x3c, - 0x5f, 0xa5, 0xae, 0xe6, 0x17, 0xfb, 0x2b, 0x15, 0x18, 0xcd, 0xe4, 0x32, 0xa6, 0x47, 0xbd, 0x4e, - 0xe7, 0xa8, 0x43, 0xcb, 0xef, 0xce, 0xee, 0xf5, 0xe4, 0x6e, 0x15, 0x40, 0xc5, 0x0b, 0x5a, 0xed, - 0xa4, 0x98, 0x30, 0x5b, 0xde, 0x89, 0x05, 0xda, 0xa0, 0x61, 0x2e, 0xa6, 0x7f, 0x31, 0x27, 0x53, - 0xa4, 0xf3, 0x56, 0x4a, 0x19, 0xef, 0xbb, 0x4f, 0xe6, 0x80, 0x4f, 0x68, 0x57, 0xaa, 0x4a, 0x11, - 0x86, 0xc5, 0xcc, 0x62, 0x39, 0xea, 0xab, 0xf6, 0x6f, 0x96, 0x60, 0xd0, 0x98, 0x34, 0xf4, 0x6b, - 0xe9, 0x64, 0x58, 0x56, 0x71, 0x9f, 0xc4, 0xda, 0x9f, 0xd4, 0xe9, 0xae, 0xf8, 0x27, 0x3d, 0xd1, - 0x99, 0x07, 0xeb, 0xee, 0xee, 0xc4, 0x89, 0x4c, 0xa6, 0xab, 0x54, 0x6e, 0xac, 0xb3, 0x1f, 0x85, - 0xd1, 0x4c, 0x33, 0x39, 0x9f, 0xbc, 0x66, 0x7e, 0xf2, 0xa1, 0xcd, 0x52, 0xe6, 0x90, 0x7d, 0x83, - 0x0e, 0x99, 0x88, 0xee, 0x0b, 0x7d, 0xd2, 0x83, 0x0d, 0x36, 0x13, 0xc4, 0x5b, 0xea, 0x31, 0x88, - 0xf7, 0x49, 0xa8, 0xb6, 0x42, 0xdf, 0x73, 0x3d, 0x95, 0x9b, 0x92, 0x85, 0x0d, 0xaf, 0x88, 0x32, - 0xac, 0xa0, 0xe8, 0x16, 0xd4, 0x6e, 0xde, 0x4a, 0xf8, 0xed, 0x8f, 0xb0, 0x6f, 0x17, 0x75, 0xe9, - 0xa3, 0x94, 0x16, 0x75, 0xbd, 0x84, 0x35, 0x2d, 0x64, 0x43, 0x3f, 0x13, 0x82, 0x32, 0x22, 0x81, - 0xd9, 0xde, 0x99, 0x74, 0x8c, 0xb1, 0x80, 0xd8, 0x5f, 0xaf, 0xc1, 0xa9, 0xbc, 0x84, 0xf2, 0xe8, - 0x23, 0xd0, 0xcf, 0xfb, 0x58, 0xcc, 0x9b, 0x25, 0x79, 0x34, 0xe6, 0x59, 0x83, 0xa2, 0x5b, 0xec, - 0x37, 0x16, 0x34, 0x05, 0x75, 0xdf, 0x59, 0x17, 0x2b, 0xe4, 0x68, 0xa8, 0x2f, 0x3a, 0x9a, 0xfa, - 0xa2, 0xc3, 0xa9, 0xfb, 0xce, 0x3a, 0xba, 0x0d, 0x95, 0x86, 0x97, 0x10, 0x47, 0x18, 0x11, 0x6e, - 0x1c, 0x09, 0x71, 0xe2, 0x70, 0x2d, 0x8d, 0xfd, 0xc4, 0x9c, 0x20, 0xfa, 0x9a, 0x05, 0xa3, 0xeb, - 0xe9, 0xec, 0x01, 0x82, 0x79, 0x3a, 0x47, 0xf0, 0x68, 0x40, 0x9a, 0x10, 0x7f, 0x07, 0x2c, 0x53, - 0x88, 0xb3, 0xdd, 0x41, 0x9f, 0xb4, 0x60, 0x60, 0xc3, 0xf3, 0x8d, 0xbc, 0xcd, 0x47, 0x30, 0x39, - 0x17, 0x19, 0x01, 0x7d, 0xe2, 0xe0, 0xff, 0x63, 0x2c, 0x29, 0x77, 0x93, 0x54, 0xfd, 0x87, 0x95, - 0x54, 0x03, 0xf7, 0x49, 0x52, 0x7d, 0xc6, 0x82, 0x9a, 0x1a, 0x69, 0x11, 0x85, 0xfd, 0xc1, 0x23, - 0x9c, 0x72, 0x6e, 0x39, 0x51, 0x7f, 0xb1, 0x26, 0x8e, 0xbe, 0x68, 0xc1, 0xa0, 0xf3, 0x5a, 0x3b, - 0x22, 0x75, 0xb2, 0x1d, 0xb6, 0x62, 0xf1, 0x88, 0xe8, 0xcb, 0xc5, 0x77, 0x66, 0x9a, 0x12, 0x99, - 0x23, 0xdb, 0xcb, 0xad, 0x58, 0x44, 0x4b, 0xe9, 0x02, 0x6c, 0x76, 0xc1, 0xde, 0x2d, 0xc1, 0xc4, - 0x3e, 0x2d, 0xa0, 0xe7, 0x61, 0x28, 0x8c, 0x1a, 0x4e, 0xe0, 0xbd, 0x66, 0xa6, 0x03, 0x51, 0x5a, - 0xd6, 0xb2, 0x01, 0xc3, 0x29, 0x4c, 0x33, 0x4e, 0xbc, 0xb4, 0x4f, 0x9c, 0xf8, 0x39, 0xe8, 0x8b, - 0x48, 0x2b, 0xcc, 0x1e, 0x16, 0x58, 0xa4, 0x02, 0x83, 0xa0, 0x47, 0xa1, 0xec, 0xb4, 0x3c, 0xe1, - 0x88, 0xa6, 0xce, 0x40, 0xd3, 0x2b, 0x0b, 0x98, 0x96, 0xa7, 0xd2, 0x56, 0x54, 0x8e, 0x25, 0x6d, - 0x05, 0x15, 0x03, 0xe2, 0xee, 0xa2, 0x5f, 0x8b, 0x81, 0xf4, 0x9d, 0x82, 0xfd, 0x46, 0x19, 0x1e, - 0xdd, 0x73, 0xbd, 0x68, 0x3f, 0x3c, 0x6b, 0x0f, 0x3f, 0x3c, 0x39, 0x3c, 0xa5, 0xfd, 0x86, 0xa7, - 0xdc, 0x65, 0x78, 0x3e, 0x49, 0xb7, 0x81, 0x4c, 0xa3, 0x52, 0xcc, 0x33, 0x90, 0xdd, 0xb2, 0xb2, - 0x88, 0x1d, 0x20, 0xa1, 0x58, 0xd3, 0xa5, 0x67, 0x80, 0x54, 0x8c, 0x74, 0xa5, 0x08, 0x31, 0xd0, - 0x35, 0x95, 0x09, 0x5f, 0xfb, 0xdd, 0x02, 0xaf, 0xed, 0xdf, 0xe9, 0x83, 0xc7, 0x7b, 0xe0, 0xde, - 0xe6, 0x2a, 0xb6, 0x7a, 0x5c, 0xc5, 0x3f, 0xe4, 0xd3, 0xf4, 0xe9, 0xdc, 0x69, 0xc2, 0xc5, 0x4f, - 0xd3, 0xde, 0x33, 0x84, 0x9e, 0x82, 0xaa, 0x17, 0xc4, 0xc4, 0x6d, 0x47, 0xdc, 0x27, 0xd9, 0x08, - 0x63, 0x5a, 0x10, 0xe5, 0x58, 0x61, 0xd0, 0x33, 0x9d, 0xeb, 0xd0, 0xed, 0x3f, 0x50, 0x50, 0xec, - 0xae, 0x19, 0x11, 0xc5, 0x55, 0x8a, 0xd9, 0x69, 0xca, 0x01, 0x38, 0x19, 0xfb, 0x6f, 0x58, 0x70, - 0xb6, 0xbb, 0x88, 0x45, 0xcf, 0xc0, 0xe0, 0x7a, 0xe4, 0x04, 0xee, 0x26, 0x7b, 0x00, 0x58, 0x2e, - 0x1d, 0xf6, 0xbd, 0xba, 0x18, 0x9b, 0x38, 0x68, 0x16, 0xc6, 0xb8, 0xe7, 0x86, 0x81, 0x21, 0x23, - 0x7f, 0xef, 0xec, 0x4e, 0x8c, 0xad, 0x65, 0x81, 0xb8, 0x13, 0xdf, 0xfe, 0x41, 0x39, 0xbf, 0x5b, - 0x5c, 0x15, 0x3b, 0xc8, 0x6a, 0x16, 0x6b, 0xb5, 0xd4, 0x03, 0xc7, 0x2d, 0x1f, 0x37, 0xc7, 0xed, - 0xeb, 0xc6, 0x71, 0xd1, 0x1c, 0x9c, 0x30, 0x5e, 0x68, 0xe2, 0xd1, 0xdc, 0xdc, 0x2d, 0x59, 0xa5, - 0x38, 0x59, 0xc9, 0xc0, 0x71, 0x47, 0x8d, 0x07, 0x7c, 0xe9, 0xfd, 0x7a, 0x09, 0xce, 0x74, 0xd5, - 0x7e, 0x8f, 0x49, 0xa2, 0x98, 0xd3, 0xdf, 0x77, 0x3c, 0xd3, 0x6f, 0x4e, 0x4a, 0x65, 0xbf, 0x49, - 0xb1, 0xff, 0xa4, 0xd4, 0x75, 0x23, 0xd0, 0x93, 0xd0, 0x8f, 0xec, 0x28, 0xbd, 0x00, 0xc3, 0x4e, - 0xab, 0xc5, 0xf1, 0x98, 0x17, 0x6d, 0x26, 0xa5, 0xd2, 0xb4, 0x09, 0xc4, 0x69, 0xdc, 0x9e, 0x74, - 0x9a, 0x3f, 0xb5, 0xa0, 0x86, 0xc9, 0x06, 0xe7, 0x46, 0xe8, 0xa6, 0x18, 0x22, 0xab, 0x88, 0xfc, - 0xb1, 0x74, 0x60, 0x63, 0x8f, 0xe5, 0x55, 0xcd, 0x1b, 0xec, 0xce, 0x17, 0xbb, 0x4a, 0x07, 0x7a, - 0xb1, 0x4b, 0xbd, 0xd9, 0x54, 0xee, 0xfe, 0x66, 0x93, 0xfd, 0xbd, 0x01, 0xfa, 0x79, 0xad, 0x70, - 0x36, 0x22, 0xf5, 0x98, 0xce, 0x6f, 0x3b, 0xf2, 0xc5, 0x22, 0x51, 0xf3, 0x7b, 0x0d, 0x2f, 0x62, - 0x5a, 0x9e, 0xba, 0x20, 0x2b, 0x1d, 0x28, 0xa1, 0x4c, 0x79, 0xdf, 0x84, 0x32, 0x2f, 0xc0, 0x70, - 0x1c, 0x6f, 0xae, 0x44, 0xde, 0xb6, 0x93, 0x90, 0x2b, 0x64, 0x47, 0xe8, 0xbe, 0x3a, 0x09, 0xc4, - 0xea, 0x25, 0x0d, 0xc4, 0x69, 0x5c, 0x34, 0x0f, 0x63, 0x3a, 0xad, 0x0b, 0x89, 0x12, 0x16, 0x73, - 0xc1, 0x57, 0x82, 0x8a, 0xf8, 0xd6, 0x89, 0x60, 0x04, 0x02, 0xee, 0xac, 0x43, 0xf9, 0x69, 0xaa, - 0x90, 0x76, 0xa4, 0x3f, 0xcd, 0x4f, 0x53, 0xed, 0xd0, 0xbe, 0x74, 0xd4, 0x40, 0x4b, 0x70, 0x92, - 0x2f, 0x8c, 0xe9, 0x56, 0xcb, 0xf8, 0xa2, 0x81, 0x74, 0xde, 0xce, 0xf9, 0x4e, 0x14, 0x9c, 0x57, - 0x0f, 0x3d, 0x07, 0x83, 0xaa, 0x78, 0x61, 0x4e, 0xdc, 0xed, 0x28, 0xdb, 0x92, 0x6a, 0x66, 0xa1, - 0x8e, 0x4d, 0x3c, 0xf4, 0x01, 0x78, 0x58, 0xff, 0xe5, 0x81, 0x79, 0xfc, 0xc2, 0x73, 0x4e, 0x64, - 0xcc, 0x52, 0x2f, 0x04, 0xcd, 0xe7, 0xa2, 0xd5, 0x71, 0xb7, 0xfa, 0x68, 0x1d, 0xce, 0x2a, 0xd0, - 0x85, 0x20, 0x61, 0x51, 0x36, 0x31, 0x99, 0x71, 0x62, 0x72, 0x2d, 0xf2, 0xc5, 0x4b, 0xd3, 0xea, - 0x11, 0xd9, 0x79, 0x2f, 0xb9, 0x94, 0x87, 0x89, 0x17, 0xf1, 0x1e, 0xad, 0xa0, 0x29, 0xa8, 0x91, - 0xc0, 0x59, 0xf7, 0xc9, 0xf2, 0xec, 0x02, 0xcb, 0xbc, 0x65, 0xdc, 0xaf, 0x5e, 0x90, 0x00, 0xac, - 0x71, 0x94, 0xdf, 0xef, 0x50, 0xd7, 0x07, 0x8d, 0x57, 0xe0, 0x54, 0xc3, 0x6d, 0x51, 0x8d, 0xd0, - 0x73, 0xc9, 0xb4, 0xcb, 0xdc, 0x1c, 0xe9, 0xc4, 0xf0, 0x84, 0xaa, 0xca, 0xa9, 0x7d, 0x7e, 0x76, - 0xa5, 0x03, 0x07, 0xe7, 0xd6, 0x64, 0xee, 0xb0, 0x51, 0x78, 0x7b, 0x67, 0xfc, 0x64, 0xc6, 0x1d, - 0x96, 0x16, 0x62, 0x0e, 0x43, 0x97, 0x01, 0xb1, 0x08, 0x89, 0x4b, 0x49, 0xd2, 0x52, 0x2a, 0xe8, - 0xf8, 0x29, 0xf6, 0x49, 0xca, 0xb9, 0xef, 0x62, 0x07, 0x06, 0xce, 0xa9, 0x45, 0x35, 0x9a, 0x20, - 0x64, 0xad, 0x8f, 0x3f, 0x9c, 0xd6, 0x68, 0xae, 0xf2, 0x62, 0x2c, 0xe1, 0xf6, 0x7f, 0xb4, 0x60, - 0x58, 0x6d, 0xed, 0x63, 0x08, 0x27, 0xf2, 0xd3, 0xe1, 0x44, 0xf3, 0x87, 0x67, 0x8e, 0xac, 0xe7, - 0x5d, 0x7c, 0xd2, 0xbf, 0x39, 0x08, 0xa0, 0x19, 0xa8, 0x92, 0x5d, 0x56, 0x57, 0xd9, 0xf5, 0xc0, - 0x32, 0xaf, 0xbc, 0x8c, 0x3c, 0x95, 0xfb, 0x9b, 0x91, 0x67, 0x15, 0x4e, 0x4b, 0xcd, 0x82, 0x5f, - 0xf6, 0x5d, 0x0a, 0x63, 0xc5, 0x0b, 0xab, 0x33, 0x8f, 0x8a, 0x86, 0x4e, 0x2f, 0xe4, 0x21, 0xe1, - 0xfc, 0xba, 0x29, 0x85, 0x66, 0x60, 0x5f, 0x2d, 0x53, 0x6d, 0xff, 0xc5, 0x0d, 0xf9, 0x34, 0x4f, - 0x66, 0xfb, 0x2f, 0x5e, 0x5c, 0xc5, 0x1a, 0x27, 0x5f, 0x06, 0xd4, 0x0a, 0x92, 0x01, 0x70, 0x60, - 0x19, 0x20, 0xb9, 0xd1, 0x60, 0x57, 0x6e, 0x24, 0x2f, 0x15, 0x86, 0xba, 0x5e, 0x2a, 0xbc, 0x17, - 0x46, 0xbc, 0x60, 0x93, 0x44, 0x5e, 0x42, 0xea, 0x6c, 0x2f, 0x30, 0x4e, 0x55, 0xd5, 0x1a, 0xc0, - 0x42, 0x0a, 0x8a, 0x33, 0xd8, 0x69, 0x16, 0x3a, 0xd2, 0x03, 0x0b, 0xed, 0x22, 0xb8, 0x46, 0x8b, - 0x11, 0x5c, 0x27, 0x0e, 0x2f, 0xb8, 0xc6, 0x8e, 0x54, 0x70, 0xa1, 0x42, 0x04, 0x57, 0x4f, 0x32, - 0xc1, 0x38, 0x99, 0x9e, 0xda, 0xe7, 0x64, 0xda, 0x4d, 0x6a, 0x9d, 0xbe, 0x67, 0xa9, 0x95, 0x2f, - 0x90, 0x1e, 0x3a, 0x6a, 0x81, 0xf4, 0x99, 0x12, 0x9c, 0xd6, 0x2c, 0x9b, 0x6e, 0x14, 0x6f, 0x83, - 0x32, 0x2d, 0xf6, 0x10, 0x1c, 0xbf, 0xa3, 0x33, 0x02, 0xe1, 0x74, 0x4c, 0x9d, 0x82, 0x60, 0x03, - 0x8b, 0xc5, 0x93, 0x91, 0x88, 0x65, 0x95, 0xce, 0xf2, 0xf3, 0x59, 0x51, 0x8e, 0x15, 0x06, 0x5d, - 0x8a, 0xf4, 0xb7, 0x88, 0xd1, 0xcd, 0xe6, 0x2b, 0x9c, 0xd5, 0x20, 0x6c, 0xe2, 0xa1, 0x27, 0x39, - 0x11, 0xc6, 0x4b, 0x28, 0x4f, 0x1f, 0x12, 0x8f, 0x67, 0x4b, 0xf6, 0xa1, 0xa0, 0xb2, 0x3b, 0x2c, - 0x70, 0xb0, 0xd2, 0xd9, 0x1d, 0xe6, 0xee, 0xa6, 0x30, 0xec, 0xff, 0x69, 0xc1, 0x99, 0xdc, 0xa1, - 0x38, 0x06, 0x39, 0x7d, 0x3b, 0x2d, 0xa7, 0x57, 0x8b, 0x3a, 0xc4, 0x18, 0x5f, 0xd1, 0x45, 0x66, - 0xff, 0x7b, 0x0b, 0x46, 0x34, 0xfe, 0x31, 0x7c, 0xaa, 0x97, 0xfe, 0xd4, 0xe2, 0xce, 0x6b, 0xb5, - 0x8e, 0x6f, 0xfb, 0xfd, 0x12, 0xa8, 0x1c, 0xa2, 0xd3, 0xae, 0xcc, 0xd0, 0xbc, 0xcf, 0xad, 0xf1, - 0x0e, 0xf4, 0xb3, 0x4b, 0xef, 0xb8, 0x18, 0x87, 0x9e, 0x34, 0x7d, 0x76, 0x81, 0xae, 0x1d, 0x0a, - 0xd8, 0xdf, 0x18, 0x0b, 0x82, 0x2c, 0xe7, 0xb9, 0x17, 0x53, 0xc6, 0x5f, 0x17, 0x21, 0x78, 0x3a, - 0xe7, 0xb9, 0x28, 0xc7, 0x0a, 0x83, 0x4a, 0x12, 0xcf, 0x0d, 0x83, 0x59, 0xdf, 0x89, 0xe5, 0xc3, - 0xac, 0x4a, 0x92, 0x2c, 0x48, 0x00, 0xd6, 0x38, 0xec, 0x3e, 0xdc, 0x8b, 0x5b, 0xbe, 0xb3, 0x63, - 0x9c, 0xca, 0x8d, 0x5c, 0x14, 0x0a, 0x84, 0x4d, 0x3c, 0xbb, 0x09, 0xe3, 0xe9, 0x8f, 0x98, 0x23, - 0x1b, 0xcc, 0x19, 0xb5, 0xa7, 0xe1, 0x9c, 0x82, 0x9a, 0xc3, 0x6a, 0x2d, 0xb6, 0x1d, 0xc1, 0x13, - 0xb4, 0x4b, 0xa6, 0x04, 0x60, 0x8d, 0x63, 0xff, 0x03, 0x0b, 0x4e, 0xe6, 0x0c, 0x5a, 0x81, 0x21, - 0x8e, 0x89, 0xe6, 0x36, 0x79, 0x3a, 0xc0, 0xdb, 0x61, 0xa0, 0x4e, 0x36, 0x1c, 0xe9, 0xee, 0x68, - 0x70, 0xcf, 0x39, 0x5e, 0x8c, 0x25, 0xdc, 0xfe, 0xed, 0x12, 0x8c, 0xa6, 0xfb, 0x1a, 0xb3, 0xb0, - 0x21, 0x3e, 0x4c, 0x5e, 0xec, 0x86, 0xdb, 0x24, 0xda, 0xa1, 0x5f, 0x6e, 0x65, 0xc2, 0x86, 0x3a, - 0x30, 0x70, 0x4e, 0x2d, 0x96, 0x41, 0xb8, 0xae, 0x46, 0x5b, 0xae, 0xc8, 0xeb, 0x45, 0xae, 0x48, - 0x3d, 0x99, 0xa6, 0x6b, 0x84, 0x22, 0x89, 0x4d, 0xfa, 0x54, 0x17, 0x61, 0x7e, 0xd8, 0x33, 0x6d, - 0xcf, 0x4f, 0xbc, 0x40, 0x7c, 0xb2, 0x58, 0xab, 0x4a, 0x17, 0x59, 0xea, 0x44, 0xc1, 0x79, 0xf5, - 0xec, 0xef, 0xf7, 0x81, 0x0a, 0xa9, 0x66, 0xae, 0x6b, 0x05, 0x39, 0xfe, 0x1d, 0x34, 0xf8, 0x4c, - 0xad, 0xad, 0xbe, 0xbd, 0x7c, 0x49, 0xb8, 0x29, 0xc7, 0xb4, 0xe7, 0xaa, 0x01, 0x5b, 0xd3, 0x20, - 0x6c, 0xe2, 0xd1, 0x9e, 0xf8, 0xde, 0x36, 0xe1, 0x95, 0xfa, 0xd3, 0x3d, 0x59, 0x94, 0x00, 0xac, - 0x71, 0x68, 0x4f, 0xea, 0xde, 0xc6, 0x86, 0xb0, 0x4b, 0xa8, 0x9e, 0xd0, 0xd1, 0xc1, 0x0c, 0xc2, - 0x73, 0xcc, 0x87, 0x5b, 0x42, 0xff, 0x36, 0x72, 0xcc, 0x87, 0x5b, 0x98, 0x41, 0xe8, 0x2c, 0x05, - 0x61, 0xd4, 0x74, 0x7c, 0xef, 0x35, 0x52, 0x57, 0x54, 0x84, 0xde, 0xad, 0x66, 0xe9, 0x6a, 0x27, - 0x0a, 0xce, 0xab, 0x47, 0x17, 0x74, 0x2b, 0x22, 0x75, 0xcf, 0x4d, 0xcc, 0xd6, 0x20, 0xbd, 0xa0, - 0x57, 0x3a, 0x30, 0x70, 0x4e, 0x2d, 0x34, 0x0d, 0xa3, 0x32, 0x24, 0x5e, 0x26, 0x3c, 0x1a, 0x4c, - 0x27, 0x58, 0xc1, 0x69, 0x30, 0xce, 0xe2, 0x53, 0x26, 0xd9, 0x14, 0x39, 0xd1, 0x98, 0x9a, 0x6e, - 0x30, 0x49, 0x99, 0x2b, 0x0d, 0x2b, 0x0c, 0xfb, 0x13, 0x65, 0x2a, 0xd4, 0xbb, 0xa4, 0x1e, 0x3c, - 0x36, 0x47, 0xd3, 0xf4, 0x8a, 0xec, 0xeb, 0x61, 0x45, 0x3e, 0x0b, 0x43, 0x37, 0xe3, 0x30, 0x50, - 0x4e, 0x9c, 0x95, 0xae, 0x4e, 0x9c, 0x06, 0x56, 0xbe, 0x13, 0x67, 0x7f, 0x51, 0x4e, 0x9c, 0x03, - 0xf7, 0xe8, 0xc4, 0xf9, 0x87, 0x15, 0x50, 0xef, 0xf5, 0x5c, 0x25, 0xc9, 0xad, 0x30, 0xda, 0xf2, - 0x82, 0x06, 0x4b, 0x25, 0xf0, 0x35, 0x0b, 0x86, 0xf8, 0x7e, 0x59, 0x34, 0x83, 0xf0, 0x36, 0x0a, - 0x7a, 0x08, 0x26, 0x45, 0x6c, 0x72, 0xcd, 0x20, 0x94, 0x79, 0xcb, 0xd7, 0x04, 0xe1, 0x54, 0x8f, - 0xd0, 0x47, 0x01, 0xa4, 0x11, 0x77, 0x43, 0x72, 0xe0, 0x85, 0x62, 0xfa, 0x87, 0xc9, 0x86, 0x56, - 0xa9, 0xd7, 0x14, 0x11, 0x6c, 0x10, 0x44, 0x9f, 0xd1, 0x01, 0x8a, 0x3c, 0xda, 0xe3, 0xc3, 0x47, - 0x32, 0x36, 0xbd, 0x84, 0x27, 0x62, 0x18, 0xf0, 0x82, 0x06, 0x5d, 0x27, 0xc2, 0xd9, 0xed, 0x6d, - 0x79, 0x69, 0x38, 0x16, 0x43, 0xa7, 0x3e, 0xe3, 0xf8, 0x4e, 0xe0, 0x92, 0x68, 0x81, 0xa3, 0x9b, - 0x8f, 0xeb, 0xb3, 0x02, 0x2c, 0x1b, 0xea, 0x78, 0xe9, 0xa8, 0xd2, 0xcb, 0x4b, 0x47, 0x67, 0xdf, - 0x07, 0x63, 0x1d, 0x93, 0x79, 0xa0, 0x68, 0xc4, 0x7b, 0x0f, 0x64, 0xb4, 0x7f, 0xa7, 0x5f, 0x0b, - 0xad, 0xab, 0x61, 0x9d, 0x3f, 0x9c, 0x13, 0xe9, 0x19, 0x15, 0x2a, 0x73, 0x81, 0x4b, 0xc4, 0x78, - 0xa0, 0x5f, 0x15, 0x62, 0x93, 0x24, 0x5d, 0xa3, 0x2d, 0x27, 0x22, 0xc1, 0x51, 0xaf, 0xd1, 0x15, - 0x45, 0x04, 0x1b, 0x04, 0xd1, 0x66, 0x2a, 0x1c, 0xe9, 0xe2, 0xe1, 0xc3, 0x91, 0x58, 0x82, 0xb2, - 0xbc, 0xf7, 0x25, 0xbe, 0x68, 0xc1, 0x48, 0x90, 0x5a, 0xb9, 0xc5, 0x78, 0x20, 0xe7, 0xef, 0x0a, - 0xfe, 0xdc, 0x5b, 0xba, 0x0c, 0x67, 0xe8, 0xe7, 0x89, 0xb4, 0xca, 0x01, 0x45, 0x9a, 0x7e, 0xb8, - 0xab, 0xbf, 0xdb, 0xc3, 0x5d, 0x28, 0x50, 0x2f, 0x17, 0x0e, 0x14, 0xfe, 0x72, 0x21, 0xe4, 0xbc, - 0x5a, 0x78, 0x03, 0x6a, 0x6e, 0x44, 0x9c, 0xe4, 0x1e, 0x1f, 0xb1, 0x63, 0xbe, 0x1d, 0xb3, 0xb2, - 0x01, 0xac, 0xdb, 0xb2, 0xff, 0x4f, 0x1f, 0x9c, 0x90, 0x23, 0x22, 0xa3, 0x17, 0xa8, 0x7c, 0xe4, - 0x74, 0xb5, 0xae, 0xac, 0xe4, 0xe3, 0x25, 0x09, 0xc0, 0x1a, 0x87, 0xea, 0x63, 0xed, 0x98, 0x2c, - 0xb7, 0x48, 0xb0, 0xe8, 0xad, 0xc7, 0xe2, 0x32, 0x56, 0x6d, 0x94, 0x6b, 0x1a, 0x84, 0x4d, 0x3c, - 0xaa, 0xdb, 0x3b, 0x86, 0xd2, 0x6a, 0xe8, 0xf6, 0x52, 0x51, 0x95, 0x70, 0xf4, 0x2b, 0xb9, 0xb9, - 0x90, 0x8b, 0x89, 0xf9, 0xeb, 0x08, 0xda, 0x38, 0xe0, 0xbb, 0xa7, 0x7f, 0xd7, 0x82, 0xd3, 0xbc, - 0x54, 0x8e, 0xe4, 0xb5, 0x56, 0xdd, 0x49, 0x48, 0x5c, 0xcc, 0xdb, 0x04, 0x39, 0xfd, 0xd3, 0xe6, - 0xe5, 0x3c, 0xb2, 0x38, 0xbf, 0x37, 0xe8, 0x75, 0x0b, 0x46, 0xb7, 0x52, 0xe9, 0x62, 0xa4, 0xe8, - 0x38, 0x6c, 0x26, 0x87, 0x54, 0xa3, 0x7a, 0xab, 0xa5, 0xcb, 0x63, 0x9c, 0xa5, 0x6e, 0xff, 0x0f, - 0x0b, 0x4c, 0x36, 0x7a, 0xfc, 0x59, 0x66, 0x0e, 0xae, 0x0a, 0x4a, 0xed, 0xb2, 0xd2, 0x55, 0xbb, - 0x7c, 0x14, 0xca, 0x6d, 0xaf, 0x2e, 0xce, 0x17, 0xfa, 0x8a, 0x78, 0x61, 0x0e, 0xd3, 0x72, 0xfb, - 0x9f, 0x57, 0xb4, 0x19, 0x44, 0x84, 0xd4, 0xfd, 0x48, 0x7c, 0xf6, 0x86, 0xca, 0x53, 0xc7, 0xbf, - 0xfc, 0x6a, 0x47, 0x9e, 0xba, 0x9f, 0x3e, 0x78, 0xc4, 0x24, 0x1f, 0xa0, 0x6e, 0x69, 0xea, 0x06, - 0xf6, 0x09, 0x97, 0xbc, 0x09, 0x55, 0x7a, 0x04, 0x63, 0xf6, 0xcc, 0x6a, 0xaa, 0x53, 0xd5, 0x4b, - 0xa2, 0xfc, 0xee, 0xee, 0xc4, 0xbb, 0x0f, 0xde, 0x2d, 0x59, 0x1b, 0xab, 0xf6, 0x51, 0x0c, 0x35, - 0xfa, 0x9b, 0x45, 0x76, 0x8a, 0xc3, 0xdd, 0x35, 0xc5, 0x33, 0x25, 0xa0, 0x90, 0xb0, 0x51, 0x4d, - 0x07, 0x05, 0x50, 0x63, 0x4f, 0x44, 0x33, 0xa2, 0xfc, 0x0c, 0xb8, 0xa2, 0xe2, 0x2b, 0x25, 0xe0, - 0xee, 0xee, 0xc4, 0x0b, 0x07, 0x27, 0xaa, 0xaa, 0x63, 0x4d, 0xc2, 0xfe, 0x52, 0x9f, 0x5e, 0xbb, - 0x22, 0x3d, 0xe1, 0x8f, 0xc4, 0xda, 0x7d, 0x3e, 0xb3, 0x76, 0xcf, 0x75, 0xac, 0xdd, 0x11, 0xfd, - 0x94, 0x71, 0x6a, 0x35, 0x1e, 0xb7, 0x22, 0xb0, 0xbf, 0xbd, 0x81, 0x69, 0x40, 0xaf, 0xb6, 0xbd, - 0x88, 0xc4, 0x2b, 0x51, 0x3b, 0xf0, 0x82, 0x06, 0x5b, 0x8e, 0x55, 0x53, 0x03, 0x4a, 0x81, 0x71, - 0x16, 0x9f, 0x1e, 0xea, 0xe9, 0x9c, 0xdf, 0x70, 0xb6, 0xf9, 0xaa, 0x32, 0x32, 0xb6, 0xad, 0x8a, - 0x72, 0xac, 0x30, 0xec, 0x6f, 0xb0, 0x5b, 0x74, 0x23, 0xa4, 0x9c, 0xae, 0x09, 0x9f, 0xbd, 0xc9, - 0xcd, 0xd3, 0xbd, 0xa9, 0x35, 0xc1, 0x1f, 0xe2, 0xe6, 0x30, 0x74, 0x0b, 0x06, 0xd6, 0xf9, 0xeb, - 0x92, 0xc5, 0x64, 0xdc, 0x17, 0x4f, 0x55, 0xb2, 0x77, 0x7b, 0xe4, 0xbb, 0x95, 0x77, 0xf5, 0x4f, - 0x2c, 0xa9, 0xd9, 0xdf, 0xae, 0xc0, 0x68, 0xe6, 0xd5, 0xe6, 0x54, 0xa2, 0xdd, 0xd2, 0xbe, 0x89, - 0x76, 0x3f, 0x04, 0x50, 0x27, 0x2d, 0x3f, 0xdc, 0x61, 0xea, 0x58, 0xdf, 0x81, 0xd5, 0x31, 0xa5, - 0xc1, 0xcf, 0xa9, 0x56, 0xb0, 0xd1, 0xa2, 0xc8, 0x71, 0xc7, 0xf3, 0xf6, 0x66, 0x72, 0xdc, 0x19, - 0xef, 0x72, 0xf4, 0x1f, 0xef, 0xbb, 0x1c, 0x1e, 0x8c, 0xf2, 0x2e, 0xaa, 0xc0, 0xed, 0x7b, 0x88, - 0xcf, 0x66, 0xa1, 0x2f, 0x73, 0xe9, 0x66, 0x70, 0xb6, 0xdd, 0xfb, 0xf9, 0x28, 0x3b, 0x7a, 0x07, - 0xd4, 0xe4, 0x3c, 0xc7, 0xe3, 0x35, 0x9d, 0xfc, 0x42, 0x2e, 0x03, 0xf6, 0x58, 0xba, 0xf8, 0xd9, - 0x91, 0x83, 0x02, 0xee, 0x57, 0x0e, 0x0a, 0xfb, 0x0b, 0x25, 0xaa, 0xc7, 0xf3, 0x7e, 0xa9, 0x74, - 0x4a, 0x4f, 0x40, 0xbf, 0xd3, 0x4e, 0x36, 0xc3, 0x8e, 0xf7, 0x29, 0xa7, 0x59, 0x29, 0x16, 0x50, - 0xb4, 0x08, 0x7d, 0x75, 0x9d, 0x22, 0xe7, 0x20, 0xf3, 0xa9, 0x4d, 0xa2, 0x4e, 0x42, 0x30, 0x6b, - 0x05, 0x3d, 0x02, 0x7d, 0x89, 0xd3, 0x90, 0xd1, 0x7a, 0x2c, 0x42, 0x7b, 0xcd, 0x69, 0xc4, 0x98, - 0x95, 0x9a, 0xe2, 0xbb, 0x6f, 0x1f, 0xf1, 0xfd, 0x02, 0x0c, 0xc7, 0x5e, 0x23, 0x70, 0x92, 0x76, - 0x44, 0x8c, 0x5b, 0x43, 0xed, 0x33, 0x62, 0x02, 0x71, 0x1a, 0xd7, 0xfe, 0xdd, 0x21, 0x38, 0xb5, - 0x3a, 0xbb, 0x24, 0x13, 0xbf, 0x1f, 0x59, 0xc0, 0x5d, 0x1e, 0x8d, 0xe3, 0x0b, 0xb8, 0xeb, 0x42, - 0xdd, 0x37, 0x02, 0xee, 0x7c, 0x23, 0xe0, 0x2e, 0x1d, 0xfd, 0x54, 0x2e, 0x22, 0xfa, 0x29, 0xaf, - 0x07, 0xbd, 0x44, 0x3f, 0x1d, 0x59, 0x04, 0xde, 0x9e, 0x1d, 0x3a, 0x50, 0x04, 0x9e, 0x0a, 0x4f, - 0x2c, 0x24, 0x2e, 0xa5, 0xcb, 0x54, 0xe5, 0x86, 0x27, 0xaa, 0xd0, 0x30, 0x1e, 0x73, 0x25, 0x58, - 0xfd, 0xcb, 0xc5, 0x77, 0xa0, 0x87, 0xd0, 0x30, 0x11, 0xf6, 0x65, 0x86, 0x23, 0x0e, 0x14, 0x11, - 0x8e, 0x98, 0xd7, 0x9d, 0x7d, 0xc3, 0x11, 0x5f, 0x80, 0x61, 0xd7, 0x0f, 0x03, 0xb2, 0x12, 0x85, - 0x49, 0xe8, 0x86, 0xbe, 0x50, 0xeb, 0xf5, 0x43, 0x34, 0x26, 0x10, 0xa7, 0x71, 0xbb, 0xc5, 0x32, - 0xd6, 0x0e, 0x1b, 0xcb, 0x08, 0xf7, 0x29, 0x96, 0xf1, 0x17, 0x75, 0xd4, 0xfd, 0x20, 0x9b, 0x91, - 0x0f, 0x15, 0x3f, 0x23, 0xbd, 0x84, 0xde, 0xa3, 0x37, 0xf8, 0x03, 0x91, 0x54, 0x31, 0x9e, 0x0d, - 0x9b, 0x54, 0xf1, 0x1b, 0x62, 0x43, 0xf2, 0xca, 0x11, 0x2c, 0xd8, 0x1b, 0xab, 0x9a, 0x8c, 0x7a, - 0x34, 0x52, 0x17, 0xe1, 0x74, 0x47, 0x0e, 0x93, 0x15, 0xe0, 0x2b, 0x25, 0xf8, 0xb1, 0x7d, 0xbb, - 0x80, 0x6e, 0x01, 0x24, 0x4e, 0x43, 0x2c, 0x54, 0x71, 0x61, 0x72, 0x48, 0xc7, 0xce, 0x35, 0xd9, - 0x1e, 0x4f, 0x67, 0xa3, 0xfe, 0xb2, 0xab, 0x08, 0xf9, 0x9b, 0xf9, 0x73, 0x86, 0x7e, 0x47, 0xd6, - 0x4f, 0x1c, 0xfa, 0x04, 0x33, 0x08, 0x15, 0xff, 0x11, 0x69, 0xe8, 0xd7, 0xd5, 0xd5, 0xf4, 0x61, - 0x56, 0x8a, 0x05, 0x14, 0x3d, 0x07, 0x83, 0x8e, 0xef, 0xf3, 0xa0, 0x21, 0x12, 0x8b, 0x17, 0xa2, - 0x74, 0xfa, 0x41, 0x0d, 0xc2, 0x26, 0x9e, 0xfd, 0x17, 0x25, 0x98, 0xd8, 0x87, 0xa7, 0x74, 0x04, - 0x8b, 0x56, 0x7a, 0x0e, 0x16, 0x15, 0x81, 0x14, 0xfd, 0x5d, 0x02, 0x29, 0x9e, 0x83, 0xc1, 0x84, - 0x38, 0x4d, 0xe1, 0x0a, 0x26, 0x2c, 0x01, 0xfa, 0x06, 0x58, 0x83, 0xb0, 0x89, 0x47, 0xb9, 0xd8, - 0x88, 0xe3, 0xba, 0x24, 0x8e, 0x65, 0xa4, 0x84, 0xb0, 0xa6, 0x16, 0x16, 0x86, 0xc1, 0x8c, 0xd4, - 0xd3, 0x29, 0x12, 0x38, 0x43, 0x32, 0x3b, 0xe0, 0xb5, 0x1e, 0x07, 0xfc, 0xeb, 0x25, 0x78, 0x74, - 0x4f, 0xe9, 0xd6, 0x73, 0x10, 0x4b, 0x3b, 0x26, 0x51, 0x76, 0xe1, 0x5c, 0x8b, 0x49, 0x84, 0x19, - 0x84, 0x8f, 0x52, 0xab, 0x65, 0xbc, 0x5e, 0x5f, 0x74, 0x44, 0x17, 0x1f, 0xa5, 0x14, 0x09, 0x9c, - 0x21, 0x79, 0xaf, 0xcb, 0xf2, 0xdb, 0x7d, 0xf0, 0x78, 0x0f, 0x3a, 0x40, 0x81, 0x91, 0x6f, 0xe9, - 0x28, 0xcd, 0xf2, 0x7d, 0x8a, 0xd2, 0xbc, 0xb7, 0xe1, 0x7a, 0x33, 0xb8, 0xb3, 0xa7, 0x08, 0xbb, - 0x6f, 0x94, 0xe0, 0x6c, 0x77, 0x85, 0x05, 0xbd, 0x07, 0x46, 0x23, 0xe5, 0xfa, 0x66, 0x06, 0x78, - 0x9e, 0xe4, 0xf6, 0x96, 0x14, 0x08, 0x67, 0x71, 0xd1, 0x24, 0x40, 0xcb, 0x49, 0x36, 0xe3, 0x0b, - 0xb7, 0xbd, 0x38, 0x11, 0x69, 0x9e, 0x46, 0xf8, 0x0d, 0x9f, 0x2c, 0xc5, 0x06, 0x06, 0x25, 0xc7, - 0xfe, 0xcd, 0x85, 0x57, 0xc3, 0x84, 0x57, 0xe2, 0x87, 0xad, 0x93, 0xf2, 0x51, 0x1c, 0x03, 0x84, - 0xb3, 0xb8, 0x94, 0x1c, 0xbb, 0x43, 0xe6, 0x1d, 0xe5, 0xa7, 0x30, 0x46, 0x6e, 0x51, 0x95, 0x62, - 0x03, 0x23, 0x1b, 0xba, 0x5a, 0xd9, 0x3f, 0x74, 0xd5, 0xfe, 0x67, 0x25, 0x38, 0xd3, 0x55, 0xe1, - 0xed, 0x8d, 0x4d, 0x3d, 0x78, 0xe1, 0xa6, 0xf7, 0xb8, 0xc3, 0x0e, 0x16, 0xa6, 0xf8, 0xa7, 0x5d, - 0x56, 0x9a, 0x08, 0x53, 0xbc, 0xf7, 0xec, 0x0b, 0x0f, 0xde, 0x78, 0x76, 0x44, 0x26, 0xf6, 0x1d, - 0x20, 0x32, 0x31, 0x33, 0x19, 0x95, 0x1e, 0xa5, 0xc3, 0x9f, 0xf7, 0x75, 0x1d, 0x5e, 0x7a, 0x40, - 0xee, 0xc9, 0x9a, 0x3d, 0x07, 0x27, 0xbc, 0x80, 0x3d, 0x90, 0xb6, 0xda, 0x5e, 0x17, 0x99, 0x7f, - 0x78, 0x7a, 0x4b, 0x15, 0xfe, 0xb0, 0x90, 0x81, 0xe3, 0x8e, 0x1a, 0x0f, 0x60, 0xa4, 0xe8, 0xbd, - 0x0d, 0xe9, 0x01, 0x39, 0xf7, 0x32, 0x9c, 0x96, 0x43, 0xb1, 0xe9, 0x44, 0xa4, 0x2e, 0x84, 0x6d, - 0x2c, 0x02, 0x5e, 0xce, 0xf0, 0xa0, 0x99, 0x1c, 0x04, 0x9c, 0x5f, 0x8f, 0xbd, 0x49, 0x15, 0xb6, - 0x3c, 0x57, 0x1c, 0x05, 0xf5, 0x9b, 0x54, 0xb4, 0x10, 0x73, 0x98, 0x96, 0x17, 0xb5, 0xe3, 0x91, - 0x17, 0x1f, 0x82, 0x9a, 0x1a, 0x6f, 0xee, 0xbb, 0xaf, 0x16, 0x79, 0x87, 0xef, 0xbe, 0x5a, 0xe1, - 0x06, 0xd6, 0x7e, 0x8f, 0xa6, 0xbe, 0x13, 0x86, 0x94, 0xf5, 0xab, 0xd7, 0x97, 0xc1, 0xec, 0x2f, - 0xf5, 0xc3, 0x70, 0x2a, 0xdb, 0x67, 0xca, 0xec, 0x6d, 0xed, 0x6b, 0xf6, 0x66, 0x61, 0x1b, 0xed, - 0x40, 0x3e, 0x1b, 0x68, 0x84, 0x6d, 0xb4, 0x03, 0x82, 0x39, 0x8c, 0x1e, 0x3a, 0xea, 0xd1, 0x0e, - 0x6e, 0x07, 0xc2, 0x0f, 0x55, 0x1d, 0x3a, 0xe6, 0x58, 0x29, 0x16, 0x50, 0xf4, 0x71, 0x0b, 0x86, - 0x62, 0x76, 0xa7, 0xc2, 0x2f, 0x0d, 0xc4, 0x22, 0xbf, 0x7c, 0xf8, 0x64, 0xa6, 0x2a, 0xb3, 0x2d, - 0xf3, 0x5b, 0x32, 0x4b, 0x70, 0x8a, 0x22, 0xfa, 0x94, 0x05, 0x35, 0xf5, 0xba, 0x91, 0x78, 0x03, - 0x74, 0xb5, 0xd8, 0x64, 0xaa, 0xdc, 0xda, 0xac, 0xae, 0xa7, 0x54, 0x56, 0x4b, 0xac, 0x09, 0xa3, - 0x58, 0x59, 0xf4, 0x07, 0x8e, 0xc6, 0xa2, 0x0f, 0x39, 0xd6, 0xfc, 0x77, 0x40, 0xad, 0xe9, 0x04, - 0xde, 0x06, 0x89, 0x13, 0x6e, 0x64, 0x97, 0x39, 0x9e, 0x65, 0x21, 0xd6, 0x70, 0xaa, 0x00, 0xc4, - 0xec, 0xc3, 0x12, 0xc3, 0x2a, 0xce, 0x14, 0x80, 0x55, 0x5d, 0x8c, 0x4d, 0x1c, 0xd3, 0x84, 0x0f, - 0xf7, 0xd5, 0x84, 0x3f, 0xb8, 0xb7, 0x09, 0xdf, 0xfe, 0xc7, 0x16, 0x9c, 0xce, 0x9d, 0xb5, 0x07, - 0xd7, 0x1d, 0xd5, 0xfe, 0x72, 0x05, 0x4e, 0xe6, 0xa4, 0xed, 0x45, 0x3b, 0xe6, 0x7a, 0xb6, 0x8a, - 0xf0, 0xec, 0x48, 0x3b, 0x2a, 0xc8, 0x61, 0xcc, 0x59, 0xc4, 0x07, 0xbb, 0x40, 0xd3, 0x97, 0x58, - 0xe5, 0xe3, 0xbd, 0xc4, 0x32, 0x96, 0x65, 0xdf, 0x7d, 0x5d, 0x96, 0x95, 0x7d, 0x6e, 0x96, 0xbe, - 0x69, 0xc1, 0x78, 0xb3, 0xcb, 0x5b, 0x11, 0xc2, 0x1c, 0x7c, 0xfd, 0x68, 0x5e, 0xa2, 0x98, 0x79, - 0xe4, 0xce, 0xee, 0x44, 0xd7, 0x27, 0x3a, 0x70, 0xd7, 0x5e, 0xd9, 0xdf, 0x2f, 0x03, 0xcb, 0x19, - 0xcd, 0x52, 0x33, 0xee, 0xa0, 0x8f, 0x99, 0xd9, 0xbf, 0xad, 0xa2, 0x32, 0x55, 0xf3, 0xc6, 0x55, - 0xf6, 0x70, 0x3e, 0x82, 0x79, 0xc9, 0xc4, 0xb3, 0x4c, 0xab, 0xd4, 0x03, 0xd3, 0xf2, 0x65, 0x9a, - 0xf5, 0x72, 0xf1, 0x69, 0xd6, 0x6b, 0xd9, 0x14, 0xeb, 0x7b, 0x4f, 0x71, 0xdf, 0x03, 0x39, 0xc5, - 0xbf, 0x6a, 0x71, 0xc6, 0x93, 0x99, 0x05, 0xad, 0x19, 0x58, 0x7b, 0x68, 0x06, 0x4f, 0x41, 0x35, - 0x26, 0xfe, 0xc6, 0x25, 0xe2, 0xf8, 0x42, 0x83, 0xd0, 0x5e, 0x05, 0xa2, 0x1c, 0x2b, 0x0c, 0xf6, - 0x0e, 0xb3, 0xef, 0x87, 0xb7, 0x2e, 0x34, 0x5b, 0xc9, 0x8e, 0xd0, 0x25, 0xf4, 0x3b, 0xcc, 0x0a, - 0x82, 0x0d, 0x2c, 0xfb, 0xef, 0x94, 0xf8, 0x0a, 0x14, 0xae, 0x29, 0xcf, 0x67, 0x5e, 0xce, 0xec, - 0xdd, 0xab, 0xe3, 0x23, 0x00, 0x6e, 0xd8, 0x6c, 0x51, 0x3d, 0x73, 0x2d, 0x14, 0x37, 0x75, 0x97, - 0x0e, 0xfd, 0x4e, 0xbf, 0x68, 0x4f, 0x7f, 0x86, 0x2e, 0xc3, 0x06, 0xbd, 0x14, 0x2f, 0x2d, 0xef, - 0xcb, 0x4b, 0x53, 0x6c, 0xa5, 0x6f, 0x1f, 0x69, 0xf7, 0x17, 0x16, 0xa4, 0x34, 0x22, 0xd4, 0x82, - 0x0a, 0xed, 0xee, 0x8e, 0xd8, 0xa1, 0xcb, 0xc5, 0xa9, 0x5f, 0x94, 0x35, 0x8a, 0x65, 0xcf, 0x7e, - 0x62, 0x4e, 0x08, 0xf9, 0xc2, 0x83, 0x85, 0x8f, 0xea, 0xd5, 0xe2, 0x08, 0x5e, 0x0a, 0xc3, 0x2d, - 0x7e, 0xdd, 0xac, 0xbd, 0x61, 0xec, 0xe7, 0x61, 0xac, 0xa3, 0x53, 0xec, 0x91, 0xbc, 0x90, 0x4a, - 0x9f, 0xcc, 0x72, 0x65, 0x01, 0xbd, 0x98, 0xc3, 0xec, 0x6f, 0x58, 0x70, 0x22, 0xdb, 0x3c, 0x7a, - 0xc3, 0x82, 0xb1, 0x38, 0xdb, 0xde, 0x51, 0x8d, 0x9d, 0xf2, 0x42, 0xed, 0x00, 0xe1, 0xce, 0x4e, - 0xd8, 0xff, 0x57, 0x2c, 0xfe, 0x1b, 0x5e, 0x50, 0x0f, 0x6f, 0x29, 0xc5, 0xc4, 0xea, 0xaa, 0x98, - 0xd0, 0xfd, 0xe8, 0x6e, 0x92, 0x7a, 0xdb, 0xef, 0x08, 0x0f, 0x5e, 0x15, 0xe5, 0x58, 0x61, 0xb0, - 0x68, 0xc8, 0xb6, 0x78, 0x87, 0x21, 0xb3, 0x28, 0xe7, 0x44, 0x39, 0x56, 0x18, 0xe8, 0x59, 0x18, - 0x32, 0x3e, 0x52, 0xae, 0x4b, 0xa6, 0x90, 0x1b, 0x22, 0x33, 0xc6, 0x29, 0x2c, 0x34, 0x09, 0xa0, - 0x94, 0x1c, 0x29, 0x22, 0x99, 0x61, 0x4a, 0x71, 0xa2, 0x18, 0x1b, 0x18, 0x2c, 0xf6, 0xd8, 0x6f, - 0xc7, 0xec, 0xe6, 0xa5, 0x5f, 0xe7, 0x06, 0x9e, 0x15, 0x65, 0x58, 0x41, 0x29, 0x37, 0x69, 0x3a, - 0x41, 0xdb, 0xf1, 0xe9, 0x08, 0x89, 0xa3, 0xa6, 0xda, 0x86, 0x4b, 0x0a, 0x82, 0x0d, 0x2c, 0xfa, - 0xc5, 0x89, 0xd7, 0x24, 0x2f, 0x85, 0x81, 0xf4, 0x1e, 0xd4, 0x97, 0x71, 0xa2, 0x1c, 0x2b, 0x0c, - 0xfb, 0xbf, 0x59, 0x30, 0xaa, 0x93, 0x1e, 0xf0, 0xe7, 0xf0, 0xcd, 0x93, 0xb1, 0xb5, 0xef, 0xc9, - 0x38, 0x1d, 0xe2, 0x5d, 0xea, 0x29, 0xc4, 0xdb, 0x8c, 0xbe, 0x2e, 0xef, 0x19, 0x7d, 0xfd, 0x13, - 0xfa, 0xa9, 0x65, 0x1e, 0xa6, 0x3d, 0x98, 0xf7, 0xcc, 0x32, 0xb2, 0xa1, 0xdf, 0x75, 0x54, 0x72, - 0xa0, 0x21, 0x7e, 0x76, 0x98, 0x9d, 0x66, 0x48, 0x02, 0x62, 0x2f, 0x43, 0x4d, 0xdd, 0x49, 0xc9, - 0x83, 0xaa, 0x95, 0x7f, 0x50, 0xed, 0x29, 0x0a, 0x74, 0x66, 0xfd, 0x5b, 0x3f, 0x78, 0xec, 0x2d, - 0x7f, 0xfc, 0x83, 0xc7, 0xde, 0xf2, 0xbd, 0x1f, 0x3c, 0xf6, 0x96, 0x8f, 0xdf, 0x79, 0xcc, 0xfa, - 0xd6, 0x9d, 0xc7, 0xac, 0x3f, 0xbe, 0xf3, 0x98, 0xf5, 0xbd, 0x3b, 0x8f, 0x59, 0xdf, 0xbf, 0xf3, - 0x98, 0xf5, 0xc5, 0xff, 0xfc, 0xd8, 0x5b, 0x5e, 0xca, 0x75, 0x1f, 0xa5, 0x3f, 0x9e, 0x76, 0xeb, - 0x53, 0xdb, 0xe7, 0x99, 0x07, 0x23, 0xdd, 0x5e, 0x53, 0xc6, 0x9a, 0x9a, 0x92, 0xdb, 0xeb, 0xff, - 0x05, 0x00, 0x00, 0xff, 0xff, 0xf9, 0x7b, 0xca, 0xa6, 0xc3, 0xea, 0x00, 0x00, + 0x75, 0x98, 0x66, 0x3f, 0x80, 0xdd, 0x87, 0x0f, 0x92, 0x4d, 0xf2, 0x0e, 0xa4, 0xee, 0x0e, 0xf4, + 0x9c, 0x7d, 0x3a, 0x47, 0x77, 0x80, 0x8f, 0xbe, 0x93, 0x2f, 0x3e, 0x5b, 0x32, 0x3e, 0x48, 0x10, + 0x24, 0x40, 0xe0, 0x1a, 0x20, 0x29, 0x9d, 0x7c, 0x3a, 0x0d, 0x76, 0x1b, 0x8b, 0x21, 0x66, 0x67, + 0xe6, 0x66, 0x66, 0x41, 0xe0, 0x2c, 0xc9, 0x92, 0x25, 0xdb, 0x4a, 0xf4, 0x71, 0x8a, 0x94, 0x94, + 0xcf, 0x49, 0xa4, 0xc8, 0x96, 0x93, 0xb2, 0x2b, 0x51, 0xc5, 0x49, 0x7e, 0xc4, 0x89, 0x93, 0x72, + 0xd9, 0x4e, 0xa5, 0x94, 0x52, 0x52, 0x76, 0xa5, 0x5c, 0x96, 0x93, 0xd8, 0x88, 0xc4, 0x54, 0x2a, + 0xa9, 0x54, 0xc5, 0x55, 0x4e, 0xfc, 0x23, 0x61, 0xf2, 0x23, 0xd5, 0xdf, 0x3d, 0xb3, 0xb3, 0xc0, + 0x02, 0x18, 0x90, 0x94, 0x72, 0xff, 0x76, 0xfb, 0xbd, 0x79, 0xaf, 0xa7, 0xa7, 0xfb, 0xbd, 0xd7, + 0xaf, 0xdf, 0x7b, 0x0d, 0x0b, 0x2d, 0x37, 0xd9, 0xe8, 0xac, 0x4d, 0x34, 0x82, 0xf6, 0xa4, 0x13, + 0xb5, 0x82, 0x30, 0x0a, 0x6e, 0xb3, 0x1f, 0xcf, 0x36, 0x9a, 0x93, 0x5b, 0x17, 0x27, 0xc3, 0xcd, + 0xd6, 0xa4, 0x13, 0xba, 0xf1, 0xa4, 0x13, 0x86, 0x9e, 0xdb, 0x70, 0x12, 0x37, 0xf0, 0x27, 0xb7, + 0x9e, 0x73, 0xbc, 0x70, 0xc3, 0x79, 0x6e, 0xb2, 0x45, 0x7c, 0x12, 0x39, 0x09, 0x69, 0x4e, 0x84, + 0x51, 0x90, 0x04, 0xe8, 0xc7, 0x34, 0xb5, 0x09, 0x49, 0x8d, 0xfd, 0x78, 0xad, 0xd1, 0x9c, 0xd8, + 0xba, 0x38, 0x11, 0x6e, 0xb6, 0x26, 0x28, 0xb5, 0x09, 0x83, 0xda, 0x84, 0xa4, 0x76, 0xfe, 0x59, + 0xa3, 0x2f, 0xad, 0xa0, 0x15, 0x4c, 0x32, 0xa2, 0x6b, 0x9d, 0x75, 0xf6, 0x8f, 0xfd, 0x61, 0xbf, + 0x38, 0xb3, 0xf3, 0xf6, 0xe6, 0x8b, 0xf1, 0x84, 0x1b, 0xd0, 0xee, 0x4d, 0x36, 0x82, 0x88, 0x4c, + 0x6e, 0x75, 0x75, 0xe8, 0xfc, 0x15, 0x8d, 0x43, 0xb6, 0x13, 0xe2, 0xc7, 0x6e, 0xe0, 0xc7, 0xcf, + 0xd2, 0x2e, 0x90, 0x68, 0x8b, 0x44, 0xe6, 0xeb, 0x19, 0x08, 0x79, 0x94, 0x9e, 0xd7, 0x94, 0xda, + 0x4e, 0x63, 0xc3, 0xf5, 0x49, 0xb4, 0xa3, 0x1f, 0x6f, 0x93, 0xc4, 0xc9, 0x7b, 0x6a, 0xb2, 0xd7, + 0x53, 0x51, 0xc7, 0x4f, 0xdc, 0x36, 0xe9, 0x7a, 0xe0, 0x3d, 0xfb, 0x3d, 0x10, 0x37, 0x36, 0x48, + 0xdb, 0xe9, 0x7a, 0xee, 0x87, 0x7b, 0x3d, 0xd7, 0x49, 0x5c, 0x6f, 0xd2, 0xf5, 0x93, 0x38, 0x89, + 0xb2, 0x0f, 0xd9, 0xaf, 0xc3, 0xc8, 0xd4, 0xad, 0x95, 0xa9, 0x4e, 0xb2, 0x31, 0x13, 0xf8, 0xeb, + 0x6e, 0x0b, 0xbd, 0x00, 0x43, 0x0d, 0xaf, 0x13, 0x27, 0x24, 0xba, 0xee, 0xb4, 0xc9, 0x98, 0x75, + 0xc1, 0x7a, 0xba, 0x3e, 0x7d, 0xfa, 0x1b, 0xbb, 0xe3, 0xef, 0xb8, 0xbb, 0x3b, 0x3e, 0x34, 0xa3, + 0x41, 0xd8, 0xc4, 0x43, 0x3f, 0x08, 0x83, 0x51, 0xe0, 0x91, 0x29, 0x7c, 0x7d, 0xac, 0xc4, 0x1e, + 0x39, 0x21, 0x1e, 0x19, 0xc4, 0xbc, 0x19, 0x4b, 0xb8, 0xfd, 0x87, 0x25, 0x80, 0xa9, 0x30, 0x5c, + 0x8e, 0x82, 0xdb, 0xa4, 0x91, 0xa0, 0x0f, 0x43, 0x8d, 0x0e, 0x5d, 0xd3, 0x49, 0x1c, 0xc6, 0x6d, + 0xe8, 0xe2, 0x0f, 0x4d, 0xf0, 0x37, 0x99, 0x30, 0xdf, 0x44, 0x4f, 0x1c, 0x8a, 0x3d, 0xb1, 0xf5, + 0xdc, 0xc4, 0xd2, 0x1a, 0x7d, 0x7e, 0x91, 0x24, 0xce, 0x34, 0x12, 0xcc, 0x40, 0xb7, 0x61, 0x45, + 0x15, 0xf9, 0x50, 0x89, 0x43, 0xd2, 0x60, 0x1d, 0x1b, 0xba, 0xb8, 0x30, 0x71, 0x94, 0x19, 0x3a, + 0xa1, 0x7b, 0xbe, 0x12, 0x92, 0xc6, 0xf4, 0xb0, 0xe0, 0x5c, 0xa1, 0xff, 0x30, 0xe3, 0x83, 0xb6, + 0x60, 0x20, 0x4e, 0x9c, 0xa4, 0x13, 0x8f, 0x95, 0x19, 0xc7, 0xeb, 0x85, 0x71, 0x64, 0x54, 0xa7, + 0x47, 0x05, 0xcf, 0x01, 0xfe, 0x1f, 0x0b, 0x6e, 0xf6, 0x9f, 0x58, 0x30, 0xaa, 0x91, 0x17, 0xdc, + 0x38, 0x41, 0x3f, 0xd9, 0x35, 0xb8, 0x13, 0xfd, 0x0d, 0x2e, 0x7d, 0x9a, 0x0d, 0xed, 0x49, 0xc1, + 0xac, 0x26, 0x5b, 0x8c, 0x81, 0x6d, 0x43, 0xd5, 0x4d, 0x48, 0x3b, 0x1e, 0x2b, 0x5d, 0x28, 0x3f, + 0x3d, 0x74, 0xf1, 0x4a, 0x51, 0xef, 0x39, 0x3d, 0x22, 0x98, 0x56, 0xe7, 0x29, 0x79, 0xcc, 0xb9, + 0xd8, 0xbf, 0x36, 0x6c, 0xbe, 0x1f, 0x1d, 0x70, 0xf4, 0x1c, 0x0c, 0xc5, 0x41, 0x27, 0x6a, 0x10, + 0x4c, 0xc2, 0x20, 0x1e, 0xb3, 0x2e, 0x94, 0xe9, 0xd4, 0xa3, 0x33, 0x75, 0x45, 0x37, 0x63, 0x13, + 0x07, 0x7d, 0xde, 0x82, 0xe1, 0x26, 0x89, 0x13, 0xd7, 0x67, 0xfc, 0x65, 0xe7, 0x57, 0x8f, 0xdc, + 0x79, 0xd9, 0x38, 0xab, 0x89, 0x4f, 0x9f, 0x11, 0x2f, 0x32, 0x6c, 0x34, 0xc6, 0x38, 0xc5, 0x9f, + 0xae, 0xb8, 0x26, 0x89, 0x1b, 0x91, 0x1b, 0xd2, 0xff, 0x6c, 0xce, 0x18, 0x2b, 0x6e, 0x56, 0x83, + 0xb0, 0x89, 0x87, 0x7c, 0xa8, 0xd2, 0x15, 0x15, 0x8f, 0x55, 0x58, 0xff, 0xe7, 0x8f, 0xd6, 0x7f, + 0x31, 0xa8, 0x74, 0xb1, 0xea, 0xd1, 0xa7, 0xff, 0x62, 0xcc, 0xd9, 0xa0, 0xcf, 0x59, 0x30, 0x26, + 0x56, 0x3c, 0x26, 0x7c, 0x40, 0x6f, 0x6d, 0xb8, 0x09, 0xf1, 0xdc, 0x38, 0x19, 0xab, 0xb2, 0x3e, + 0x4c, 0xf6, 0x37, 0xb7, 0xe6, 0xa2, 0xa0, 0x13, 0x5e, 0x73, 0xfd, 0xe6, 0xf4, 0x05, 0xc1, 0x69, + 0x6c, 0xa6, 0x07, 0x61, 0xdc, 0x93, 0x25, 0xfa, 0x92, 0x05, 0xe7, 0x7d, 0xa7, 0x4d, 0xe2, 0xd0, + 0xa1, 0x9f, 0x96, 0x83, 0xa7, 0x3d, 0xa7, 0xb1, 0xc9, 0x7a, 0x34, 0x70, 0xb8, 0x1e, 0xd9, 0xa2, + 0x47, 0xe7, 0xaf, 0xf7, 0x24, 0x8d, 0xf7, 0x60, 0x8b, 0xbe, 0x66, 0xc1, 0xa9, 0x20, 0x0a, 0x37, + 0x1c, 0x9f, 0x34, 0x25, 0x34, 0x1e, 0x1b, 0x64, 0x4b, 0xef, 0x43, 0x47, 0xfb, 0x44, 0x4b, 0x59, + 0xb2, 0x8b, 0x81, 0xef, 0x26, 0x41, 0xb4, 0x42, 0x92, 0xc4, 0xf5, 0x5b, 0xf1, 0xf4, 0xd9, 0xbb, + 0xbb, 0xe3, 0xa7, 0xba, 0xb0, 0x70, 0x77, 0x7f, 0xd0, 0x4f, 0xc1, 0x50, 0xbc, 0xe3, 0x37, 0x6e, + 0xb9, 0x7e, 0x33, 0xb8, 0x13, 0x8f, 0xd5, 0x8a, 0x58, 0xbe, 0x2b, 0x8a, 0xa0, 0x58, 0x80, 0x9a, + 0x01, 0x36, 0xb9, 0xe5, 0x7f, 0x38, 0x3d, 0x95, 0xea, 0x45, 0x7f, 0x38, 0x3d, 0x99, 0xf6, 0x60, + 0x8b, 0x7e, 0xde, 0x82, 0x91, 0xd8, 0x6d, 0xf9, 0x4e, 0xd2, 0x89, 0xc8, 0x35, 0xb2, 0x13, 0x8f, + 0x01, 0xeb, 0xc8, 0xd5, 0x23, 0x8e, 0x8a, 0x41, 0x72, 0xfa, 0xac, 0xe8, 0xe3, 0x88, 0xd9, 0x1a, + 0xe3, 0x34, 0xdf, 0xbc, 0x85, 0xa6, 0xa7, 0xf5, 0x50, 0xb1, 0x0b, 0x4d, 0x4f, 0xea, 0x9e, 0x2c, + 0xd1, 0x4f, 0xc0, 0x49, 0xde, 0xa4, 0x46, 0x36, 0x1e, 0x1b, 0x66, 0x82, 0xf6, 0xcc, 0xdd, 0xdd, + 0xf1, 0x93, 0x2b, 0x19, 0x18, 0xee, 0xc2, 0x46, 0xaf, 0xc3, 0x78, 0x48, 0xa2, 0xb6, 0x9b, 0x2c, + 0xf9, 0xde, 0x8e, 0x14, 0xdf, 0x8d, 0x20, 0x24, 0x4d, 0xd1, 0x9d, 0x78, 0x6c, 0xe4, 0x82, 0xf5, + 0x74, 0x6d, 0xfa, 0x5d, 0xa2, 0x9b, 0xe3, 0xcb, 0x7b, 0xa3, 0xe3, 0xfd, 0xe8, 0xd9, 0xff, 0xaa, + 0x04, 0x27, 0xb3, 0x8a, 0x13, 0xfd, 0x1d, 0x0b, 0x4e, 0xdc, 0xbe, 0x93, 0xac, 0x06, 0x9b, 0xc4, + 0x8f, 0xa7, 0x77, 0xa8, 0x78, 0x63, 0x2a, 0x63, 0xe8, 0x62, 0xa3, 0x58, 0x15, 0x3d, 0x71, 0x35, + 0xcd, 0xe5, 0x92, 0x9f, 0x44, 0x3b, 0xd3, 0x8f, 0x8a, 0xb7, 0x3b, 0x71, 0xf5, 0xd6, 0xaa, 0x09, + 0xc5, 0xd9, 0x4e, 0x9d, 0xff, 0x8c, 0x05, 0x67, 0xf2, 0x48, 0xa0, 0x93, 0x50, 0xde, 0x24, 0x3b, + 0xdc, 0x2a, 0xc3, 0xf4, 0x27, 0x7a, 0x15, 0xaa, 0x5b, 0x8e, 0xd7, 0x21, 0xc2, 0xba, 0x99, 0x3b, + 0xda, 0x8b, 0xa8, 0x9e, 0x61, 0x4e, 0xf5, 0x47, 0x4b, 0x2f, 0x5a, 0xf6, 0xef, 0x95, 0x61, 0xc8, + 0xd0, 0x6f, 0xf7, 0xc1, 0x62, 0x0b, 0x52, 0x16, 0xdb, 0x62, 0x61, 0xaa, 0xb9, 0xa7, 0xc9, 0x76, + 0x27, 0x63, 0xb2, 0x2d, 0x15, 0xc7, 0x72, 0x4f, 0x9b, 0x0d, 0x25, 0x50, 0x0f, 0x42, 0x6a, 0x91, + 0x53, 0xd5, 0x5f, 0x29, 0xe2, 0x13, 0x2e, 0x49, 0x72, 0xd3, 0x23, 0x77, 0x77, 0xc7, 0xeb, 0xea, + 0x2f, 0xd6, 0x8c, 0xec, 0x6f, 0x59, 0x70, 0xc6, 0xe8, 0xe3, 0x4c, 0xe0, 0x37, 0x5d, 0xf6, 0x69, + 0x2f, 0x40, 0x25, 0xd9, 0x09, 0xa5, 0xd9, 0xaf, 0x46, 0x6a, 0x75, 0x27, 0x24, 0x98, 0x41, 0xa8, + 0xa1, 0xdf, 0x26, 0x71, 0xec, 0xb4, 0x48, 0xd6, 0xd0, 0x5f, 0xe4, 0xcd, 0x58, 0xc2, 0x51, 0x04, + 0xc8, 0x73, 0xe2, 0x64, 0x35, 0x72, 0xfc, 0x98, 0x91, 0x5f, 0x75, 0xdb, 0x44, 0x0c, 0xf0, 0x5f, + 0xe8, 0x6f, 0xc6, 0xd0, 0x27, 0xa6, 0x1f, 0xb9, 0xbb, 0x3b, 0x8e, 0x16, 0xba, 0x28, 0xe1, 0x1c, + 0xea, 0xf6, 0x97, 0x2c, 0x78, 0x24, 0xdf, 0x16, 0x43, 0x4f, 0xc1, 0x00, 0xdf, 0xf2, 0x89, 0xb7, + 0xd3, 0x9f, 0x84, 0xb5, 0x62, 0x01, 0x45, 0x93, 0x50, 0x57, 0x7a, 0x42, 0xbc, 0xe3, 0x29, 0x81, + 0x5a, 0xd7, 0xca, 0x45, 0xe3, 0xd0, 0x41, 0xa3, 0x7f, 0x84, 0xe5, 0xa6, 0x06, 0x8d, 0x6d, 0x92, + 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, 0x9d, 0x46, 0x8f, 0x1b, 0x72, 0x6b, 0x7a, 0x48, 0x50, 0x28, + 0x5f, 0x23, 0x3b, 0x5c, 0x88, 0x3d, 0x03, 0x35, 0x3e, 0x39, 0x83, 0x48, 0x8c, 0xb8, 0x7a, 0xb7, + 0x25, 0xd1, 0x8e, 0x15, 0x06, 0xb2, 0x61, 0x80, 0x09, 0x27, 0xba, 0x58, 0xa9, 0x1a, 0x02, 0xfa, + 0x11, 0x6f, 0xb2, 0x16, 0x2c, 0x20, 0x76, 0x9c, 0xea, 0xce, 0x72, 0x44, 0xd8, 0xc7, 0x6d, 0x5e, + 0x76, 0x89, 0xd7, 0x8c, 0xe9, 0xb6, 0xc1, 0xf1, 0xfd, 0x20, 0x11, 0x3b, 0x00, 0x63, 0xdb, 0x30, + 0xa5, 0x9b, 0xb1, 0x89, 0x43, 0x99, 0x7a, 0xce, 0x1a, 0xf1, 0xf8, 0x88, 0x0a, 0xa6, 0x0b, 0xac, + 0x05, 0x0b, 0x88, 0x7d, 0xb7, 0xc4, 0x36, 0x28, 0x6a, 0xe9, 0x93, 0xfb, 0xb1, 0xbb, 0x8d, 0x52, + 0xb2, 0x72, 0xb9, 0x38, 0xc1, 0x45, 0x7a, 0xef, 0x70, 0xdf, 0xc8, 0x88, 0x4b, 0x5c, 0x28, 0xd7, + 0xbd, 0x77, 0xb9, 0xbf, 0x5d, 0x82, 0xf1, 0xf4, 0x03, 0x5d, 0xd2, 0x96, 0x6e, 0xa9, 0x0c, 0x46, + 0x59, 0x27, 0x86, 0x81, 0x8f, 0x4d, 0xbc, 0x1e, 0x02, 0xab, 0x74, 0x9c, 0x02, 0xcb, 0x94, 0xa7, + 0xe5, 0x7d, 0xe4, 0xe9, 0x53, 0x6a, 0xd4, 0x2b, 0x19, 0x01, 0x96, 0xd6, 0x29, 0x17, 0xa0, 0x12, + 0x27, 0x24, 0x1c, 0xab, 0xa6, 0xe5, 0xd1, 0x4a, 0x42, 0x42, 0xcc, 0x20, 0xf6, 0x7f, 0x2b, 0xc1, + 0xa3, 0xe9, 0x31, 0xd4, 0x2a, 0xe0, 0x7d, 0x29, 0x15, 0xf0, 0x6e, 0x53, 0x05, 0xdc, 0xdb, 0x1d, + 0x7f, 0x67, 0x8f, 0xc7, 0xbe, 0x6b, 0x34, 0x04, 0x9a, 0xcb, 0x8c, 0xe2, 0x64, 0x7a, 0x14, 0xef, + 0xed, 0x8e, 0x3f, 0xde, 0xe3, 0x1d, 0x33, 0xc3, 0xfc, 0x14, 0x0c, 0x44, 0xc4, 0x89, 0x03, 0x5f, + 0x0c, 0xb4, 0xfa, 0x1c, 0x98, 0xb5, 0x62, 0x01, 0xb5, 0xff, 0x6d, 0x3d, 0x3b, 0xd8, 0x73, 0xdc, + 0x09, 0x17, 0x44, 0xc8, 0x85, 0x0a, 0x33, 0xeb, 0xb9, 0x68, 0xb8, 0x76, 0xb4, 0x65, 0x44, 0xd5, + 0x80, 0x22, 0x3d, 0x5d, 0xa3, 0x5f, 0x8d, 0x36, 0x61, 0xc6, 0x02, 0x6d, 0x43, 0xad, 0x21, 0xad, + 0xed, 0x52, 0x11, 0x7e, 0x29, 0x61, 0x6b, 0x6b, 0x8e, 0xc3, 0x54, 0x5e, 0x2b, 0x13, 0x5d, 0x71, + 0x43, 0x04, 0xca, 0x2d, 0x37, 0x11, 0x9f, 0xf5, 0x88, 0xfb, 0xa9, 0x39, 0xd7, 0x78, 0xc5, 0x41, + 0xaa, 0x44, 0xe6, 0xdc, 0x04, 0x53, 0xfa, 0xe8, 0x67, 0x2d, 0x18, 0x8a, 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, 0xfe, 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, 0xda, 0x4e, 0x12, 0xb9, 0xdb, 0xc2, 0x4f, + 0x72, 0x44, 0x7b, 0x7f, 0x91, 0xd1, 0xd2, 0xcc, 0x99, 0xa6, 0xe6, 0x8d, 0x58, 0x30, 0x42, 0x6d, + 0xa8, 0xb6, 0x49, 0xd4, 0x22, 0x63, 0xb5, 0x22, 0x7c, 0xc2, 0x8b, 0x94, 0x94, 0x66, 0x58, 0xa7, + 0xd6, 0x11, 0x6b, 0xc3, 0x9c, 0x0b, 0x7a, 0x15, 0x6a, 0x31, 0xf1, 0x48, 0x83, 0xda, 0x37, 0x75, + 0xc6, 0xf1, 0x87, 0xfb, 0xb4, 0xf5, 0xa8, 0x61, 0xb1, 0x22, 0x1e, 0xe5, 0x0b, 0x4c, 0xfe, 0xc3, + 0x8a, 0x24, 0x1d, 0xc0, 0xd0, 0xeb, 0xb4, 0x5c, 0x7f, 0x0c, 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, 0xac, 0x43, 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, 0xa1, 0xea, 0x6b, 0xea, 0xd4, 0xd5, 0xec, 0x81, + 0x81, 0x40, 0x25, 0xc1, 0xd5, 0x95, 0xa5, 0xeb, 0xb9, 0x32, 0xfb, 0xb5, 0xb4, 0xcc, 0x3e, 0x2a, + 0x8b, 0xff, 0x1f, 0xa4, 0xf4, 0xbf, 0xb4, 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, 0xe1, 0xdb, 0x71, 0xe0, 0x2f, 0x07, 0xae, 0x2f, 0x44, 0x10, 0xdd, 0x08, + 0x9f, 0xbc, 0xbb, 0x3b, 0x3e, 0x4c, 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, 0x46, 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, 0xd3, 0x63, 0x62, 0x84, 0x4e, 0x66, 0x00, 0x31, 0xee, 0xea, 0x0b, 0x7a, 0x15, + 0xea, 0x6d, 0x67, 0xfb, 0x46, 0xd8, 0x74, 0x12, 0xb9, 0x9f, 0xec, 0xed, 0x06, 0xe8, 0x24, 0xae, + 0x37, 0xc1, 0x8f, 0xeb, 0x27, 0xe6, 0xfd, 0x64, 0x29, 0x5a, 0x49, 0x22, 0xd7, 0x6f, 0x71, 0x77, + 0xde, 0xa2, 0x24, 0x83, 0x35, 0x45, 0xfb, 0xcb, 0x56, 0x56, 0x49, 0xa9, 0xd1, 0x89, 0x9c, 0x84, + 0xb4, 0x76, 0xd0, 0x47, 0xa0, 0x4a, 0x37, 0x7e, 0x72, 0x54, 0x6e, 0x15, 0xa9, 0x39, 0x8d, 0x2f, + 0xa1, 0x95, 0x28, 0xfd, 0x17, 0x63, 0xce, 0xd4, 0xfe, 0x4a, 0x3d, 0x6b, 0x2c, 0xb0, 0xc3, 0xdb, + 0x8b, 0x00, 0xad, 0x60, 0x95, 0xb4, 0x43, 0x8f, 0x0e, 0x8b, 0xc5, 0x4e, 0x00, 0x94, 0xaf, 0x63, + 0x4e, 0x41, 0xb0, 0x81, 0x85, 0xfe, 0x92, 0x05, 0xd0, 0x92, 0x73, 0x5e, 0x1a, 0x02, 0x37, 0x8a, + 0x7c, 0x1d, 0xbd, 0xa2, 0x74, 0x5f, 0x14, 0x43, 0x6c, 0x30, 0x47, 0x3f, 0x63, 0x41, 0x2d, 0x91, + 0xdd, 0xe7, 0xaa, 0x71, 0xb5, 0xc8, 0x9e, 0xc8, 0x97, 0xd6, 0x36, 0x91, 0x1a, 0x12, 0xc5, 0x17, + 0xfd, 0x9c, 0x05, 0x10, 0xef, 0xf8, 0x8d, 0xe5, 0xc0, 0x73, 0x1b, 0x3b, 0x42, 0x63, 0xde, 0x2c, + 0xd4, 0x1f, 0xa3, 0xa8, 0x4f, 0x8f, 0xd2, 0xd1, 0xd0, 0xff, 0xb1, 0xc1, 0x19, 0x7d, 0x0c, 0x6a, + 0xb1, 0x98, 0x6e, 0x42, 0x47, 0xae, 0x16, 0xeb, 0x15, 0xe2, 0xb4, 0x85, 0x78, 0x15, 0xff, 0xb0, + 0xe2, 0x89, 0x7e, 0xc1, 0x82, 0x13, 0x61, 0xda, 0xcf, 0x27, 0xd4, 0x61, 0x71, 0x32, 0x20, 0xe3, + 0x47, 0x9c, 0x3e, 0x7d, 0x77, 0x77, 0xfc, 0x44, 0xa6, 0x11, 0x67, 0x7b, 0x41, 0x25, 0xa0, 0x9e, + 0xc1, 0x4b, 0x21, 0xf7, 0x39, 0x0e, 0x6a, 0x09, 0x38, 0x97, 0x05, 0xe2, 0x6e, 0x7c, 0xb4, 0x0c, + 0x67, 0x68, 0xef, 0x76, 0xb8, 0xf9, 0x29, 0xd5, 0x4b, 0xcc, 0x94, 0x61, 0x6d, 0xfa, 0x31, 0x31, + 0x43, 0x98, 0x57, 0x3f, 0x8b, 0x83, 0x73, 0x9f, 0x44, 0xbf, 0x67, 0xc1, 0x63, 0x2e, 0x53, 0x03, + 0xa6, 0xc3, 0x5c, 0x6b, 0x04, 0x71, 0x12, 0x4b, 0x0a, 0x95, 0x15, 0xbd, 0xd4, 0xcf, 0xf4, 0xf7, + 0x8b, 0x37, 0x78, 0x6c, 0x7e, 0x8f, 0x2e, 0xe1, 0x3d, 0x3b, 0x8c, 0x7e, 0x04, 0x46, 0xe4, 0xba, + 0x58, 0xa6, 0x22, 0x98, 0x29, 0xda, 0xfa, 0xf4, 0xa9, 0xbb, 0xbb, 0xe3, 0x23, 0xab, 0x26, 0x00, + 0xa7, 0xf1, 0xec, 0x6f, 0x96, 0x52, 0xe7, 0x21, 0xca, 0x09, 0xc9, 0xc4, 0x4d, 0x43, 0xfa, 0x7f, + 0xa4, 0xf4, 0x2c, 0x54, 0xdc, 0x28, 0xef, 0x92, 0x16, 0x37, 0xaa, 0x29, 0xc6, 0x06, 0x73, 0x6a, + 0x94, 0x9e, 0x72, 0xb2, 0xae, 0x4e, 0x21, 0x01, 0x5f, 0x2d, 0xb2, 0x4b, 0xdd, 0xa7, 0x57, 0xe7, + 0x44, 0xd7, 0x4e, 0x75, 0x81, 0x70, 0x77, 0x97, 0xec, 0x6f, 0xa6, 0xcf, 0x60, 0x8c, 0xc5, 0xdb, + 0xc7, 0xf9, 0xd2, 0xe7, 0x2d, 0x18, 0x8a, 0x02, 0xcf, 0x73, 0xfd, 0x16, 0x15, 0x34, 0x42, 0x5b, + 0x7e, 0xf0, 0x58, 0x14, 0x96, 0x90, 0x28, 0xcc, 0xb4, 0xc5, 0x9a, 0x27, 0x36, 0x3b, 0x60, 0xff, + 0x89, 0x05, 0x63, 0xbd, 0x04, 0x22, 0x22, 0xf0, 0x4e, 0xb9, 0xda, 0x55, 0x74, 0xc5, 0x92, 0x3f, + 0x4b, 0x3c, 0xa2, 0x1c, 0xcf, 0xb5, 0xe9, 0x27, 0xc5, 0x6b, 0xbe, 0x73, 0xb9, 0x37, 0x2a, 0xde, + 0x8b, 0x0e, 0x7a, 0x05, 0x4e, 0x1a, 0xef, 0x15, 0xab, 0x81, 0xa9, 0x4f, 0x4f, 0x50, 0x0b, 0x64, + 0x2a, 0x03, 0xbb, 0xb7, 0x3b, 0xfe, 0x48, 0xb6, 0x4d, 0x48, 0xec, 0x2e, 0x3a, 0xf6, 0xaf, 0x94, + 0xb2, 0x5f, 0x4b, 0x29, 0xdb, 0xb7, 0xac, 0xae, 0xed, 0xfc, 0xfb, 0x8f, 0x43, 0xc1, 0xb1, 0x8d, + 0xbf, 0x0a, 0xe0, 0xe8, 0x8d, 0xf3, 0x00, 0x4f, 0x88, 0xed, 0x7f, 0x5d, 0x81, 0x3d, 0x7a, 0xd6, + 0x87, 0xf5, 0x7c, 0xe0, 0x63, 0xc5, 0xcf, 0x5a, 0xea, 0xc8, 0xa9, 0xcc, 0x16, 0x79, 0xf3, 0xb8, + 0xc6, 0x9e, 0x6f, 0x60, 0x62, 0x1e, 0xa5, 0xa0, 0xdc, 0xd8, 0xe9, 0xc3, 0x2d, 0xf4, 0x55, 0x2b, + 0x7d, 0x68, 0xc6, 0xc3, 0xce, 0xdc, 0x63, 0xeb, 0x93, 0x71, 0x12, 0xc7, 0x3b, 0xa6, 0xcf, 0x6f, + 0x7a, 0x9d, 0xd1, 0x4d, 0x00, 0xac, 0xbb, 0xbe, 0xe3, 0xb9, 0x6f, 0xd0, 0xed, 0x49, 0x95, 0x69, + 0x58, 0x66, 0xb2, 0x5c, 0x56, 0xad, 0xd8, 0xc0, 0x38, 0xff, 0x17, 0x61, 0xc8, 0x78, 0xf3, 0x9c, + 0xe0, 0x8a, 0x33, 0x66, 0x70, 0x45, 0xdd, 0x88, 0x89, 0x38, 0xff, 0x5e, 0x38, 0x99, 0xed, 0xe0, + 0x41, 0x9e, 0xb7, 0xff, 0xd7, 0x60, 0xf6, 0x14, 0x6b, 0x95, 0x44, 0x6d, 0xda, 0xb5, 0xb7, 0x3d, + 0x4b, 0x6f, 0x7b, 0x96, 0xde, 0xf6, 0x2c, 0x99, 0x87, 0x03, 0xc2, 0x6b, 0x32, 0x78, 0x9f, 0xbc, + 0x26, 0x29, 0x3f, 0x50, 0xad, 0x70, 0x3f, 0x90, 0x7d, 0xb7, 0x0a, 0x29, 0x3b, 0x8a, 0x8f, 0xf7, + 0x0f, 0xc2, 0x60, 0x44, 0xc2, 0xe0, 0x06, 0x5e, 0x10, 0x3a, 0x44, 0x07, 0xd0, 0xf3, 0x66, 0x2c, + 0xe1, 0x54, 0xd7, 0x84, 0x4e, 0xb2, 0x21, 0x94, 0x88, 0xd2, 0x35, 0xcb, 0x4e, 0xb2, 0x81, 0x19, + 0x04, 0xbd, 0x17, 0x46, 0x13, 0x27, 0x6a, 0x51, 0x7b, 0x7b, 0x8b, 0x7d, 0x56, 0x71, 0xd6, 0xf9, + 0x88, 0xc0, 0x1d, 0x5d, 0x4d, 0x41, 0x71, 0x06, 0x1b, 0xbd, 0x0e, 0x95, 0x0d, 0xe2, 0xb5, 0xc5, + 0x90, 0xaf, 0x14, 0x27, 0xe3, 0xd9, 0xbb, 0x5e, 0x21, 0x5e, 0x9b, 0x4b, 0x20, 0xfa, 0x0b, 0x33, + 0x56, 0x74, 0xbe, 0xd5, 0x37, 0x3b, 0x71, 0x12, 0xb4, 0xdd, 0x37, 0xa4, 0x8b, 0xef, 0xfd, 0x05, + 0x33, 0xbe, 0x26, 0xe9, 0x73, 0x5f, 0x8a, 0xfa, 0x8b, 0x35, 0x67, 0xd6, 0x8f, 0xa6, 0x1b, 0xb1, + 0x4f, 0xb5, 0x23, 0x3c, 0x75, 0x45, 0xf7, 0x63, 0x56, 0xd2, 0xe7, 0xfd, 0x50, 0x7f, 0xb1, 0xe6, + 0x8c, 0x76, 0xd4, 0xbc, 0x1f, 0x62, 0x7d, 0xb8, 0x51, 0x70, 0x1f, 0xf8, 0x9c, 0xcf, 0x9d, 0xff, + 0x4f, 0x42, 0xb5, 0xb1, 0xe1, 0x44, 0xc9, 0xd8, 0x30, 0x9b, 0x34, 0xca, 0xa7, 0x33, 0x43, 0x1b, + 0x31, 0x87, 0xa1, 0xc7, 0xa1, 0x1c, 0x91, 0x75, 0x16, 0xb7, 0x69, 0x44, 0xf4, 0x60, 0xb2, 0x8e, + 0x69, 0xbb, 0xfd, 0x4b, 0xa5, 0xb4, 0xb9, 0x94, 0x7e, 0x6f, 0x3e, 0xdb, 0x1b, 0x9d, 0x28, 0x96, + 0x7e, 0x1f, 0x63, 0xb6, 0xb3, 0x66, 0x2c, 0xe1, 0xe8, 0x13, 0x16, 0x0c, 0xde, 0x8e, 0x03, 0xdf, + 0x27, 0x89, 0x50, 0x4d, 0x37, 0x0b, 0x1e, 0x8a, 0xab, 0x9c, 0xba, 0xee, 0x83, 0x68, 0xc0, 0x92, + 0x2f, 0xed, 0x2e, 0xd9, 0x6e, 0x78, 0x9d, 0x66, 0x57, 0x90, 0xc6, 0x25, 0xde, 0x8c, 0x25, 0x9c, + 0xa2, 0xba, 0x3e, 0x47, 0xad, 0xa4, 0x51, 0xe7, 0x7d, 0x81, 0x2a, 0xe0, 0xf6, 0x5f, 0x1b, 0x80, + 0xb3, 0xb9, 0x8b, 0x83, 0x1a, 0x32, 0xcc, 0x54, 0xb8, 0xec, 0x7a, 0x44, 0x86, 0x27, 0x31, 0x43, + 0xe6, 0xa6, 0x6a, 0xc5, 0x06, 0x06, 0xfa, 0x69, 0x80, 0xd0, 0x89, 0x9c, 0x36, 0x51, 0x7e, 0xd9, + 0x23, 0xdb, 0x0b, 0xb4, 0x1f, 0xcb, 0x92, 0xa6, 0xde, 0x9b, 0xaa, 0xa6, 0x18, 0x1b, 0x2c, 0xd1, + 0x0b, 0x30, 0x14, 0x11, 0x8f, 0x38, 0x31, 0x0b, 0xfb, 0xcd, 0xe6, 0x30, 0x60, 0x0d, 0xc2, 0x26, + 0x1e, 0x7a, 0x4a, 0x45, 0x72, 0x65, 0x22, 0x5a, 0xd2, 0xd1, 0x5c, 0xe8, 0x4d, 0x0b, 0x46, 0xd7, + 0x5d, 0x8f, 0x68, 0xee, 0x22, 0xe3, 0x60, 0xe9, 0xe8, 0x2f, 0x79, 0xd9, 0xa4, 0xab, 0x25, 0x64, + 0xaa, 0x39, 0xc6, 0x19, 0xf6, 0xf4, 0x33, 0x6f, 0x91, 0x88, 0x89, 0xd6, 0x81, 0xf4, 0x67, 0xbe, + 0xc9, 0x9b, 0xb1, 0x84, 0xa3, 0x29, 0x38, 0x11, 0x3a, 0x71, 0x3c, 0x13, 0x91, 0x26, 0xf1, 0x13, + 0xd7, 0xf1, 0x78, 0x3e, 0x40, 0x4d, 0xc7, 0x03, 0x2f, 0xa7, 0xc1, 0x38, 0x8b, 0x8f, 0x3e, 0x00, + 0x8f, 0x72, 0xc7, 0xc7, 0xa2, 0x1b, 0xc7, 0xae, 0xdf, 0xd2, 0xd3, 0x40, 0xf8, 0x7f, 0xc6, 0x05, + 0xa9, 0x47, 0xe7, 0xf3, 0xd1, 0x70, 0xaf, 0xe7, 0xd1, 0x33, 0x50, 0x8b, 0x37, 0xdd, 0x70, 0x26, + 0x6a, 0xc6, 0xec, 0xd0, 0xa3, 0xa6, 0xbd, 0x8d, 0x2b, 0xa2, 0x1d, 0x2b, 0x0c, 0xd4, 0x80, 0x61, + 0xfe, 0x49, 0x78, 0x28, 0x9a, 0x90, 0x8f, 0xcf, 0xf6, 0x54, 0x8f, 0x22, 0x65, 0x6d, 0x02, 0x3b, + 0x77, 0x2e, 0xc9, 0x23, 0x18, 0x7e, 0x62, 0x70, 0xd3, 0x20, 0x83, 0x53, 0x44, 0xed, 0x5f, 0x2c, + 0xa5, 0x77, 0xdc, 0xe6, 0x22, 0x45, 0x31, 0x5d, 0x8a, 0xc9, 0x4d, 0x27, 0x92, 0xde, 0x98, 0x23, + 0xa6, 0x2d, 0x08, 0xba, 0x37, 0x9d, 0xc8, 0x5c, 0xd4, 0x8c, 0x01, 0x96, 0x9c, 0xd0, 0x6d, 0xa8, + 0x24, 0x9e, 0x53, 0x50, 0x9e, 0x93, 0xc1, 0x51, 0x3b, 0x40, 0x16, 0xa6, 0x62, 0xcc, 0x78, 0xa0, + 0xc7, 0xa8, 0xd5, 0xbf, 0x26, 0x8f, 0x48, 0x84, 0xa1, 0xbe, 0x16, 0x63, 0xd6, 0x6a, 0xff, 0x2a, + 0xe4, 0xc8, 0x55, 0xa5, 0xc8, 0xd0, 0x45, 0x00, 0xba, 0x81, 0x5c, 0x8e, 0xc8, 0xba, 0xbb, 0x2d, + 0x0c, 0x09, 0xb5, 0x76, 0xaf, 0x2b, 0x08, 0x36, 0xb0, 0xe4, 0x33, 0x2b, 0x9d, 0x75, 0xfa, 0x4c, + 0xa9, 0xfb, 0x19, 0x0e, 0xc1, 0x06, 0x16, 0x7a, 0x1e, 0x06, 0xdc, 0xb6, 0xd3, 0x52, 0x21, 0x98, + 0x8f, 0xd1, 0x45, 0x3b, 0xcf, 0x5a, 0xee, 0xed, 0x8e, 0x8f, 0xaa, 0x0e, 0xb1, 0x26, 0x2c, 0x70, + 0xd1, 0xaf, 0x58, 0x30, 0xdc, 0x08, 0xda, 0xed, 0xc0, 0xe7, 0xdb, 0x2e, 0xb1, 0x87, 0xbc, 0x7d, + 0x5c, 0x6a, 0x7e, 0x62, 0xc6, 0x60, 0xc6, 0x37, 0x91, 0x2a, 0x21, 0xcb, 0x04, 0xe1, 0x54, 0xaf, + 0xcc, 0xb5, 0x5d, 0xdd, 0x67, 0x6d, 0xff, 0x86, 0x05, 0xa7, 0xf8, 0xb3, 0xc6, 0x6e, 0x50, 0xe4, + 0x1e, 0x05, 0xc7, 0xfc, 0x5a, 0x5d, 0x1b, 0x64, 0xe5, 0xa5, 0xeb, 0x82, 0xe3, 0xee, 0x4e, 0xa2, + 0x39, 0x38, 0xb5, 0x1e, 0x44, 0x0d, 0x62, 0x0e, 0x84, 0x10, 0x4c, 0x8a, 0xd0, 0xe5, 0x2c, 0x02, + 0xee, 0x7e, 0x06, 0xdd, 0x84, 0x47, 0x8c, 0x46, 0x73, 0x1c, 0xb8, 0x6c, 0x7a, 0x42, 0x50, 0x7b, + 0xe4, 0x72, 0x2e, 0x16, 0xee, 0xf1, 0x74, 0xda, 0x61, 0x52, 0xef, 0xc3, 0x61, 0xf2, 0x1a, 0x9c, + 0x6b, 0x74, 0x8f, 0xcc, 0x56, 0xdc, 0x59, 0x8b, 0xb9, 0xa4, 0xaa, 0x4d, 0x7f, 0x9f, 0x20, 0x70, + 0x6e, 0xa6, 0x17, 0x22, 0xee, 0x4d, 0x03, 0x7d, 0x04, 0x6a, 0x11, 0x61, 0x5f, 0x25, 0x16, 0x89, + 0x38, 0x47, 0xdc, 0x25, 0x6b, 0x0b, 0x94, 0x93, 0xd5, 0xb2, 0x57, 0x34, 0xc4, 0x58, 0x71, 0x44, + 0x77, 0x60, 0x30, 0x74, 0x92, 0xc6, 0x86, 0x48, 0xbf, 0x39, 0x72, 0xfc, 0x8b, 0x62, 0xce, 0x7c, + 0xe0, 0x7a, 0x92, 0x2f, 0x73, 0x26, 0x58, 0x72, 0xa3, 0xd6, 0x48, 0x23, 0x68, 0x87, 0x81, 0x4f, + 0xfc, 0x24, 0x1e, 0x1b, 0xd1, 0xd6, 0xc8, 0x8c, 0x6a, 0xc5, 0x06, 0xc6, 0xf9, 0xf7, 0xc1, 0xa9, + 0xae, 0x85, 0x77, 0x20, 0xe7, 0xca, 0x2c, 0x3c, 0x92, 0x3f, 0xc5, 0x0f, 0xe4, 0x62, 0xf9, 0x47, + 0x99, 0x20, 0x57, 0xc3, 0xec, 0xed, 0xc3, 0x5d, 0xe7, 0x40, 0x99, 0xf8, 0x5b, 0x42, 0xe2, 0x5f, + 0x3e, 0xda, 0x48, 0x5f, 0xf2, 0xb7, 0xf8, 0x0a, 0x65, 0x3e, 0x89, 0x4b, 0xfe, 0x16, 0xa6, 0xb4, + 0xd1, 0x17, 0xad, 0x94, 0xd9, 0xc6, 0x9d, 0x7c, 0x1f, 0x3a, 0x16, 0x3b, 0xbf, 0x6f, 0x4b, 0xce, + 0xfe, 0x37, 0x25, 0xb8, 0xb0, 0x1f, 0x91, 0x3e, 0x86, 0xef, 0x49, 0x18, 0x88, 0xd9, 0xb1, 0xb5, + 0x10, 0xa1, 0x43, 0x74, 0x66, 0xf1, 0x83, 0xec, 0xd7, 0xb0, 0x00, 0x21, 0x0f, 0xca, 0x6d, 0x27, + 0x14, 0xbe, 0x9f, 0xf9, 0xa3, 0xa6, 0xbd, 0xd0, 0xff, 0x8e, 0xb7, 0xe8, 0x84, 0xdc, 0xa3, 0x60, + 0x34, 0x60, 0xca, 0x06, 0x25, 0x50, 0x75, 0xa2, 0xc8, 0x91, 0x67, 0xa4, 0xd7, 0x8a, 0xe1, 0x37, + 0x45, 0x49, 0xf2, 0x23, 0xa6, 0x54, 0x13, 0xe6, 0xcc, 0xec, 0xcf, 0x0e, 0xa6, 0x52, 0x3f, 0xd8, + 0xc1, 0x77, 0x0c, 0x03, 0xc2, 0xe5, 0x63, 0x15, 0x9d, 0x6d, 0xc4, 0x73, 0xf7, 0xd8, 0xae, 0x4e, + 0x64, 0x40, 0x0b, 0x56, 0xe8, 0x33, 0x16, 0xcb, 0x33, 0x96, 0xe9, 0x30, 0x62, 0x2f, 0x75, 0x3c, + 0x69, 0xcf, 0x66, 0xf6, 0xb2, 0x6c, 0xc4, 0x26, 0x77, 0xaa, 0x63, 0x43, 0x9e, 0x31, 0x97, 0xdd, + 0x51, 0xc9, 0x4c, 0x64, 0x09, 0x47, 0xdb, 0x39, 0x07, 0xdc, 0x05, 0xe4, 0xaa, 0xf6, 0x71, 0xa4, + 0xfd, 0x55, 0x0b, 0x4e, 0xb9, 0xd9, 0x93, 0x4a, 0xb1, 0xf3, 0x38, 0x62, 0x08, 0x45, 0xef, 0x83, + 0x50, 0xa5, 0x7c, 0xbb, 0x40, 0xb8, 0xbb, 0x33, 0xa8, 0x09, 0x15, 0xd7, 0x5f, 0x0f, 0x84, 0xc9, + 0x31, 0x7d, 0xb4, 0x4e, 0xcd, 0xfb, 0xeb, 0x81, 0x5e, 0xcd, 0xf4, 0x1f, 0x66, 0xd4, 0xd1, 0x02, + 0x9c, 0x89, 0x84, 0x6f, 0xe8, 0x8a, 0x1b, 0xd3, 0x1d, 0xfc, 0x82, 0xdb, 0x76, 0x13, 0x66, 0x2e, + 0x94, 0xa7, 0xc7, 0xee, 0xee, 0x8e, 0x9f, 0xc1, 0x39, 0x70, 0x9c, 0xfb, 0x14, 0x7a, 0x03, 0x06, + 0x65, 0x62, 0x74, 0xad, 0x88, 0x5d, 0x5c, 0xf7, 0xfc, 0x57, 0x93, 0x69, 0x45, 0xe4, 0x40, 0x4b, + 0x86, 0xf6, 0x9b, 0x43, 0xd0, 0x7d, 0x88, 0x89, 0x3e, 0x0a, 0xf5, 0x48, 0x25, 0x6b, 0x5b, 0x45, + 0x28, 0x57, 0xf9, 0x7d, 0xc5, 0x01, 0xaa, 0x32, 0x5c, 0x74, 0x5a, 0xb6, 0xe6, 0x48, 0xb7, 0x17, + 0xb1, 0x3e, 0xeb, 0x2c, 0x60, 0x6e, 0x0b, 0xae, 0xfa, 0x1c, 0x6b, 0xc7, 0x6f, 0x60, 0xc6, 0x03, + 0x45, 0x30, 0xb0, 0x41, 0x1c, 0x2f, 0xd9, 0x28, 0xc6, 0xe5, 0x7e, 0x85, 0xd1, 0xca, 0xa6, 0xec, + 0xf0, 0x56, 0x2c, 0x38, 0xa1, 0x6d, 0x18, 0xdc, 0xe0, 0x13, 0x40, 0x58, 0xfc, 0x8b, 0x47, 0x1d, + 0xdc, 0xd4, 0xac, 0xd2, 0x9f, 0x5b, 0x34, 0x60, 0xc9, 0x8e, 0x45, 0xc7, 0x18, 0xe7, 0xf7, 0x7c, + 0xe9, 0x16, 0x97, 0xad, 0xd4, 0xff, 0xe1, 0xfd, 0x87, 0x61, 0x38, 0x22, 0x8d, 0xc0, 0x6f, 0xb8, + 0x1e, 0x69, 0x4e, 0x49, 0x77, 0xfa, 0x41, 0x72, 0x5c, 0xd8, 0xae, 0x19, 0x1b, 0x34, 0x70, 0x8a, + 0x22, 0xfa, 0xb4, 0x05, 0xa3, 0x2a, 0xc3, 0x93, 0x7e, 0x10, 0x22, 0xdc, 0xb7, 0x0b, 0x05, 0xe5, + 0x93, 0x32, 0x9a, 0xd3, 0xe8, 0xee, 0xee, 0xf8, 0x68, 0xba, 0x0d, 0x67, 0xf8, 0xa2, 0x57, 0x00, + 0x82, 0x35, 0x1e, 0x02, 0x33, 0x95, 0x08, 0x5f, 0xee, 0x41, 0x5e, 0x75, 0x94, 0x27, 0xbb, 0x49, + 0x0a, 0xd8, 0xa0, 0x86, 0xae, 0x01, 0xf0, 0x65, 0xb3, 0xba, 0x13, 0xca, 0x6d, 0x81, 0x4c, 0x52, + 0x82, 0x15, 0x05, 0xb9, 0xb7, 0x3b, 0xde, 0xed, 0x5b, 0x63, 0x61, 0x06, 0xc6, 0xe3, 0xe8, 0xa7, + 0x60, 0x30, 0xee, 0xb4, 0xdb, 0x8e, 0xf2, 0xf4, 0x16, 0x98, 0x3e, 0xc7, 0xe9, 0x1a, 0xa2, 0x88, + 0x37, 0x60, 0xc9, 0x11, 0xdd, 0xa6, 0x42, 0x35, 0x16, 0x4e, 0x3f, 0xb6, 0x8a, 0xb8, 0x4d, 0x30, + 0xc4, 0xde, 0xe9, 0x3d, 0x32, 0xa2, 0x07, 0xe7, 0xe0, 0xdc, 0xdb, 0x1d, 0x7f, 0x24, 0xdd, 0xbe, + 0x10, 0x88, 0x84, 0xb6, 0x5c, 0x9a, 0xe8, 0xaa, 0xac, 0x93, 0x42, 0x5f, 0x5b, 0xa6, 0xef, 0x3f, + 0xad, 0xeb, 0xa4, 0xb0, 0xe6, 0xde, 0x63, 0x66, 0x3e, 0x8c, 0x16, 0xe1, 0x74, 0x23, 0xf0, 0x93, + 0x28, 0xf0, 0x3c, 0x5e, 0xfc, 0x87, 0xef, 0xd0, 0xb8, 0x27, 0xf8, 0x9d, 0xa2, 0xdb, 0xa7, 0x67, + 0xba, 0x51, 0x70, 0xde, 0x73, 0xb6, 0x9f, 0x8e, 0x0d, 0x14, 0x83, 0xf3, 0x3c, 0x0c, 0x93, 0xed, + 0x84, 0x44, 0xbe, 0xe3, 0xdd, 0xc0, 0x0b, 0xd2, 0x07, 0xca, 0xd6, 0xc0, 0x25, 0xa3, 0x1d, 0xa7, + 0xb0, 0x90, 0xad, 0xdc, 0x12, 0x46, 0x92, 0x26, 0x77, 0x4b, 0x48, 0x27, 0x84, 0xfd, 0xbf, 0x4b, + 0x29, 0x83, 0x6c, 0x35, 0x22, 0x04, 0x05, 0x50, 0xf5, 0x83, 0xa6, 0x92, 0xfd, 0x57, 0x8b, 0x91, + 0xfd, 0xd7, 0x83, 0xa6, 0x51, 0x4c, 0x85, 0xfe, 0x8b, 0x31, 0xe7, 0xc3, 0xaa, 0x4d, 0xc8, 0xb2, + 0x1c, 0x0c, 0x20, 0x36, 0x1a, 0x45, 0x72, 0x56, 0xd5, 0x26, 0x96, 0x4c, 0x46, 0x38, 0xcd, 0x17, + 0x6d, 0x42, 0x75, 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, 0x79, 0xbc, 0x6f, 0xb1, + 0x38, 0xd9, 0x2d, 0xe2, 0xd3, 0x65, 0x6d, 0x06, 0x06, 0xfd, 0x48, 0x26, 0xeb, 0xf0, 0x5d, 0xbd, + 0x4a, 0x5b, 0xdd, 0xa1, 0x14, 0x26, 0x18, 0x09, 0x23, 0x86, 0xe8, 0xe3, 0x56, 0x3a, 0xff, 0xb3, + 0x54, 0xc4, 0x06, 0xc3, 0xcc, 0x81, 0xde, 0x37, 0x95, 0xd4, 0xfe, 0xa2, 0x05, 0x83, 0xd3, 0x4e, + 0x63, 0x33, 0x58, 0x5f, 0x47, 0xcf, 0x40, 0xad, 0xd9, 0x89, 0xcc, 0x54, 0x54, 0xb5, 0xcd, 0x9f, + 0x15, 0xed, 0x58, 0x61, 0xd0, 0x39, 0xbc, 0xee, 0x34, 0x64, 0x26, 0x74, 0x99, 0xcf, 0xe1, 0xcb, + 0xac, 0x05, 0x0b, 0x08, 0x7a, 0x01, 0x86, 0xda, 0xce, 0xb6, 0x7c, 0x38, 0xeb, 0x6e, 0x5f, 0xd4, + 0x20, 0x6c, 0xe2, 0xd9, 0xff, 0xc2, 0x82, 0xb1, 0x69, 0x27, 0x76, 0x1b, 0x53, 0x9d, 0x64, 0x63, + 0xda, 0x4d, 0xd6, 0x3a, 0x8d, 0x4d, 0x92, 0xf0, 0xf4, 0x77, 0xda, 0xcb, 0x4e, 0x4c, 0x97, 0x92, + 0xda, 0xd7, 0xa9, 0x5e, 0xde, 0x10, 0xed, 0x58, 0x61, 0xa0, 0x37, 0x60, 0x28, 0x74, 0xe2, 0xf8, + 0x4e, 0x10, 0x35, 0x31, 0x59, 0x2f, 0xa6, 0xf8, 0xc4, 0x0a, 0x69, 0x44, 0x24, 0xc1, 0x64, 0x5d, + 0x1c, 0x09, 0x6b, 0xfa, 0xd8, 0x64, 0x66, 0x7f, 0xde, 0x82, 0x73, 0xd3, 0xc4, 0x89, 0x48, 0xc4, + 0x6a, 0x55, 0xa8, 0x17, 0x99, 0xf1, 0x82, 0x4e, 0x13, 0xbd, 0x0e, 0xb5, 0x84, 0x36, 0xd3, 0x6e, + 0x59, 0xc5, 0x76, 0x8b, 0x9d, 0xe8, 0xae, 0x0a, 0xe2, 0x58, 0xb1, 0xb1, 0xff, 0xba, 0x05, 0xc3, + 0xec, 0x70, 0x6c, 0x96, 0x24, 0x8e, 0xeb, 0x75, 0x95, 0x74, 0xb2, 0xfa, 0x2c, 0xe9, 0x74, 0x01, + 0x2a, 0x1b, 0x41, 0x9b, 0x64, 0x0f, 0x76, 0xaf, 0x04, 0x74, 0x5b, 0x4d, 0x21, 0xe8, 0x39, 0xfa, + 0xe1, 0x5d, 0x3f, 0x71, 0xe8, 0x12, 0x90, 0xce, 0xd7, 0x13, 0xfc, 0xa3, 0xab, 0x66, 0x6c, 0xe2, + 0xd8, 0xbf, 0x5d, 0x87, 0x41, 0x71, 0xfa, 0xdf, 0x77, 0x09, 0x04, 0xb9, 0xbf, 0x2f, 0xf5, 0xdc, + 0xdf, 0xc7, 0x30, 0xd0, 0x60, 0x05, 0xe3, 0x84, 0x19, 0x79, 0xad, 0x90, 0x70, 0x11, 0x5e, 0x83, + 0x4e, 0x77, 0x8b, 0xff, 0xc7, 0x82, 0x15, 0xfa, 0x82, 0x05, 0x27, 0x1a, 0x81, 0xef, 0x93, 0x86, + 0xb6, 0x71, 0x2a, 0x45, 0x44, 0x05, 0xcc, 0xa4, 0x89, 0xea, 0x93, 0x99, 0x0c, 0x00, 0x67, 0xd9, + 0xa3, 0x97, 0x60, 0x84, 0x8f, 0xd9, 0xcd, 0x94, 0xc7, 0x58, 0x57, 0xfa, 0x31, 0x81, 0x38, 0x8d, + 0x8b, 0x26, 0xb8, 0xe7, 0x5d, 0xd4, 0xd4, 0x19, 0xd0, 0x8e, 0x35, 0xa3, 0x9a, 0x8e, 0x81, 0x81, + 0x22, 0x40, 0x11, 0x59, 0x8f, 0x48, 0xbc, 0x21, 0xa2, 0x23, 0x98, 0x7d, 0x35, 0x78, 0xb8, 0x74, + 0x69, 0xdc, 0x45, 0x09, 0xe7, 0x50, 0x47, 0x9b, 0x62, 0x83, 0x59, 0x2b, 0x42, 0x86, 0x8a, 0xcf, + 0xdc, 0x73, 0x9f, 0x39, 0x0e, 0xd5, 0x78, 0xc3, 0x89, 0x9a, 0xcc, 0xae, 0x2b, 0xf3, 0x14, 0x9d, + 0x15, 0xda, 0x80, 0x79, 0x3b, 0x9a, 0x85, 0x93, 0x99, 0x3a, 0x45, 0xb1, 0xf0, 0xec, 0xaa, 0x74, + 0x8c, 0x4c, 0x85, 0xa3, 0x18, 0x77, 0x3d, 0x61, 0x3a, 0x1f, 0x86, 0xf6, 0x71, 0x3e, 0xec, 0xa8, + 0x18, 0x3c, 0xee, 0x73, 0x7d, 0xb9, 0x90, 0x01, 0xe8, 0x2b, 0xe0, 0xee, 0x73, 0x99, 0x80, 0xbb, + 0x11, 0xd6, 0x81, 0x9b, 0xc5, 0x74, 0xe0, 0xe0, 0xd1, 0x75, 0x0f, 0x32, 0x5a, 0xee, 0xcf, 0x2d, + 0x90, 0xdf, 0x75, 0xc6, 0x69, 0x6c, 0x10, 0x3a, 0x65, 0xd0, 0x7b, 0x61, 0x54, 0x6d, 0xa1, 0x67, + 0x82, 0x8e, 0xcf, 0x03, 0xe5, 0xca, 0xfa, 0x08, 0x17, 0xa7, 0xa0, 0x38, 0x83, 0x8d, 0x26, 0xa1, + 0x4e, 0xc7, 0x89, 0x3f, 0xca, 0x75, 0xad, 0xda, 0xa6, 0x4f, 0x2d, 0xcf, 0x8b, 0xa7, 0x34, 0x0e, + 0x0a, 0xe0, 0x94, 0xe7, 0xc4, 0x09, 0xeb, 0x01, 0xdd, 0x51, 0x1f, 0xb2, 0x58, 0x01, 0x8b, 0xf9, + 0x5f, 0xc8, 0x12, 0xc2, 0xdd, 0xb4, 0xed, 0x6f, 0x55, 0x60, 0x24, 0x25, 0x19, 0x0f, 0xa8, 0xa4, + 0x9f, 0x81, 0x9a, 0xd4, 0x9b, 0xd9, 0xb2, 0x2a, 0x4a, 0xb9, 0x2a, 0x0c, 0xaa, 0xb4, 0xd6, 0xb4, + 0x56, 0xcd, 0x1a, 0x15, 0x86, 0xc2, 0xc5, 0x26, 0x1e, 0x13, 0xca, 0x89, 0x17, 0xcf, 0x78, 0x2e, + 0xf1, 0x13, 0xde, 0xcd, 0x62, 0x84, 0xf2, 0xea, 0xc2, 0x8a, 0x49, 0x54, 0x0b, 0xe5, 0x0c, 0x00, + 0x67, 0xd9, 0xa3, 0x4f, 0x59, 0x30, 0xe2, 0xdc, 0x89, 0x75, 0x55, 0x53, 0x11, 0x5a, 0x77, 0x44, + 0x25, 0x95, 0x2a, 0x94, 0xca, 0x5d, 0xbe, 0xa9, 0x26, 0x9c, 0x66, 0x8a, 0xde, 0xb2, 0x00, 0x91, + 0x6d, 0xd2, 0x90, 0xc1, 0x7f, 0xa2, 0x2f, 0x03, 0x45, 0xec, 0x34, 0x2f, 0x75, 0xd1, 0xe5, 0x52, + 0xbd, 0xbb, 0x1d, 0xe7, 0xf4, 0xc1, 0xfe, 0xa7, 0x65, 0xb5, 0xa0, 0x74, 0xbc, 0xa9, 0x63, 0xc4, + 0xbd, 0x59, 0x87, 0x8f, 0x7b, 0xd3, 0xf1, 0x03, 0xdd, 0x39, 0x90, 0xa9, 0x94, 0xa9, 0xd2, 0x03, + 0x4a, 0x99, 0xfa, 0x19, 0x2b, 0x55, 0x40, 0x68, 0xe8, 0xe2, 0x2b, 0xc5, 0xc6, 0xba, 0x4e, 0xf0, + 0xd8, 0x86, 0x8c, 0x74, 0x4f, 0x87, 0xb4, 0x50, 0x69, 0x6a, 0xa0, 0x1d, 0x48, 0x1a, 0xfe, 0xfb, + 0x32, 0x0c, 0x19, 0x9a, 0x34, 0xd7, 0x2c, 0xb2, 0x1e, 0x32, 0xb3, 0xa8, 0x74, 0x00, 0xb3, 0xe8, + 0xa7, 0xa1, 0xde, 0x90, 0x52, 0xbe, 0x98, 0x12, 0xba, 0x59, 0xdd, 0xa1, 0x05, 0xbd, 0x6a, 0xc2, + 0x9a, 0x27, 0x9a, 0x4b, 0x25, 0xda, 0x08, 0x0d, 0x51, 0x61, 0x1a, 0x22, 0x2f, 0x13, 0x46, 0x68, + 0x8a, 0xee, 0x67, 0x58, 0x9d, 0xa9, 0xd0, 0x15, 0xef, 0x25, 0x23, 0xd2, 0x79, 0x9d, 0xa9, 0xe5, + 0x79, 0xd9, 0x8c, 0x4d, 0x1c, 0xfb, 0x5b, 0x96, 0xfa, 0xb8, 0xf7, 0xa1, 0xa2, 0xc2, 0xed, 0x74, + 0x45, 0x85, 0x4b, 0x85, 0x0c, 0x73, 0x8f, 0x52, 0x0a, 0xd7, 0x61, 0x70, 0x26, 0x68, 0xb7, 0x1d, + 0xbf, 0x89, 0x7e, 0x00, 0x06, 0x1b, 0xfc, 0xa7, 0x70, 0xec, 0xb0, 0xe3, 0x41, 0x01, 0xc5, 0x12, + 0x86, 0x1e, 0x83, 0x8a, 0x13, 0xb5, 0xa4, 0x33, 0x87, 0x85, 0xc2, 0x4c, 0x45, 0xad, 0x18, 0xb3, + 0x56, 0xfb, 0x1f, 0x56, 0x80, 0x9d, 0x40, 0x3b, 0x11, 0x69, 0xae, 0x06, 0xac, 0x84, 0xdf, 0xb1, + 0x1e, 0xaa, 0xe9, 0xcd, 0xd2, 0xc3, 0x7c, 0xb0, 0x66, 0x1c, 0xae, 0x94, 0xef, 0xf3, 0xe1, 0x4a, + 0x8f, 0xf3, 0xb2, 0xca, 0x43, 0x74, 0x5e, 0x66, 0x7f, 0xd6, 0x02, 0xa4, 0xc2, 0x16, 0xf4, 0x81, + 0xf6, 0x24, 0xd4, 0x55, 0x00, 0x83, 0x30, 0xac, 0xb4, 0x88, 0x90, 0x00, 0xac, 0x71, 0xfa, 0xd8, + 0x21, 0x3f, 0x29, 0xe5, 0x77, 0x39, 0x1d, 0x45, 0xcb, 0xa4, 0xbe, 0x10, 0xe7, 0xf6, 0xef, 0x94, + 0xe0, 0x11, 0xae, 0x92, 0x17, 0x1d, 0xdf, 0x69, 0x91, 0x36, 0xed, 0x55, 0xbf, 0x21, 0x0a, 0x0d, + 0xba, 0x35, 0x73, 0x65, 0x54, 0xec, 0x51, 0xd7, 0x2e, 0x5f, 0x73, 0x7c, 0x95, 0xcd, 0xfb, 0x6e, + 0x82, 0x19, 0x71, 0x14, 0x43, 0x4d, 0xd6, 0x8c, 0x17, 0xb2, 0xb8, 0x20, 0x46, 0x4a, 0x2c, 0x09, + 0xbd, 0x49, 0xb0, 0x62, 0x44, 0x0d, 0x57, 0x2f, 0x68, 0x6c, 0x62, 0x12, 0x06, 0x4c, 0xee, 0x1a, + 0x41, 0x89, 0x0b, 0xa2, 0x1d, 0x2b, 0x0c, 0xfb, 0x77, 0x2c, 0xc8, 0x6a, 0x24, 0xa3, 0x56, 0x9a, + 0xb5, 0x67, 0xad, 0xb4, 0x03, 0x14, 0x2b, 0xfb, 0x49, 0x18, 0x72, 0x12, 0x6a, 0x44, 0xf0, 0x6d, + 0x77, 0xf9, 0x70, 0xc7, 0x1a, 0x8b, 0x41, 0xd3, 0x5d, 0x77, 0xd9, 0x76, 0xdb, 0x24, 0x67, 0xff, + 0x8f, 0x0a, 0x9c, 0xea, 0xca, 0xdd, 0x40, 0x2f, 0xc2, 0x70, 0x43, 0x4c, 0x8f, 0x50, 0x3a, 0xb4, + 0xea, 0x66, 0x10, 0x9b, 0x86, 0xe1, 0x14, 0x66, 0x1f, 0x13, 0x74, 0x1e, 0x4e, 0x47, 0x74, 0xa3, + 0xdf, 0x21, 0x53, 0xeb, 0x09, 0x89, 0x56, 0x48, 0x23, 0xf0, 0x9b, 0xbc, 0xa2, 0x5f, 0x79, 0xfa, + 0xd1, 0xbb, 0xbb, 0xe3, 0xa7, 0x71, 0x37, 0x18, 0xe7, 0x3d, 0x83, 0x42, 0x18, 0xf1, 0x4c, 0x1b, + 0x50, 0x6c, 0x00, 0x0e, 0x65, 0x3e, 0x2a, 0x1b, 0x21, 0xd5, 0x8c, 0xd3, 0x0c, 0xd2, 0x86, 0x64, + 0xf5, 0x01, 0x19, 0x92, 0x9f, 0xd4, 0x86, 0x24, 0x3f, 0x7f, 0xff, 0x60, 0xc1, 0xb9, 0x3b, 0xc7, + 0x6d, 0x49, 0xbe, 0x0c, 0x35, 0x19, 0x9b, 0xd4, 0x57, 0x4c, 0x8f, 0x49, 0xa7, 0x87, 0x44, 0x7b, + 0x0a, 0xbe, 0xff, 0x52, 0x14, 0x19, 0x83, 0x79, 0x3d, 0x48, 0xa6, 0x3c, 0x2f, 0xb8, 0x43, 0x95, + 0xf4, 0x8d, 0x98, 0x08, 0x0f, 0x8b, 0x7d, 0xaf, 0x04, 0x39, 0x9b, 0x15, 0xba, 0x1e, 0xb5, 0x65, + 0x90, 0x5a, 0x8f, 0x07, 0xb3, 0x0e, 0xd0, 0x36, 0x8f, 0xdf, 0xe2, 0x3a, 0xf0, 0x03, 0x45, 0x6f, + 0xb6, 0x74, 0x48, 0x97, 0x4a, 0x7d, 0x50, 0x61, 0x5d, 0x17, 0x01, 0xb4, 0x41, 0x27, 0x02, 0xdb, + 0xd5, 0xf1, 0xb0, 0xb6, 0xfb, 0xb0, 0x81, 0x45, 0xf7, 0xde, 0xae, 0x1f, 0x27, 0x8e, 0xe7, 0x5d, + 0x71, 0xfd, 0x44, 0x38, 0x11, 0x95, 0xb2, 0x9f, 0xd7, 0x20, 0x6c, 0xe2, 0x9d, 0x7f, 0x8f, 0xf1, + 0xfd, 0x0e, 0xf2, 0xdd, 0x37, 0xe0, 0xdc, 0x9c, 0x9b, 0xa8, 0x74, 0x0c, 0x35, 0xdf, 0xa8, 0xbd, + 0xa6, 0xd2, 0x8b, 0xac, 0x9e, 0xe9, 0x45, 0x46, 0x3a, 0x44, 0x29, 0x9d, 0xbd, 0x91, 0x4d, 0x87, + 0xb0, 0x5f, 0x84, 0x33, 0x73, 0x6e, 0x72, 0xd9, 0xf5, 0xc8, 0x01, 0x99, 0xd8, 0xbf, 0x35, 0x00, + 0xc3, 0x66, 0x42, 0xdf, 0x41, 0x32, 0xa4, 0x3e, 0x4f, 0x4d, 0x32, 0xf1, 0x76, 0xae, 0x3a, 0x5c, + 0xbb, 0x75, 0xe4, 0xec, 0xc2, 0xfc, 0x11, 0x33, 0xac, 0x32, 0xcd, 0x13, 0x9b, 0x1d, 0x40, 0x77, + 0xa0, 0xba, 0xce, 0xc2, 0xf5, 0xcb, 0x45, 0x44, 0x20, 0xe4, 0x8d, 0xa8, 0x5e, 0x8e, 0x3c, 0xe0, + 0x9f, 0xf3, 0xa3, 0x9a, 0x34, 0x4a, 0xe7, 0x80, 0x19, 0x21, 0xa6, 0x22, 0xfb, 0x4b, 0x61, 0xf4, + 0x52, 0x09, 0xd5, 0x43, 0xa8, 0x84, 0x94, 0x80, 0x1e, 0x78, 0x40, 0x02, 0x9a, 0xa5, 0x5e, 0x24, + 0x1b, 0xcc, 0xce, 0x13, 0x31, 0xf1, 0x83, 0x6c, 0x10, 0x8c, 0xd4, 0x8b, 0x14, 0x18, 0x67, 0xf1, + 0xd1, 0xc7, 0x94, 0x88, 0xaf, 0x15, 0xe1, 0x7f, 0x35, 0x67, 0xf4, 0x71, 0x4b, 0xf7, 0xcf, 0x96, + 0x60, 0x74, 0xce, 0xef, 0x2c, 0xcf, 0x2d, 0x77, 0xd6, 0x3c, 0xb7, 0x71, 0x8d, 0xec, 0x50, 0x11, + 0xbe, 0x49, 0x76, 0xe6, 0x67, 0xc5, 0x0a, 0x52, 0x73, 0xe6, 0x1a, 0x6d, 0xc4, 0x1c, 0x46, 0x85, + 0xd1, 0xba, 0xeb, 0xb7, 0x48, 0x14, 0x46, 0xae, 0x70, 0x8d, 0x1a, 0xc2, 0xe8, 0xb2, 0x06, 0x61, + 0x13, 0x8f, 0xd2, 0x0e, 0xee, 0xf8, 0x24, 0xca, 0x1a, 0xbc, 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, 0xf0, 0xc8, 0x04, 0xe0, 0xaf, 0xf0, 0x66, 0x2c, 0xe1, 0x14, 0x75, 0x93, 0xec, + 0xcc, 0xd2, 0xdd, 0x71, 0x26, 0x0f, 0xe7, 0x1a, 0x6f, 0xc6, 0x12, 0xce, 0x4a, 0x16, 0xa6, 0x87, + 0xe3, 0xbb, 0xae, 0x64, 0x61, 0xba, 0xfb, 0x3d, 0xf6, 0xd9, 0xbf, 0x6c, 0xc1, 0xb0, 0x19, 0x96, + 0x85, 0x5a, 0x19, 0x5b, 0x78, 0xa9, 0xab, 0xe2, 0xed, 0x8f, 0xe7, 0x5d, 0x01, 0xd6, 0x72, 0x93, + 0x20, 0x8c, 0x9f, 0x25, 0x7e, 0xcb, 0xf5, 0x09, 0x3b, 0x6d, 0xe7, 0xe1, 0x5c, 0xa9, 0x98, 0xaf, + 0x99, 0xa0, 0x49, 0x0e, 0x61, 0x4c, 0xdb, 0xb7, 0xe0, 0x54, 0x57, 0xf2, 0x55, 0x1f, 0x26, 0xc8, + 0xbe, 0xa9, 0xaf, 0x36, 0x86, 0x21, 0x4a, 0x58, 0x96, 0xcd, 0x99, 0x81, 0x53, 0x7c, 0x21, 0x51, + 0x4e, 0x2b, 0x8d, 0x0d, 0xd2, 0x56, 0x09, 0x75, 0xcc, 0x0f, 0x7f, 0x33, 0x0b, 0xc4, 0xdd, 0xf8, + 0xf6, 0xe7, 0x2c, 0x18, 0x49, 0xe5, 0xc3, 0x15, 0x64, 0x2c, 0xb1, 0x95, 0x16, 0xb0, 0x28, 0x41, + 0x16, 0x2a, 0x5d, 0x66, 0xca, 0x54, 0xaf, 0x34, 0x0d, 0xc2, 0x26, 0x9e, 0xfd, 0xc5, 0x12, 0xd4, + 0x64, 0xa4, 0x45, 0x1f, 0x5d, 0xf9, 0x8c, 0x05, 0x23, 0xea, 0xec, 0x83, 0x39, 0xd5, 0x4a, 0x45, + 0x24, 0x2f, 0xd0, 0x1e, 0xa8, 0x6d, 0xb9, 0xbf, 0x1e, 0x68, 0xcb, 0x1d, 0x9b, 0xcc, 0x70, 0x9a, + 0x37, 0xba, 0x09, 0x10, 0xef, 0xc4, 0x09, 0x69, 0x1b, 0xee, 0x3d, 0xdb, 0x58, 0x71, 0x13, 0x8d, + 0x20, 0x22, 0x74, 0x7d, 0x5d, 0x0f, 0x9a, 0x64, 0x45, 0x61, 0x6a, 0x13, 0x4a, 0xb7, 0x61, 0x83, + 0x92, 0xfd, 0xf7, 0x4b, 0x70, 0x32, 0xdb, 0x25, 0xf4, 0x41, 0x18, 0x96, 0xdc, 0x8d, 0xeb, 0xcc, + 0x64, 0x78, 0xc9, 0x30, 0x36, 0x60, 0xf7, 0x76, 0xc7, 0xc7, 0xbb, 0xaf, 0x93, 0x9b, 0x30, 0x51, + 0x70, 0x8a, 0x18, 0x3f, 0x80, 0x12, 0x27, 0xa5, 0xd3, 0x3b, 0x53, 0x61, 0x28, 0x4e, 0x91, 0x8c, + 0x03, 0x28, 0x13, 0x8a, 0x33, 0xd8, 0x68, 0x19, 0xce, 0x18, 0x2d, 0xd7, 0x89, 0xdb, 0xda, 0x58, + 0x0b, 0x22, 0xb9, 0x03, 0x7b, 0x4c, 0x07, 0x80, 0x75, 0xe3, 0xe0, 0xdc, 0x27, 0xa9, 0xb6, 0x6f, + 0x38, 0xa1, 0xd3, 0x70, 0x93, 0x1d, 0xe1, 0xaf, 0x54, 0xb2, 0x69, 0x46, 0xb4, 0x63, 0x85, 0x61, + 0x2f, 0x42, 0xa5, 0xcf, 0x19, 0xd4, 0x97, 0xe5, 0xff, 0x32, 0xd4, 0x28, 0x39, 0x69, 0xde, 0x15, + 0x41, 0x32, 0x80, 0x9a, 0xbc, 0x91, 0x04, 0xd9, 0x50, 0x76, 0x1d, 0x79, 0xc6, 0xa7, 0x5e, 0x6b, + 0x3e, 0x8e, 0x3b, 0x6c, 0x33, 0x4d, 0x81, 0xe8, 0x49, 0x28, 0x93, 0xed, 0x30, 0x7b, 0x98, 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, 0xae, 0xae, 0x40, 0x41, 0x9b, + 0x52, 0x76, 0x5b, 0x45, 0x84, 0x46, 0x49, 0xba, 0x3d, 0xa4, 0x76, 0x07, 0x40, 0x27, 0x06, 0x16, + 0x25, 0x5f, 0x2e, 0x40, 0xa5, 0x11, 0x88, 0xa4, 0xe5, 0x9a, 0x26, 0xc3, 0x84, 0x36, 0x83, 0xd8, + 0xb7, 0x60, 0xf4, 0x9a, 0x1f, 0xdc, 0x61, 0xf5, 0xdb, 0x59, 0xb9, 0x32, 0x4a, 0x78, 0x9d, 0xfe, + 0xc8, 0x9a, 0x08, 0x0c, 0x8a, 0x39, 0x4c, 0xd5, 0x71, 0x2a, 0xf5, 0xaa, 0xe3, 0x64, 0x7f, 0xdc, + 0x82, 0x61, 0x95, 0x61, 0x34, 0xb7, 0xb5, 0x49, 0xe9, 0xb6, 0xa2, 0xa0, 0x13, 0x66, 0xe9, 0xb2, + 0x4b, 0x8a, 0x30, 0x87, 0x99, 0xa9, 0x77, 0xa5, 0x7d, 0x52, 0xef, 0x2e, 0x40, 0x65, 0xd3, 0xf5, + 0x9b, 0xd9, 0x5b, 0x37, 0xae, 0xb9, 0x7e, 0x13, 0x33, 0x08, 0xed, 0xc2, 0x49, 0xd5, 0x05, 0xa9, + 0x10, 0x5e, 0x84, 0xe1, 0xb5, 0x8e, 0xeb, 0x35, 0x65, 0x1d, 0xb6, 0x8c, 0x47, 0x65, 0xda, 0x80, + 0xe1, 0x14, 0x26, 0xdd, 0xd7, 0xad, 0xb9, 0xbe, 0x13, 0xed, 0x2c, 0x6b, 0x0d, 0xa4, 0x84, 0xd2, + 0xb4, 0x82, 0x60, 0x03, 0xcb, 0x7e, 0xb3, 0x0c, 0xa3, 0xe9, 0x3c, 0xab, 0x3e, 0xb6, 0x57, 0x4f, + 0x42, 0x95, 0xa5, 0x5e, 0x65, 0x3f, 0x2d, 0x2f, 0x5d, 0xc6, 0x61, 0x28, 0x86, 0x01, 0x5e, 0xb4, + 0xa1, 0x98, 0x1b, 0x6b, 0x54, 0x27, 0x95, 0x1f, 0x86, 0xc5, 0x9d, 0x89, 0x3a, 0x11, 0x82, 0x15, + 0xfa, 0x94, 0x05, 0x83, 0x41, 0x68, 0xd6, 0xff, 0xf9, 0x40, 0x91, 0x39, 0x68, 0x22, 0xa9, 0x46, + 0x58, 0xc4, 0xea, 0xd3, 0xcb, 0xcf, 0x21, 0x59, 0x9f, 0xff, 0x51, 0x18, 0x36, 0x31, 0xf7, 0x33, + 0x8a, 0x6b, 0xa6, 0x51, 0xfc, 0x19, 0x73, 0x52, 0x88, 0x2c, 0xbb, 0x3e, 0x96, 0xdb, 0x0d, 0xa8, + 0x36, 0x54, 0xa0, 0xc0, 0xa1, 0xaa, 0x77, 0xaa, 0x2a, 0x0a, 0xec, 0xb0, 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, 0xce, 0xc2, + 0x54, 0x32, 0x66, 0x79, 0xff, 0x64, 0x4c, 0xfb, 0xad, 0x12, 0x9c, 0xea, 0x9a, 0x54, 0xe8, 0x0d, + 0xa8, 0x46, 0xf4, 0x2d, 0xc5, 0xeb, 0x2d, 0x14, 0x96, 0x3e, 0x19, 0xcf, 0x37, 0xb5, 0xde, 0x4d, + 0xb7, 0x63, 0xce, 0x12, 0x5d, 0x05, 0xa4, 0xc3, 0x59, 0x94, 0xa7, 0x92, 0xbf, 0xf2, 0x79, 0xf1, + 0x28, 0x9a, 0xea, 0xc2, 0xc0, 0x39, 0x4f, 0xa1, 0x97, 0xb2, 0x0e, 0xcf, 0x72, 0xfa, 0x7c, 0x73, + 0x2f, 0xdf, 0xa5, 0xfd, 0xcf, 0x4a, 0x30, 0x92, 0x2a, 0xc7, 0x84, 0x3c, 0xa8, 0x11, 0x8f, 0x39, + 0xff, 0xa5, 0xb2, 0x39, 0x6a, 0x75, 0x63, 0xa5, 0x20, 0x2f, 0x09, 0xba, 0x58, 0x71, 0x78, 0x38, + 0x0e, 0xe1, 0x5f, 0x84, 0x61, 0xd9, 0xa1, 0x0f, 0x38, 0x6d, 0x4f, 0x0c, 0xa0, 0x9a, 0xa3, 0x97, + 0x0c, 0x18, 0x4e, 0x61, 0xda, 0xbf, 0x5b, 0x86, 0x31, 0x7e, 0x5a, 0xd2, 0x54, 0x33, 0x6f, 0x51, + 0xee, 0xb7, 0xfe, 0xb2, 0x2e, 0x9a, 0xc6, 0x07, 0x72, 0xed, 0xa8, 0x97, 0x09, 0xe4, 0x33, 0xea, + 0x2b, 0x82, 0xeb, 0x2b, 0x99, 0x08, 0x2e, 0x6e, 0x76, 0xb7, 0x8e, 0xa9, 0x47, 0xdf, 0x5d, 0x21, + 0x5d, 0xbf, 0x5a, 0x82, 0x13, 0x99, 0x9b, 0x1a, 0xd0, 0x9b, 0xe9, 0xe2, 0xbe, 0x56, 0x11, 0x3e, + 0xf5, 0x3d, 0x8b, 0xf7, 0x1f, 0xac, 0xc4, 0xef, 0x03, 0x5a, 0x2a, 0xf6, 0x1f, 0x94, 0x60, 0x34, + 0x7d, 0xc5, 0xc4, 0x43, 0x38, 0x52, 0xef, 0x86, 0x3a, 0xab, 0xa2, 0xce, 0xae, 0xce, 0xe4, 0x2e, + 0x79, 0x5e, 0xb0, 0x5a, 0x36, 0x62, 0x0d, 0x7f, 0x28, 0x2a, 0x27, 0xdb, 0x7f, 0xd7, 0x82, 0xb3, + 0xfc, 0x2d, 0xb3, 0xf3, 0xf0, 0xaf, 0xe4, 0x8d, 0xee, 0xab, 0xc5, 0x76, 0x30, 0x53, 0xec, 0x6f, + 0xbf, 0xf1, 0x65, 0x57, 0xf6, 0x89, 0xde, 0xa6, 0xa7, 0xc2, 0x43, 0xd8, 0xd9, 0x03, 0x4d, 0x06, + 0xfb, 0x0f, 0xca, 0xa0, 0x6f, 0x29, 0x44, 0xae, 0xc8, 0x85, 0x2c, 0xa4, 0xe8, 0xe1, 0xca, 0x8e, + 0xdf, 0xd0, 0xf7, 0x21, 0xd6, 0x32, 0xa9, 0x90, 0x3f, 0x6f, 0xc1, 0x90, 0xeb, 0xbb, 0x89, 0xeb, + 0xb0, 0x6d, 0x74, 0x31, 0x37, 0xa8, 0x29, 0x76, 0xf3, 0x9c, 0x72, 0x10, 0x99, 0xe7, 0x38, 0x8a, + 0x19, 0x36, 0x39, 0xa3, 0x0f, 0x8b, 0x20, 0xeb, 0x72, 0x61, 0x59, 0xbc, 0xb5, 0x4c, 0x64, 0x75, + 0x48, 0x0d, 0xaf, 0x24, 0x2a, 0x28, 0xf9, 0x1d, 0x53, 0x52, 0xaa, 0x7e, 0xae, 0xbe, 0x2f, 0x9a, + 0x36, 0x63, 0xce, 0xc8, 0x8e, 0x01, 0x75, 0x8f, 0xc5, 0x01, 0x03, 0x58, 0x27, 0xa1, 0xee, 0x74, + 0x92, 0xa0, 0x4d, 0x87, 0x49, 0x1c, 0x35, 0xe9, 0x10, 0x5d, 0x09, 0xc0, 0x1a, 0xc7, 0x7e, 0xb3, + 0x0a, 0x99, 0xe4, 0x44, 0xb4, 0x6d, 0xde, 0xb0, 0x69, 0x15, 0x7b, 0xc3, 0xa6, 0xea, 0x4c, 0xde, + 0x2d, 0x9b, 0xa8, 0x05, 0xd5, 0x70, 0xc3, 0x89, 0xa5, 0x59, 0xfd, 0xb2, 0xda, 0xc7, 0xd1, 0xc6, + 0x7b, 0xbb, 0xe3, 0x3f, 0xd1, 0x9f, 0xd7, 0x95, 0xce, 0xd5, 0x49, 0x5e, 0x94, 0x44, 0xb3, 0x66, + 0x34, 0x30, 0xa7, 0x7f, 0x90, 0x3b, 0xe4, 0x3e, 0x21, 0xca, 0xc5, 0x63, 0x12, 0x77, 0xbc, 0x44, + 0xcc, 0x86, 0x97, 0x0b, 0x5c, 0x65, 0x9c, 0xb0, 0x4e, 0xab, 0xe7, 0xff, 0xb1, 0xc1, 0x14, 0x7d, + 0x10, 0xea, 0x71, 0xe2, 0x44, 0xc9, 0x21, 0x13, 0x61, 0xd5, 0xa0, 0xaf, 0x48, 0x22, 0x58, 0xd3, + 0x43, 0xaf, 0xb0, 0x1a, 0xb0, 0x6e, 0xbc, 0x71, 0xc8, 0xdc, 0x08, 0x59, 0x2f, 0x56, 0x50, 0xc0, + 0x06, 0x35, 0x74, 0x11, 0x80, 0xcd, 0x6d, 0x1e, 0x10, 0x58, 0x63, 0x5e, 0x26, 0x25, 0x0a, 0xb1, + 0x82, 0x60, 0x03, 0xcb, 0xfe, 0x21, 0x48, 0xd7, 0x85, 0x40, 0xe3, 0xb2, 0x0c, 0x05, 0xf7, 0x42, + 0xb3, 0x1c, 0x87, 0x54, 0xc5, 0x88, 0xdf, 0xb0, 0xc0, 0x2c, 0x5e, 0x81, 0x5e, 0xe7, 0x55, 0x32, + 0xac, 0x22, 0x4e, 0x0e, 0x0d, 0xba, 0x13, 0x8b, 0x4e, 0x98, 0x39, 0xc2, 0x96, 0xa5, 0x32, 0xce, + 0xbf, 0x07, 0x6a, 0x12, 0x7a, 0x20, 0xa3, 0xee, 0x63, 0x70, 0x3a, 0x7b, 0xff, 0xb8, 0x38, 0x75, + 0xda, 0xdf, 0xf5, 0x23, 0xfd, 0x39, 0xa5, 0x5e, 0xfe, 0x9c, 0x3e, 0xee, 0x59, 0xfd, 0x4d, 0x0b, + 0x2e, 0xec, 0x77, 0x4d, 0x3a, 0x7a, 0x0c, 0x2a, 0x77, 0x9c, 0x48, 0x16, 0xe7, 0x66, 0x82, 0xf2, + 0x96, 0x13, 0xf9, 0x98, 0xb5, 0xa2, 0x1d, 0x18, 0xe0, 0x51, 0x63, 0xc2, 0x5a, 0x7f, 0xb9, 0xd8, + 0x4b, 0xdb, 0xaf, 0x11, 0x63, 0xbb, 0xc0, 0x23, 0xd6, 0xb0, 0x60, 0x68, 0x7f, 0xdb, 0x02, 0xb4, + 0xb4, 0x45, 0xa2, 0xc8, 0x6d, 0x1a, 0x71, 0x6e, 0xec, 0xda, 0x15, 0xe3, 0x7a, 0x15, 0x33, 0x15, + 0x36, 0x73, 0xed, 0x8a, 0xf1, 0x2f, 0xff, 0xda, 0x95, 0xd2, 0xc1, 0xae, 0x5d, 0x41, 0x4b, 0x70, + 0xb6, 0xcd, 0xb7, 0x1b, 0xfc, 0x2a, 0x03, 0xbe, 0xf7, 0x50, 0x89, 0x67, 0xe7, 0xee, 0xee, 0x8e, + 0x9f, 0x5d, 0xcc, 0x43, 0xc0, 0xf9, 0xcf, 0xd9, 0xef, 0x01, 0xc4, 0xc3, 0xdb, 0x66, 0xf2, 0x62, + 0x95, 0x7a, 0xba, 0x5f, 0xec, 0x2f, 0x57, 0xe1, 0x44, 0xa6, 0x74, 0x2b, 0xdd, 0xea, 0x75, 0x07, + 0x47, 0x1d, 0x59, 0x7f, 0x77, 0x77, 0xaf, 0xaf, 0x70, 0x2b, 0x1f, 0xaa, 0xae, 0x1f, 0x76, 0x92, + 0x62, 0x72, 0x4d, 0x79, 0x27, 0xe6, 0x29, 0x41, 0xc3, 0x5d, 0x4c, 0xff, 0x62, 0xce, 0xa6, 0xc8, + 0xe0, 0xad, 0x94, 0x31, 0x5e, 0x79, 0x40, 0xee, 0x80, 0x4f, 0xe8, 0x50, 0xaa, 0x6a, 0x11, 0x8e, + 0xc5, 0xcc, 0x64, 0x39, 0xee, 0xa3, 0xf6, 0x5f, 0x2f, 0xc1, 0x90, 0xf1, 0xd1, 0xd0, 0x2f, 0xa5, + 0x4b, 0x3b, 0x59, 0xc5, 0xbd, 0x12, 0xa3, 0x3f, 0xa1, 0x8b, 0x37, 0xf1, 0x57, 0x7a, 0xaa, 0xbb, + 0xaa, 0xd3, 0xbd, 0xdd, 0xf1, 0x93, 0x99, 0xba, 0x4d, 0xa9, 0x4a, 0x4f, 0xe7, 0x3f, 0x0a, 0x27, + 0x32, 0x64, 0x72, 0x5e, 0x79, 0x35, 0x7d, 0xbd, 0xfc, 0x11, 0xdd, 0x52, 0xe6, 0x90, 0x7d, 0x9d, + 0x0e, 0x99, 0x48, 0xb7, 0x0b, 0x3c, 0xd2, 0x87, 0x0f, 0x36, 0x93, 0x55, 0x5b, 0xea, 0x33, 0xab, + 0xf6, 0x69, 0xa8, 0x85, 0x81, 0xe7, 0x36, 0x5c, 0x55, 0xad, 0x90, 0xe5, 0xf1, 0x2e, 0x8b, 0x36, + 0xac, 0xa0, 0xe8, 0x0e, 0xd4, 0xd5, 0x4d, 0xfc, 0xc2, 0xbf, 0x5d, 0xd4, 0xa1, 0x8f, 0x32, 0x5a, + 0xf4, 0x0d, 0xfb, 0x9a, 0x17, 0xb2, 0x61, 0x80, 0x29, 0x41, 0x99, 0x22, 0xc0, 0x7c, 0xef, 0x4c, + 0x3b, 0xc6, 0x58, 0x40, 0xec, 0xaf, 0xd5, 0xe1, 0x4c, 0x5e, 0xfd, 0x6c, 0xf4, 0x11, 0x18, 0xe0, + 0x7d, 0x2c, 0xe6, 0x8a, 0x86, 0x3c, 0x1e, 0x73, 0x8c, 0xa0, 0xe8, 0x16, 0xfb, 0x8d, 0x05, 0x4f, + 0xc1, 0xdd, 0x73, 0xd6, 0xc4, 0x0c, 0x39, 0x1e, 0xee, 0x0b, 0x8e, 0xe6, 0xbe, 0xe0, 0x70, 0xee, + 0x9e, 0xb3, 0x86, 0xb6, 0xa1, 0xda, 0x72, 0x13, 0xe2, 0x08, 0x27, 0xc2, 0xad, 0x63, 0x61, 0x4e, + 0x1c, 0x6e, 0xa5, 0xb1, 0x9f, 0x98, 0x33, 0x44, 0x5f, 0xb5, 0xe0, 0xc4, 0x5a, 0x3a, 0x85, 0x5e, + 0x08, 0x4f, 0xe7, 0x18, 0x6a, 0xa4, 0xa7, 0x19, 0xf1, 0x7b, 0x87, 0x32, 0x8d, 0x38, 0xdb, 0x1d, + 0xf4, 0x49, 0x0b, 0x06, 0xd7, 0x5d, 0xcf, 0x28, 0x97, 0x7b, 0x0c, 0x1f, 0xe7, 0x32, 0x63, 0xa0, + 0x77, 0x1c, 0xfc, 0x7f, 0x8c, 0x25, 0xe7, 0x5e, 0x9a, 0x6a, 0xe0, 0xa8, 0x9a, 0x6a, 0xf0, 0x01, + 0x69, 0xaa, 0x4f, 0x5b, 0x50, 0x57, 0x23, 0x2d, 0xd2, 0xa2, 0x3f, 0x78, 0x8c, 0x9f, 0x9c, 0x7b, + 0x4e, 0xd4, 0x5f, 0xac, 0x99, 0xa3, 0x2f, 0x58, 0x30, 0xe4, 0xbc, 0xd1, 0x89, 0x48, 0x93, 0x6c, + 0x05, 0x61, 0x2c, 0x2e, 0x2d, 0x7c, 0xb5, 0xf8, 0xce, 0x4c, 0x51, 0x26, 0xb3, 0x64, 0x6b, 0x29, + 0x8c, 0x45, 0xfa, 0x92, 0x6e, 0xc0, 0x66, 0x17, 0xec, 0xdd, 0x12, 0x8c, 0xef, 0x43, 0x01, 0xbd, + 0x08, 0xc3, 0x41, 0xd4, 0x72, 0x7c, 0xf7, 0x0d, 0xb3, 0x26, 0x86, 0xb2, 0xb2, 0x96, 0x0c, 0x18, + 0x4e, 0x61, 0x9a, 0x89, 0xdb, 0xa5, 0x7d, 0x12, 0xb7, 0x2f, 0x40, 0x25, 0x22, 0x61, 0x90, 0xdd, + 0x2c, 0xb0, 0xd4, 0x01, 0x06, 0x41, 0x8f, 0x43, 0xd9, 0x09, 0x5d, 0x11, 0x88, 0xa6, 0xf6, 0x40, + 0x53, 0xcb, 0xf3, 0x98, 0xb6, 0xa7, 0xea, 0x48, 0x54, 0xef, 0x4b, 0x1d, 0x09, 0xaa, 0x06, 0xc4, + 0xd9, 0xc5, 0x80, 0x56, 0x03, 0xe9, 0x33, 0x05, 0xfb, 0xad, 0x32, 0x3c, 0xbe, 0xe7, 0x7c, 0xd1, + 0x71, 0x78, 0xd6, 0x1e, 0x71, 0x78, 0x72, 0x78, 0x4a, 0xfb, 0x0d, 0x4f, 0xb9, 0xc7, 0xf0, 0x7c, + 0x92, 0x2e, 0x03, 0x59, 0x4b, 0xa4, 0x98, 0x6b, 0xe7, 0x7a, 0x95, 0x26, 0x11, 0x2b, 0x40, 0x42, + 0xb1, 0xe6, 0x4b, 0xf7, 0x00, 0xa9, 0xa4, 0xe5, 0x6a, 0x11, 0x6a, 0xa0, 0x67, 0x6d, 0x11, 0x3e, + 0xf7, 0x7b, 0x65, 0x42, 0xdb, 0xbf, 0x50, 0x82, 0x27, 0xfb, 0x90, 0xde, 0xe6, 0x2c, 0xb6, 0xfa, + 0x9c, 0xc5, 0xdf, 0xdd, 0x9f, 0xc9, 0xfe, 0xab, 0x16, 0x9c, 0xef, 0xad, 0x3c, 0xd0, 0x73, 0x30, + 0xb4, 0x16, 0x39, 0x7e, 0x63, 0x83, 0x5d, 0xa5, 0x29, 0x07, 0x85, 0x8d, 0xb5, 0x6e, 0xc6, 0x26, + 0x0e, 0xdd, 0xde, 0xf2, 0x98, 0x04, 0x03, 0x43, 0x26, 0x99, 0xd2, 0xed, 0xed, 0x6a, 0x16, 0x88, + 0xbb, 0xf1, 0xed, 0x3f, 0x2b, 0xe5, 0x77, 0x8b, 0x1b, 0x19, 0x07, 0xf9, 0x4e, 0xe2, 0x2b, 0x94, + 0xfa, 0x90, 0x25, 0xe5, 0xfb, 0x2d, 0x4b, 0x2a, 0xbd, 0x64, 0x09, 0x9a, 0x85, 0x93, 0xc6, 0x55, + 0x2b, 0x3c, 0x71, 0x98, 0x07, 0xdc, 0xaa, 0x6a, 0x1a, 0xcb, 0x19, 0x38, 0xee, 0x7a, 0x02, 0x3d, + 0x03, 0x35, 0xd7, 0x8f, 0x49, 0xa3, 0x13, 0xf1, 0x40, 0x6f, 0x23, 0x59, 0x6b, 0x5e, 0xb4, 0x63, + 0x85, 0x61, 0xff, 0x72, 0x09, 0xce, 0xf5, 0xb4, 0xb3, 0xee, 0x93, 0xec, 0x32, 0x3f, 0x47, 0xe5, + 0xfe, 0x7c, 0x0e, 0x73, 0x90, 0xaa, 0xfb, 0x0e, 0xd2, 0x1f, 0xf6, 0x9e, 0x98, 0xd4, 0xe6, 0xfe, + 0x9e, 0x1d, 0xa5, 0x97, 0x60, 0xc4, 0x09, 0x43, 0x8e, 0xc7, 0xe2, 0x35, 0x33, 0xd5, 0x74, 0xa6, + 0x4c, 0x20, 0x4e, 0xe3, 0xf6, 0xa5, 0x3d, 0xff, 0xd8, 0x82, 0x3a, 0x26, 0xeb, 0x5c, 0x3a, 0xa0, + 0xdb, 0x62, 0x88, 0xac, 0x22, 0xea, 0x6e, 0xd2, 0x81, 0x8d, 0x5d, 0x56, 0x8f, 0x32, 0x6f, 0xb0, + 0xbb, 0xaf, 0xe4, 0x29, 0x1d, 0xe8, 0x4a, 0x1e, 0x75, 0x29, 0x4b, 0xb9, 0xf7, 0xa5, 0x2c, 0xf6, + 0xd7, 0x07, 0xe9, 0xeb, 0x85, 0xc1, 0x4c, 0x44, 0x9a, 0x31, 0xfd, 0xbe, 0x9d, 0xc8, 0x13, 0x93, + 0x44, 0x7d, 0xdf, 0x1b, 0x78, 0x01, 0xd3, 0xf6, 0xd4, 0x51, 0x4c, 0xe9, 0x40, 0xb5, 0x44, 0xca, + 0xfb, 0xd6, 0x12, 0x79, 0x09, 0x46, 0xe2, 0x78, 0x63, 0x39, 0x72, 0xb7, 0x9c, 0x84, 0x5c, 0x23, + 0x3b, 0xc2, 0xca, 0xd2, 0xf9, 0xff, 0x2b, 0x57, 0x34, 0x10, 0xa7, 0x71, 0xd1, 0x1c, 0x9c, 0xd2, + 0x15, 0x3d, 0x48, 0x94, 0xb0, 0xe8, 0x7e, 0x3e, 0x13, 0x54, 0xb2, 0xaf, 0xae, 0x01, 0x22, 0x10, + 0x70, 0xf7, 0x33, 0x54, 0xbe, 0xa5, 0x1a, 0x69, 0x47, 0x06, 0xd2, 0xf2, 0x2d, 0x45, 0x87, 0xf6, + 0xa5, 0xeb, 0x09, 0xb4, 0x08, 0xa7, 0xf9, 0xc4, 0x98, 0x0a, 0x43, 0xe3, 0x8d, 0x06, 0xd3, 0xf5, + 0x0e, 0xe7, 0xba, 0x51, 0x70, 0xde, 0x73, 0xe8, 0x05, 0x18, 0x52, 0xcd, 0xf3, 0xb3, 0xe2, 0x14, + 0x41, 0x79, 0x31, 0x14, 0x99, 0xf9, 0x26, 0x36, 0xf1, 0xd0, 0x07, 0xe0, 0x51, 0xfd, 0x97, 0xa7, + 0x80, 0xf1, 0xa3, 0xb5, 0x59, 0x51, 0x2c, 0x49, 0x5d, 0x01, 0x32, 0x97, 0x8b, 0xd6, 0xc4, 0xbd, + 0x9e, 0x47, 0x6b, 0x70, 0x5e, 0x81, 0x2e, 0xf9, 0x09, 0xcb, 0xe7, 0x88, 0xc9, 0xb4, 0x13, 0x93, + 0x1b, 0x91, 0x27, 0xee, 0x50, 0x55, 0xb7, 0x33, 0xce, 0xb9, 0xc9, 0x95, 0x3c, 0x4c, 0xbc, 0x80, + 0xf7, 0xa0, 0x82, 0x26, 0xa1, 0x4e, 0x7c, 0x67, 0xcd, 0x23, 0x4b, 0x33, 0xf3, 0xac, 0xe8, 0x92, + 0x71, 0x92, 0x77, 0x49, 0x02, 0xb0, 0xc6, 0x51, 0x11, 0xa6, 0xc3, 0x3d, 0x6f, 0x0a, 0x5d, 0x86, + 0x33, 0xad, 0x46, 0x48, 0x6d, 0x0f, 0xb7, 0x41, 0xa6, 0x1a, 0x2c, 0xa0, 0x8e, 0x7e, 0x18, 0x5e, + 0x88, 0x52, 0x85, 0x4f, 0xcf, 0xcd, 0x2c, 0x77, 0xe1, 0xe0, 0xdc, 0x27, 0x59, 0xe0, 0x65, 0x14, + 0x6c, 0xef, 0x8c, 0x9d, 0xce, 0x04, 0x5e, 0xd2, 0x46, 0xcc, 0x61, 0xe8, 0x2a, 0x20, 0x16, 0x8b, + 0x7f, 0x25, 0x49, 0x42, 0x65, 0xec, 0x8c, 0x9d, 0x61, 0xaf, 0xa4, 0xc2, 0xc8, 0x2e, 0x77, 0x61, + 0xe0, 0x9c, 0xa7, 0xec, 0xff, 0x60, 0xc1, 0x88, 0x5a, 0xaf, 0xf7, 0x21, 0x1b, 0xc5, 0x4b, 0x67, + 0xa3, 0xcc, 0x1d, 0x5d, 0xe2, 0xb1, 0x9e, 0xf7, 0x08, 0x69, 0xfe, 0xd9, 0x21, 0x00, 0x2d, 0x15, + 0x95, 0x42, 0xb2, 0x7a, 0x2a, 0xa4, 0x87, 0x56, 0x22, 0xe5, 0x55, 0x58, 0xa9, 0x3e, 0xd8, 0x0a, + 0x2b, 0x2b, 0x70, 0x56, 0x9a, 0x0b, 0xfc, 0xac, 0xe8, 0x4a, 0x10, 0x2b, 0x01, 0x57, 0x9b, 0x7e, + 0x5c, 0x10, 0x3a, 0x3b, 0x9f, 0x87, 0x84, 0xf3, 0x9f, 0x4d, 0x59, 0x29, 0x83, 0xfb, 0x59, 0x29, + 0x7a, 0x4d, 0x2f, 0xac, 0xcb, 0xbb, 0x3e, 0x32, 0x6b, 0x7a, 0xe1, 0xf2, 0x0a, 0xd6, 0x38, 0xf9, + 0x82, 0xbd, 0x5e, 0x90, 0x60, 0x87, 0x03, 0x0b, 0x76, 0x29, 0x62, 0x86, 0x7a, 0x8a, 0x18, 0xe9, + 0x93, 0x1e, 0xee, 0xe9, 0x93, 0x7e, 0x2f, 0x8c, 0xba, 0xfe, 0x06, 0x89, 0xdc, 0x84, 0x34, 0xd9, + 0x5a, 0x60, 0xe2, 0xa7, 0xa6, 0xd5, 0xfa, 0x7c, 0x0a, 0x8a, 0x33, 0xd8, 0x69, 0xb9, 0x38, 0xda, + 0x87, 0x5c, 0xec, 0xa1, 0x8d, 0x4e, 0x14, 0xa3, 0x8d, 0x4e, 0x1e, 0x5d, 0x1b, 0x9d, 0x3a, 0x56, + 0x6d, 0x84, 0x0a, 0xd1, 0x46, 0x7d, 0x09, 0x7a, 0x63, 0xfb, 0x77, 0x66, 0x9f, 0xed, 0x5f, 0x2f, + 0x55, 0x74, 0xf6, 0xd0, 0xaa, 0x28, 0x5f, 0xcb, 0x3c, 0x72, 0x28, 0x2d, 0xf3, 0xe9, 0x12, 0x9c, + 0xd5, 0x72, 0x98, 0xce, 0x7e, 0x77, 0x9d, 0x4a, 0x22, 0x76, 0x5d, 0x14, 0x3f, 0xb7, 0x31, 0x92, + 0xa3, 0x74, 0x9e, 0x95, 0x82, 0x60, 0x03, 0x8b, 0xe5, 0x18, 0x91, 0x88, 0x95, 0xdb, 0xcd, 0x0a, + 0xe9, 0x19, 0xd1, 0x8e, 0x15, 0x06, 0x9d, 0x5f, 0xf4, 0xb7, 0xc8, 0xdb, 0xcc, 0x16, 0x95, 0x9b, + 0xd1, 0x20, 0x6c, 0xe2, 0xa1, 0xa7, 0x39, 0x13, 0x26, 0x20, 0xa8, 0xa0, 0x1e, 0x16, 0xf7, 0xc7, + 0x4a, 0x99, 0xa0, 0xa0, 0xb2, 0x3b, 0x2c, 0x99, 0xac, 0xda, 0xdd, 0x1d, 0x16, 0x02, 0xa5, 0x30, + 0xec, 0xff, 0x69, 0xc1, 0xb9, 0xdc, 0xa1, 0xb8, 0x0f, 0xca, 0x77, 0x3b, 0xad, 0x7c, 0x57, 0x8a, + 0xda, 0x6e, 0x18, 0x6f, 0xd1, 0x43, 0x11, 0xff, 0x3b, 0x0b, 0x46, 0x35, 0xfe, 0x7d, 0x78, 0x55, + 0x37, 0xfd, 0xaa, 0xc5, 0xed, 0xac, 0xea, 0x5d, 0xef, 0xf6, 0xbb, 0x25, 0x50, 0x85, 0x1e, 0xa7, + 0x1a, 0xb2, 0x8c, 0xee, 0x3e, 0x27, 0x89, 0x3b, 0x30, 0xc0, 0x0e, 0x42, 0xe3, 0x62, 0x82, 0x3c, + 0xd2, 0xfc, 0xd9, 0xa1, 0xaa, 0x3e, 0x64, 0x66, 0x7f, 0x63, 0x2c, 0x18, 0xb2, 0x62, 0xd0, 0x6e, + 0x4c, 0xa5, 0x79, 0x53, 0xa4, 0x65, 0xe9, 0x62, 0xd0, 0xa2, 0x1d, 0x2b, 0x0c, 0xaa, 0x1e, 0xdc, + 0x46, 0xe0, 0xcf, 0x78, 0x4e, 0x2c, 0xef, 0x48, 0x54, 0xea, 0x61, 0x5e, 0x02, 0xb0, 0xc6, 0x61, + 0x67, 0xa4, 0x6e, 0x1c, 0x7a, 0xce, 0x8e, 0xb1, 0x7f, 0x36, 0xea, 0x13, 0x28, 0x10, 0x36, 0xf1, + 0xec, 0x36, 0x8c, 0xa5, 0x5f, 0x62, 0x96, 0xac, 0xb3, 0x00, 0xc5, 0xbe, 0x86, 0x73, 0x12, 0xea, + 0x0e, 0x7b, 0x6a, 0xa1, 0xe3, 0x64, 0xaf, 0x36, 0x9f, 0x92, 0x00, 0xac, 0x71, 0xec, 0x5f, 0xb3, + 0xe0, 0x74, 0xce, 0xa0, 0x15, 0x98, 0xf6, 0x96, 0x68, 0x69, 0x93, 0xa7, 0xd8, 0x7f, 0x10, 0x06, + 0x9b, 0x64, 0xdd, 0x91, 0x21, 0x70, 0x86, 0x6c, 0x9f, 0xe5, 0xcd, 0x58, 0xc2, 0xed, 0xff, 0x6e, + 0xc1, 0x89, 0x74, 0x5f, 0x63, 0x96, 0x4a, 0xc2, 0x87, 0xc9, 0x8d, 0x1b, 0xc1, 0x16, 0x89, 0x76, + 0xe8, 0x9b, 0x5b, 0x99, 0x54, 0x92, 0x2e, 0x0c, 0x9c, 0xf3, 0x14, 0x2b, 0xf3, 0xda, 0x54, 0xa3, + 0x2d, 0x67, 0xe4, 0xcd, 0x22, 0x67, 0xa4, 0xfe, 0x98, 0xe6, 0x71, 0xb9, 0x62, 0x89, 0x4d, 0xfe, + 0xf6, 0xb7, 0x2b, 0xa0, 0xf2, 0x62, 0x59, 0xfc, 0x51, 0x41, 0xd1, 0x5b, 0x07, 0xcd, 0x20, 0x52, + 0x93, 0xa1, 0xb2, 0x57, 0x40, 0x00, 0xf7, 0x92, 0x98, 0xae, 0x4b, 0xf5, 0x86, 0xab, 0x1a, 0x84, + 0x4d, 0x3c, 0xda, 0x13, 0xcf, 0xdd, 0x22, 0xfc, 0xa1, 0x81, 0x74, 0x4f, 0x16, 0x24, 0x00, 0x6b, + 0x1c, 0xda, 0x93, 0xa6, 0xbb, 0xbe, 0x2e, 0xb6, 0xfc, 0xaa, 0x27, 0x74, 0x74, 0x30, 0x83, 0xf0, + 0xca, 0xdd, 0xc1, 0xa6, 0xb0, 0x82, 0x8d, 0xca, 0xdd, 0xc1, 0x26, 0x66, 0x10, 0x6a, 0xb7, 0xf9, + 0x41, 0xd4, 0x66, 0x57, 0xcf, 0x37, 0x15, 0x17, 0x61, 0xfd, 0x2a, 0xbb, 0xed, 0x7a, 0x37, 0x0a, + 0xce, 0x7b, 0x8e, 0xce, 0xc0, 0x30, 0x22, 0x4d, 0xb7, 0x91, 0x98, 0xd4, 0x20, 0x3d, 0x03, 0x97, + 0xbb, 0x30, 0x70, 0xce, 0x53, 0x68, 0x0a, 0x4e, 0xc8, 0xbc, 0x66, 0x59, 0xb5, 0x66, 0x28, 0x5d, + 0x25, 0x03, 0xa7, 0xc1, 0x38, 0x8b, 0x4f, 0xa5, 0x5a, 0x5b, 0x14, 0xb6, 0x62, 0xc6, 0xb2, 0x21, + 0xd5, 0x64, 0xc1, 0x2b, 0xac, 0x30, 0xec, 0x4f, 0x94, 0xa9, 0x16, 0xee, 0x51, 0xd0, 0xed, 0xbe, + 0x45, 0x0b, 0xa6, 0x67, 0x64, 0xa5, 0x8f, 0x19, 0xf9, 0x3c, 0x0c, 0xdf, 0x8e, 0x03, 0x5f, 0x45, + 0xe2, 0x55, 0x7b, 0x46, 0xe2, 0x19, 0x58, 0xf9, 0x91, 0x78, 0x03, 0x45, 0x45, 0xe2, 0x0d, 0x1e, + 0x32, 0x12, 0xef, 0x9b, 0x55, 0x50, 0x57, 0x88, 0x5c, 0x27, 0xc9, 0x9d, 0x20, 0xda, 0x74, 0xfd, + 0x16, 0xcb, 0x07, 0xff, 0xaa, 0x05, 0xc3, 0x7c, 0xbd, 0x2c, 0x98, 0x99, 0x54, 0xeb, 0x05, 0xdd, + 0x4d, 0x91, 0x62, 0x36, 0xb1, 0x6a, 0x30, 0xca, 0x5c, 0xd1, 0x69, 0x82, 0x70, 0xaa, 0x47, 0xe8, + 0xa3, 0x00, 0xd2, 0x3f, 0xba, 0x2e, 0x45, 0xe6, 0x7c, 0x31, 0xfd, 0xc3, 0x64, 0x5d, 0xdb, 0xc0, + 0xab, 0x8a, 0x09, 0x36, 0x18, 0xa2, 0x4f, 0xeb, 0x2c, 0x33, 0x1e, 0xb2, 0xff, 0xe1, 0x63, 0x19, + 0x9b, 0x7e, 0x72, 0xcc, 0x30, 0x0c, 0xba, 0x7e, 0x8b, 0xce, 0x13, 0x11, 0xb1, 0xf4, 0xae, 0xbc, + 0x5a, 0x0a, 0x0b, 0x81, 0xd3, 0x9c, 0x76, 0x3c, 0xc7, 0x6f, 0x90, 0x68, 0x9e, 0xa3, 0x9b, 0x17, + 0x53, 0xb3, 0x06, 0x2c, 0x09, 0x75, 0x5d, 0xbe, 0x52, 0xed, 0xe7, 0xf2, 0x95, 0xf3, 0xef, 0x83, + 0x53, 0x5d, 0x1f, 0xf3, 0x40, 0x29, 0x65, 0x87, 0xcf, 0x46, 0xb3, 0xff, 0xf9, 0x80, 0x56, 0x5a, + 0xd7, 0x83, 0x26, 0xbf, 0x02, 0x24, 0xd2, 0x5f, 0x54, 0xd8, 0xb8, 0x05, 0x4e, 0x11, 0xe3, 0x72, + 0x6b, 0xd5, 0x88, 0x4d, 0x96, 0x74, 0x8e, 0x86, 0x4e, 0x44, 0xfc, 0xe3, 0x9e, 0xa3, 0xcb, 0x8a, + 0x09, 0x36, 0x18, 0xa2, 0x8d, 0x54, 0x4e, 0xc9, 0xe5, 0xa3, 0xe7, 0x94, 0xb0, 0x2a, 0x53, 0x79, + 0x55, 0xfb, 0xbf, 0x60, 0xc1, 0xa8, 0x9f, 0x9a, 0xb9, 0xc5, 0x84, 0x91, 0xe6, 0xaf, 0x0a, 0x7e, + 0x03, 0x55, 0xba, 0x0d, 0x67, 0xf8, 0xe7, 0xa9, 0xb4, 0xea, 0x01, 0x55, 0x9a, 0xbe, 0x4b, 0x68, + 0xa0, 0xd7, 0x5d, 0x42, 0xc8, 0x57, 0x97, 0xa9, 0x0d, 0x16, 0x7e, 0x99, 0x1a, 0xe4, 0x5c, 0xa4, + 0x76, 0x0b, 0xea, 0x8d, 0x88, 0x38, 0xc9, 0x21, 0xef, 0xd5, 0x62, 0x07, 0xf4, 0x33, 0x92, 0x00, + 0xd6, 0xb4, 0xec, 0xff, 0x53, 0x81, 0x93, 0x72, 0x44, 0x64, 0x08, 0x3a, 0xd5, 0x8f, 0x9c, 0xaf, + 0x36, 0x6e, 0x95, 0x7e, 0xbc, 0x22, 0x01, 0x58, 0xe3, 0x50, 0x7b, 0xac, 0x13, 0x93, 0xa5, 0x90, + 0xf8, 0x0b, 0xee, 0x5a, 0x2c, 0xce, 0x39, 0xd5, 0x42, 0xb9, 0xa1, 0x41, 0xd8, 0xc4, 0xa3, 0xc6, + 0x38, 0xb7, 0x8b, 0xe3, 0x6c, 0xfa, 0x8a, 0xb0, 0xb7, 0xb1, 0x84, 0xa3, 0x5f, 0xcc, 0xad, 0x30, + 0x5b, 0x4c, 0xe2, 0x56, 0x57, 0xe4, 0xfd, 0x01, 0xaf, 0x62, 0xfc, 0xdb, 0x16, 0x9c, 0xe5, 0xad, + 0x72, 0x24, 0x6f, 0x84, 0x4d, 0x27, 0x21, 0x71, 0x31, 0x15, 0xdf, 0x73, 0xfa, 0xa7, 0x9d, 0xbc, + 0x79, 0x6c, 0x71, 0x7e, 0x6f, 0xd0, 0x9b, 0x16, 0x9c, 0xd8, 0x4c, 0xd5, 0xfc, 0x90, 0xaa, 0xe3, + 0xa8, 0xe9, 0xf8, 0x29, 0xa2, 0x7a, 0xa9, 0xa5, 0xdb, 0x63, 0x9c, 0xe5, 0x6e, 0xff, 0x99, 0x05, + 0xa6, 0x18, 0xbd, 0xff, 0xa5, 0x42, 0x0e, 0x6e, 0x0a, 0x4a, 0xeb, 0xb2, 0xda, 0xd3, 0xba, 0x7c, + 0x1c, 0xca, 0x1d, 0xb7, 0x29, 0xf6, 0x17, 0xfa, 0xf4, 0x75, 0x7e, 0x16, 0xd3, 0x76, 0xfb, 0x9f, + 0x54, 0xb5, 0xdf, 0x42, 0xe4, 0x45, 0x7d, 0x4f, 0xbc, 0xf6, 0xba, 0x2a, 0x36, 0xc6, 0xdf, 0xfc, + 0x7a, 0x57, 0xb1, 0xb1, 0x1f, 0x3b, 0x78, 0xda, 0x1b, 0x1f, 0xa0, 0x5e, 0xb5, 0xc6, 0x06, 0xf7, + 0xc9, 0x79, 0xbb, 0x0d, 0x35, 0xba, 0x05, 0x63, 0x0e, 0xc8, 0x5a, 0xaa, 0x53, 0xb5, 0x2b, 0xa2, + 0xfd, 0xde, 0xee, 0xf8, 0x8f, 0x1e, 0xbc, 0x5b, 0xf2, 0x69, 0xac, 0xe8, 0xa3, 0x18, 0xea, 0xf4, + 0x37, 0x4b, 0xcf, 0x13, 0x9b, 0xbb, 0x1b, 0x4a, 0x66, 0x4a, 0x40, 0x21, 0xb9, 0x7f, 0x9a, 0x0f, + 0xf2, 0xa1, 0xce, 0x6e, 0xad, 0x65, 0x4c, 0xf9, 0x1e, 0x70, 0x59, 0x25, 0xc9, 0x49, 0xc0, 0xbd, + 0xdd, 0xf1, 0x97, 0x0e, 0xce, 0x54, 0x3d, 0x8e, 0x35, 0x0b, 0xfb, 0x8b, 0x15, 0x3d, 0x77, 0x45, + 0x8d, 0xb9, 0xef, 0x89, 0xb9, 0xfb, 0x62, 0x66, 0xee, 0x5e, 0xe8, 0x9a, 0xbb, 0xa3, 0xfa, 0x76, + 0xd5, 0xd4, 0x6c, 0xbc, 0xdf, 0x86, 0xc0, 0xfe, 0xfe, 0x06, 0x66, 0x01, 0xbd, 0xde, 0x71, 0x23, + 0x12, 0x2f, 0x47, 0x1d, 0xdf, 0xf5, 0x5b, 0x6c, 0x3a, 0xd6, 0x4c, 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, 0x9a, 0x60, + 0x5e, 0xb3, 0x4b, 0xcd, 0x09, 0x7e, 0x37, 0x30, 0x87, 0xa1, 0x3b, 0x30, 0xb8, 0xc6, 0xef, 0xc9, + 0x2b, 0xa6, 0x8e, 0xb9, 0xb8, 0x74, 0x8f, 0xdd, 0x86, 0x22, 0x6f, 0xe0, 0xbb, 0xa7, 0x7f, 0x62, + 0xc9, 0xcd, 0xfe, 0x46, 0x05, 0x4e, 0x64, 0x2e, 0x92, 0x4d, 0x55, 0x4b, 0x2d, 0xed, 0x5b, 0x2d, + 0xf5, 0x43, 0x00, 0x4d, 0x12, 0x7a, 0xc1, 0x0e, 0x33, 0xc7, 0x2a, 0x07, 0x36, 0xc7, 0x94, 0x05, + 0x3f, 0xab, 0xa8, 0x60, 0x83, 0xa2, 0x28, 0x54, 0xc6, 0x8b, 0xaf, 0x66, 0x0a, 0x95, 0x19, 0xb7, + 0x1d, 0x0c, 0xdc, 0xdf, 0xdb, 0x0e, 0x5c, 0x38, 0xc1, 0xbb, 0xa8, 0xb2, 0x6f, 0x0f, 0x91, 0x64, + 0xcb, 0xf2, 0x17, 0x66, 0xd3, 0x64, 0x70, 0x96, 0xee, 0x83, 0xbc, 0x27, 0x1a, 0xbd, 0x1b, 0xea, + 0xf2, 0x3b, 0xc7, 0x63, 0x75, 0x5d, 0xc1, 0x40, 0x4e, 0x03, 0x76, 0x7f, 0xb3, 0xf8, 0x69, 0x7f, + 0xbe, 0x44, 0xad, 0x67, 0xfe, 0x4f, 0x55, 0xa2, 0x79, 0x0a, 0x06, 0x9c, 0x4e, 0xb2, 0x11, 0x74, + 0xdd, 0xb5, 0x37, 0xc5, 0x5a, 0xb1, 0x80, 0xa2, 0x05, 0xa8, 0x34, 0x75, 0x75, 0x91, 0x83, 0x8c, + 0xa2, 0x76, 0x44, 0x3a, 0x09, 0xc1, 0x8c, 0x0a, 0x7a, 0x0c, 0x2a, 0x89, 0xd3, 0x92, 0x89, 0x4e, + 0x2c, 0xb9, 0x75, 0xd5, 0x69, 0xc5, 0x98, 0xb5, 0x9a, 0x4a, 0xb3, 0xb2, 0x8f, 0xd2, 0x7c, 0x09, + 0x46, 0x62, 0xb7, 0xe5, 0x3b, 0x49, 0x27, 0x22, 0xc6, 0xe1, 0x9a, 0x8e, 0x97, 0x30, 0x81, 0x38, + 0x8d, 0x6b, 0xff, 0xd6, 0x30, 0x9c, 0x59, 0x99, 0x59, 0x94, 0x35, 0xb3, 0x8f, 0x2d, 0x57, 0x29, + 0x8f, 0xc7, 0xfd, 0xcb, 0x55, 0xea, 0xc1, 0xdd, 0x33, 0x72, 0x95, 0x3c, 0x23, 0x57, 0x29, 0x9d, + 0x38, 0x52, 0x2e, 0x22, 0x71, 0x24, 0xaf, 0x07, 0xfd, 0x24, 0x8e, 0x1c, 0x5b, 0xf2, 0xd2, 0x9e, + 0x1d, 0x3a, 0x50, 0xf2, 0x92, 0xca, 0xec, 0x2a, 0x24, 0xa4, 0xbf, 0xc7, 0xa7, 0xca, 0xcd, 0xec, + 0x52, 0x59, 0x35, 0x3c, 0x5d, 0x45, 0x08, 0xd8, 0x57, 0x8b, 0xef, 0x40, 0x1f, 0x59, 0x35, 0x22, + 0x63, 0xc6, 0xcc, 0xe4, 0x1a, 0x2c, 0x22, 0x93, 0x2b, 0xaf, 0x3b, 0xfb, 0x66, 0x72, 0xbd, 0x04, + 0x23, 0x0d, 0x2f, 0xf0, 0xc9, 0x72, 0x14, 0x24, 0x41, 0x23, 0xf0, 0x84, 0x31, 0xad, 0x44, 0xc2, + 0x8c, 0x09, 0xc4, 0x69, 0xdc, 0x5e, 0x69, 0x60, 0xf5, 0xa3, 0xa6, 0x81, 0xc1, 0x03, 0x4a, 0x03, + 0xfb, 0x39, 0x9d, 0xb0, 0x3c, 0xc4, 0xbe, 0xc8, 0x87, 0x8a, 0xff, 0x22, 0xfd, 0x64, 0x2d, 0xa3, + 0xb7, 0xf8, 0x65, 0x77, 0xd4, 0x1c, 0x9d, 0x09, 0xda, 0xd4, 0xdc, 0x1a, 0x66, 0x43, 0xf2, 0xda, + 0x31, 0x4c, 0xd8, 0x5b, 0x2b, 0x9a, 0x8d, 0xba, 0x00, 0x4f, 0x37, 0xe1, 0x74, 0x47, 0x8e, 0x92, + 0x50, 0xfd, 0xe5, 0x12, 0x7c, 0xdf, 0xbe, 0x5d, 0x40, 0x77, 0x00, 0x12, 0xa7, 0x25, 0x26, 0xaa, + 0x38, 0xa6, 0x38, 0x62, 0x50, 0xe3, 0xaa, 0xa4, 0xc7, 0x2b, 0x81, 0xa8, 0xbf, 0xec, 0x00, 0x40, + 0xfe, 0x66, 0xb1, 0x8c, 0x81, 0xd7, 0x55, 0x30, 0x11, 0x07, 0x1e, 0xc1, 0x0c, 0x42, 0xd5, 0x7f, + 0x44, 0x5a, 0xfa, 0x76, 0x66, 0xf5, 0xf9, 0x30, 0x6b, 0xc5, 0x02, 0x8a, 0x5e, 0x80, 0x21, 0xc7, + 0xf3, 0x78, 0x56, 0x0a, 0x89, 0xc5, 0x6d, 0x37, 0xba, 0x72, 0x9b, 0x06, 0x61, 0x13, 0xcf, 0xfe, + 0xd3, 0x12, 0x8c, 0xef, 0x23, 0x53, 0xba, 0xf2, 0xec, 0xaa, 0x7d, 0xe7, 0xd9, 0x89, 0xcc, 0x80, + 0x81, 0x1e, 0x99, 0x01, 0x2f, 0xc0, 0x50, 0x42, 0x9c, 0xb6, 0x08, 0x83, 0x12, 0xfb, 0x6f, 0x7d, + 0xee, 0xaa, 0x41, 0xd8, 0xc4, 0xa3, 0x52, 0x6c, 0xd4, 0x69, 0x34, 0x48, 0x1c, 0xcb, 0xd0, 0x7f, + 0xe1, 0xc3, 0x2c, 0x2c, 0xaf, 0x80, 0xb9, 0x86, 0xa7, 0x52, 0x2c, 0x70, 0x86, 0x65, 0x76, 0xc0, + 0xeb, 0x7d, 0x0e, 0xf8, 0xd7, 0x4a, 0xf0, 0xf8, 0x9e, 0xda, 0xad, 0xef, 0xac, 0x8c, 0x4e, 0x4c, + 0xa2, 0xec, 0xc4, 0xb9, 0x11, 0x93, 0x08, 0x33, 0x08, 0x1f, 0xa5, 0x30, 0x34, 0x6e, 0xbf, 0x2e, + 0x3a, 0x65, 0x88, 0x8f, 0x52, 0x8a, 0x05, 0xce, 0xb0, 0x3c, 0xec, 0xb4, 0xfc, 0x7b, 0x25, 0x78, + 0xb2, 0x0f, 0x1b, 0xa0, 0xc0, 0xd4, 0xaa, 0x74, 0x82, 0x5b, 0xf9, 0x01, 0xe5, 0x21, 0x1e, 0x72, + 0xb8, 0xbe, 0x5e, 0x82, 0xf3, 0xbd, 0x55, 0x31, 0xfa, 0x71, 0xba, 0x87, 0x97, 0xb1, 0x4f, 0x66, + 0x6e, 0xdc, 0x69, 0xbe, 0x7f, 0x4f, 0x81, 0x70, 0x16, 0x17, 0x4d, 0x00, 0x84, 0x4e, 0xb2, 0x11, + 0x5f, 0xda, 0x76, 0xe3, 0x44, 0xd4, 0x7e, 0x19, 0xe5, 0x27, 0x46, 0xb2, 0x15, 0x1b, 0x18, 0x94, + 0x1d, 0xfb, 0x37, 0x1b, 0x5c, 0x0f, 0x12, 0xfe, 0x10, 0xdf, 0x46, 0x9c, 0x96, 0x37, 0x65, 0x18, + 0x20, 0x9c, 0xc5, 0xa5, 0xec, 0xd8, 0x99, 0x24, 0xef, 0x28, 0xdf, 0x5f, 0x30, 0x76, 0x0b, 0xaa, + 0x15, 0x1b, 0x18, 0xd9, 0xac, 0xbf, 0xea, 0xfe, 0x59, 0x7f, 0xf6, 0x3f, 0x2e, 0xc1, 0xb9, 0x9e, + 0xa6, 0x5c, 0x7f, 0x0b, 0xf0, 0xe1, 0xcb, 0xd4, 0x3b, 0xdc, 0xdc, 0x39, 0x60, 0x46, 0xd9, 0x1f, + 0xf7, 0x98, 0x69, 0x22, 0xa3, 0xec, 0xf0, 0x29, 0xd9, 0x0f, 0xdf, 0x78, 0x76, 0x25, 0x91, 0x55, + 0x0e, 0x90, 0x44, 0x96, 0xf9, 0x18, 0xd5, 0x3e, 0x17, 0xf2, 0x9f, 0x97, 0x7b, 0x0e, 0x2f, 0xdd, + 0xfa, 0xf5, 0xe5, 0x1d, 0x9d, 0x85, 0x93, 0xae, 0xcf, 0x6e, 0x4d, 0x5a, 0xe9, 0xac, 0x89, 0x72, + 0x20, 0xa5, 0xf4, 0xdd, 0xe6, 0xf3, 0x19, 0x38, 0xee, 0x7a, 0xe2, 0x21, 0x4c, 0xea, 0x3b, 0xdc, + 0x90, 0x1e, 0x2c, 0xad, 0x14, 0x2d, 0xc1, 0x59, 0x39, 0x14, 0x1b, 0x4e, 0x44, 0x9a, 0x42, 0x8d, + 0xc4, 0x22, 0x8d, 0xe1, 0x1c, 0x4f, 0x85, 0xc8, 0x41, 0xc0, 0xf9, 0xcf, 0xb1, 0x8b, 0x6a, 0x82, + 0xd0, 0x6d, 0x88, 0x4d, 0x8e, 0xbe, 0xa8, 0x86, 0x36, 0x62, 0x0e, 0xb3, 0x3f, 0x04, 0x75, 0xf5, + 0xfe, 0x3c, 0x98, 0x5a, 0x4d, 0xba, 0xae, 0x60, 0x6a, 0x35, 0xe3, 0x0c, 0x2c, 0xfa, 0xb5, 0xa8, + 0x49, 0x9c, 0x59, 0x3d, 0xd7, 0xc8, 0x0e, 0xb3, 0x8f, 0xed, 0x1f, 0x86, 0x61, 0xe5, 0x67, 0xe9, + 0xf7, 0xfa, 0x1e, 0xfb, 0x8b, 0x03, 0x30, 0x92, 0x2a, 0xc9, 0x97, 0x72, 0x6b, 0x5a, 0xfb, 0xba, + 0x35, 0x59, 0x70, 0x7c, 0xc7, 0x97, 0x77, 0x7b, 0x19, 0xc1, 0xf1, 0x1d, 0x9f, 0x60, 0x0e, 0xa3, + 0xe6, 0x6d, 0x33, 0xda, 0xc1, 0x1d, 0x5f, 0x04, 0xb1, 0x2a, 0xf3, 0x76, 0x96, 0xb5, 0x62, 0x01, + 0x45, 0x1f, 0xb7, 0x60, 0x38, 0x66, 0x3e, 0x73, 0xee, 0x14, 0x16, 0x93, 0xee, 0xea, 0xd1, 0x2b, + 0x0e, 0xaa, 0xf2, 0x93, 0x2c, 0x2e, 0xc5, 0x6c, 0xc1, 0x29, 0x8e, 0xe8, 0x53, 0x16, 0xd4, 0xd5, + 0x15, 0x24, 0xe2, 0xa2, 0xbe, 0x95, 0x62, 0x2b, 0x1e, 0x72, 0x6f, 0xa2, 0x3a, 0x7e, 0x50, 0xa5, + 0xe7, 0xb0, 0x66, 0x8c, 0x62, 0xe5, 0xb1, 0x1d, 0x3c, 0x1e, 0x8f, 0x2d, 0xe4, 0x78, 0x6b, 0xdf, + 0x0d, 0xf5, 0xb6, 0xe3, 0xbb, 0xeb, 0x24, 0x4e, 0xb8, 0x13, 0x55, 0x16, 0x62, 0x95, 0x8d, 0x58, + 0xc3, 0xa9, 0x42, 0x8e, 0xd9, 0x8b, 0x25, 0x86, 0xd7, 0x93, 0x29, 0xe4, 0x15, 0xdd, 0x8c, 0x4d, + 0x1c, 0xd3, 0x45, 0x0b, 0x0f, 0xd4, 0x45, 0x3b, 0xb4, 0x8f, 0x8b, 0xf6, 0x1f, 0x58, 0x70, 0x36, + 0xf7, 0xab, 0x3d, 0xbc, 0xe1, 0x86, 0xf6, 0x97, 0xaa, 0x70, 0x3a, 0xa7, 0xb6, 0x26, 0xda, 0x31, + 0xe7, 0xb3, 0x55, 0xc4, 0xc9, 0x7d, 0xfa, 0x20, 0x5a, 0x0e, 0x63, 0xce, 0x24, 0x3e, 0xd8, 0x01, + 0x89, 0x3e, 0xa4, 0x28, 0xdf, 0xdf, 0x43, 0x0a, 0x63, 0x5a, 0x56, 0x1e, 0xe8, 0xb4, 0xac, 0xee, + 0x3d, 0x2d, 0xd1, 0xaf, 0x5b, 0x30, 0xd6, 0xee, 0x51, 0xd0, 0x5d, 0x38, 0x1e, 0x6f, 0x1e, 0x4f, + 0xb9, 0xf8, 0xe9, 0xc7, 0xee, 0xee, 0x8e, 0xf7, 0xac, 0xa3, 0x8f, 0x7b, 0xf6, 0xca, 0xfe, 0x76, + 0x19, 0x58, 0x61, 0x57, 0x56, 0x3f, 0x6d, 0x07, 0x7d, 0xcc, 0x2c, 0xd1, 0x6b, 0x15, 0x55, 0x4e, + 0x96, 0x13, 0x57, 0x25, 0x7e, 0xf9, 0x08, 0xe6, 0x55, 0xfc, 0xcd, 0x0a, 0xad, 0x52, 0x1f, 0x42, + 0xcb, 0x93, 0xb5, 0x90, 0xcb, 0xc5, 0xd7, 0x42, 0xae, 0x67, 0xeb, 0x20, 0xef, 0xfd, 0x89, 0x2b, + 0x0f, 0xe5, 0x27, 0xfe, 0x9b, 0x16, 0x17, 0x3c, 0x99, 0xaf, 0xa0, 0x2d, 0x03, 0x6b, 0x0f, 0xcb, + 0xe0, 0x19, 0xa8, 0xc5, 0xc4, 0x5b, 0xbf, 0x42, 0x1c, 0x4f, 0x58, 0x10, 0xfa, 0xd4, 0x58, 0xb4, + 0x63, 0x85, 0xc1, 0x2e, 0x4b, 0xf5, 0xbc, 0xe0, 0xce, 0xa5, 0x76, 0x98, 0xec, 0x08, 0x5b, 0x42, + 0x5f, 0x96, 0xaa, 0x20, 0xd8, 0xc0, 0xb2, 0xff, 0x56, 0x89, 0xcf, 0x40, 0x11, 0x7a, 0xf0, 0x62, + 0xe6, 0x7a, 0xbb, 0xfe, 0x4f, 0xed, 0x3f, 0x02, 0xd0, 0x50, 0x17, 0xc8, 0x8b, 0x33, 0xa1, 0x2b, + 0x47, 0xbe, 0xdd, 0x5a, 0xd0, 0xd3, 0xaf, 0xa1, 0xdb, 0xb0, 0xc1, 0x2f, 0x25, 0x4b, 0xcb, 0xfb, + 0xca, 0xd2, 0x94, 0x58, 0xa9, 0xec, 0xa3, 0xed, 0xfe, 0xd4, 0x82, 0x94, 0x45, 0x84, 0x42, 0xa8, + 0xd2, 0xee, 0xee, 0x14, 0x73, 0x37, 0xbe, 0x49, 0x9a, 0x8a, 0x46, 0x31, 0xed, 0xd9, 0x4f, 0xcc, + 0x19, 0x21, 0x4f, 0x44, 0x28, 0xf0, 0x51, 0xbd, 0x5e, 0x1c, 0xc3, 0x2b, 0x41, 0xb0, 0xc9, 0x0f, + 0x36, 0x75, 0xb4, 0x83, 0xfd, 0x22, 0x9c, 0xea, 0xea, 0x14, 0xbb, 0xc9, 0x2a, 0xa0, 0xda, 0x27, + 0x33, 0x5d, 0x59, 0xda, 0x24, 0xe6, 0x30, 0xfb, 0xeb, 0x16, 0x9c, 0xcc, 0x92, 0x47, 0x6f, 0x59, + 0x70, 0x2a, 0xce, 0xd2, 0x3b, 0xae, 0xb1, 0x53, 0x51, 0x86, 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, 0xf2, 0x35, 0x57, 0x44, 0x3b, 0x56, 0x18, 0x2c, 0x3d, + 0xad, 0x23, 0x8a, 0xa5, 0x67, 0x26, 0xe5, 0xac, 0x68, 0xc7, 0x0a, 0x03, 0x3d, 0x0f, 0xc3, 0xc6, + 0x4b, 0xca, 0x79, 0xc9, 0x0c, 0x72, 0x43, 0x65, 0xc6, 0x38, 0x85, 0x85, 0x26, 0x00, 0x94, 0x91, + 0x23, 0x55, 0x24, 0x73, 0x14, 0x29, 0x49, 0x14, 0x63, 0x03, 0x83, 0x25, 0x83, 0x7a, 0x9d, 0x98, + 0xf9, 0xf8, 0x07, 0x74, 0x01, 0xcf, 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, 0xe8, 0x30, 0x7d, 0xec, 0x23, 0xda, 0xb1, 0xc2, 0xb0, 0xff, + 0xab, 0x05, 0x27, 0x74, 0x6a, 0x39, 0xbf, 0xb3, 0xda, 0xdc, 0xa9, 0x5a, 0xfb, 0xee, 0x54, 0xd3, + 0x39, 0xb7, 0xa5, 0xbe, 0x72, 0x6e, 0xcd, 0x74, 0xd8, 0xf2, 0x9e, 0xe9, 0xb0, 0x3f, 0xa0, 0xef, + 0x43, 0xe5, 0x79, 0xb3, 0x43, 0x79, 0x77, 0xa1, 0x22, 0x1b, 0x06, 0x1a, 0x8e, 0xaa, 0xab, 0x32, + 0xcc, 0xf7, 0x0e, 0x33, 0x53, 0x0c, 0x49, 0x40, 0xec, 0x25, 0xa8, 0xab, 0xd3, 0x0f, 0xb9, 0x51, + 0xb5, 0xf2, 0x37, 0xaa, 0x7d, 0xa5, 0xe5, 0x4d, 0xaf, 0x7d, 0xe3, 0x3b, 0x4f, 0xbc, 0xe3, 0xf7, + 0xbf, 0xf3, 0xc4, 0x3b, 0xfe, 0xe8, 0x3b, 0x4f, 0xbc, 0xe3, 0xe3, 0x77, 0x9f, 0xb0, 0xbe, 0x71, + 0xf7, 0x09, 0xeb, 0xf7, 0xef, 0x3e, 0x61, 0xfd, 0xd1, 0xdd, 0x27, 0xac, 0x6f, 0xdf, 0x7d, 0xc2, + 0xfa, 0xc2, 0x7f, 0x7a, 0xe2, 0x1d, 0xaf, 0xe4, 0x86, 0x07, 0xd2, 0x1f, 0xcf, 0x36, 0x9a, 0x93, + 0x5b, 0x17, 0x59, 0x84, 0x1a, 0x5d, 0x5e, 0x93, 0xc6, 0x9c, 0x9a, 0x94, 0xcb, 0xeb, 0xff, 0x05, + 0x00, 0x00, 0xff, 0xff, 0x99, 0x73, 0x76, 0xcf, 0xcd, 0xe0, 0x00, 0x00, } func (m *AWSAuthConfig) Marshal() (dAtA []byte, err error) { @@ -5326,11 +5188,6 @@ 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))) @@ -5464,20 +5321,6 @@ func (m *AppProjectSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.DestinationServiceAccounts) > 0 { - for iNdEx := len(m.DestinationServiceAccounts) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.DestinationServiceAccounts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenerated(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x72 - } - } i-- if m.PermitOnlyProjectScopedClusters { dAtA[i] = 1 @@ -5836,44 +5679,6 @@ func (m *ApplicationDestination) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func (m *ApplicationDestinationServiceAccount) 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 *ApplicationDestinationServiceAccount) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ApplicationDestinationServiceAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - i -= len(m.DefaultServiceAccount) - copy(dAtA[i:], m.DefaultServiceAccount) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.DefaultServiceAccount))) - i-- - dAtA[i] = 0x1a - i -= len(m.Namespace) - copy(dAtA[i:], m.Namespace) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.Namespace))) - i-- - dAtA[i] = 0x12 - i -= len(m.Server) - copy(dAtA[i:], m.Server) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.Server))) - i-- - dAtA[i] = 0xa - return len(dAtA) - i, nil -} - func (m *ApplicationList) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -6077,15 +5882,6 @@ func (m *ApplicationSetApplicationStatus) MarshalToSizedBuffer(dAtA []byte) (int _ = i var l int _ = l - if len(m.TargetRevisions) > 0 { - for iNdEx := len(m.TargetRevisions) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.TargetRevisions[iNdEx]) - copy(dAtA[i:], m.TargetRevisions[iNdEx]) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.TargetRevisions[iNdEx]))) - i-- - dAtA[i] = 0x32 - } - } i -= len(m.Step) copy(dAtA[i:], m.Step) i = encodeVarintGenerated(dAtA, i, uint64(len(m.Step))) @@ -6790,20 +6586,6 @@ func (m *ApplicationSetStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.Resources) > 0 { - for iNdEx := len(m.Resources) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Resources[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenerated(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - } if len(m.ApplicationStatus) > 0 { for iNdEx := len(m.ApplicationStatus) - 1; iNdEx >= 0; iNdEx-- { { @@ -7165,43 +6947,6 @@ func (m *ApplicationSetTerminalGenerator) MarshalToSizedBuffer(dAtA []byte) (int return len(dAtA) - i, nil } -func (m *ApplicationSetTree) 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 *ApplicationSetTree) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ApplicationSetTree) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Nodes) > 0 { - for iNdEx := len(m.Nodes) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Nodes[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 *ApplicationSource) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -7369,25 +7114,6 @@ func (m *ApplicationSourceHelm) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.APIVersions) > 0 { - for iNdEx := len(m.APIVersions) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.APIVersions[iNdEx]) - copy(dAtA[i:], m.APIVersions[iNdEx]) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.APIVersions[iNdEx]))) - i-- - dAtA[i] = 0x6a - } - } - i -= len(m.KubeVersion) - copy(dAtA[i:], m.KubeVersion) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.KubeVersion))) - i-- - dAtA[i] = 0x62 - i -= len(m.Namespace) - copy(dAtA[i:], m.Namespace) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.Namespace))) - i-- - dAtA[i] = 0x5a if m.ValuesObject != nil { { size, err := m.ValuesObject.MarshalToSizedBuffer(dAtA[:i]) @@ -7559,30 +7285,6 @@ func (m *ApplicationSourceKustomize) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l - if len(m.APIVersions) > 0 { - for iNdEx := len(m.APIVersions) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.APIVersions[iNdEx]) - copy(dAtA[i:], m.APIVersions[iNdEx]) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.APIVersions[iNdEx]))) - i-- - dAtA[i] = 0x1 - i-- - dAtA[i] = 0x82 - } - } - i -= len(m.KubeVersion) - copy(dAtA[i:], m.KubeVersion) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.KubeVersion))) - i-- - dAtA[i] = 0x7a - 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]) @@ -8164,9 +7866,6 @@ func (m *ApplicationTree) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - i = encodeVarintGenerated(dAtA, i, uint64(m.ShardsCount)) - i-- - dAtA[i] = 0x20 if len(m.Hosts) > 0 { for iNdEx := len(m.Hosts) - 1; iNdEx >= 0; iNdEx-- { { @@ -8328,41 +8027,6 @@ func (m *BasicAuthBitbucketServer) MarshalToSizedBuffer(dAtA []byte) (int, error return len(dAtA) - i, nil } -func (m *BearerTokenBitbucket) 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 *BearerTokenBitbucket) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *BearerTokenBitbucket) 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 *BearerTokenBitbucketCloud) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -9090,39 +8754,6 @@ func (m *ConfigManagementPlugin) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func (m *ConfigMapKeyRef) 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 *ConfigMapKeyRef) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ConfigMapKeyRef) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - i -= len(m.Key) - copy(dAtA[i:], m.Key) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.Key))) - i-- - dAtA[i] = 0x12 - i -= len(m.ConfigMapName) - copy(dAtA[i:], m.ConfigMapName) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.ConfigMapName))) - i-- - dAtA[i] = 0xa - return len(dAtA) - i, nil -} - func (m *ConnectionState) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -11557,38 +11188,6 @@ func (m *PullRequestGeneratorBitbucketServer) MarshalToSizedBuffer(dAtA []byte) _ = i var l int _ = l - if m.CARef != nil { - { - size, err := m.CARef.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenerated(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x3a - } - i-- - if m.Insecure { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x30 - 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]) @@ -11676,18 +11275,6 @@ func (m *PullRequestGeneratorGitLab) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l - if m.CARef != nil { - { - size, err := m.CARef.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenerated(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x3a - } i-- if m.Insecure { dAtA[i] = 1 @@ -11920,13 +11507,6 @@ func (m *RepoCreds) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - i -= len(m.NoProxy) - copy(dAtA[i:], m.NoProxy) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.NoProxy))) - i-- - dAtA[i] = 0x1 - i-- - dAtA[i] = 0xba i-- if m.ForceHttpBasicAuth { dAtA[i] = 1 @@ -12078,13 +11658,6 @@ func (m *Repository) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - i -= len(m.NoProxy) - copy(dAtA[i:], m.NoProxy) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.NoProxy))) - i-- - dAtA[i] = 0x1 - i-- - dAtA[i] = 0xba i-- if m.ForceHttpBasicAuth { dAtA[i] = 1 @@ -12531,14 +12104,6 @@ func (m *ResourceActions) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - i-- - if m.MergeBuiltinActions { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x18 if len(m.Definitions) > 0 { for iNdEx := len(m.Definitions) - 1; iNdEx >= 0; iNdEx-- { { @@ -13282,16 +12847,6 @@ 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]) @@ -13766,38 +13321,6 @@ func (m *SCMProviderGeneratorBitbucketServer) MarshalToSizedBuffer(dAtA []byte) _ = i var l int _ = l - if m.CARef != nil { - { - size, err := m.CARef.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenerated(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x3a - } - i-- - if m.Insecure { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x30 - 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 - } i-- if m.AllBranches { dAtA[i] = 1 @@ -14032,18 +13555,6 @@ func (m *SCMProviderGeneratorGitlab) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l - if m.CARef != nil { - { - size, err := m.CARef.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenerated(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x4a - } i -= len(m.Topic) copy(dAtA[i:], m.Topic) i = encodeVarintGenerated(dAtA, i, uint64(len(m.Topic))) @@ -14889,8 +14400,6 @@ 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 } @@ -14999,12 +14508,6 @@ func (m *AppProjectSpec) Size() (n int) { } } n += 2 - if len(m.DestinationServiceAccounts) > 0 { - for _, e := range m.DestinationServiceAccounts { - l = e.Size() - n += 1 + l + sovGenerated(uint64(l)) - } - } return n } @@ -15077,21 +14580,6 @@ func (m *ApplicationDestination) Size() (n int) { return n } -func (m *ApplicationDestinationServiceAccount) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Server) - n += 1 + l + sovGenerated(uint64(l)) - l = len(m.Namespace) - n += 1 + l + sovGenerated(uint64(l)) - l = len(m.DefaultServiceAccount) - n += 1 + l + sovGenerated(uint64(l)) - return n -} - func (m *ApplicationList) Size() (n int) { if m == nil { return 0 @@ -15182,12 +14670,6 @@ func (m *ApplicationSetApplicationStatus) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) l = len(m.Step) n += 1 + l + sovGenerated(uint64(l)) - if len(m.TargetRevisions) > 0 { - for _, s := range m.TargetRevisions { - l = len(s) - n += 1 + l + sovGenerated(uint64(l)) - } - } return n } @@ -15449,12 +14931,6 @@ func (m *ApplicationSetStatus) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } - if len(m.Resources) > 0 { - for _, e := range m.Resources { - l = e.Size() - n += 1 + l + sovGenerated(uint64(l)) - } - } return n } @@ -15576,21 +15052,6 @@ func (m *ApplicationSetTerminalGenerator) Size() (n int) { return n } -func (m *ApplicationSetTree) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Nodes) > 0 { - for _, e := range m.Nodes { - l = e.Size() - n += 1 + l + sovGenerated(uint64(l)) - } - } - return n -} - func (m *ApplicationSource) Size() (n int) { if m == nil { return 0 @@ -15679,16 +15140,6 @@ func (m *ApplicationSourceHelm) Size() (n int) { l = m.ValuesObject.Size() n += 1 + l + sovGenerated(uint64(l)) } - l = len(m.Namespace) - n += 1 + l + sovGenerated(uint64(l)) - l = len(m.KubeVersion) - n += 1 + l + sovGenerated(uint64(l)) - if len(m.APIVersions) > 0 { - for _, s := range m.APIVersions { - l = len(s) - n += 1 + l + sovGenerated(uint64(l)) - } - } return n } @@ -15776,15 +15227,6 @@ func (m *ApplicationSourceKustomize) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } - n += 2 - l = len(m.KubeVersion) - n += 1 + l + sovGenerated(uint64(l)) - if len(m.APIVersions) > 0 { - for _, s := range m.APIVersions { - l = len(s) - n += 2 + l + sovGenerated(uint64(l)) - } - } return n } @@ -15978,7 +15420,6 @@ func (m *ApplicationTree) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } - n += 1 + sovGenerated(uint64(m.ShardsCount)) return n } @@ -16026,19 +15467,6 @@ func (m *BasicAuthBitbucketServer) Size() (n int) { return n } -func (m *BearerTokenBitbucket) 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 *BearerTokenBitbucketCloud) Size() (n int) { if m == nil { return 0 @@ -16303,19 +15731,6 @@ func (m *ConfigManagementPlugin) Size() (n int) { return n } -func (m *ConfigMapKeyRef) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ConfigMapName) - n += 1 + l + sovGenerated(uint64(l)) - l = len(m.Key) - n += 1 + l + sovGenerated(uint64(l)) - return n -} - func (m *ConnectionState) Size() (n int) { if m == nil { return 0 @@ -17247,15 +16662,6 @@ func (m *PullRequestGeneratorBitbucketServer) Size() (n int) { l = m.BasicAuth.Size() n += 1 + l + sovGenerated(uint64(l)) } - if m.BearerToken != nil { - l = m.BearerToken.Size() - n += 1 + l + sovGenerated(uint64(l)) - } - n += 2 - if m.CARef != nil { - l = m.CARef.Size() - n += 1 + l + sovGenerated(uint64(l)) - } return n } @@ -17299,10 +16705,6 @@ func (m *PullRequestGeneratorGitLab) Size() (n int) { l = len(m.PullRequestState) n += 1 + l + sovGenerated(uint64(l)) n += 2 - if m.CARef != nil { - l = m.CARef.Size() - n += 1 + l + sovGenerated(uint64(l)) - } return n } @@ -17400,8 +16802,6 @@ func (m *RepoCreds) Size() (n int) { l = len(m.Proxy) n += 2 + l + sovGenerated(uint64(l)) n += 3 - l = len(m.NoProxy) - n += 2 + l + sovGenerated(uint64(l)) return n } @@ -17464,8 +16864,6 @@ func (m *Repository) Size() (n int) { l = len(m.GCPServiceAccountKey) n += 2 + l + sovGenerated(uint64(l)) n += 3 - l = len(m.NoProxy) - n += 2 + l + sovGenerated(uint64(l)) return n } @@ -17590,7 +16988,6 @@ func (m *ResourceActions) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } - n += 2 return n } @@ -17891,8 +17288,6 @@ func (m *RevisionHistory) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } - l = m.InitiatedBy.Size() - n += 1 + l + sovGenerated(uint64(l)) return n } @@ -18050,15 +17445,6 @@ func (m *SCMProviderGeneratorBitbucketServer) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } n += 2 - if m.BearerToken != nil { - l = m.BearerToken.Size() - n += 1 + l + sovGenerated(uint64(l)) - } - n += 2 - if m.CARef != nil { - l = m.CARef.Size() - n += 1 + l + sovGenerated(uint64(l)) - } return n } @@ -18156,10 +17542,6 @@ func (m *SCMProviderGeneratorGitlab) Size() (n int) { } l = len(m.Topic) n += 1 + l + sovGenerated(uint64(l)) - if m.CARef != nil { - l = m.CARef.Size() - n += 1 + l + sovGenerated(uint64(l)) - } return n } @@ -18474,7 +17856,6 @@ 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 @@ -18551,11 +17932,6 @@ func (this *AppProjectSpec) String() string { repeatedStringForClusterResourceBlacklist += fmt.Sprintf("%v", f) + "," } repeatedStringForClusterResourceBlacklist += "}" - repeatedStringForDestinationServiceAccounts := "[]ApplicationDestinationServiceAccount{" - for _, f := range this.DestinationServiceAccounts { - repeatedStringForDestinationServiceAccounts += strings.Replace(strings.Replace(f.String(), "ApplicationDestinationServiceAccount", "ApplicationDestinationServiceAccount", 1), `&`, ``, 1) + "," - } - repeatedStringForDestinationServiceAccounts += "}" s := strings.Join([]string{`&AppProjectSpec{`, `SourceRepos:` + fmt.Sprintf("%v", this.SourceRepos) + `,`, `Destinations:` + repeatedStringForDestinations + `,`, @@ -18570,7 +17946,6 @@ func (this *AppProjectSpec) String() string { `ClusterResourceBlacklist:` + repeatedStringForClusterResourceBlacklist + `,`, `SourceNamespaces:` + fmt.Sprintf("%v", this.SourceNamespaces) + `,`, `PermitOnlyProjectScopedClusters:` + fmt.Sprintf("%v", this.PermitOnlyProjectScopedClusters) + `,`, - `DestinationServiceAccounts:` + repeatedStringForDestinationServiceAccounts + `,`, `}`, }, "") return s @@ -18632,18 +18007,6 @@ func (this *ApplicationDestination) String() string { }, "") return s } -func (this *ApplicationDestinationServiceAccount) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&ApplicationDestinationServiceAccount{`, - `Server:` + fmt.Sprintf("%v", this.Server) + `,`, - `Namespace:` + fmt.Sprintf("%v", this.Namespace) + `,`, - `DefaultServiceAccount:` + fmt.Sprintf("%v", this.DefaultServiceAccount) + `,`, - `}`, - }, "") - return s -} func (this *ApplicationList) String() string { if this == nil { return "nil" @@ -18705,7 +18068,6 @@ func (this *ApplicationSetApplicationStatus) String() string { `Message:` + fmt.Sprintf("%v", this.Message) + `,`, `Status:` + fmt.Sprintf("%v", this.Status) + `,`, `Step:` + fmt.Sprintf("%v", this.Step) + `,`, - `TargetRevisions:` + fmt.Sprintf("%v", this.TargetRevisions) + `,`, `}`, }, "") return s @@ -18864,15 +18226,9 @@ func (this *ApplicationSetStatus) String() string { repeatedStringForApplicationStatus += strings.Replace(strings.Replace(f.String(), "ApplicationSetApplicationStatus", "ApplicationSetApplicationStatus", 1), `&`, ``, 1) + "," } repeatedStringForApplicationStatus += "}" - repeatedStringForResources := "[]ResourceStatus{" - for _, f := range this.Resources { - repeatedStringForResources += strings.Replace(strings.Replace(f.String(), "ResourceStatus", "ResourceStatus", 1), `&`, ``, 1) + "," - } - repeatedStringForResources += "}" s := strings.Join([]string{`&ApplicationSetStatus{`, `Conditions:` + repeatedStringForConditions + `,`, `ApplicationStatus:` + repeatedStringForApplicationStatus + `,`, - `Resources:` + repeatedStringForResources + `,`, `}`, }, "") return s @@ -18961,21 +18317,6 @@ func (this *ApplicationSetTerminalGenerator) String() string { }, "") return s } -func (this *ApplicationSetTree) String() string { - if this == nil { - return "nil" - } - repeatedStringForNodes := "[]ResourceNode{" - for _, f := range this.Nodes { - repeatedStringForNodes += strings.Replace(strings.Replace(f.String(), "ResourceNode", "ResourceNode", 1), `&`, ``, 1) + "," - } - repeatedStringForNodes += "}" - s := strings.Join([]string{`&ApplicationSetTree{`, - `Nodes:` + repeatedStringForNodes + `,`, - `}`, - }, "") - return s -} func (this *ApplicationSource) String() string { if this == nil { return "nil" @@ -19032,9 +18373,6 @@ func (this *ApplicationSourceHelm) String() string { `IgnoreMissingValueFiles:` + fmt.Sprintf("%v", this.IgnoreMissingValueFiles) + `,`, `SkipCrds:` + fmt.Sprintf("%v", this.SkipCrds) + `,`, `ValuesObject:` + strings.Replace(fmt.Sprintf("%v", this.ValuesObject), "RawExtension", "runtime.RawExtension", 1) + `,`, - `Namespace:` + fmt.Sprintf("%v", this.Namespace) + `,`, - `KubeVersion:` + fmt.Sprintf("%v", this.KubeVersion) + `,`, - `APIVersions:` + fmt.Sprintf("%v", this.APIVersions) + `,`, `}`, }, "") return s @@ -19109,9 +18447,6 @@ func (this *ApplicationSourceKustomize) String() string { `Replicas:` + repeatedStringForReplicas + `,`, `Patches:` + repeatedStringForPatches + `,`, `Components:` + fmt.Sprintf("%v", this.Components) + `,`, - `LabelWithoutSelector:` + fmt.Sprintf("%v", this.LabelWithoutSelector) + `,`, - `KubeVersion:` + fmt.Sprintf("%v", this.KubeVersion) + `,`, - `APIVersions:` + fmt.Sprintf("%v", this.APIVersions) + `,`, `}`, }, "") return s @@ -19254,7 +18589,6 @@ func (this *ApplicationTree) String() string { `Nodes:` + repeatedStringForNodes + `,`, `OrphanedNodes:` + repeatedStringForOrphanedNodes + `,`, `Hosts:` + repeatedStringForHosts + `,`, - `ShardsCount:` + fmt.Sprintf("%v", this.ShardsCount) + `,`, `}`, }, "") return s @@ -19293,16 +18627,6 @@ func (this *BasicAuthBitbucketServer) String() string { }, "") return s } -func (this *BearerTokenBitbucket) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&BearerTokenBitbucket{`, - `TokenRef:` + strings.Replace(this.TokenRef.String(), "SecretRef", "SecretRef", 1) + `,`, - `}`, - }, "") - return s -} func (this *BearerTokenBitbucketCloud) String() string { if this == nil { return "nil" @@ -19505,17 +18829,6 @@ func (this *ConfigManagementPlugin) String() string { }, "") return s } -func (this *ConfigMapKeyRef) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&ConfigMapKeyRef{`, - `ConfigMapName:` + fmt.Sprintf("%v", this.ConfigMapName) + `,`, - `Key:` + fmt.Sprintf("%v", this.Key) + `,`, - `}`, - }, "") - return s -} func (this *ConnectionState) String() string { if this == nil { return "nil" @@ -20274,9 +19587,6 @@ func (this *PullRequestGeneratorBitbucketServer) String() string { `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(), "BearerTokenBitbucket", "BearerTokenBitbucket", 1) + `,`, - `Insecure:` + fmt.Sprintf("%v", this.Insecure) + `,`, - `CARef:` + strings.Replace(this.CARef.String(), "ConfigMapKeyRef", "ConfigMapKeyRef", 1) + `,`, `}`, }, "") return s @@ -20303,7 +19613,6 @@ func (this *PullRequestGeneratorGitLab) String() string { `Labels:` + fmt.Sprintf("%v", this.Labels) + `,`, `PullRequestState:` + fmt.Sprintf("%v", this.PullRequestState) + `,`, `Insecure:` + fmt.Sprintf("%v", this.Insecure) + `,`, - `CARef:` + strings.Replace(this.CARef.String(), "ConfigMapKeyRef", "ConfigMapKeyRef", 1) + `,`, `}`, }, "") return s @@ -20369,7 +19678,6 @@ func (this *RepoCreds) String() string { `GCPServiceAccountKey:` + fmt.Sprintf("%v", this.GCPServiceAccountKey) + `,`, `Proxy:` + fmt.Sprintf("%v", this.Proxy) + `,`, `ForceHttpBasicAuth:` + fmt.Sprintf("%v", this.ForceHttpBasicAuth) + `,`, - `NoProxy:` + fmt.Sprintf("%v", this.NoProxy) + `,`, `}`, }, "") return s @@ -20417,7 +19725,6 @@ func (this *Repository) String() string { `Project:` + fmt.Sprintf("%v", this.Project) + `,`, `GCPServiceAccountKey:` + fmt.Sprintf("%v", this.GCPServiceAccountKey) + `,`, `ForceHttpBasicAuth:` + fmt.Sprintf("%v", this.ForceHttpBasicAuth) + `,`, - `NoProxy:` + fmt.Sprintf("%v", this.NoProxy) + `,`, `}`, }, "") return s @@ -20523,7 +19830,6 @@ func (this *ResourceActions) String() string { s := strings.Join([]string{`&ResourceActions{`, `ActionDiscoveryLua:` + fmt.Sprintf("%v", this.ActionDiscoveryLua) + `,`, `Definitions:` + repeatedStringForDefinitions + `,`, - `MergeBuiltinActions:` + fmt.Sprintf("%v", this.MergeBuiltinActions) + `,`, `}`, }, "") return s @@ -20737,7 +20043,6 @@ 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 @@ -20846,9 +20151,6 @@ func (this *SCMProviderGeneratorBitbucketServer) String() string { `API:` + fmt.Sprintf("%v", this.API) + `,`, `BasicAuth:` + strings.Replace(this.BasicAuth.String(), "BasicAuthBitbucketServer", "BasicAuthBitbucketServer", 1) + `,`, `AllBranches:` + fmt.Sprintf("%v", this.AllBranches) + `,`, - `BearerToken:` + strings.Replace(this.BearerToken.String(), "BearerTokenBitbucket", "BearerTokenBitbucket", 1) + `,`, - `Insecure:` + fmt.Sprintf("%v", this.Insecure) + `,`, - `CARef:` + strings.Replace(this.CARef.String(), "ConfigMapKeyRef", "ConfigMapKeyRef", 1) + `,`, `}`, }, "") return s @@ -20908,7 +20210,6 @@ func (this *SCMProviderGeneratorGitlab) String() string { `Insecure:` + fmt.Sprintf("%v", this.Insecure) + `,`, `IncludeSharedProjects:` + valueToStringGenerated(this.IncludeSharedProjects) + `,`, `Topic:` + fmt.Sprintf("%v", this.Topic) + `,`, - `CARef:` + strings.Replace(this.CARef.String(), "ConfigMapKeyRef", "ConfigMapKeyRef", 1) + `,`, `}`, }, "") return s @@ -21213,38 +20514,6 @@ 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:]) @@ -21985,40 +21254,6 @@ func (m *AppProjectSpec) Unmarshal(dAtA []byte) error { } } m.PermitOnlyProjectScopedClusters = bool(v != 0) - case 14: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DestinationServiceAccounts", 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.DestinationServiceAccounts = append(m.DestinationServiceAccounts, ApplicationDestinationServiceAccount{}) - if err := m.DestinationServiceAccounts[len(m.DestinationServiceAccounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -22700,7 +21935,7 @@ func (m *ApplicationDestination) Unmarshal(dAtA []byte) error { } return nil } -func (m *ApplicationDestinationServiceAccount) Unmarshal(dAtA []byte) error { +func (m *ApplicationList) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -22723,17 +21958,17 @@ func (m *ApplicationDestinationServiceAccount) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ApplicationDestinationServiceAccount: wiretype end group for non-group") + return fmt.Errorf("proto: ApplicationList: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ApplicationDestinationServiceAccount: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ApplicationList: 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 ListMeta", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -22743,29 +21978,30 @@ func (m *ApplicationDestinationServiceAccount) 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 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 Namespace", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -22775,55 +22011,25 @@ func (m *ApplicationDestinationServiceAccount) 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.Namespace = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DefaultServiceAccount", 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.Items = append(m.Items, Application{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - m.DefaultServiceAccount = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -22846,7 +22052,7 @@ func (m *ApplicationDestinationServiceAccount) Unmarshal(dAtA []byte) error { } return nil } -func (m *ApplicationList) Unmarshal(dAtA []byte) error { +func (m *ApplicationMatchExpression) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -22869,17 +22075,17 @@ func (m *ApplicationList) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ApplicationList: wiretype end group for non-group") + return fmt.Errorf("proto: ApplicationMatchExpression: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ApplicationList: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ApplicationMatchExpression: 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 Key", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -22889,30 +22095,29 @@ func (m *ApplicationList) 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.Key = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Operator", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -22922,25 +22127,55 @@ func (m *ApplicationList) 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.Items = append(m.Items, Application{}) - if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.Operator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + 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 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.Values = append(m.Values, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex @@ -22963,7 +22198,7 @@ func (m *ApplicationList) Unmarshal(dAtA []byte) error { } return nil } -func (m *ApplicationMatchExpression) Unmarshal(dAtA []byte) error { +func (m *ApplicationPreservedFields) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -22986,15 +22221,15 @@ func (m *ApplicationMatchExpression) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ApplicationMatchExpression: wiretype end group for non-group") + return fmt.Errorf("proto: ApplicationPreservedFields: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ApplicationMatchExpression: illegal tag %d (wire type %d)", fieldNum, wire) + 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 Key", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -23022,43 +22257,11 @@ func (m *ApplicationMatchExpression) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Key = string(dAtA[iNdEx:postIndex]) + 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 Operator", 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.Operator = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - 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 Labels", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -23086,121 +22289,7 @@ func (m *ApplicationMatchExpression) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Values = append(m.Values, 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 *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])) + m.Labels = append(m.Labels, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex @@ -23565,38 +22654,6 @@ func (m *ApplicationSetApplicationStatus) Unmarshal(dAtA []byte) error { } m.Step = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TargetRevisions", 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.TargetRevisions = append(m.TargetRevisions, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -25580,40 +24637,6 @@ func (m *ApplicationSetStatus) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Resources", 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.Resources = append(m.Resources, ResourceStatus{}) - if err := m.Resources[len(m.Resources)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -26710,7 +25733,7 @@ func (m *ApplicationSetTerminalGenerator) Unmarshal(dAtA []byte) error { } return nil } -func (m *ApplicationSetTree) Unmarshal(dAtA []byte) error { +func (m *ApplicationSource) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -26733,17 +25756,17 @@ func (m *ApplicationSetTree) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ApplicationSetTree: wiretype end group for non-group") + return fmt.Errorf("proto: ApplicationSource: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ApplicationSetTree: illegal tag %d (wire type %d)", fieldNum, wire) + 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 Nodes", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RepoURL", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -26753,79 +25776,27 @@ func (m *ApplicationSetTree) 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.Nodes = append(m.Nodes, ResourceNode{}) - if err := m.Nodes[len(m.Nodes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.RepoURL = 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 *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: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RepoURL", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -26853,43 +25824,11 @@ func (m *ApplicationSource) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.RepoURL = string(dAtA[iNdEx:postIndex]) + m.Path = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + 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 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) + return fmt.Errorf("proto: wrong wireType = %d for field TargetRevision", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -27636,102 +26575,6 @@ func (m *ApplicationSourceHelm) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 11: - 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 12: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field KubeVersion", 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.KubeVersion = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 13: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field APIVersions", 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.APIVersions = append(m.APIVersions, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -28506,11 +27349,61 @@ func (m *ApplicationSourceKustomize) Unmarshal(dAtA []byte) error { } 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) + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err } - var v 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 *ApplicationSourcePlugin) 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: ApplicationSourcePlugin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ApplicationSourcePlugin: 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 @@ -28520,17 +27413,29 @@ func (m *ApplicationSourceKustomize) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - m.LabelWithoutSelector = bool(v != 0) - case 15: + 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 KubeVersion", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Env", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -28540,29 +27445,31 @@ func (m *ApplicationSourceKustomize) 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.KubeVersion = string(dAtA[iNdEx:postIndex]) + m.Env = append(m.Env, &EnvEntry{}) + if err := m.Env[len(m.Env)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 16: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field APIVersions", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Parameters", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -28572,23 +27479,25 @@ func (m *ApplicationSourceKustomize) 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.APIVersions = append(m.APIVersions, string(dAtA[iNdEx:postIndex])) + m.Parameters = append(m.Parameters, ApplicationSourcePluginParameter{}) + if err := m.Parameters[len(m.Parameters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -28611,7 +27520,7 @@ func (m *ApplicationSourceKustomize) Unmarshal(dAtA []byte) error { } return nil } -func (m *ApplicationSourcePlugin) Unmarshal(dAtA []byte) error { +func (m *ApplicationSourcePluginParameter) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -28634,10 +27543,10 @@ func (m *ApplicationSourcePlugin) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ApplicationSourcePlugin: wiretype end group for non-group") + return fmt.Errorf("proto: ApplicationSourcePluginParameter: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ApplicationSourcePlugin: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ApplicationSourcePluginParameter: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -28672,159 +27581,9 @@ func (m *ApplicationSourcePlugin) 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 Env", 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.Env = append(m.Env, &EnvEntry{}) - if err := m.Env[len(m.Env)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 3: 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 - } - m.Parameters = append(m.Parameters, ApplicationSourcePluginParameter{}) - if err := m.Parameters[len(m.Parameters)-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 *ApplicationSourcePluginParameter) 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: ApplicationSourcePluginParameter: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ApplicationSourcePluginParameter: 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 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field OptionalMap", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field OptionalMap", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -29989,25 +28748,6 @@ func (m *ApplicationTree) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ShardsCount", wireType) - } - m.ShardsCount = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ShardsCount |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -30396,7 +29136,7 @@ func (m *BasicAuthBitbucketServer) Unmarshal(dAtA []byte) error { } return nil } -func (m *BearerTokenBitbucket) Unmarshal(dAtA []byte) error { +func (m *BearerTokenBitbucketCloud) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -30419,10 +29159,10 @@ func (m *BearerTokenBitbucket) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: BearerTokenBitbucket: wiretype end group for non-group") + return fmt.Errorf("proto: BearerTokenBitbucketCloud: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: BearerTokenBitbucket: 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: @@ -30482,7 +29222,7 @@ func (m *BearerTokenBitbucket) Unmarshal(dAtA []byte) error { } return nil } -func (m *BearerTokenBitbucketCloud) Unmarshal(dAtA []byte) error { +func (m *ChartDetails) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -30505,17 +29245,17 @@ func (m *BearerTokenBitbucketCloud) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: BearerTokenBitbucketCloud: wiretype end group for non-group") + return fmt.Errorf("proto: ChartDetails: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: BearerTokenBitbucketCloud: illegal tag %d (wire type %d)", fieldNum, wire) + 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 TokenRef", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -30525,27 +29265,87 @@ func (m *BearerTokenBitbucketCloud) 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.TokenRef == nil { - m.TokenRef = &SecretRef{} + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Home", wireType) } - if err := m.TokenRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + 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.Home = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Maintainers", 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.Maintainers = append(m.Maintainers, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex @@ -30568,7 +29368,7 @@ func (m *BearerTokenBitbucketCloud) Unmarshal(dAtA []byte) error { } return nil } -func (m *ChartDetails) Unmarshal(dAtA []byte) error { +func (m *Cluster) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -30591,193 +29391,47 @@ func (m *ChartDetails) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ChartDetails: wiretype end group for non-group") + return fmt.Errorf("proto: Cluster: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ChartDetails: illegal tag %d (wire type %d)", fieldNum, wire) + 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 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 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Home", 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.Home = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Maintainers", 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.Maintainers = append(m.Maintainers, 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 *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 Server", 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.Server = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Server", 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.Server = 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 { @@ -32902,7 +31556,7 @@ func (m *ConfigManagementPlugin) Unmarshal(dAtA []byte) error { } return nil } -func (m *ConfigMapKeyRef) Unmarshal(dAtA []byte) error { +func (m *ConnectionState) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -32925,15 +31579,15 @@ func (m *ConfigMapKeyRef) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ConfigMapKeyRef: wiretype end group for non-group") + return fmt.Errorf("proto: ConnectionState: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ConfigMapKeyRef: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ConnectionState: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConfigMapName", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -32961,11 +31615,11 @@ func (m *ConfigMapKeyRef) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ConfigMapName = string(dAtA[iNdEx:postIndex]) + m.Status = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -32993,7 +31647,43 @@ func (m *ConfigMapKeyRef) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Key = string(dAtA[iNdEx:postIndex]) + m.Message = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ModifiedAt", 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.ModifiedAt == nil { + m.ModifiedAt = &v1.Time{} + } + if err := m.ModifiedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -33016,7 +31706,7 @@ func (m *ConfigMapKeyRef) Unmarshal(dAtA []byte) error { } return nil } -func (m *ConnectionState) Unmarshal(dAtA []byte) error { +func (m *DuckTypeGenerator) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -33039,15 +31729,15 @@ func (m *ConnectionState) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ConnectionState: wiretype end group for non-group") + return fmt.Errorf("proto: DuckTypeGenerator: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ConnectionState: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: DuckTypeGenerator: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ConfigMapRef", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -33075,11 +31765,11 @@ func (m *ConnectionState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Status = string(dAtA[iNdEx:postIndex]) + m.ConfigMapRef = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -33107,181 +31797,31 @@ func (m *ConnectionState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Message = string(dAtA[iNdEx:postIndex]) + m.Name = string(dAtA[iNdEx:postIndex]) 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 ModifiedAt", 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.ModifiedAt == nil { - m.ModifiedAt = &v1.Time{} - } - if err := m.ModifiedAt.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 *DuckTypeGenerator) 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: DuckTypeGenerator: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: DuckTypeGenerator: 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 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.ConfigMapRef = 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 != 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 LabelSelector", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field LabelSelector", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -40687,263 +39227,45 @@ func (m *PullRequestGeneratorAzureDevOps) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.TokenRef == nil { - m.TokenRef = &SecretRef{} - } - if err := m.TokenRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 6: - 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 *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 { - 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 3: - if wireType != 2 { - 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 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BasicAuth", 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.BasicAuth == nil { - m.BasicAuth = &BasicAuthBitbucketServer{} - } - 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 BearerToken", 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 m.TokenRef == nil { + m.TokenRef = &SecretRef{} } - if err := m.BearerToken.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.TokenRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex + case 6: + 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:]) @@ -40965,7 +39287,7 @@ func (m *PullRequestGeneratorBitbucket) Unmarshal(dAtA []byte) error { } return nil } -func (m *PullRequestGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { +func (m *PullRequestGeneratorBitbucket) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -40988,15 +39310,15 @@ func (m *PullRequestGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: PullRequestGeneratorBitbucketServer: wiretype end group for non-group") + return fmt.Errorf("proto: PullRequestGeneratorBitbucket: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: PullRequestGeneratorBitbucketServer: illegal tag %d (wire type %d)", fieldNum, wire) + 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 Project", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -41024,7 +39346,7 @@ func (m *PullRequestGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Project = string(dAtA[iNdEx:postIndex]) + m.Owner = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { @@ -41156,17 +39478,67 @@ func (m *PullRequestGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.BearerToken == nil { - m.BearerToken = &BearerTokenBitbucket{} + m.BearerToken = &BearerTokenBitbucketCloud{} } if err := m.BearerToken.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Insecure", wireType) + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err } - var v 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 *PullRequestGeneratorBitbucketServer) 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: PullRequestGeneratorBitbucketServer: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PullRequestGeneratorBitbucketServer: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + 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 @@ -41176,15 +39548,91 @@ func (m *PullRequestGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - m.Insecure = bool(v != 0) - case 7: + 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 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CARef", wireType) + 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 3: + if wireType != 2 { + 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 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BasicAuth", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -41211,10 +39659,10 @@ func (m *PullRequestGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.CARef == nil { - m.CARef = &ConfigMapKeyRef{} + if m.BasicAuth == nil { + m.BasicAuth = &BasicAuthBitbucketServer{} } - if err := m.CARef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.BasicAuth.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -41568,42 +40016,6 @@ func (m *PullRequestGeneratorGitLab) Unmarshal(dAtA []byte) error { } } m.Insecure = bool(v != 0) - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CARef", 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.CARef == nil { - m.CARef = &ConfigMapKeyRef{} - } - if err := m.CARef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -42593,43 +41005,11 @@ func (m *RepoCreds) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Type = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 13: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field GCPServiceAccountKey", 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.GCPServiceAccountKey = string(dAtA[iNdEx:postIndex]) + m.Type = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 19: + case 13: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Proxy", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field GCPServiceAccountKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -42657,31 +41037,11 @@ func (m *RepoCreds) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Proxy = string(dAtA[iNdEx:postIndex]) + m.GCPServiceAccountKey = 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) - case 23: + case 19: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NoProxy", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Proxy", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -42709,8 +41069,28 @@ func (m *RepoCreds) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.NoProxy = string(dAtA[iNdEx:postIndex]) + 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:]) @@ -43485,38 +41865,6 @@ func (m *Repository) Unmarshal(dAtA []byte) error { } } m.ForceHttpBasicAuth = bool(v != 0) - case 23: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NoProxy", 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.NoProxy = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -44321,193 +42669,15 @@ func (m *ResourceActionParam) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ResourceActionParam: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ResourceActionParam: 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 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 - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Type", 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.Type = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Default", 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.Default = 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 *ResourceActions) 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: ResourceActions: wiretype end group for non-group") + return fmt.Errorf("proto: ResourceActionParam: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ResourceActions: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ResourceActionParam: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ActionDiscoveryLua", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -44535,13 +42705,13 @@ func (m *ResourceActions) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ActionDiscoveryLua = string(dAtA[iNdEx:postIndex]) + m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Definitions", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -44551,31 +42721,175 @@ func (m *ResourceActions) 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.Definitions = append(m.Definitions, ResourceActionDefinition{}) - if err := m.Definitions[len(m.Definitions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", 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.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Default", 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.Default = 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 *ResourceActions) 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: ResourceActions: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceActions: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ActionDiscoveryLua", 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.ActionDiscoveryLua = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MergeBuiltinActions", wireType) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Definitions", wireType) } - var v int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -44585,12 +42899,26 @@ func (m *ResourceActions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - m.MergeBuiltinActions = bool(v != 0) + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Definitions = append(m.Definitions, ResourceActionDefinition{}) + if err := m.Definitions[len(m.Definitions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -47453,85 +45781,15 @@ func (m *RevisionHistory) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Source.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DeployStartedAt", 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.DeployStartedAt == nil { - m.DeployStartedAt = &v1.Time{} - } - if err := m.DeployStartedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 8: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Sources", 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.Sources = append(m.Sources, ApplicationSource{}) - if err := m.Sources[len(m.Sources)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Source.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 9: + case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Revisions", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DeployStartedAt", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -47541,27 +45799,31 @@ func (m *RevisionHistory) 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.Revisions = append(m.Revisions, string(dAtA[iNdEx:postIndex])) + if m.DeployStartedAt == nil { + m.DeployStartedAt = &v1.Time{} + } + if err := m.DeployStartedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 10: + case 8: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InitiatedBy", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Sources", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -47588,10 +45850,43 @@ func (m *RevisionHistory) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.InitiatedBy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Sources = append(m.Sources, ApplicationSource{}) + if err := m.Sources[len(m.Sources)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Revisions", 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.Revisions = append(m.Revisions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -48465,211 +46760,13 @@ func (m *SCMProviderGeneratorAWSCodeCommit) Unmarshal(dAtA []byte) error { 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 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field AllBranches", 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.AllBranches = bool(v != 0) - 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 *SCMProviderGeneratorAzureDevOps) 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: SCMProviderGeneratorAzureDevOps: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SCMProviderGeneratorAzureDevOps: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 5: - 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 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Organization = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 6: - if wireType != 2 { - 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 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TeamProject", 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.TeamProject = string(dAtA[iNdEx:postIndex]) + m.Role = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 8: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AccessTokenRef", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Region", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -48679,29 +46776,25 @@ func (m *SCMProviderGeneratorAzureDevOps) 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.AccessTokenRef == nil { - m.AccessTokenRef = &SecretRef{} - } - if err := m.AccessTokenRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Region = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 9: + case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AllBranches", wireType) } @@ -48742,7 +46835,7 @@ func (m *SCMProviderGeneratorAzureDevOps) Unmarshal(dAtA []byte) error { } return nil } -func (m *SCMProviderGeneratorBitbucket) Unmarshal(dAtA []byte) error { +func (m *SCMProviderGeneratorAzureDevOps) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -48765,15 +46858,15 @@ func (m *SCMProviderGeneratorBitbucket) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: SCMProviderGeneratorBitbucket: wiretype end group for non-group") + return fmt.Errorf("proto: SCMProviderGeneratorAzureDevOps: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: SCMProviderGeneratorBitbucket: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SCMProviderGeneratorAzureDevOps: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 1: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Organization", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -48801,11 +46894,11 @@ func (m *SCMProviderGeneratorBitbucket) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Owner = string(dAtA[iNdEx:postIndex]) + m.Organization = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field API", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -48833,11 +46926,43 @@ func (m *SCMProviderGeneratorBitbucket) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.User = string(dAtA[iNdEx:postIndex]) + m.API = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: + case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AppPasswordRef", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field TeamProject", 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.TeamProject = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccessTokenRef", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -48864,14 +46989,14 @@ func (m *SCMProviderGeneratorBitbucket) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.AppPasswordRef == nil { - m.AppPasswordRef = &SecretRef{} + if m.AccessTokenRef == nil { + m.AccessTokenRef = &SecretRef{} } - if err := m.AppPasswordRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.AccessTokenRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 4: + case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AllBranches", wireType) } @@ -48912,7 +47037,7 @@ func (m *SCMProviderGeneratorBitbucket) Unmarshal(dAtA []byte) error { } return nil } -func (m *SCMProviderGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { +func (m *SCMProviderGeneratorBitbucket) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -48935,15 +47060,15 @@ func (m *SCMProviderGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: SCMProviderGeneratorBitbucketServer: wiretype end group for non-group") + return fmt.Errorf("proto: SCMProviderGeneratorBitbucket: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: SCMProviderGeneratorBitbucketServer: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SCMProviderGeneratorBitbucket: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -48971,11 +47096,11 @@ func (m *SCMProviderGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Project = string(dAtA[iNdEx:postIndex]) + m.Owner = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field API", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -49003,11 +47128,11 @@ func (m *SCMProviderGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.API = string(dAtA[iNdEx:postIndex]) + m.User = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BasicAuth", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AppPasswordRef", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -49034,10 +47159,10 @@ func (m *SCMProviderGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.BasicAuth == nil { - m.BasicAuth = &BasicAuthBitbucketServer{} + if m.AppPasswordRef == nil { + m.AppPasswordRef = &SecretRef{} } - if err := m.BasicAuth.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.AppPasswordRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -49061,11 +47186,61 @@ func (m *SCMProviderGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { } } m.AllBranches = bool(v != 0) - case 5: + 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 *SCMProviderGeneratorBitbucketServer) 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: SCMProviderGeneratorBitbucketServer: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SCMProviderGeneratorBitbucketServer: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BearerToken", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -49075,33 +47250,29 @@ func (m *SCMProviderGeneratorBitbucketServer) 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.BearerToken == nil { - m.BearerToken = &BearerTokenBitbucket{} - } - if err := m.BearerToken.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Project = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Insecure", wireType) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field API", wireType) } - var v int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -49111,15 +47282,27 @@ func (m *SCMProviderGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - m.Insecure = bool(v != 0) - case 7: + 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 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CARef", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BasicAuth", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -49146,13 +47329,33 @@ func (m *SCMProviderGeneratorBitbucketServer) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.CARef == nil { - m.CARef = &ConfigMapKeyRef{} + if m.BasicAuth == nil { + m.BasicAuth = &BasicAuthBitbucketServer{} } - if err := m.CARef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.BasicAuth.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllBranches", 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.AllBranches = bool(v != 0) default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -50021,42 +48224,6 @@ func (m *SCMProviderGeneratorGitlab) Unmarshal(dAtA []byte) error { } m.Topic = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 9: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CARef", 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.CARef == nil { - m.CARef = &ConfigMapKeyRef{} - } - if err := m.CARef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/pkg/apis/application/v1alpha1/generated.proto b/pkg/apis/application/v1alpha1/generated.proto index 0b79d47202cb9..e04f6f3928083 100644 --- a/pkg/apis/application/v1alpha1/generated.proto +++ b/pkg/apis/application/v1alpha1/generated.proto @@ -22,9 +22,6 @@ 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: @@ -38,7 +35,7 @@ message AWSAuthConfig { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:path=appprojects,shortName=appproj;appprojs message AppProject { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; optional AppProjectSpec spec = 2; @@ -48,7 +45,7 @@ message AppProject { // AppProjectList is list of AppProject resources // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object message AppProjectList { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; repeated AppProject items = 2; } @@ -68,10 +65,10 @@ message AppProjectSpec { repeated ProjectRole roles = 4; // ClusterResourceWhitelist contains list of whitelisted cluster level resources - repeated .k8s.io.apimachinery.pkg.apis.meta.v1.GroupKind clusterResourceWhitelist = 5; + repeated k8s.io.apimachinery.pkg.apis.meta.v1.GroupKind clusterResourceWhitelist = 5; // NamespaceResourceBlacklist contains list of blacklisted namespace level resources - repeated .k8s.io.apimachinery.pkg.apis.meta.v1.GroupKind namespaceResourceBlacklist = 6; + repeated k8s.io.apimachinery.pkg.apis.meta.v1.GroupKind namespaceResourceBlacklist = 6; // OrphanedResources specifies if controller should monitor orphaned resources of apps in this project optional OrphanedResourcesMonitorSettings orphanedResources = 7; @@ -80,22 +77,19 @@ message AppProjectSpec { repeated SyncWindow syncWindows = 8; // NamespaceResourceWhitelist contains list of whitelisted namespace level resources - repeated .k8s.io.apimachinery.pkg.apis.meta.v1.GroupKind namespaceResourceWhitelist = 9; + repeated k8s.io.apimachinery.pkg.apis.meta.v1.GroupKind namespaceResourceWhitelist = 9; // SignatureKeys contains a list of PGP key IDs that commits in Git must be signed with in order to be allowed for sync repeated SignatureKey signatureKeys = 10; // ClusterResourceBlacklist contains list of blacklisted cluster level resources - repeated .k8s.io.apimachinery.pkg.apis.meta.v1.GroupKind clusterResourceBlacklist = 11; + repeated k8s.io.apimachinery.pkg.apis.meta.v1.GroupKind clusterResourceBlacklist = 11; // SourceNamespaces defines the namespaces application resources are allowed to be created in repeated string sourceNamespaces = 12; // PermitOnlyProjectScopedClusters determines whether destinations can only reference clusters which are project-scoped optional bool permitOnlyProjectScopedClusters = 13; - - // DestinationServiceAccounts holds information about the service accounts to be impersonated for the application sync operation for each destination. - repeated ApplicationDestinationServiceAccount destinationServiceAccounts = 14; } // AppProjectStatus contains status information for AppProject CRs @@ -112,9 +106,8 @@ message AppProjectStatus { // +kubebuilder:printcolumn:name="Sync Status",type=string,JSONPath=`.status.sync.status` // +kubebuilder:printcolumn:name="Health Status",type=string,JSONPath=`.status.health.status` // +kubebuilder:printcolumn:name="Revision",type=string,JSONPath=`.status.sync.revision`,priority=10 -// +kubebuilder:printcolumn:name="Project",type=string,JSONPath=`.spec.project`,priority=10 message Application { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; optional ApplicationSpec spec = 2; @@ -132,7 +125,7 @@ message ApplicationCondition { optional string message = 2; // LastTransitionTime is the time the condition was last observed - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time lastTransitionTime = 3; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastTransitionTime = 3; } // ApplicationDestination holds information about the application's destination @@ -148,22 +141,10 @@ message ApplicationDestination { optional string name = 3; } -// ApplicationDestinationServiceAccount holds information about the service account to be impersonated for the application sync operation. -message ApplicationDestinationServiceAccount { - // Server specifies the URL of the target cluster's Kubernetes control plane API. - optional string server = 1; - - // Namespace specifies the target namespace for the application's resources. - optional string namespace = 2; - - // ServiceAccountName to be used for impersonation during the sync operation - optional string defaultServiceAccount = 3; -} - // ApplicationList is list of Application resources // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object message ApplicationList { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; repeated Application items = 2; } @@ -189,7 +170,7 @@ message ApplicationPreservedFields { // +kubebuilder:resource:path=applicationsets,shortName=appset;appsets // +kubebuilder:subresource:status message ApplicationSet { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; optional ApplicationSetSpec spec = 2; @@ -202,7 +183,7 @@ message ApplicationSetApplicationStatus { optional string application = 1; // LastTransitionTime is the time the status was last updated - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time lastTransitionTime = 2; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastTransitionTime = 2; // Message contains human-readable message indicating details about the status optional string message = 3; @@ -212,12 +193,9 @@ message ApplicationSetApplicationStatus { // Step tracks which step this Application should be updated in optional string step = 5; - - // TargetRevision tracks the desired revisions the Application should be synced to. - repeated string targetrevisions = 6; } -// ApplicationSetCondition contains details about an applicationset condition, which is usually an error or warning +// ApplicationSetCondition contains details about an applicationset condition, which is usally an error or warning message ApplicationSetCondition { // Type is an applicationset condition type optional string type = 1; @@ -226,7 +204,7 @@ message ApplicationSetCondition { optional string message = 2; // LastTransitionTime is the time the condition was last observed - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time lastTransitionTime = 3; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastTransitionTime = 3; // True/False/Unknown optional string status = 4; @@ -254,7 +232,7 @@ message ApplicationSetGenerator { optional MergeGenerator merge = 8; // Selector allows to post-filter all generator. - optional .k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 9; + optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 9; optional PluginGenerator plugin = 10; } @@ -263,7 +241,7 @@ message ApplicationSetGenerator { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:object:root=true message ApplicationSetList { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; repeated ApplicationSet items = 2; } @@ -284,13 +262,13 @@ message ApplicationSetNestedGenerator { optional PullRequestGenerator pullRequest = 6; // Matrix should have the form of NestedMatrixGenerator - optional .k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.JSON matrix = 7; + optional k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.JSON matrix = 7; // Merge should have the form of NestedMergeGenerator - optional .k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.JSON merge = 8; + optional k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.JSON merge = 8; // Selector allows to post-filter all generator. - optional .k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 9; + optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 9; optional PluginGenerator plugin = 10; } @@ -311,7 +289,7 @@ message ApplicationSetResourceIgnoreDifferences { message ApplicationSetRolloutStep { repeated ApplicationMatchExpression matchExpressions = 1; - optional .k8s.io.apimachinery.pkg.util.intstr.IntOrString maxUpdate = 2; + optional k8s.io.apimachinery.pkg.util.intstr.IntOrString maxUpdate = 2; } message ApplicationSetRolloutStrategy { @@ -349,9 +327,6 @@ message ApplicationSetStatus { repeated ApplicationSetCondition conditions = 1; repeated ApplicationSetApplicationStatus applicationStatus = 2; - - // Resources is a list of Applications resources managed by this application set. - repeated ResourceStatus resources = 3; } // ApplicationSetStrategy configures how generated Applications are updated in sequence. @@ -414,14 +389,7 @@ message ApplicationSetTerminalGenerator { optional PluginGenerator plugin = 7; // Selector allows to post-filter all generator. - optional .k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 8; -} - -// ApplicationSetTree holds nodes which belongs to the application -// Used to build a tree of an ApplicationSet and its children -message ApplicationSetTree { - // Nodes contains list of nodes which are directly managed by the applicationset - repeated ResourceNode nodes = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 8; } // ApplicationSource contains all required information about the source of an application @@ -503,18 +471,7 @@ message ApplicationSourceHelm { // 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; - - // Namespace is an optional namespace to template with. If left empty, defaults to the app's destination namespace. - optional string namespace = 11; - - // KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - // uses the Kubernetes version of the target cluster. - optional string kubeVersion = 12; - - // APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - // Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - repeated string apiVersions = 13; + optional k8s.io.apimachinery.pkg.runtime.RawExtension valuesObject = 10; } // ApplicationSourceJsonnet holds options specific to applications of type Jsonnet @@ -569,17 +526,6 @@ message ApplicationSourceKustomize { // 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; - - // KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - // uses the Kubernetes version of the target cluster. - optional string kubeVersion = 15; - - // APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - // Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - repeated string apiVersions = 16; } // ApplicationSourcePlugin holds options specific to config management plugins @@ -655,14 +601,14 @@ message ApplicationStatus { repeated ApplicationCondition conditions = 5; // ReconciledAt indicates when the application state was reconciled using the latest git version - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time reconciledAt = 6; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time reconciledAt = 6; // OperationState contains information about any ongoing operations, such as a sync optional OperationState operationState = 7; // ObservedAt indicates when the application state was updated without querying latest git state // Deprecated: controller no longer updates ObservedAt field - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time observedAt = 8; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time observedAt = 8; // SourceType specifies the type of this application optional string sourceType = 9; @@ -700,9 +646,6 @@ message ApplicationTree { // Hosts holds list of Kubernetes nodes that run application related pods repeated HostInfo hosts = 3; - - // ShardsCount contains total number of shards the application tree is split into - optional int64 shardsCount = 4; } // ApplicationWatchEvent contains information about application change. @@ -738,12 +681,6 @@ message BasicAuthBitbucketServer { optional SecretRef passwordRef = 2; } -// BearerTokenBitbucket defines the Bearer token for BitBucket AppToken auth. -message BearerTokenBitbucket { - // Password (or personal access token) reference. - optional SecretRef tokenRef = 1; -} - // BearerTokenBitbucketCloud defines the Bearer token for BitBucket AppToken auth. message BearerTokenBitbucketCloud { // Password (or personal access token) reference. @@ -772,11 +709,11 @@ message Cluster { // Config holds cluster information for connecting to a cluster optional ClusterConfig config = 3; - // Deprecated: use Info.ConnectionState field instead. + // DEPRECATED: use Info.ConnectionState field instead. // ConnectionState contains information about cluster connection state optional ConnectionState connectionState = 4; - // Deprecated: use Info.ServerVersion field instead. + // DEPRECATED: use Info.ServerVersion field instead. // The server version optional string serverVersion = 5; @@ -784,7 +721,7 @@ message Cluster { repeated string namespaces = 6; // RefreshRequestedAt holds time when cluster cache refresh has been requested - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time refreshRequestedAt = 7; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time refreshRequestedAt = 7; // Info holds information about cluster cache and state optional ClusterInfo info = 8; @@ -814,7 +751,7 @@ message ClusterCacheInfo { optional int64 apisCount = 2; // LastCacheSyncTime holds time of most recent cache synchronization - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time lastCacheSyncTime = 3; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastCacheSyncTime = 3; } // ClusterConfig is the configuration attributes. This structure is subset of the go-client @@ -845,7 +782,7 @@ message ClusterGenerator { // Selector defines a label selector to match against all clusters registered with ArgoCD. // Clusters today are stored as Kubernetes Secrets, thus the Secret labels will be used // for matching the selector. - optional .k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 1; optional ApplicationSetTemplate template = 2; @@ -873,7 +810,7 @@ message ClusterInfo { // ClusterList is a collection of Clusters. message ClusterList { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; repeated Cluster items = 2; } @@ -920,13 +857,6 @@ message ConfigManagementPlugin { optional bool lockRepo = 4; } -// Utility struct for a reference to a configmap key. -message ConfigMapKeyRef { - optional string configMapName = 1; - - optional string key = 2; -} - // ConnectionState contains information about remote resource connection state, currently used for clusters and repositories message ConnectionState { // Status contains the current status indicator for the connection @@ -936,7 +866,7 @@ message ConnectionState { optional string message = 2; // ModifiedAt contains the timestamp when this connection status has been determined - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time attemptedAt = 3; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time attemptedAt = 3; } // DuckType defines a generator to match against clusters registered with ArgoCD. @@ -951,7 +881,7 @@ message DuckTypeGenerator { optional int64 requeueAfterSeconds = 3; - optional .k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector labelSelector = 4; + optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector labelSelector = 4; optional ApplicationSetTemplate template = 5; @@ -1042,7 +972,7 @@ message GnuPGPublicKey { // GnuPGPublicKeyList is a collection of GnuPGPublicKey objects message GnuPGPublicKeyList { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; repeated GnuPGPublicKey items = 2; } @@ -1090,7 +1020,7 @@ message HostInfo { repeated HostResourceInfo resourcesInfo = 2; - optional .k8s.io.api.core.v1.NodeSystemInfo systemInfo = 3; + optional k8s.io.api.core.v1.NodeSystemInfo systemInfo = 3; } // TODO: describe this type @@ -1183,7 +1113,7 @@ message KustomizeReplica { optional string name = 1; // Number of replicas - optional .k8s.io.apimachinery.pkg.util.intstr.IntOrString count = 2; + optional k8s.io.apimachinery.pkg.util.intstr.IntOrString count = 2; } message KustomizeResId { @@ -1205,7 +1135,7 @@ message KustomizeSelector { // ListGenerator include items info message ListGenerator { // +kubebuilder:validation:Optional - repeated .k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.JSON elements = 1; + repeated k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.JSON elements = 1; optional ApplicationSetTemplate template = 2; @@ -1307,10 +1237,10 @@ message OperationState { optional SyncOperationResult syncResult = 4; // StartedAt contains time of operation start - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time startedAt = 6; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time startedAt = 6; // FinishedAt contains time of operation completion - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time finishedAt = 7; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time finishedAt = 7; // RetryCount contains time of operation retries optional int64 retryCount = 8; @@ -1384,7 +1314,7 @@ message PluginGenerator { 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; + map parameters = 1; } // ProjectRole represents a role that has access to a project @@ -1482,15 +1412,6 @@ message PullRequestGeneratorBitbucketServer { // Credentials for Basic auth optional BasicAuthBitbucketServer basicAuth = 4; - - // Credentials for AccessToken (Bearer auth) - optional BearerTokenBitbucket bearerToken = 5; - - // Allow self-signed TLS / Certificates; default: false - optional bool insecure = 6; - - // ConfigMap key holding the trusted certificates - optional ConfigMapKeyRef caRef = 7; } // PullRequestGeneratorFilter is a single pull request filter. @@ -1521,9 +1442,6 @@ message PullRequestGeneratorGitLab { // Skips validating the SCM provider's TLS certificate - useful for self-signed certificates.; default: false optional bool insecure = 6; - - // ConfigMap key holding the trusted certificates - optional ConfigMapKeyRef caRef = 7; } // PullRequestGeneratorGitea defines connection info specific to Gitea. @@ -1575,7 +1493,7 @@ message RefTarget { // RepoCreds holds the definition for repository credentials message RepoCreds { - // URL is the URL to which these credentials match + // URL is the URL that this credentials matches to optional string url = 1; // Username for authenticating at the repo server @@ -1619,14 +1537,11 @@ message RepoCreds { // ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections optional bool forceHttpBasicAuth = 20; - - // NoProxy specifies a list of targets where the proxy isn't used, applies only in cases where the proxy is applied - optional string noProxy = 23; } // RepositoryList is a collection of Repositories. message RepoCredsList { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; repeated RepoCreds items = 2; } @@ -1691,7 +1606,7 @@ message Repository { // Proxy specifies the HTTP/HTTPS proxy used to access the repo optional string proxy = 19; - // Reference between project and repository that allows it to be automatically added as an item inside SourceRepos project entity + // Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity optional string project = 20; // GCPServiceAccountKey specifies the service account key in JSON format to be used for getting credentials to Google Cloud Source repos @@ -1699,9 +1614,6 @@ message Repository { // ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections optional bool forceHttpBasicAuth = 22; - - // NoProxy specifies a list of targets where the proxy isn't used, applies only in cases where the proxy is applied - optional string noProxy = 23; } // A RepositoryCertificate is either SSH known hosts entry or TLS certificate @@ -1724,7 +1636,7 @@ message RepositoryCertificate { // RepositoryCertificateList is a collection of RepositoryCertificates message RepositoryCertificateList { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; // List of certificates to be processed repeated RepositoryCertificate items = 2; @@ -1732,7 +1644,7 @@ message RepositoryCertificateList { // RepositoryList is a collection of Repositories. message RepositoryList { - optional .k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; repeated Repository items = 2; } @@ -1777,8 +1689,6 @@ message ResourceActions { optional string actionDiscoveryLua = 1; repeated ResourceActionDefinition definitions = 2; - - optional bool mergeBuiltinActions = 3; } // ResourceDiff holds the diff of a live and target resource object @@ -1843,7 +1753,7 @@ message ResourceNetworkingInfo { map labels = 3; - repeated .k8s.io.api.core.v1.LoadBalancerIngress ingress = 4; + repeated k8s.io.api.core.v1.LoadBalancerIngress ingress = 4; // ExternalURLs holds list of URLs which should be available externally. List is populated for ingress resources using rules hostnames. repeated string externalURLs = 5; @@ -1866,7 +1776,7 @@ message ResourceNode { optional HealthStatus health = 7; - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time createdAt = 8; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time createdAt = 8; } // ResourceOverride holds configuration to customize resource diffing and health assessment @@ -1973,7 +1883,7 @@ message RevisionHistory { optional string revision = 2; // DeployedAt holds the time the sync operation completed - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time deployedAt = 4; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time deployedAt = 4; // ID is an auto incrementing identifier of the RevisionHistory optional int64 id = 5; @@ -1982,16 +1892,13 @@ message RevisionHistory { optional ApplicationSource source = 6; // DeployStartedAt holds the time the sync operation started - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time deployStartedAt = 7; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time deployStartedAt = 7; // Sources is a reference to the application sources used for the sync operation repeated ApplicationSource sources = 8; // 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 @@ -2002,7 +1909,7 @@ message RevisionMetadata { optional string author = 1; // Date specifies when the revision was authored - optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time date = 2; + optional k8s.io.apimachinery.pkg.apis.meta.v1.Time date = 2; // Tags specifies any tags currently attached to the revision // Floating tags can move from one revision to another @@ -2111,15 +2018,6 @@ message SCMProviderGeneratorBitbucketServer { // Scan all branches instead of just the default branch. optional bool allBranches = 4; - - // Credentials for AccessToken (Bearer auth) - optional BearerTokenBitbucket bearerToken = 5; - - // Allow self-signed TLS / Certificates; default: false - optional bool insecure = 6; - - // ConfigMap key holding the trusted certificates - optional ConfigMapKeyRef caRef = 7; } // SCMProviderGeneratorFilter is a single repository filter. @@ -2203,9 +2101,6 @@ message SCMProviderGeneratorGitlab { // Filter repos list based on Gitlab Topic. optional string topic = 8; - - // ConfigMap key holding the trusted certificates - optional ConfigMapKeyRef caRef = 9; } // Utility struct for a reference to a secret key. diff --git a/pkg/apis/application/v1alpha1/openapi_generated.go b/pkg/apis/application/v1alpha1/openapi_generated.go index 1b2533532bc0e..8b0ab9c602535 100644 --- a/pkg/apis/application/v1alpha1/openapi_generated.go +++ b/pkg/apis/application/v1alpha1/openapi_generated.go @@ -22,7 +22,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "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.ApplicationDestinationServiceAccount": schema_pkg_apis_application_v1alpha1_ApplicationDestinationServiceAccount(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), @@ -42,7 +41,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "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.ApplicationSetTree": schema_pkg_apis_application_v1alpha1_ApplicationSetTree(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), @@ -57,7 +55,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "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.BearerTokenBitbucket": schema_pkg_apis_application_v1alpha1_BearerTokenBitbucket(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), @@ -70,7 +67,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "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.ConfigMapKeyRef": schema_pkg_apis_application_v1alpha1_ConfigMapKeyRef(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), @@ -196,13 +192,6 @@ 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: "", - }, - }, }, }, }, @@ -474,25 +463,11 @@ func schema_pkg_apis_application_v1alpha1_AppProjectSpec(ref common.ReferenceCal Format: "", }, }, - "destinationServiceAccounts": { - SchemaProps: spec.SchemaProps{ - Description: "DestinationServiceAccounts holds information about the service accounts to be impersonated for the application sync operation for each destination.", - 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.ApplicationDestinationServiceAccount"), - }, - }, - }, - }, - }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationDestination", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationDestinationServiceAccount", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OrphanedResourcesMonitorSettings", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ProjectRole", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SignatureKey", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncWindow", "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationDestination", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OrphanedResourcesMonitorSettings", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ProjectRole", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SignatureKey", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncWindow", "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind"}, } } @@ -651,40 +626,6 @@ func schema_pkg_apis_application_v1alpha1_ApplicationDestination(ref common.Refe } } -func schema_pkg_apis_application_v1alpha1_ApplicationDestinationServiceAccount(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ApplicationDestinationServiceAccount holds information about the service account to be impersonated for the application sync operation.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "server": { - SchemaProps: spec.SchemaProps{ - Description: "Server specifies the URL of the target cluster's Kubernetes control plane API.", - Type: []string{"string"}, - Format: "", - }, - }, - "namespace": { - SchemaProps: spec.SchemaProps{ - Description: "Namespace specifies the target namespace for the application's resources.", - Type: []string{"string"}, - Format: "", - }, - }, - "defaultServiceAccount": { - SchemaProps: spec.SchemaProps{ - Description: "ServiceAccountName to be used for impersonation during the sync operation", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - func schema_pkg_apis_application_v1alpha1_ApplicationList(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -905,23 +846,8 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetApplicationStatus(ref co Format: "", }, }, - "targetRevisions": { - SchemaProps: spec.SchemaProps{ - Description: "TargetRevision tracks the desired revisions the Application should be synced to.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, }, - Required: []string{"application", "message", "status", "step", "targetRevisions"}, + Required: []string{"application", "message", "status", "step"}, }, }, Dependencies: []string{ @@ -933,7 +859,7 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetCondition(ref common.Ref return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "ApplicationSetCondition contains details about an applicationset condition, which is usually an error or warning", + Description: "ApplicationSetCondition contains details about an applicationset condition, which is usally an error or warning", Type: []string{"object"}, Properties: map[string]spec.Schema{ "type": { @@ -1405,25 +1331,11 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetStatus(ref common.Refere }, }, }, - "resources": { - SchemaProps: spec.SchemaProps{ - Description: "Resources is a list of Applications resources managed by this application set.", - 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.ResourceStatus"), - }, - }, - }, - }, - }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetApplicationStatus", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetCondition", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceStatus"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetApplicationStatus", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetCondition"}, } } @@ -1633,35 +1545,6 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetTerminalGenerator(ref co } } -func schema_pkg_apis_application_v1alpha1_ApplicationSetTree(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ApplicationSetTree holds nodes which belongs to the application Used to build a tree of an ApplicationSet and its children", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "nodes": { - SchemaProps: spec.SchemaProps{ - Description: "Nodes contains list of nodes which are directly managed by the applicationset", - 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.ResourceNode"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceNode"}, - } -} - func schema_pkg_apis_application_v1alpha1_ApplicationSource(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1884,35 +1767,6 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSourceHelm(ref common.Refer Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), }, }, - "namespace": { - SchemaProps: spec.SchemaProps{ - Description: "Namespace is an optional namespace to template with. If left empty, defaults to the app's destination namespace.", - Type: []string{"string"}, - Format: "", - }, - }, - "kubeVersion": { - SchemaProps: spec.SchemaProps{ - Description: "KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD uses the Kubernetes version of the target cluster.", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersions": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, Argo CD uses the API versions of the target cluster. The format is [group/]version/kind.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, }, }, }, @@ -2125,35 +1979,6 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSourceKustomize(ref common. }, }, }, - "labelWithoutSelector": { - SchemaProps: spec.SchemaProps{ - Description: "LabelWithoutSelector specifies whether to apply common labels to resource selectors or not", - Type: []string{"boolean"}, - Format: "", - }, - }, - "kubeVersion": { - SchemaProps: spec.SchemaProps{ - Description: "KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD uses the Kubernetes version of the target cluster.", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersions": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, Argo CD uses the API versions of the target cluster. The format is [group/]version/kind.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, }, }, }, @@ -2652,28 +2477,6 @@ func schema_pkg_apis_application_v1alpha1_BasicAuthBitbucketServer(ref common.Re } } -func schema_pkg_apis_application_v1alpha1_BearerTokenBitbucket(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "BearerTokenBitbucket defines the Bearer token for BitBucket AppToken auth.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "tokenRef": { - SchemaProps: spec.SchemaProps{ - 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_BearerTokenBitbucketCloud(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2769,14 +2572,14 @@ func schema_pkg_apis_application_v1alpha1_Cluster(ref common.ReferenceCallback) }, "connectionState": { SchemaProps: spec.SchemaProps{ - Description: "Deprecated: use Info.ConnectionState field instead. ConnectionState contains information about cluster connection state", + Description: "DEPRECATED: use Info.ConnectionState field instead. ConnectionState contains information about cluster connection state", Default: map[string]interface{}{}, Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConnectionState"), }, }, "serverVersion": { SchemaProps: spec.SchemaProps{ - Description: "Deprecated: use Info.ServerVersion field instead. The server version", + Description: "DEPRECATED: use Info.ServerVersion field instead. The server version", Type: []string{"string"}, Format: "", }, @@ -3273,34 +3076,6 @@ func schema_pkg_apis_application_v1alpha1_ConfigManagementPlugin(ref common.Refe } } -func schema_pkg_apis_application_v1alpha1_ConfigMapKeyRef(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Utility struct for a reference to a configmap key.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "configMapName": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "key": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"configMapName", "key"}, - }, - }, - } -} - func schema_pkg_apis_application_v1alpha1_ConnectionState(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -4248,6 +4023,7 @@ func schema_pkg_apis_application_v1alpha1_KustomizeReplica(ref common.ReferenceC "count": { SchemaProps: spec.SchemaProps{ Description: "Number of replicas", + Default: map[string]interface{}{}, Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), }, }, @@ -4369,7 +4145,8 @@ func schema_pkg_apis_application_v1alpha1_ListGenerator(ref common.ReferenceCall Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"), + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"), }, }, }, @@ -4707,6 +4484,7 @@ func schema_pkg_apis_application_v1alpha1_OperationState(ref common.ReferenceCal "startedAt": { SchemaProps: spec.SchemaProps{ Description: "StartedAt contains time of operation start", + Default: map[string]interface{}{}, Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, @@ -5005,7 +4783,8 @@ func schema_pkg_apis_application_v1alpha1_PluginInput(ref common.ReferenceCallba Allows: true, Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"), + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"), }, }, }, @@ -5324,31 +5103,12 @@ func schema_pkg_apis_application_v1alpha1_PullRequestGeneratorBitbucketServer(re Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BasicAuthBitbucketServer"), }, }, - "bearerToken": { - SchemaProps: spec.SchemaProps{ - Description: "Credentials for AccessToken (Bearer auth)", - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BearerTokenBitbucket"), - }, - }, - "insecure": { - SchemaProps: spec.SchemaProps{ - Description: "Allow self-signed TLS / Certificates; default: false", - Type: []string{"boolean"}, - Format: "", - }, - }, - "caRef": { - SchemaProps: spec.SchemaProps{ - Description: "ConfigMap key holding the trusted certificates", - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConfigMapKeyRef"), - }, - }, }, Required: []string{"project", "repo", "api"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BasicAuthBitbucketServer", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BearerTokenBitbucket", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConfigMapKeyRef"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BasicAuthBitbucketServer"}, } } @@ -5434,18 +5194,12 @@ func schema_pkg_apis_application_v1alpha1_PullRequestGeneratorGitLab(ref common. Format: "", }, }, - "caRef": { - SchemaProps: spec.SchemaProps{ - Description: "ConfigMap key holding the trusted certificates", - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConfigMapKeyRef"), - }, - }, }, Required: []string{"project"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConfigMapKeyRef", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SecretRef"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SecretRef"}, } } @@ -5613,7 +5367,7 @@ func schema_pkg_apis_application_v1alpha1_RepoCreds(ref common.ReferenceCallback Properties: map[string]spec.Schema{ "url": { SchemaProps: spec.SchemaProps{ - Description: "URL is the URL to which these credentials match", + Description: "URL is the URL that this credentials matches to", Default: "", Type: []string{"string"}, Format: "", @@ -5717,13 +5471,6 @@ func schema_pkg_apis_application_v1alpha1_RepoCreds(ref common.ReferenceCallback Format: "", }, }, - "noProxy": { - SchemaProps: spec.SchemaProps{ - Description: "NoProxy specifies a list of targets where the proxy isn't used, applies only in cases where the proxy is applied", - Type: []string{"string"}, - Format: "", - }, - }, }, Required: []string{"url"}, }, @@ -5909,7 +5656,7 @@ func schema_pkg_apis_application_v1alpha1_Repository(ref common.ReferenceCallbac }, "project": { SchemaProps: spec.SchemaProps{ - Description: "Reference between project and repository that allows it to be automatically added as an item inside SourceRepos project entity", + Description: "Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity", Type: []string{"string"}, Format: "", }, @@ -5928,13 +5675,6 @@ func schema_pkg_apis_application_v1alpha1_Repository(ref common.ReferenceCallbac Format: "", }, }, - "noProxy": { - SchemaProps: spec.SchemaProps{ - Description: "NoProxy specifies a list of targets where the proxy isn't used, applies only in cases where the proxy is applied", - Type: []string{"string"}, - Format: "", - }, - }, }, Required: []string{"repo"}, }, @@ -6903,6 +6643,7 @@ func schema_pkg_apis_application_v1alpha1_RevisionHistory(ref common.ReferenceCa "deployedAt": { SchemaProps: spec.SchemaProps{ Description: "DeployedAt holds the time the sync operation completed", + Default: map[string]interface{}{}, Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, @@ -6956,19 +6697,12 @@ 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", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OperationInitiator", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } @@ -6989,6 +6723,7 @@ func schema_pkg_apis_application_v1alpha1_RevisionMetadata(ref common.ReferenceC "date": { SchemaProps: spec.SchemaProps{ Description: "Date specifies when the revision was authored", + Default: map[string]interface{}{}, Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, @@ -7313,31 +7048,12 @@ func schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorBitbucketServer(re Format: "", }, }, - "bearerToken": { - SchemaProps: spec.SchemaProps{ - Description: "Credentials for AccessToken (Bearer auth)", - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BearerTokenBitbucket"), - }, - }, - "insecure": { - SchemaProps: spec.SchemaProps{ - Description: "Allow self-signed TLS / Certificates; default: false", - Type: []string{"boolean"}, - Format: "", - }, - }, - "caRef": { - SchemaProps: spec.SchemaProps{ - Description: "ConfigMap key holding the trusted certificates", - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConfigMapKeyRef"), - }, - }, }, Required: []string{"project", "api"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BasicAuthBitbucketServer", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BearerTokenBitbucket", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConfigMapKeyRef"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BasicAuthBitbucketServer"}, } } @@ -7571,18 +7287,12 @@ func schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorGitlab(ref common. Format: "", }, }, - "caRef": { - SchemaProps: spec.SchemaProps{ - Description: "ConfigMap key holding the trusted certificates", - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConfigMapKeyRef"), - }, - }, }, Required: []string{"group"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConfigMapKeyRef", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SecretRef"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SecretRef"}, } } diff --git a/pkg/apis/application/v1alpha1/repository_types.go b/pkg/apis/application/v1alpha1/repository_types.go index 5a30d24fbcfdb..31e8c47971414 100644 --- a/pkg/apis/application/v1alpha1/repository_types.go +++ b/pkg/apis/application/v1alpha1/repository_types.go @@ -3,7 +3,6 @@ package v1alpha1 import ( "fmt" "net/url" - "strings" "github.com/argoproj/argo-cd/v2/util/cert" "github.com/argoproj/argo-cd/v2/util/git" @@ -15,7 +14,7 @@ import ( // RepoCreds holds the definition for repository credentials type RepoCreds struct { - // URL is the URL to which these credentials match + // URL is the URL that this credentials matches to URL string `json:"url" protobuf:"bytes,1,opt,name=url"` // Username for authenticating at the repo server Username string `json:"username,omitempty" protobuf:"bytes,2,opt,name=username"` @@ -45,8 +44,6 @@ type RepoCreds struct { 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"` - // NoProxy specifies a list of targets where the proxy isn't used, applies only in cases where the proxy is applied - NoProxy string `json:"noProxy,omitempty" protobuf:"bytes,23,opt,name=noProxy"` } // Repository is a repository holding application configurations @@ -90,14 +87,12 @@ type Repository struct { GitHubAppEnterpriseBaseURL string `json:"githubAppEnterpriseBaseUrl,omitempty" protobuf:"bytes,18,opt,name=githubAppEnterpriseBaseUrl"` // Proxy specifies the HTTP/HTTPS proxy used to access the repo Proxy string `json:"proxy,omitempty" protobuf:"bytes,19,opt,name=proxy"` - // Reference between project and repository that allows it to be automatically added as an item inside SourceRepos project entity + // Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity 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"` - // NoProxy specifies a list of targets where the proxy isn't used, applies only in cases where the proxy is applied - NoProxy string `json:"noProxy,omitempty" protobuf:"bytes,23,opt,name=noProxy"` } // IsInsecure returns true if the repository has been configured to skip server verification @@ -188,9 +183,6 @@ func (repo *Repository) CopyCredentialsFrom(source *RepoCreds) { if repo.Proxy == "" { repo.Proxy = source.Proxy } - if repo.NoProxy == "" { - repo.NoProxy = source.NoProxy - } repo.ForceHttpBasicAuth = source.ForceHttpBasicAuth } } @@ -201,13 +193,13 @@ 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, repo.NoProxy, store, repo.ForceHttpBasicAuth) + 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, repo.Proxy, repo.NoProxy) + return git.NewSSHCreds(repo.SSHPrivateKey, getCAPath(repo.Repo), repo.IsInsecure(), store) } 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, repo.NoProxy, store) + 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, store) @@ -235,22 +227,21 @@ func getCAPath(repoURL string) string { } hostname := "" - var parsedURL *url.URL - var err error - // Without schema in url, url.Parse() treats the url as differently - // and may incorrectly parses the hostname if url contains a path or port. - // To ensure proper parsing, prepend a dummy schema. - if !strings.Contains(repoURL, "://") { - parsedURL, err = url.Parse("protocol://" + repoURL) - } else { - parsedURL, err = url.Parse(repoURL) - } + // url.Parse() will happily parse most things thrown at it. When the URL + // is either https or oci, we use the parsed hostname to retrieve the cert, + // otherwise we'll use the parsed path (OCI repos are often specified as + // hostname, without protocol). + parsedURL, err := url.Parse(repoURL) if err != nil { log.Warnf("Could not parse repo URL '%s': %v", repoURL, err) return "" } + if parsedURL.Scheme == "https" || parsedURL.Scheme == "oci" { + hostname = parsedURL.Host + } else if parsedURL.Scheme == "" { + hostname = parsedURL.Path + } - hostname = parsedURL.Hostname() if hostname == "" { log.Warnf("Could not get hostname for repository '%s'", repoURL) return "" diff --git a/pkg/apis/application/v1alpha1/types.go b/pkg/apis/application/v1alpha1/types.go index 1337bd8c72772..49be82a443bc4 100644 --- a/pkg/apis/application/v1alpha1/types.go +++ b/pkg/apis/application/v1alpha1/types.go @@ -51,7 +51,6 @@ import ( // +kubebuilder:printcolumn:name="Sync Status",type=string,JSONPath=`.status.sync.status` // +kubebuilder:printcolumn:name="Health Status",type=string,JSONPath=`.status.health.status` // +kubebuilder:printcolumn:name="Revision",type=string,JSONPath=`.status.sync.revision`,priority=10 -// +kubebuilder:printcolumn:name="Project",type=string,JSONPath=`.spec.project`,priority=10 type Application struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata" protobuf:"bytes,1,opt,name=metadata"` @@ -206,11 +205,6 @@ func (s ApplicationSources) Equals(other ApplicationSources) bool { return true } -// IsZero returns true if the application source is considered empty -func (a ApplicationSources) IsZero() bool { - return len(a) == 0 -} - func (a *ApplicationSpec) GetSource() ApplicationSource { // if Application has multiple sources, return the first source in sources if a.HasMultipleSources() { @@ -233,20 +227,12 @@ func (a *ApplicationSpec) GetSources() ApplicationSources { } func (a *ApplicationSpec) HasMultipleSources() bool { - return len(a.Sources) > 0 + return a.Sources != nil && len(a.Sources) > 0 } -func (a *ApplicationSpec) GetSourcePtrByPosition(sourcePosition int) *ApplicationSource { - // if Application has multiple sources, return the first source in sources - return a.GetSourcePtrByIndex(sourcePosition - 1) -} - -func (a *ApplicationSpec) GetSourcePtrByIndex(sourceIndex int) *ApplicationSource { +func (a *ApplicationSpec) GetSourcePtr() *ApplicationSource { // if Application has multiple sources, return the first source in sources if a.HasMultipleSources() { - if sourceIndex > 0 { - return &a.Sources[sourceIndex] - } return &a.Sources[0] } return a.Source @@ -262,11 +248,6 @@ func (a *ApplicationSource) AllowsConcurrentProcessing() bool { return true } -// IsRef returns true when the application source is of type Ref -func (a *ApplicationSource) IsRef() bool { - return a.Ref != "" -} - // IsHelm returns true when the application source is of type Helm func (a *ApplicationSource) IsHelm() bool { return a.Chart != "" @@ -292,51 +273,6 @@ func (a *ApplicationSource) IsZero() bool { a.Plugin.IsZero() } -// GetNamespaceOrDefault gets the static namespace configured in the source. If none is configured, returns the given -// default. -func (a *ApplicationSource) GetNamespaceOrDefault(defaultNamespace string) string { - if a == nil { - return defaultNamespace - } - if a.Helm != nil && a.Helm.Namespace != "" { - return a.Helm.Namespace - } - if a.Kustomize != nil && a.Kustomize.Namespace != "" { - return a.Kustomize.Namespace - } - return defaultNamespace -} - -// GetKubeVersionOrDefault gets the static Kubernetes API version configured in the source. If none is configured, -// returns the given default. -func (a *ApplicationSource) GetKubeVersionOrDefault(defaultKubeVersion string) string { - if a == nil { - return defaultKubeVersion - } - if a.Helm != nil && a.Helm.KubeVersion != "" { - return a.Helm.KubeVersion - } - if a.Kustomize != nil && a.Kustomize.KubeVersion != "" { - return a.Kustomize.KubeVersion - } - return defaultKubeVersion -} - -// GetAPIVersionsOrDefault gets the static API versions list configured in the source. If none is configured, returns -// the given default. -func (a *ApplicationSource) GetAPIVersionsOrDefault(defaultAPIVersions []string) []string { - if a == nil { - return defaultAPIVersions - } - if a.Helm != nil && len(a.Helm.APIVersions) > 0 { - return a.Helm.APIVersions - } - if a.Kustomize != nil && len(a.Kustomize.APIVersions) > 0 { - return a.Kustomize.APIVersions - } - return defaultAPIVersions -} - // ApplicationSourceType specifies the type of the application's source type ApplicationSourceType string @@ -387,14 +323,6 @@ type ApplicationSourceHelm struct { // 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"` - // Namespace is an optional namespace to template with. If left empty, defaults to the app's destination namespace. - Namespace string `json:"namespace,omitempty" protobuf:"bytes,11,opt,name=namespace"` - // KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - // uses the Kubernetes version of the target cluster. - KubeVersion string `json:"kubeVersion,omitempty" protobuf:"bytes,12,opt,name=kubeVersion"` - // APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - // Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - APIVersions []string `json:"apiVersions,omitempty" protobuf:"bytes,13,opt,name=apiVersions"` } // HelmParameter is a parameter that's passed to helm template during manifest generation @@ -476,7 +404,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.ValuesIsEmpty() && !h.PassCredentials && !h.IgnoreMissingValueFiles && !h.SkipCrds && h.KubeVersion == "" && len(h.APIVersions) == 0 && h.Namespace == "" + 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=]: @@ -541,14 +469,6 @@ type ApplicationSourceKustomize struct { 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"` - // KubeVersion specifies the Kubernetes API version to pass to Helm when templating manifests. By default, Argo CD - // uses the Kubernetes version of the target cluster. - KubeVersion string `json:"kubeVersion,omitempty" protobuf:"bytes,15,opt,name=kubeVersion"` - // APIVersions specifies the Kubernetes resource API versions to pass to Helm when templating manifests. By default, - // Argo CD uses the API versions of the target cluster. The format is [group/]version/kind. - APIVersions []string `json:"apiVersions,omitempty" protobuf:"bytes,16,opt,name=apiVersions"` } type KustomizeReplica struct { @@ -654,9 +574,7 @@ func (k *ApplicationSourceKustomize) IsZero() bool { len(k.CommonLabels) == 0 && len(k.CommonAnnotations) == 0 && len(k.Patches) == 0 && - len(k.Components) == 0 && - k.KubeVersion == "" && - len(k.APIVersions) == 0 + len(k.Components) == 0 } // MergeImage merges a new Kustomize image identifier in to a list of images @@ -669,7 +587,7 @@ func (k *ApplicationSourceKustomize) MergeImage(image KustomizeImage) { } } -// MergeReplica merges a new Kustomize replica identifier in to a list of replicas +// 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 { @@ -1299,6 +1217,7 @@ func (r *RetryStrategy) NextRetryAt(lastAttempt time.Time, retryCounts int64) (t if r.Backoff.Factor != nil { factor = *r.Backoff.Factor } + } // Formula: timeToWait = duration * factor^retry_number // Note that timeToWait should equal to duration for the first retry attempt. @@ -1482,8 +1401,6 @@ 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. @@ -1520,7 +1437,7 @@ type SyncStatusCode string const ( // SyncStatusCodeUnknown indicates that the status of a sync could not be reliably determined SyncStatusCodeUnknown SyncStatusCode = "Unknown" - // SyncStatusCodeSynced indicates that desired and live states match + // SyncStatusCodeOutOfSync indicates that desired and live states match SyncStatusCodeSynced SyncStatusCode = "Synced" // SyncStatusCodeOutOfSync indicates that there is a drift between desired and live states SyncStatusCodeOutOfSync SyncStatusCode = "OutOfSync" @@ -1640,60 +1557,6 @@ type ApplicationTree struct { OrphanedNodes []ResourceNode `json:"orphanedNodes,omitempty" protobuf:"bytes,2,rep,name=orphanedNodes"` // Hosts holds list of Kubernetes nodes that run application related pods Hosts []HostInfo `json:"hosts,omitempty" protobuf:"bytes,3,rep,name=hosts"` - // ShardsCount contains total number of shards the application tree is split into - ShardsCount int64 `json:"shardsCount,omitempty" protobuf:"bytes,4,opt,name=shardsCount"` -} - -func (t *ApplicationTree) Merge(other *ApplicationTree) { - t.Nodes = append(t.Nodes, other.Nodes...) - t.OrphanedNodes = append(t.OrphanedNodes, other.OrphanedNodes...) - t.Hosts = append(t.Hosts, other.Hosts...) - t.Normalize() -} - -// GetShards split application tree into shards with populated metadata -func (t *ApplicationTree) GetShards(size int64) []*ApplicationTree { - t.Normalize() - if size == 0 { - return []*ApplicationTree{t} - } - - var items []func(*ApplicationTree) - for i := range t.Nodes { - item := t.Nodes[i] - items = append(items, func(shard *ApplicationTree) { - shard.Nodes = append(shard.Nodes, item) - }) - } - for i := range t.OrphanedNodes { - item := t.OrphanedNodes[i] - items = append(items, func(shard *ApplicationTree) { - shard.OrphanedNodes = append(shard.OrphanedNodes, item) - }) - } - for i := range t.Hosts { - item := t.Hosts[i] - items = append(items, func(shard *ApplicationTree) { - shard.Hosts = append(shard.Hosts, item) - }) - } - var shards []*ApplicationTree - for len(items) > 0 { - shard := &ApplicationTree{} - shards = append(shards, shard) - cnt := 0 - for i := int64(0); i < size && i < int64(len(items)); i++ { - items[i](shard) - cnt++ - } - items = items[cnt:] - } - if len(shards) > 0 { - shards[0].ShardsCount = int64(len(shards)) - } else { - shards = []*ApplicationTree{{ShardsCount: 0}} - } - return shards } // Normalize sorts application tree nodes and hosts. The persistent order allows to @@ -1818,7 +1681,7 @@ type ResourceStatus struct { SyncWave int64 `json:"syncWave,omitempty" protobuf:"bytes,10,opt,name=syncWave"` } -// GroupVersionKind returns the GVK schema type for given resource status +// GroupKindVersion returns the GVK schema type for given resource status func (r *ResourceStatus) GroupVersionKind() schema.GroupVersionKind { return schema.GroupVersionKind{Group: r.Group, Version: r.Version, Kind: r.Kind} } @@ -1884,10 +1747,10 @@ type Cluster struct { Name string `json:"name" protobuf:"bytes,2,opt,name=name"` // Config holds cluster information for connecting to a cluster Config ClusterConfig `json:"config" protobuf:"bytes,3,opt,name=config"` - // Deprecated: use Info.ConnectionState field instead. + // DEPRECATED: use Info.ConnectionState field instead. // ConnectionState contains information about cluster connection state ConnectionState ConnectionState `json:"connectionState,omitempty" protobuf:"bytes,4,opt,name=connectionState"` - // Deprecated: use Info.ServerVersion field instead. + // DEPRECATED: use Info.ServerVersion field instead. // The server version ServerVersion string `json:"serverVersion,omitempty" protobuf:"bytes,5,opt,name=serverVersion"` // Holds list of namespaces which are accessible in that cluster. Cluster level resources will be ignored if namespace list is not empty. @@ -1991,9 +1854,6 @@ 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 @@ -2144,9 +2004,8 @@ func (o *ResourceOverride) GetActions() (ResourceActions, error) { // TODO: describe this type // TODO: describe members of this type type ResourceActions struct { - ActionDiscoveryLua string `json:"discovery.lua,omitempty" yaml:"discovery.lua,omitempty" protobuf:"bytes,1,opt,name=actionDiscoveryLua"` - Definitions []ResourceActionDefinition `json:"definitions,omitempty" protobuf:"bytes,2,rep,name=definitions"` - MergeBuiltinActions bool `json:"mergeBuiltinActions,omitempty" yaml:"mergeBuiltinActions,omitempty" protobuf:"bytes,3,opt,name=mergeBuiltinActions"` + ActionDiscoveryLua string `json:"discovery.lua,omitempty" yaml:"discovery.lua,omitempty" protobuf:"bytes,1,opt,name=actionDiscoveryLua"` + Definitions []ResourceActionDefinition `json:"definitions,omitempty" protobuf:"bytes,2,rep,name=definitions"` } // TODO: describe this type @@ -2188,8 +2047,6 @@ var validActions = map[string]bool{ var validActionPatterns = []*regexp.Regexp{ regexp.MustCompile("action/.*"), - regexp.MustCompile("update/.*"), - regexp.MustCompile("delete/.*"), } func isValidAction(action string) bool { @@ -2217,12 +2074,6 @@ func isValidResource(resource string) bool { return validResources[resource] } -func isValidObject(proj string, object string) bool { - // match against [/]/ - objectRegexp, err := regexp.Compile(fmt.Sprintf(`^%s(/[*\w-.]+)?/[*\w-.]+$`, regexp.QuoteMeta(proj))) - return objectRegexp.MatchString(object) && err == nil -} - func validatePolicy(proj string, role string, policy string) error { policyComponents := strings.Split(policy, ",") if len(policyComponents) != 6 || strings.Trim(policyComponents[0], " ") != "p" { @@ -2246,8 +2097,9 @@ func validatePolicy(proj string, role string, policy string) error { } // object object := strings.Trim(policyComponents[4], " ") - if !isValidObject(proj, object) { - return status.Errorf(codes.InvalidArgument, "invalid policy rule '%s': object must be of form '%s/*', '%s[/]/' or '%s/', not '%s'", policy, proj, proj, proj, object) + objectRegexp, err := regexp.Compile(fmt.Sprintf(`^%s/[*\w-.]+$`, regexp.QuoteMeta(proj))) + if err != nil || !objectRegexp.MatchString(object) { + return status.Errorf(codes.InvalidArgument, "invalid policy rule '%s': object must be of form '%s/*' or '%s/', not '%s'", policy, proj, proj, object) } // effect effect := strings.Trim(policyComponents[5], " ") @@ -2346,8 +2198,6 @@ type AppProjectSpec struct { SourceNamespaces []string `json:"sourceNamespaces,omitempty" protobuf:"bytes,12,opt,name=sourceNamespaces"` // PermitOnlyProjectScopedClusters determines whether destinations can only reference clusters which are project-scoped PermitOnlyProjectScopedClusters bool `json:"permitOnlyProjectScopedClusters,omitempty" protobuf:"bytes,13,opt,name=permitOnlyProjectScopedClusters"` - // DestinationServiceAccounts holds information about the service accounts to be impersonated for the application sync operation for each destination. - DestinationServiceAccounts []ApplicationDestinationServiceAccount `json:"destinationServiceAccounts,omitempty" protobuf:"bytes,14,name=destinationServiceAccounts"` } // SyncWindows is a collection of sync windows in this project @@ -2384,6 +2234,7 @@ func (s *SyncWindows) Active() *SyncWindows { } func (s *SyncWindows) active(currentTime time.Time) *SyncWindows { + // If SyncWindows.Active() is called outside of a UTC locale, it should be // first converted to UTC before we scan through the SyncWindows. currentTime = currentTime.In(time.UTC) @@ -2417,6 +2268,7 @@ func (s *SyncWindows) InactiveAllows() *SyncWindows { } func (s *SyncWindows) inactiveAllows(currentTime time.Time) *SyncWindows { + // If SyncWindows.InactiveAllows() is called outside of a UTC locale, it should be // first converted to UTC before we scan through the SyncWindows. currentTime = currentTime.In(time.UTC) @@ -2458,6 +2310,7 @@ func (w *SyncWindow) scheduleOffsetByTimeZone() time.Duration { func (s *AppProjectSpec) AddWindow(knd string, sch string, dur string, app []string, ns []string, cl []string, ms bool, timeZone string) error { if len(knd) == 0 || len(sch) == 0 || len(dur) == 0 { return fmt.Errorf("cannot create window: require kind, schedule, duration and one or more of applications, namespaces and clusters") + } window := &SyncWindow{ @@ -2486,6 +2339,7 @@ func (s *AppProjectSpec) AddWindow(knd string, sch string, dur string, app []str s.SyncWindows = append(s.SyncWindows, window) return nil + } // DeleteWindow deletes a sync window with the given id from the AppProject @@ -2591,8 +2445,10 @@ func (w *SyncWindows) hasDeny() (bool, bool) { if a.Kind == "deny" { if !denyFound { manualEnabled = a.ManualSync - } else if manualEnabled { - manualEnabled = a.ManualSync + } else { + if manualEnabled { + manualEnabled = a.ManualSync + } } denyFound = true } @@ -2634,6 +2490,7 @@ func (w SyncWindow) Active() bool { } func (w SyncWindow) active(currentTime time.Time) bool { + // If SyncWindow.Active() is called outside of a UTC locale, it should be // first converted to UTC before search currentTime = currentTime.UTC() @@ -2651,6 +2508,7 @@ func (w SyncWindow) active(currentTime time.Time) bool { // Update updates a sync window's settings with the given parameter func (w *SyncWindow) Update(s string, d string, a []string, n []string, c []string, tz string) error { + if len(s) == 0 && len(d) == 0 && len(a) == 0 && len(n) == 0 && len(c) == 0 { return fmt.Errorf("cannot update: require one or more of schedule, duration, application, namespace, or cluster") } @@ -2681,6 +2539,7 @@ func (w *SyncWindow) Update(s string, d string, a []string, n []string, c []stri // Validate checks whether a sync window has valid configuration. The error returned indicates any problems that has been found. func (w *SyncWindow) Validate() error { + // Default timeZone to UTC if timeZone is not specified if w.TimeZone == "" { w.TimeZone = "UTC" @@ -2696,11 +2555,11 @@ func (w *SyncWindow) Validate() error { specParser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow) _, err := specParser.Parse(w.Schedule) if err != nil { - return fmt.Errorf("cannot parse schedule '%s': %w", w.Schedule, err) + return fmt.Errorf("cannot parse schedule '%s': %s", w.Schedule, err) } _, err = time.ParseDuration(w.Duration) if err != nil { - return fmt.Errorf("cannot parse duration '%s': %w", w.Duration, err) + return fmt.Errorf("cannot parse duration '%s': %s", w.Duration, err) } return nil } @@ -2764,16 +2623,6 @@ type KustomizeOptions struct { BinaryPath string `protobuf:"bytes,2,opt,name=binaryPath"` } -// ApplicationDestinationServiceAccount holds information about the service account to be impersonated for the application sync operation. -type ApplicationDestinationServiceAccount struct { - // Server specifies the URL of the target cluster's Kubernetes control plane API. - Server string `json:"server,omitempty" protobuf:"bytes,1,opt,name=server"` - // Namespace specifies the target namespace for the application's resources. - Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,opt,name=namespace"` - // ServiceAccountName to be used for impersonation during the sync operation - DefaultServiceAccount string `json:"defaultServiceAccount,omitempty" protobuf:"bytes,3,opt,name=defaultServiceAccount"` -} - // CascadedDeletion indicates if the deletion finalizer is set and controller should delete the application and it's cascaded resources func (app *Application) CascadedDeletion() bool { for _, finalizer := range app.ObjectMeta.Finalizers { @@ -3136,9 +2985,6 @@ 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, @@ -3225,6 +3071,7 @@ func (r ResourceDiff) TargetObject() (*unstructured.Unstructured, error) { // SetInferredServer sets the Server field of the destination. See IsServerInferred() for details. func (d *ApplicationDestination) SetInferredServer(server string) { + d.isServerInferred = true d.Server = server } @@ -3279,15 +3126,3 @@ func (a *Application) QualifiedName() string { func (a *Application) RBACName(defaultNS string) string { return security.RBACName(defaultNS, a.Spec.GetProject(), a.Namespace, a.Name) } - -// GetAnnotation returns the value of the specified annotation if it exists, -// e.g., a.GetAnnotation("argocd.argoproj.io/manifest-generate-paths"). -// If the annotation does not exist, it returns an empty string. -func (a *Application) GetAnnotation(annotation string) string { - v, exists := a.Annotations[annotation] - if !exists { - return "" - } - - return v -} diff --git a/pkg/apis/application/v1alpha1/types_test.go b/pkg/apis/application/v1alpha1/types_test.go index 08b83c238a93d..b9fb26cf21875 100644 --- a/pkg/apis/application/v1alpha1/types_test.go +++ b/pkg/apis/application/v1alpha1/types_test.go @@ -7,13 +7,13 @@ import ( "os" "path" "reflect" + "strings" "testing" "time" - "github.com/stretchr/testify/require" - "k8s.io/utils/ptr" - argocdcommon "github.com/argoproj/argo-cd/v2/common" + "github.com/stretchr/testify/require" + "k8s.io/utils/pointer" "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/stretchr/testify/assert" @@ -111,85 +111,74 @@ func TestAppProject_IsDestinationPermitted(t *testing.T) { projDest []ApplicationDestination appDest ApplicationDestination isPermitted bool - }{ - { - projDest: []ApplicationDestination{{ - Server: "https://kubernetes.default.svc", Namespace: "default", - }}, - appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "default"}, - isPermitted: true, - }, - { - projDest: []ApplicationDestination{{ - Server: "https://kubernetes.default.svc", Namespace: "default", - }}, - appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "kube-system"}, - isPermitted: false, - }, - { - projDest: []ApplicationDestination{{ - Server: "https://my-cluster", Namespace: "default", - }}, - appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "default"}, - isPermitted: false, - }, - { - projDest: []ApplicationDestination{{ - Server: "https://kubernetes.default.svc", Namespace: "*", - }}, - appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "kube-system"}, - isPermitted: true, - }, - { - projDest: []ApplicationDestination{{ - Server: "https://*.default.svc", Namespace: "default", - }}, - appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "default"}, - isPermitted: true, - }, - { - projDest: []ApplicationDestination{{ - Server: "https://team1-*", Namespace: "default", - }}, - appDest: ApplicationDestination{Server: "https://test2-dev-cluster", Namespace: "default"}, - isPermitted: false, - }, - { - projDest: []ApplicationDestination{{ - Server: "https://kubernetes.default.svc", Namespace: "test-*", - }}, - appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "test-foo"}, - isPermitted: true, - }, - { - projDest: []ApplicationDestination{{ - Server: "https://kubernetes.default.svc", Namespace: "test-*", - }}, - appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "test"}, - isPermitted: false, - }, - { - projDest: []ApplicationDestination{{ - Server: "*", Namespace: "*", - }}, - appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "test"}, - isPermitted: true, - }, + }{{ + projDest: []ApplicationDestination{{ + Server: "https://kubernetes.default.svc", Namespace: "default", + }}, + appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "default"}, + isPermitted: true, + }, { + projDest: []ApplicationDestination{{ + Server: "https://kubernetes.default.svc", Namespace: "default", + }}, + appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "kube-system"}, + isPermitted: false, + }, { + projDest: []ApplicationDestination{{ + Server: "https://my-cluster", Namespace: "default", + }}, + appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "default"}, + isPermitted: false, + }, { + projDest: []ApplicationDestination{{ + Server: "https://kubernetes.default.svc", Namespace: "*", + }}, + appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "kube-system"}, + isPermitted: true, + }, { + projDest: []ApplicationDestination{{ + Server: "https://*.default.svc", Namespace: "default", + }}, + appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "default"}, + isPermitted: true, + }, { + projDest: []ApplicationDestination{{ + Server: "https://team1-*", Namespace: "default", + }}, + appDest: ApplicationDestination{Server: "https://test2-dev-cluster", Namespace: "default"}, + isPermitted: false, + }, { + projDest: []ApplicationDestination{{ + Server: "https://kubernetes.default.svc", Namespace: "test-*", + }}, + appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "test-foo"}, + isPermitted: true, + }, { + projDest: []ApplicationDestination{{ + Server: "https://kubernetes.default.svc", Namespace: "test-*", + }}, + appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "test"}, + isPermitted: false, + }, { + projDest: []ApplicationDestination{{ + Server: "*", Namespace: "*", + }}, + appDest: ApplicationDestination{Server: "https://kubernetes.default.svc", Namespace: "test"}, + isPermitted: true, + }, { projDest: []ApplicationDestination{{ Server: "", Namespace: "*", Name: "test", }}, appDest: ApplicationDestination{Name: "test", Namespace: "test"}, isPermitted: true, - }, - { + }, { projDest: []ApplicationDestination{{ Server: "", Namespace: "*", Name: "test2", }}, appDest: ApplicationDestination{Name: "test", Namespace: "test"}, isPermitted: false, - }, - } + }} for _, data := range testData { proj := AppProject{ @@ -438,8 +427,8 @@ func TestAppProject_IsDestinationPermitted_PermitOnlyProjectScopedClusters(t *te _, err := proj.IsDestinationPermitted(ApplicationDestination{Server: "https://my-cluster.123.com", Namespace: "default"}, func(_ string) ([]*Cluster, error) { return nil, errors.New("some error") }) - require.Error(t, err) - assert.Contains(t, err.Error(), "could not retrieve project clusters") + assert.NotNil(t, err) + assert.True(t, strings.Contains(err.Error(), "could not retrieve project clusters")) } func TestAppProject_IsGroupKindPermitted(t *testing.T) { @@ -497,14 +486,14 @@ func TestAppProject_GetRoleByName(t *testing.T) { t.Run("NotExists", func(t *testing.T) { p := &AppProject{} role, i, err := p.GetRoleByName("test-role") - require.Error(t, err) + assert.Error(t, err) assert.Equal(t, -1, i) assert.Nil(t, role) }) t.Run("NotExists", func(t *testing.T) { p := AppProject{Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role"}}}} role, i, err := p.GetRoleByName("test-role") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, 0, i) assert.Equal(t, &ProjectRole{Name: "test-role"}, role) }) @@ -514,20 +503,20 @@ func TestAppProject_AddGroupToRole(t *testing.T) { t.Run("NoRole", func(t *testing.T) { p := &AppProject{} got, err := p.AddGroupToRole("test-role", "test-group") - require.Error(t, err) + assert.Error(t, err) assert.False(t, got) }) t.Run("NoGroup", func(t *testing.T) { p := &AppProject{Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role", Groups: []string{}}}}} got, err := p.AddGroupToRole("test-role", "test-group") - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, got) assert.Len(t, p.Spec.Roles[0].Groups, 1) }) t.Run("Exists", func(t *testing.T) { p := &AppProject{Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role", Groups: []string{"test-group"}}}}} got, err := p.AddGroupToRole("test-role", "test-group") - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, got) }) } @@ -536,21 +525,21 @@ func TestAppProject_RemoveGroupFromRole(t *testing.T) { t.Run("NoRole", func(t *testing.T) { p := &AppProject{} got, err := p.RemoveGroupFromRole("test-role", "test-group") - require.Error(t, err) + assert.Error(t, err) assert.False(t, got) }) t.Run("NoGroup", func(t *testing.T) { p := &AppProject{Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role", Groups: []string{}}}}} got, err := p.RemoveGroupFromRole("test-role", "test-group") - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, got) }) t.Run("Exists", func(t *testing.T) { p := &AppProject{Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role", Groups: []string{"test-group"}}}}} got, err := p.RemoveGroupFromRole("test-role", "test-group") - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, got) - assert.Empty(t, p.Spec.Roles[0].Groups) + assert.Len(t, p.Spec.Roles[0].Groups, 0) }) } @@ -566,14 +555,14 @@ func newTestProject() *AppProject { func TestAppProject_ValidateSources(t *testing.T) { p := newTestProject() err := p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) badSources := []string{ "!*", } for _, badName := range badSources { p.Spec.SourceRepos = []string{badName} err = p.ValidateProject() - require.Error(t, err) + assert.Error(t, err) } duplicateSources := []string{ @@ -582,21 +571,21 @@ func TestAppProject_ValidateSources(t *testing.T) { } p.Spec.SourceRepos = duplicateSources err = p.ValidateProject() - require.Error(t, err) + assert.Error(t, err) } // TestAppProject_ValidateDestinations tests for an invalid destination func TestAppProject_ValidateDestinations(t *testing.T) { p := newTestProject() err := p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) badNamespaces := []string{ "!*", } for _, badName := range badNamespaces { p.Spec.Destinations[0].Namespace = badName err = p.ValidateProject() - require.Error(t, err) + assert.Error(t, err) } goodNamespaces := []string{ @@ -606,7 +595,7 @@ func TestAppProject_ValidateDestinations(t *testing.T) { for _, goodNamespace := range goodNamespaces { p.Spec.Destinations[0].Namespace = goodNamespace err = p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) } badServers := []string{ @@ -615,7 +604,7 @@ func TestAppProject_ValidateDestinations(t *testing.T) { for _, badServer := range badServers { p.Spec.Destinations[0].Server = badServer err = p.ValidateProject() - require.Error(t, err) + assert.Error(t, err) } goodServers := []string{ @@ -625,7 +614,7 @@ func TestAppProject_ValidateDestinations(t *testing.T) { for _, badName := range goodServers { p.Spec.Destinations[0].Server = badName err = p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) } badNames := []string{ @@ -634,7 +623,7 @@ func TestAppProject_ValidateDestinations(t *testing.T) { for _, badName := range badNames { p.Spec.Destinations[0].Name = badName err = p.ValidateProject() - require.Error(t, err) + assert.Error(t, err) } goodNames := []string{ @@ -644,7 +633,7 @@ func TestAppProject_ValidateDestinations(t *testing.T) { for _, goodName := range goodNames { p.Spec.Destinations[0].Name = goodName err = p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) } validDestination := ApplicationDestination{ @@ -654,12 +643,12 @@ func TestAppProject_ValidateDestinations(t *testing.T) { p.Spec.Destinations[0] = validDestination err = p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) // no duplicates allowed p.Spec.Destinations = []ApplicationDestination{validDestination, validDestination} err = p.ValidateProject() - require.Error(t, err) + assert.Error(t, err) cluster1Destination := ApplicationDestination{ Name: "cluster1", @@ -672,12 +661,12 @@ func TestAppProject_ValidateDestinations(t *testing.T) { // allow multiple destinations with blank server, same namespace but unique cluster name p.Spec.Destinations = []ApplicationDestination{cluster1Destination, cluster2Destination} err = p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) t.Run("must reject duplicate source namespaces", func(t *testing.T) { p.Spec.SourceNamespaces = []string{"argocd", "argocd"} err = p.ValidateProject() - require.Error(t, err) + assert.Error(t, err) }) } @@ -685,7 +674,7 @@ func TestAppProject_ValidateDestinations(t *testing.T) { func TestAppProject_ValidateRoleName(t *testing.T) { p := newTestProject() err := p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) badRoleNames := []string{ "", " ", @@ -701,7 +690,7 @@ func TestAppProject_ValidateRoleName(t *testing.T) { for _, badName := range badRoleNames { p.Spec.Roles[0].Name = badName err = p.ValidateProject() - require.Error(t, err) + assert.Error(t, err) } goodRoleNames := []string{ "MY-ROLE", @@ -710,7 +699,7 @@ func TestAppProject_ValidateRoleName(t *testing.T) { for _, goodName := range goodRoleNames { p.Spec.Roles[0].Name = goodName err = p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) } } @@ -718,10 +707,10 @@ func TestAppProject_ValidateRoleName(t *testing.T) { func TestAppProject_ValidateGroupName(t *testing.T) { p := newTestProject() err := p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) p.Spec.Roles[0].Groups = []string{"mygroup"} err = p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) badGroupNames := []string{ "", " ", @@ -735,7 +724,7 @@ func TestAppProject_ValidateGroupName(t *testing.T) { for _, badName := range badGroupNames { p.Spec.Roles[0].Groups = []string{badName} err = p.ValidateProject() - require.Error(t, err) + assert.Error(t, err) } goodGroupNames := []string{ "my:group", @@ -743,7 +732,7 @@ func TestAppProject_ValidateGroupName(t *testing.T) { for _, goodName := range goodGroupNames { p.Spec.Roles[0].Groups = []string{goodName} err = p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) } } @@ -751,15 +740,15 @@ func TestAppProject_ValidateSyncWindowList(t *testing.T) { t.Run("WorkingSyncWindow", func(t *testing.T) { p := newTestProjectWithSyncWindows() err := p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("HasNilSyncWindow", func(t *testing.T) { p := newTestProjectWithSyncWindows() err := p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) p.Spec.SyncWindows = append(p.Spec.SyncWindows, nil) err = p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) }) } @@ -767,7 +756,7 @@ func TestAppProject_ValidateSyncWindowList(t *testing.T) { func TestAppProject_InvalidPolicyRules(t *testing.T) { p := newTestProject() err := p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) type badPolicy struct { policy string errmsg string @@ -801,8 +790,9 @@ func TestAppProject_InvalidPolicyRules(t *testing.T) { for _, bad := range badPolicies { p.Spec.Roles[0].Policies = []string{bad.policy} err = p.ValidateProject() - require.Error(t, err) - assert.Contains(t, err.Error(), bad.errmsg) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), bad.errmsg) + } } } @@ -810,7 +800,7 @@ func TestAppProject_InvalidPolicyRules(t *testing.T) { func TestAppProject_ValidPolicyRules(t *testing.T) { p := newTestProject() err := p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) goodPolicies := []string{ "p,proj:my-proj:my-role,applications,get,my-proj/*,allow", "p, proj:my-proj:my-role, applications, get, my-proj/*, allow", @@ -823,19 +813,15 @@ func TestAppProject_ValidPolicyRules(t *testing.T) { "p, proj:my-proj:my-role, applications, *, my-proj/foo, allow", "p, proj:my-proj:my-role, applications, create, my-proj/foo, allow", "p, proj:my-proj:my-role, applications, update, my-proj/foo, allow", - "p, proj:my-proj:my-role, applications, update/*, my-proj/foo, allow", - "p, proj:my-proj:my-role, applications, update/*/Pod/*, my-proj/foo, allow", "p, proj:my-proj:my-role, applications, sync, my-proj/foo, allow", "p, proj:my-proj:my-role, applications, delete, my-proj/foo, allow", - "p, proj:my-proj:my-role, applications, delete/*, my-proj/foo, allow", - "p, proj:my-proj:my-role, applications, delete/*/Pod/*, my-proj/foo, allow", "p, proj:my-proj:my-role, applications, action/*, my-proj/foo, allow", "p, proj:my-proj:my-role, applications, action/apps/Deployment/restart, my-proj/foo, allow", } for _, good := range goodPolicies { p.Spec.Roles[0].Policies = []string{good} err = p.ValidateProject() - require.NoError(t, err) + assert.NoError(t, err) } } @@ -849,7 +835,7 @@ func TestExplicitType(t *testing.T) { }, } explicitType, err := src.ExplicitType() - require.Error(t, err) + assert.NotNil(t, err) assert.Nil(t, explicitType) src = ApplicationSource{ Helm: &ApplicationSourceHelm{ @@ -858,8 +844,8 @@ func TestExplicitType(t *testing.T) { } explicitType, err = src.ExplicitType() - require.NoError(t, err) - assert.Equal(t, ApplicationSourceTypeHelm, *explicitType) + assert.Nil(t, err) + assert.Equal(t, *explicitType, ApplicationSourceTypeHelm) } func TestExplicitTypeWithDirectory(t *testing.T) { @@ -868,7 +854,7 @@ func TestExplicitTypeWithDirectory(t *testing.T) { Directory: &ApplicationSourceDirectory{}, } _, err := src.ExplicitType() - require.Error(t, err, "cannot add directory with any other types") + assert.NotNil(t, err, "cannot add directory with any other types") } func TestAppSourceEquality(t *testing.T) { @@ -883,153 +869,6 @@ func TestAppSourceEquality(t *testing.T) { assert.False(t, left.Equals(right)) } -func TestAppSource_GetKubeVersionOrDefault(t *testing.T) { - defaultKV := "999.999.999" - cases := []struct { - name string - source *ApplicationSource - expect string - }{ - { - "nil source returns default", - nil, - defaultKV, - }, - { - "source without Helm or Kustomize returns default", - &ApplicationSource{}, - defaultKV, - }, - { - "source with empty Helm returns default", - &ApplicationSource{Helm: &ApplicationSourceHelm{}}, - defaultKV, - }, - { - "source with empty Kustomize returns default", - &ApplicationSource{Kustomize: &ApplicationSourceKustomize{}}, - defaultKV, - }, - { - "source with Helm override returns override", - &ApplicationSource{Helm: &ApplicationSourceHelm{KubeVersion: "1.2.3"}}, - "1.2.3", - }, - { - "source with Kustomize override returns override", - &ApplicationSource{Kustomize: &ApplicationSourceKustomize{KubeVersion: "1.2.3"}}, - "1.2.3", - }, - } - - for _, tc := range cases { - tcc := tc - t.Run(tcc.name, func(t *testing.T) { - t.Parallel() - kv := tcc.source.GetKubeVersionOrDefault(defaultKV) - assert.Equal(t, tcc.expect, kv) - }) - } -} - -func TestAppSource_GetAPIVersionsOrDefault(t *testing.T) { - defaultAPIVersions := []string{"v1", "v2"} - cases := []struct { - name string - source *ApplicationSource - expect []string - }{ - { - "nil source returns default", - nil, - defaultAPIVersions, - }, - { - "source without Helm or Kustomize returns default", - &ApplicationSource{}, - defaultAPIVersions, - }, - { - "source with empty Helm returns default", - &ApplicationSource{Helm: &ApplicationSourceHelm{}}, - defaultAPIVersions, - }, - { - "source with empty Kustomize returns default", - &ApplicationSource{Kustomize: &ApplicationSourceKustomize{}}, - defaultAPIVersions, - }, - { - "source with Helm override returns override", - &ApplicationSource{Helm: &ApplicationSourceHelm{APIVersions: []string{"v3", "v4"}}}, - []string{"v3", "v4"}, - }, - { - "source with Kustomize override returns override", - &ApplicationSource{Kustomize: &ApplicationSourceKustomize{APIVersions: []string{"v3", "v4"}}}, - []string{"v3", "v4"}, - }, - } - - for _, tc := range cases { - tcc := tc - t.Run(tcc.name, func(t *testing.T) { - t.Parallel() - kv := tcc.source.GetAPIVersionsOrDefault(defaultAPIVersions) - assert.Equal(t, tcc.expect, kv) - }) - } -} - -func TestAppSource_GetNamespaceOrDefault(t *testing.T) { - defaultNS := "default" - cases := []struct { - name string - source *ApplicationSource - expect string - }{ - { - "nil source returns default", - nil, - defaultNS, - }, - { - "source without Helm or Kustomize returns default", - &ApplicationSource{}, - defaultNS, - }, - { - "source with empty Helm returns default", - &ApplicationSource{Helm: &ApplicationSourceHelm{}}, - defaultNS, - }, - { - "source with empty Kustomize returns default", - &ApplicationSource{Kustomize: &ApplicationSourceKustomize{}}, - defaultNS, - }, - { - "source with Helm override returns override", - &ApplicationSource{Helm: &ApplicationSourceHelm{Namespace: "not-default"}}, - "not-default", - }, - { - "source with Kustomize override returns override", - &ApplicationSource{Kustomize: &ApplicationSourceKustomize{Namespace: "not-default"}}, - "not-default", - }, - } - - for _, tc := range cases { - tcc := tc - t.Run(tcc.name, func(t *testing.T) { - t.Parallel() - kv := tcc.source.GetNamespaceOrDefault(defaultNS) - assert.Equal(t, tcc.expect, kv) - }) - } -} - func TestAppDestinationEquality(t *testing.T) { left := &ApplicationDestination{ Server: "https://kubernetes.default.svc", @@ -1084,6 +923,7 @@ func TestAppProjectSpec_DestinationClusters(t *testing.T) { } func TestRepository_HasCredentials(t *testing.T) { + tests := []struct { name string repo Repository @@ -1268,7 +1108,6 @@ func TestRepository_CopyCredentialsFrom(t *testing.T) { {"SourceSSHPrivateKey", &Repository{}, &RepoCreds{SSHPrivateKey: "foo"}, Repository{SSHPrivateKey: "foo"}}, {"SourceTLSClientCertData", &Repository{}, &RepoCreds{TLSClientCertData: "foo"}, Repository{TLSClientCertData: "foo"}}, {"SourceTLSClientCertKey", &Repository{}, &RepoCreds{TLSClientCertKey: "foo"}, Repository{TLSClientCertKey: "foo"}}, - {"SourceContainsProxy", &Repository{}, &RepoCreds{Proxy: "http://proxy.argoproj.io:3128", NoProxy: ".example.com"}, Repository{Proxy: "http://proxy.argoproj.io:3128", NoProxy: ".example.com"}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1439,6 +1278,7 @@ func TestApplicationSourceHelm_AddParameter(t *testing.T) { t.Run("Add", func(t *testing.T) { src.AddParameter(HelmParameter{Value: "bar"}) assert.ElementsMatch(t, []HelmParameter{{Value: "bar"}}, src.Parameters) + }) t.Run("Replace", func(t *testing.T) { src.AddParameter(HelmParameter{Value: "baz"}) @@ -1451,6 +1291,7 @@ func TestApplicationSourceHelm_AddFileParameter(t *testing.T) { t.Run("Add", func(t *testing.T) { src.AddFileParameter(HelmFileParameter{Name: "foo", Path: "bar"}) assert.ElementsMatch(t, []HelmFileParameter{{Name: "foo", Path: "bar"}}, src.FileParameters) + }) t.Run("Replace", func(t *testing.T) { src.AddFileParameter(HelmFileParameter{Name: "foo", Path: "baz"}) @@ -1461,16 +1302,16 @@ func TestApplicationSourceHelm_AddFileParameter(t *testing.T) { func TestNewHelmParameter(t *testing.T) { t.Run("Invalid", func(t *testing.T) { _, err := NewHelmParameter("garbage", false) - require.EqualError(t, err, "Expected helm parameter of the form: param=value. Received: garbage") + assert.EqualError(t, err, "Expected helm parameter of the form: param=value. Received: garbage") }) t.Run("NonString", func(t *testing.T) { p, err := NewHelmParameter("foo=bar", false) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, &HelmParameter{Name: "foo", Value: "bar"}, p) }) t.Run("String", func(t *testing.T) { p, err := NewHelmParameter("foo=bar", true) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, &HelmParameter{Name: "foo", Value: "bar", ForceString: true}, p) }) } @@ -1478,16 +1319,16 @@ func TestNewHelmParameter(t *testing.T) { func TestNewKustomizeReplica(t *testing.T) { t.Run("Valid", func(t *testing.T) { r, err := NewKustomizeReplica("my-deployment=2") - require.NoError(t, err) + 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") - require.EqualError(t, err, "expected parameter of the form: name=count. Received: 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") - require.EqualError(t, err, "expected integer value for count. Received: garbage") + assert.EqualError(t, err, "expected integer value for count. Received: garbage") }) } @@ -1498,7 +1339,7 @@ func TestKustomizeReplica_GetIntCount(t *testing.T) { Count: intstr.FromString("2"), } count, err := kr.GetIntCount() - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, 2, count) }) t.Run("String which cannot be converted to integer", func(t *testing.T) { @@ -1507,7 +1348,7 @@ func TestKustomizeReplica_GetIntCount(t *testing.T) { Count: intstr.FromString("garbage"), } count, err := kr.GetIntCount() - require.EqualError(t, err, "expected integer value for count. Received: garbage") + assert.EqualError(t, err, "expected integer value for count. Received: garbage") assert.Equal(t, 0, count) }) t.Run("Integer", func(t *testing.T) { @@ -1516,7 +1357,7 @@ func TestKustomizeReplica_GetIntCount(t *testing.T) { Count: intstr.FromInt(2), } count, err := kr.GetIntCount() - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, 2, count) }) } @@ -1538,12 +1379,11 @@ func TestApplicationSourceKustomize_MergeReplica(t *testing.T) { t.Run("Replace", func(t *testing.T) { k := ApplicationSourceKustomize{Replicas: KustomizeReplicas{r1}} k.MergeReplica(r2) - assert.Len(t, k.Replicas, 1) + 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", @@ -1645,6 +1485,7 @@ func TestApplicationSourceDirectory_IsZero(t *testing.T) { } func TestApplicationSourcePlugin_IsZero(t *testing.T) { + tests := []struct { name string source *ApplicationSourcePlugin @@ -1770,7 +1611,7 @@ func TestSyncWindows_HasWindows(t *testing.T) { t.Run("False", func(t *testing.T) { proj := newTestProjectWithSyncWindows() err := proj.Spec.DeleteWindow(0) - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, proj.Spec.SyncWindows.HasWindows()) }) } @@ -1778,7 +1619,7 @@ func TestSyncWindows_HasWindows(t *testing.T) { func TestSyncWindows_Active(t *testing.T) { t.Run("WithTestProject", func(t *testing.T) { proj := newTestProjectWithSyncWindows() - assert.Len(t, *proj.Spec.SyncWindows.Active(), 1) + assert.Equal(t, 1, len(*proj.Spec.SyncWindows.Active())) }) syncWindow := func(kind string, schedule string, duration string, timeZone string) *SyncWindow { @@ -1916,24 +1757,27 @@ func TestSyncWindows_Active(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + result := tt.syncWindow.active(tt.currentTime) if result == nil { result = &SyncWindows{} } - assert.Len(t, *result, tt.expectedLength) + assert.Equal(t, tt.expectedLength, len(*result)) if len(*result) == 1 { assert.Equal(t, tt.syncWindow[tt.matchingIndex], (*result)[0]) } + }) } + } func TestSyncWindows_InactiveAllows(t *testing.T) { t.Run("WithTestProject", func(t *testing.T) { proj := newTestProjectWithSyncWindows() proj.Spec.SyncWindows[0].Schedule = "0 0 1 1 1" - assert.Len(t, *proj.Spec.SyncWindows.InactiveAllows(), 1) + assert.Equal(t, 1, len(*proj.Spec.SyncWindows.InactiveAllows())) }) syncWindow := func(kind string, schedule string, duration string, timeZone string) *SyncWindow { @@ -2089,17 +1933,20 @@ func TestSyncWindows_InactiveAllows(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + result := tt.syncWindow.inactiveAllows(tt.currentTime) if result == nil { result = &SyncWindows{} } - assert.Len(t, *result, tt.expectedLength) + assert.Equal(t, tt.expectedLength, len(*result)) if len(*result) == 1 { assert.Equal(t, tt.syncWindow[tt.matchingIndex], (*result)[0]) } + }) } + } func TestAppProjectSpec_AddWindow(t *testing.T) { @@ -2129,12 +1976,13 @@ func TestAppProjectSpec_AddWindow(t *testing.T) { t.Run(tt.name, func(t *testing.T) { switch tt.want { case "error": - require.Error(t, tt.p.Spec.AddWindow(tt.k, tt.s, tt.d, tt.a, tt.n, tt.c, tt.m, tt.t)) + assert.Error(t, tt.p.Spec.AddWindow(tt.k, tt.s, tt.d, tt.a, tt.n, tt.c, tt.m, tt.t)) case "noError": - require.NoError(t, tt.p.Spec.AddWindow(tt.k, tt.s, tt.d, tt.a, tt.n, tt.c, tt.m, tt.t)) - require.NoError(t, tt.p.Spec.DeleteWindow(0)) + assert.NoError(t, tt.p.Spec.AddWindow(tt.k, tt.s, tt.d, tt.a, tt.n, tt.c, tt.m, tt.t)) + assert.NoError(t, tt.p.Spec.DeleteWindow(0)) } }) + } } @@ -2144,13 +1992,13 @@ func TestAppProjectSpec_DeleteWindow(t *testing.T) { proj.Spec.SyncWindows = append(proj.Spec.SyncWindows, window2) t.Run("CannotFind", func(t *testing.T) { err := proj.Spec.DeleteWindow(3) - require.Error(t, err) - assert.Len(t, proj.Spec.SyncWindows, 2) + assert.Error(t, err) + assert.Equal(t, 2, len(proj.Spec.SyncWindows)) }) t.Run("Delete", func(t *testing.T) { err := proj.Spec.DeleteWindow(0) - require.NoError(t, err) - assert.Len(t, proj.Spec.SyncWindows, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(proj.Spec.SyncWindows)) }) } @@ -2160,31 +2008,31 @@ func TestSyncWindows_Matches(t *testing.T) { t.Run("MatchNamespace", func(t *testing.T) { proj.Spec.SyncWindows[0].Namespaces = []string{"default"} windows := proj.Spec.SyncWindows.Matches(app) - assert.Len(t, *windows, 1) + assert.Equal(t, 1, len(*windows)) proj.Spec.SyncWindows[0].Namespaces = nil }) t.Run("MatchCluster", func(t *testing.T) { proj.Spec.SyncWindows[0].Clusters = []string{"cluster1"} windows := proj.Spec.SyncWindows.Matches(app) - assert.Len(t, *windows, 1) + assert.Equal(t, 1, len(*windows)) proj.Spec.SyncWindows[0].Clusters = nil }) t.Run("MatchClusterName", func(t *testing.T) { proj.Spec.SyncWindows[0].Clusters = []string{"clusterName"} windows := proj.Spec.SyncWindows.Matches(app) - assert.Len(t, *windows, 1) + assert.Equal(t, 1, len(*windows)) proj.Spec.SyncWindows[0].Clusters = nil }) t.Run("MatchAppName", func(t *testing.T) { proj.Spec.SyncWindows[0].Applications = []string{"test-app"} windows := proj.Spec.SyncWindows.Matches(app) - assert.Len(t, *windows, 1) + assert.Equal(t, 1, len(*windows)) proj.Spec.SyncWindows[0].Applications = nil }) t.Run("MatchWildcardAppName", func(t *testing.T) { proj.Spec.SyncWindows[0].Applications = []string{"test-*"} windows := proj.Spec.SyncWindows.Matches(app) - assert.Len(t, *windows, 1) + assert.Equal(t, 1, len(*windows)) proj.Spec.SyncWindows[0].Applications = nil }) t.Run("NoMatch", func(t *testing.T) { @@ -2535,12 +2383,14 @@ func TestSyncWindows_hasDeny(t *testing.T) { hasDeny, manualEnabled := proj.Spec.SyncWindows.hasDeny() assert.True(t, hasDeny) assert.True(t, manualEnabled) + }) t.Run("False", func(t *testing.T) { proj := newTestProjectWithSyncWindows() hasDeny, manualEnabled := proj.Spec.SyncWindows.hasDeny() assert.False(t, hasDeny) assert.False(t, manualEnabled) + }) } @@ -2643,41 +2493,44 @@ func TestSyncWindow_Active(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + result := tt.syncWindow.active(tt.currentTime) - assert.Equal(t, tt.expectedResult, result) + assert.Equal(t, result, tt.expectedResult) + }) } + } func TestSyncWindow_Update(t *testing.T) { e := SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h", Applications: []string{"app1"}} t.Run("AddApplication", func(t *testing.T) { err := e.Update("", "", []string{"app1", "app2"}, []string{}, []string{}, "") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, []string{"app1", "app2"}, e.Applications) }) t.Run("AddNamespace", func(t *testing.T) { err := e.Update("", "", []string{}, []string{"namespace1"}, []string{}, "") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, []string{"namespace1"}, e.Namespaces) }) t.Run("AddCluster", func(t *testing.T) { err := e.Update("", "", []string{}, []string{}, []string{"cluster1"}, "") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, []string{"cluster1"}, e.Clusters) }) t.Run("MissingConfig", func(t *testing.T) { err := e.Update("", "", []string{}, []string{}, []string{}, "") - require.EqualError(t, err, "cannot update: require one or more of schedule, duration, application, namespace, or cluster") + assert.EqualError(t, err, "cannot update: require one or more of schedule, duration, application, namespace, or cluster") }) t.Run("ChangeDuration", func(t *testing.T) { err := e.Update("", "10h", []string{}, []string{}, []string{}, "") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "10h", e.Duration) }) t.Run("ChangeSchedule", func(t *testing.T) { err := e.Update("* 1 0 0 *", "", []string{}, []string{}, []string{}, "") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "* 1 0 0 *", e.Schedule) }) } @@ -2685,22 +2538,22 @@ func TestSyncWindow_Update(t *testing.T) { func TestSyncWindow_Validate(t *testing.T) { window := &SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"} t.Run("Validates", func(t *testing.T) { - require.NoError(t, window.Validate()) + assert.NoError(t, window.Validate()) }) t.Run("IncorrectKind", func(t *testing.T) { window.Kind = "wrong" - require.Error(t, window.Validate()) + assert.Error(t, window.Validate()) }) t.Run("IncorrectSchedule", func(t *testing.T) { window.Kind = "allow" window.Schedule = "* * *" - require.Error(t, window.Validate()) + assert.Error(t, window.Validate()) }) t.Run("IncorrectDuration", func(t *testing.T) { window.Kind = "allow" window.Schedule = "* * * * *" window.Duration = "1000days" - require.Error(t, window.Validate()) + assert.Error(t, window.Validate()) }) } @@ -3001,12 +2854,12 @@ func TestSyncOptions_AddOption(t *testing.T) { func TestSyncOptions_RemoveOption(t *testing.T) { options := SyncOptions{"a=1"} - assert.Empty(t, options.RemoveOption("a=1")) - assert.Empty(t, options.RemoveOption("a=1").RemoveOption("a=1")) + assert.Len(t, options.RemoveOption("a=1"), 0) + assert.Len(t, options.RemoveOption("a=1").RemoveOption("a=1"), 0) } func TestRevisionHistories_Trunc(t *testing.T) { - assert.Empty(t, RevisionHistories{}.Trunc(1)) + assert.Len(t, RevisionHistories{}.Trunc(1), 0) assert.Len(t, RevisionHistories{{}}.Trunc(1), 1) assert.Len(t, RevisionHistories{{}, {}}.Trunc(1), 1) // keep the last element, even with longer list @@ -3052,28 +2905,22 @@ func TestProjectNormalize(t *testing.T) { assert.ElementsMatch(t, p.Spec.Roles[0].JWTTokens, p.Status.JWTTokensByRole["test-role"].Items) }) t.Run("SpecRolesEmpty-StatusRolesToken", func(t *testing.T) { - p := AppProject{ - Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role"}}}, - Status: AppProjectStatus{JWTTokensByRole: tokensByRole}, - } + p := AppProject{Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role"}}}, + Status: AppProjectStatus{JWTTokensByRole: tokensByRole}} needNormalize := p.NormalizeJWTTokens() assert.True(t, needNormalize) assert.ElementsMatch(t, p.Spec.Roles[0].JWTTokens, p.Status.JWTTokensByRole["test-role"].Items) }) t.Run("SpecRolesToken-StatusRolesToken-Same", func(t *testing.T) { - p := AppProject{ - Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role", JWTTokens: testTokens}}}, - Status: AppProjectStatus{JWTTokensByRole: tokensByRole}, - } + p := AppProject{Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role", JWTTokens: testTokens}}}, + Status: AppProjectStatus{JWTTokensByRole: tokensByRole}} needNormalize := p.NormalizeJWTTokens() assert.False(t, needNormalize) assert.ElementsMatch(t, p.Spec.Roles[0].JWTTokens, p.Status.JWTTokensByRole["test-role"].Items) }) t.Run("SpecRolesToken-StatusRolesToken-DifferentToken", func(t *testing.T) { - p := AppProject{ - Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role", JWTTokens: testTokens2}}}, - Status: AppProjectStatus{JWTTokensByRole: tokensByRole}, - } + p := AppProject{Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role", JWTTokens: testTokens2}}}, + Status: AppProjectStatus{JWTTokensByRole: tokensByRole}} needNormalize := p.NormalizeJWTTokens() assert.True(t, needNormalize) assert.ElementsMatch(t, p.Spec.Roles[0].JWTTokens, p.Status.JWTTokensByRole["test-role"].Items) @@ -3081,14 +2928,10 @@ func TestProjectNormalize(t *testing.T) { t.Run("SpecRolesToken-StatusRolesToken-DifferentRole", func(t *testing.T) { jwtTokens0 := []JWTToken{{IssuedAt: issuedAt}} jwtTokens1 := []JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}} - p := AppProject{ - Spec: AppProjectSpec{Roles: []ProjectRole{ - {Name: "test-role", JWTTokens: jwtTokens0}, - {Name: "test-role1", JWTTokens: jwtTokens1}, - {Name: "test-role2"}, - }}, - Status: AppProjectStatus{JWTTokensByRole: tokensByRole}, - } + p := AppProject{Spec: AppProjectSpec{Roles: []ProjectRole{{Name: "test-role", JWTTokens: jwtTokens0}, + {Name: "test-role1", JWTTokens: jwtTokens1}, + {Name: "test-role2"}}}, + Status: AppProjectStatus{JWTTokensByRole: tokensByRole}} needNormalize := p.NormalizeJWTTokens() assert.True(t, needNormalize) assert.ElementsMatch(t, p.Spec.Roles[0].JWTTokens, p.Status.JWTTokensByRole["test-role"].Items) @@ -3112,16 +2955,17 @@ func TestRetryStrategy_NextRetryAtDefaultBackoff(t *testing.T) { for i, expected := range expectedTimes { retryAt, err := retry.NextRetryAt(now, int64(i)) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, expected.Format(time.RFC850), retryAt.Format(time.RFC850)) } + } func TestRetryStrategy_NextRetryAtCustomBackoff(t *testing.T) { retry := RetryStrategy{ Backoff: &Backoff{ Duration: "2s", - Factor: ptr.To(int64(3)), + Factor: pointer.Int64(3), MaxDuration: "1m", }, } @@ -3136,7 +2980,7 @@ func TestRetryStrategy_NextRetryAtCustomBackoff(t *testing.T) { for i, expected := range expectedTimes { retryAt, err := retry.NextRetryAt(now, int64(i)) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, expected.Format(time.RFC850), retryAt.Format(time.RFC850)) } } @@ -3196,7 +3040,7 @@ func TestRemoveEnvEntry(t *testing.T) { &EnvEntry{"gamma", "delta"}, }, } - require.NoError(t, plugins.RemoveEnvEntry("alpha")) + assert.NoError(t, plugins.RemoveEnvEntry("alpha")) want := Env{&EnvEntry{"foo", "bar"}, &EnvEntry{"gamma", "delta"}} assert.Equal(t, want, plugins.Env) }) @@ -3205,7 +3049,7 @@ func TestRemoveEnvEntry(t *testing.T) { Name: "test", Env: Env{&EnvEntry{"foo", "bar"}}, } - require.NoError(t, plugins.RemoveEnvEntry("foo")) + assert.NoError(t, plugins.RemoveEnvEntry("foo")) assert.Equal(t, Env{}, plugins.Env) }) t.Run("Remove unknown element from the list", func(t *testing.T) { @@ -3214,15 +3058,15 @@ func TestRemoveEnvEntry(t *testing.T) { Env: Env{&EnvEntry{"foo", "bar"}}, } err := plugins.RemoveEnvEntry("key") - require.EqualError(t, err, `unable to find env variable with key "key" for plugin "test"`) + assert.EqualError(t, err, `unable to find env variable with key "key" for plugin "test"`) err = plugins.RemoveEnvEntry("bar") - require.EqualError(t, err, `unable to find env variable with key "bar" for plugin "test"`) + assert.EqualError(t, err, `unable to find env variable with key "bar" for plugin "test"`) assert.Equal(t, Env{&EnvEntry{"foo", "bar"}}, plugins.Env) }) t.Run("Remove element from an empty list", func(t *testing.T) { plugins := &ApplicationSourcePlugin{Name: "test"} err := plugins.RemoveEnvEntry("key") - require.EqualError(t, err, `unable to find env variable with key "key" for plugin "test"`) + assert.EqualError(t, err, `unable to find env variable with key "key" for plugin "test"`) }) } @@ -3230,101 +3074,39 @@ func TestOrphanedResourcesMonitorSettings_IsWarn(t *testing.T) { settings := OrphanedResourcesMonitorSettings{} assert.False(t, settings.IsWarn()) - settings.Warn = ptr.To(false) + settings.Warn = pointer.Bool(false) assert.False(t, settings.IsWarn()) - settings.Warn = ptr.To(true) + settings.Warn = pointer.Bool(true) assert.True(t, settings.IsWarn()) } -func Test_isValidPolicyObject(t *testing.T) { - policyTests := []struct { - name string - policy string - isValid bool - }{ - { - name: "policy with full wildcard", - policy: "some-project/*", - isValid: true, - }, - { - name: "policy with specified project and application", - policy: "some-project/some-application", - isValid: true, - }, - { - name: "policy with full wildcard namespace and application", - policy: "some-project/*/*", - isValid: true, - }, - { - name: "policy with wildcard namespace and specified application", - policy: "some-project/*/some-application", - isValid: true, - }, - { - name: "policy with specified namespace and wildcard application", - policy: "some-project/some-namespace/*", - isValid: true, - }, - { - name: "policy with wildcard prefix namespace and specified application", - policy: "some-project/some-name*/some-application", - isValid: true, - }, - { - name: "policy with specified namespace and wildcard prefixed application", - policy: "some-project/some-namespace/some-app*", - isValid: true, - }, - { - name: "policy with valid namespace and application", - policy: "some-project/some-namespace/some-application", - isValid: true, - }, - { - name: "policy with invalid namespace character", - policy: "some-project/some~namespace/some-application", - isValid: false, - }, - { - name: "policy with invalid application character", - policy: "some-project/some-namespace/some^application", - isValid: false, - }, - } - - for _, policyTest := range policyTests { - assert.Equal(t, policyTest.isValid, isValidObject("some-project", policyTest.policy), policyTest.name) - } -} - func Test_validatePolicy_projIsNotRegex(t *testing.T) { // Make sure the "." in "some.project" isn't treated as the regex wildcard. err := validatePolicy("some.project", "org-admin", "p, proj:some.project:org-admin, applications, *, some-project/*, allow") - require.Error(t, err) + assert.Error(t, err) err = validatePolicy("some.project", "org-admin", "p, proj:some.project:org-admin, applications, *, some.project/*, allow") - require.NoError(t, err) + assert.NoError(t, err) err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, applications, *, some-project/*, allow") - require.NoError(t, err) + assert.NoError(t, err) } func Test_validatePolicy_ValidResource(t *testing.T) { err := validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, applications, *, some-project/*, allow") - require.NoError(t, err) + assert.NoError(t, err) err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, repositories, *, some-project/*, allow") - require.NoError(t, err) + assert.NoError(t, err) err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, clusters, *, some-project/*, allow") - require.NoError(t, err) + assert.NoError(t, err) err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, exec, *, some-project/*, allow") - require.NoError(t, err) + assert.NoError(t, err) err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, logs, *, some-project/*, allow") - require.NoError(t, err) + assert.NoError(t, err) err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, unknown, *, some-project/*, allow") - require.Error(t, err) + assert.Error(t, err) + } func TestEnvsubst(t *testing.T) { @@ -3364,21 +3146,22 @@ func Test_validateGroupName(t *testing.T) { t.Run(tt.name, func(t *testing.T) { err := validateGroupName(tt.groupname) if tt.isvalid { - require.NoError(t, err) + assert.NoError(t, err) } else { - require.Error(t, err) + assert.Error(t, err) } }) } } func TestGetCAPath(t *testing.T) { + temppath := t.TempDir() cert, err := os.ReadFile("../../../../test/fixture/certs/argocd-test-server.crt") if err != nil { panic(err) } - err = os.WriteFile(path.Join(temppath, "foo.example.com"), cert, 0o666) + err = os.WriteFile(path.Join(temppath, "foo.example.com"), cert, 0666) if err != nil { panic(err) } @@ -3387,25 +3170,18 @@ func TestGetCAPath(t *testing.T) { "https://foo.example.com", "oci://foo.example.com", "foo.example.com", - "foo.example.com/charts", - "https://foo.example.com:5000", - "foo.example.com:5000", - "foo.example.com:5000/charts", - "ssh://foo.example.com", } invalidpath := []string{ "https://bar.example.com", "oci://bar.example.com", "bar.example.com", - "ssh://bar.example.com", - "git@foo.example.com:organization/reponame.git", - "ssh://git@foo.example.com:organization/reponame.git", + "ssh://foo.example.com", + "git@example.com:organization/reponame.git", "/some/invalid/thing", "../another/invalid/thing", "./also/invalid", "$invalid/as/well", "..", - "://invalid", } for _, str := range validcert { @@ -3534,6 +3310,7 @@ func TestAppProjectIsSourceNamespacePermitted(t *testing.T) { // app7 is installed to someotherns, controller running in argocd assert.False(t, proj.IsAppNamespacePermitted(app7, "argocd")) }) + } func Test_RBACName(t *testing.T) { @@ -3579,7 +3356,7 @@ func TestGetSummary(t *testing.T) { app := newTestApp() summary := tree.GetSummary(app) - assert.Empty(t, summary.ExternalURLs) + assert.Equal(t, len(summary.ExternalURLs), 0) const annotationName = argocdcommon.AnnotationKeyLinkPrefix + "/my-link" const url = "https://example.com" @@ -3587,15 +3364,15 @@ func TestGetSummary(t *testing.T) { app.Annotations[annotationName] = url summary = tree.GetSummary(app) - assert.Len(t, summary.ExternalURLs, 1) - assert.Equal(t, url, summary.ExternalURLs[0]) + assert.Equal(t, len(summary.ExternalURLs), 1) + assert.Equal(t, summary.ExternalURLs[0], url) } func TestApplicationSourcePluginParameters_Environ_string(t *testing.T) { params := ApplicationSourcePluginParameters{ { Name: "version", - String_: ptr.To("1.2.3"), + String_: pointer.String("1.2.3"), }, } environ, err := params.Environ() @@ -3652,7 +3429,7 @@ func TestApplicationSourcePluginParameters_Environ_all(t *testing.T) { params := ApplicationSourcePluginParameters{ { Name: "some-name", - String_: ptr.To("1.2.3"), + String_: pointer.String("1.2.3"), OptionalArray: &OptionalArray{ Array: []string{"redis", "minio"}, }, @@ -3842,120 +3619,3 @@ func TestOptionalMapEquality(t *testing.T) { }) } } - -func TestApplicationSpec_GetSourcePtrByIndex(t *testing.T) { - testCases := []struct { - name string - application ApplicationSpec - sourceIndex int - expected *ApplicationSource - }{ - { - name: "HasMultipleSources_ReturnsFirstSource", - application: ApplicationSpec{ - Sources: []ApplicationSource{ - {RepoURL: "https://github.com/argoproj/test1.git"}, - {RepoURL: "https://github.com/argoproj/test2.git"}, - }, - }, - sourceIndex: 0, - expected: &ApplicationSource{RepoURL: "https://github.com/argoproj/test1.git"}, - }, - { - name: "HasMultipleSources_ReturnsSourceAtIndex", - application: ApplicationSpec{ - Sources: []ApplicationSource{ - {RepoURL: "https://github.com/argoproj/test1.git"}, - {RepoURL: "https://github.com/argoproj/test2.git"}, - }, - }, - sourceIndex: 1, - expected: &ApplicationSource{RepoURL: "https://github.com/argoproj/test2.git"}, - }, - { - name: "HasSingleSource_ReturnsSource", - application: ApplicationSpec{ - Source: &ApplicationSource{RepoURL: "https://github.com/argoproj/test.git"}, - }, - sourceIndex: 0, - expected: &ApplicationSource{RepoURL: "https://github.com/argoproj/test.git"}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual := tc.application.GetSourcePtrByIndex(tc.sourceIndex) - assert.Equal(t, tc.expected, actual) - }) - } -} - -func TestApplicationTree_GetShards(t *testing.T) { - tree := &ApplicationTree{ - Nodes: []ResourceNode{ - {ResourceRef: ResourceRef{Name: "node 1"}}, {ResourceRef: ResourceRef{Name: "node 2"}}, {ResourceRef: ResourceRef{Name: "node 3"}}, - }, - OrphanedNodes: []ResourceNode{ - {ResourceRef: ResourceRef{Name: "orph-node 1"}}, {ResourceRef: ResourceRef{Name: "orph-node 2"}}, {ResourceRef: ResourceRef{Name: "orph-node 3"}}, - }, - Hosts: []HostInfo{ - {Name: "host 1"}, {Name: "host 2"}, {Name: "host 3"}, - }, - } - - shards := tree.GetShards(2) - require.Len(t, shards, 5) - require.Equal(t, &ApplicationTree{ - ShardsCount: 5, - Nodes: []ResourceNode{ - {ResourceRef: ResourceRef{Name: "node 1"}}, {ResourceRef: ResourceRef{Name: "node 2"}}, - }, - }, shards[0]) - require.Equal(t, &ApplicationTree{ - Nodes: []ResourceNode{{ResourceRef: ResourceRef{Name: "node 3"}}}, - OrphanedNodes: []ResourceNode{{ResourceRef: ResourceRef{Name: "orph-node 1"}}}, - }, shards[1]) - require.Equal(t, &ApplicationTree{ - OrphanedNodes: []ResourceNode{{ResourceRef: ResourceRef{Name: "orph-node 2"}}, {ResourceRef: ResourceRef{Name: "orph-node 3"}}}, - }, shards[2]) - require.Equal(t, &ApplicationTree{ - Hosts: []HostInfo{{Name: "host 1"}, {Name: "host 2"}}, - }, shards[3]) - require.Equal(t, &ApplicationTree{ - Hosts: []HostInfo{{Name: "host 3"}}, - }, shards[4]) -} - -func TestApplicationTree_Merge(t *testing.T) { - tree := &ApplicationTree{} - tree.Merge(&ApplicationTree{ - ShardsCount: 5, - Nodes: []ResourceNode{ - {ResourceRef: ResourceRef{Name: "node 1"}}, {ResourceRef: ResourceRef{Name: "node 2"}}, - }, - }) - tree.Merge(&ApplicationTree{ - Nodes: []ResourceNode{{ResourceRef: ResourceRef{Name: "node 3"}}}, - OrphanedNodes: []ResourceNode{{ResourceRef: ResourceRef{Name: "orph-node 1"}}}, - }) - tree.Merge(&ApplicationTree{ - OrphanedNodes: []ResourceNode{{ResourceRef: ResourceRef{Name: "orph-node 2"}}, {ResourceRef: ResourceRef{Name: "orph-node 3"}}}, - }) - tree.Merge(&ApplicationTree{ - Hosts: []HostInfo{{Name: "host 1"}, {Name: "host 2"}}, - }) - tree.Merge(&ApplicationTree{ - Hosts: []HostInfo{{Name: "host 3"}}, - }) - require.Equal(t, &ApplicationTree{ - Nodes: []ResourceNode{ - {ResourceRef: ResourceRef{Name: "node 1"}}, {ResourceRef: ResourceRef{Name: "node 2"}}, {ResourceRef: ResourceRef{Name: "node 3"}}, - }, - OrphanedNodes: []ResourceNode{ - {ResourceRef: ResourceRef{Name: "orph-node 1"}}, {ResourceRef: ResourceRef{Name: "orph-node 2"}}, {ResourceRef: ResourceRef{Name: "orph-node 3"}}, - }, - Hosts: []HostInfo{ - {Name: "host 1"}, {Name: "host 2"}, {Name: "host 3"}, - }, - }, tree) -} diff --git a/pkg/apis/application/v1alpha1/values.go b/pkg/apis/application/v1alpha1/values.go index 1c0d6b76de703..942e2a651cf71 100644 --- a/pkg/apis/application/v1alpha1/values.go +++ b/pkg/apis/application/v1alpha1/values.go @@ -19,11 +19,11 @@ func (h *ApplicationSourceHelm) SetValuesString(value string) error { } else { data, err := yaml.YAMLToJSON([]byte(value)) if err != nil { - return fmt.Errorf("failed converting yaml to json: %w", err) + 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: %w", err) + return fmt.Errorf("failed to unmarshal json: %v", err) } switch v.(type) { case string: diff --git a/pkg/apis/application/v1alpha1/values_test.go b/pkg/apis/application/v1alpha1/values_test.go index 6c2c5676f3f62..f21f17168a2e8 100644 --- a/pkg/apis/application/v1alpha1/values_test.go +++ b/pkg/apis/application/v1alpha1/values_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestValues_SetString(t *testing.T) { @@ -70,12 +69,12 @@ func TestValues_SetString(t *testing.T) { if !testCase.expectError { assert.Equal(t, testCase.expectValue, source.ValuesString()) data, err := source.ValuesObject.MarshalJSON() - require.NoError(t, err) + assert.NoError(t, err) err = source.ValuesObject.UnmarshalJSON(data) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testCase.expectValue, source.ValuesString()) } else { - require.Error(t, err) + 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 7e1d69473b067..798191e02ba42 100644 --- a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go @@ -156,11 +156,6 @@ func (in *AppProjectSpec) DeepCopyInto(out *AppProjectSpec) { *out = make([]string, len(*in)) copy(*out, *in) } - if in.DestinationServiceAccounts != nil { - in, out := &in.DestinationServiceAccounts, &out.DestinationServiceAccounts - *out = make([]ApplicationDestinationServiceAccount, len(*in)) - copy(*out, *in) - } return } @@ -266,22 +261,6 @@ func (in *ApplicationDestination) DeepCopy() *ApplicationDestination { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ApplicationDestinationServiceAccount) DeepCopyInto(out *ApplicationDestinationServiceAccount) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDestinationServiceAccount. -func (in *ApplicationDestinationServiceAccount) DeepCopy() *ApplicationDestinationServiceAccount { - if in == nil { - return nil - } - out := new(ApplicationDestinationServiceAccount) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApplicationList) DeepCopyInto(out *ApplicationList) { *out = *in @@ -397,11 +376,6 @@ func (in *ApplicationSetApplicationStatus) DeepCopyInto(out *ApplicationSetAppli in, out := &in.LastTransitionTime, &out.LastTransitionTime *out = (*in).DeepCopy() } - if in.TargetRevisions != nil { - in, out := &in.TargetRevisions, &out.TargetRevisions - *out = make([]string, len(*in)) - copy(*out, *in) - } return } @@ -794,13 +768,6 @@ func (in *ApplicationSetStatus) DeepCopyInto(out *ApplicationSetStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.Resources != nil { - in, out := &in.Resources, &out.Resources - *out = make([]ResourceStatus, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } return } @@ -987,29 +954,6 @@ func (in ApplicationSetTerminalGenerators) DeepCopy() ApplicationSetTerminalGene return *out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ApplicationSetTree) DeepCopyInto(out *ApplicationSetTree) { - *out = *in - if in.Nodes != nil { - in, out := &in.Nodes, &out.Nodes - *out = make([]ResourceNode, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSetTree. -func (in *ApplicationSetTree) DeepCopy() *ApplicationSetTree { - if in == nil { - return nil - } - out := new(ApplicationSetTree) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApplicationSource) DeepCopyInto(out *ApplicationSource) { *out = *in @@ -3761,7 +3705,6 @@ func (in *RevisionHistory) DeepCopyInto(out *RevisionHistory) { *out = make([]string, len(*in)) copy(*out, *in) } - out.InitiatedBy = in.InitiatedBy return } diff --git a/pkg/ratelimiter/ratelimiter.go b/pkg/ratelimiter/ratelimiter.go index 53536f7b39a62..1c491a584873e 100644 --- a/pkg/ratelimiter/ratelimiter.go +++ b/pkg/ratelimiter/ratelimiter.go @@ -35,10 +35,10 @@ func GetDefaultAppRateLimiterConfig() *AppControllerRateLimiterConfig { // 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.TypedRateLimiter[string] { - return workqueue.NewTypedMaxOfRateLimiter[string]( +func NewCustomAppControllerRateLimiter(cfg *AppControllerRateLimiterConfig) workqueue.RateLimiter { + return workqueue.NewMaxOfRateLimiter( NewItemExponentialRateLimiterWithAutoReset(cfg.BaseDelay, cfg.MaxDelay, cfg.FailureCoolDown, cfg.BackoffFactor), - &workqueue.TypedBucketRateLimiter[string]{Limiter: rate.NewLimiter(rate.Limit(cfg.BucketQPS), int(cfg.BucketSize))}, + &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(cfg.BucketQPS), int(cfg.BucketSize))}, ) } @@ -59,9 +59,9 @@ type ItemExponentialRateLimiterWithAutoReset struct { backoffFactor float64 } -var _ workqueue.TypedRateLimiter[string] = &ItemExponentialRateLimiterWithAutoReset{} +var _ workqueue.RateLimiter = &ItemExponentialRateLimiterWithAutoReset{} -func NewItemExponentialRateLimiterWithAutoReset(baseDelay, maxDelay, failureCoolDown time.Duration, backoffFactor float64) workqueue.TypedRateLimiter[string] { +func NewItemExponentialRateLimiterWithAutoReset(baseDelay, maxDelay, failureCoolDown time.Duration, backoffFactor float64) workqueue.RateLimiter { return &ItemExponentialRateLimiterWithAutoReset{ failures: map[interface{}]failureData{}, baseDelay: baseDelay, @@ -71,7 +71,7 @@ func NewItemExponentialRateLimiterWithAutoReset(baseDelay, maxDelay, failureCool } } -func (r *ItemExponentialRateLimiterWithAutoReset) When(item string) time.Duration { +func (r *ItemExponentialRateLimiterWithAutoReset) When(item interface{}) time.Duration { r.failuresLock.Lock() defer r.failuresLock.Unlock() @@ -109,14 +109,14 @@ func (r *ItemExponentialRateLimiterWithAutoReset) When(item string) time.Duratio return calculated } -func (r *ItemExponentialRateLimiterWithAutoReset) NumRequeues(item string) int { +func (r *ItemExponentialRateLimiterWithAutoReset) NumRequeues(item interface{}) int { r.failuresLock.Lock() defer r.failuresLock.Unlock() return r.failures[item].failures } -func (r *ItemExponentialRateLimiterWithAutoReset) Forget(item string) { +func (r *ItemExponentialRateLimiterWithAutoReset) Forget(item interface{}) { r.failuresLock.Lock() defer r.failuresLock.Unlock() diff --git a/reposerver/apiclient/clientset.go b/reposerver/apiclient/clientset.go index 11bccf550203d..417dc758ef5bd 100644 --- a/reposerver/apiclient/clientset.go +++ b/reposerver/apiclient/clientset.go @@ -4,12 +4,8 @@ import ( "crypto/tls" "crypto/x509" "fmt" - "math" "time" - "github.com/argoproj/argo-cd/v2/common" - "github.com/argoproj/argo-cd/v2/util/env" - grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" log "github.com/sirupsen/logrus" @@ -21,8 +17,12 @@ import ( "github.com/argoproj/argo-cd/v2/util/io" ) -// MaxGRPCMessageSize contains max grpc message size -var MaxGRPCMessageSize = env.ParseNumFromEnv(common.EnvGRPCMaxSizeMB, 100, 0, math.MaxInt32) * 1024 * 1024 +//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 +) // TLSConfiguration describes parameters for TLS configuration to be used by a repo server API client type TLSConfiguration struct { @@ -82,7 +82,6 @@ func NewConnection(address string, timeoutSeconds int, tlsConfig *TLSConfigurati opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) } - // nolint:staticcheck conn, err := grpc.Dial(address, opts...) if err != nil { log.Errorf("Unable to connect to repository service with address %s", address) diff --git a/reposerver/apiclient/clientset_test.go b/reposerver/apiclient/clientset_test.go index c0966b799de50..617cbbd0796e5 100644 --- a/reposerver/apiclient/clientset_test.go +++ b/reposerver/apiclient/clientset_test.go @@ -3,21 +3,20 @@ package apiclient_test import ( "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "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() - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, closer) assert.NotNil(t, client) assert.Equal(t, mockClientset.RepoServerServiceClient, client) @@ -60,7 +59,7 @@ func TestNewConnection_TLSWithStrictValidation(t *testing.T) { conn, err := apiclient.NewConnection("example.com:443", 10, &tlsConfig) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, conn) } @@ -73,7 +72,7 @@ func TestNewConnection_TLSWithStrictValidationAndCertificates(t *testing.T) { conn, err := apiclient.NewConnection("example.com:443", 10, &tlsConfig) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, conn) } @@ -87,6 +86,6 @@ func TestNewConnection_InsecureConnection(t *testing.T) { conn, err := apiclient.NewConnection("example.com:80", 10, &tlsConfig) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, conn) } diff --git a/reposerver/apiclient/mocks/RepoServerServiceClient.go b/reposerver/apiclient/mocks/RepoServerServiceClient.go index 056747e5b28be..25337c53a6373 100644 --- a/reposerver/apiclient/mocks/RepoServerServiceClient.go +++ b/reposerver/apiclient/mocks/RepoServerServiceClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.21.1. DO NOT EDIT. package mocks @@ -32,10 +32,6 @@ func (_m *RepoServerServiceClient) GenerateManifest(ctx context.Context, in *api _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GenerateManifest") - } - var r0 *apiclient.ManifestResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ManifestRequest, ...grpc.CallOption) (*apiclient.ManifestResponse, error)); ok { @@ -69,10 +65,6 @@ func (_m *RepoServerServiceClient) GenerateManifestWithFiles(ctx context.Context _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GenerateManifestWithFiles") - } - var r0 apiclient.RepoServerService_GenerateManifestWithFilesClient var r1 error if rf, ok := ret.Get(0).(func(context.Context, ...grpc.CallOption) (apiclient.RepoServerService_GenerateManifestWithFilesClient, error)); ok { @@ -106,10 +98,6 @@ func (_m *RepoServerServiceClient) GetAppDetails(ctx context.Context, in *apicli _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GetAppDetails") - } - var r0 *apiclient.RepoAppDetailsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *apiclient.RepoServerAppDetailsQuery, ...grpc.CallOption) (*apiclient.RepoAppDetailsResponse, error)); ok { @@ -143,10 +131,6 @@ func (_m *RepoServerServiceClient) GetGitDirectories(ctx context.Context, in *ap _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GetGitDirectories") - } - var r0 *apiclient.GitDirectoriesResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *apiclient.GitDirectoriesRequest, ...grpc.CallOption) (*apiclient.GitDirectoriesResponse, error)); ok { @@ -180,10 +164,6 @@ func (_m *RepoServerServiceClient) GetGitFiles(ctx context.Context, in *apiclien _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GetGitFiles") - } - var r0 *apiclient.GitFilesResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *apiclient.GitFilesRequest, ...grpc.CallOption) (*apiclient.GitFilesResponse, error)); ok { @@ -217,10 +197,6 @@ func (_m *RepoServerServiceClient) GetHelmCharts(ctx context.Context, in *apicli _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GetHelmCharts") - } - var r0 *apiclient.HelmChartsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *apiclient.HelmChartsRequest, ...grpc.CallOption) (*apiclient.HelmChartsResponse, error)); ok { @@ -254,15 +230,7 @@ func (_m *RepoServerServiceClient) GetRevisionChartDetails(ctx context.Context, _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GetRevisionChartDetails") - } - var r0 *v1alpha1.ChartDetails - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *apiclient.RepoServerRevisionChartDetailsRequest, ...grpc.CallOption) (*v1alpha1.ChartDetails, error)); ok { - return rf(ctx, in, opts...) - } if rf, ok := ret.Get(0).(func(context.Context, *apiclient.RepoServerRevisionChartDetailsRequest, ...grpc.CallOption) *v1alpha1.ChartDetails); ok { r0 = rf(ctx, in, opts...) } else { @@ -271,6 +239,7 @@ func (_m *RepoServerServiceClient) GetRevisionChartDetails(ctx context.Context, } } + var r1 error if rf, ok := ret.Get(1).(func(context.Context, *apiclient.RepoServerRevisionChartDetailsRequest, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { @@ -291,10 +260,6 @@ func (_m *RepoServerServiceClient) GetRevisionMetadata(ctx context.Context, in * _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for GetRevisionMetadata") - } - var r0 *v1alpha1.RevisionMetadata var r1 error if rf, ok := ret.Get(0).(func(context.Context, *apiclient.RepoServerRevisionMetadataRequest, ...grpc.CallOption) (*v1alpha1.RevisionMetadata, error)); ok { @@ -328,10 +293,6 @@ func (_m *RepoServerServiceClient) ListApps(ctx context.Context, in *apiclient.L _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for ListApps") - } - var r0 *apiclient.AppList var r1 error if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ListAppsRequest, ...grpc.CallOption) (*apiclient.AppList, error)); ok { @@ -365,10 +326,6 @@ func (_m *RepoServerServiceClient) ListPlugins(ctx context.Context, in *emptypb. _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for ListPlugins") - } - var r0 *apiclient.PluginList var r1 error if rf, ok := ret.Get(0).(func(context.Context, *emptypb.Empty, ...grpc.CallOption) (*apiclient.PluginList, error)); ok { @@ -402,10 +359,6 @@ func (_m *RepoServerServiceClient) ListRefs(ctx context.Context, in *apiclient.L _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for ListRefs") - } - var r0 *apiclient.Refs var r1 error if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ListRefsRequest, ...grpc.CallOption) (*apiclient.Refs, error)); ok { @@ -439,10 +392,6 @@ func (_m *RepoServerServiceClient) ResolveRevision(ctx context.Context, in *apic _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for ResolveRevision") - } - var r0 *apiclient.ResolveRevisionResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ResolveRevisionRequest, ...grpc.CallOption) (*apiclient.ResolveRevisionResponse, error)); ok { @@ -476,10 +425,6 @@ func (_m *RepoServerServiceClient) TestRepository(ctx context.Context, in *apicl _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for TestRepository") - } - var r0 *apiclient.TestRepositoryResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *apiclient.TestRepositoryRequest, ...grpc.CallOption) (*apiclient.TestRepositoryResponse, error)); ok { @@ -502,49 +447,13 @@ func (_m *RepoServerServiceClient) TestRepository(ctx context.Context, in *apicl return r0, r1 } -// UpdateRevisionForPaths provides a mock function with given fields: ctx, in, opts -func (_m *RepoServerServiceClient) UpdateRevisionForPaths(ctx context.Context, in *apiclient.UpdateRevisionForPathsRequest, opts ...grpc.CallOption) (*apiclient.UpdateRevisionForPathsResponse, 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...) - - if len(ret) == 0 { - panic("no return value specified for UpdateRevisionForPaths") - } - - var r0 *apiclient.UpdateRevisionForPathsResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *apiclient.UpdateRevisionForPathsRequest, ...grpc.CallOption) (*apiclient.UpdateRevisionForPathsResponse, error)); ok { - return rf(ctx, in, opts...) - } - if rf, ok := ret.Get(0).(func(context.Context, *apiclient.UpdateRevisionForPathsRequest, ...grpc.CallOption) *apiclient.UpdateRevisionForPathsResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*apiclient.UpdateRevisionForPathsResponse) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *apiclient.UpdateRevisionForPathsRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 +type mockConstructorTestingTNewRepoServerServiceClient interface { + mock.TestingT + Cleanup(func()) } // NewRepoServerServiceClient creates a new instance of RepoServerServiceClient. 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 NewRepoServerServiceClient(t interface { - mock.TestingT - Cleanup(func()) -}) *RepoServerServiceClient { +func NewRepoServerServiceClient(t mockConstructorTestingTNewRepoServerServiceClient) *RepoServerServiceClient { mock := &RepoServerServiceClient{} mock.Mock.Test(t) diff --git a/reposerver/apiclient/mocks/RepoServerService_GenerateManifestWithFilesClient.go b/reposerver/apiclient/mocks/RepoServerService_GenerateManifestWithFilesClient.go index eaed2fcb9e571..79151a7ca1f58 100644 --- a/reposerver/apiclient/mocks/RepoServerService_GenerateManifestWithFilesClient.go +++ b/reposerver/apiclient/mocks/RepoServerService_GenerateManifestWithFilesClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.13.1. DO NOT EDIT. package mocks @@ -21,15 +21,7 @@ type RepoServerService_GenerateManifestWithFilesClient struct { func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseAndRecv() (*apiclient.ManifestResponse, error) { ret := _m.Called() - if len(ret) == 0 { - panic("no return value specified for CloseAndRecv") - } - var r0 *apiclient.ManifestResponse - var r1 error - if rf, ok := ret.Get(0).(func() (*apiclient.ManifestResponse, error)); ok { - return rf() - } if rf, ok := ret.Get(0).(func() *apiclient.ManifestResponse); ok { r0 = rf() } else { @@ -38,6 +30,7 @@ func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseAndRecv() (*ap } } + var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -51,10 +44,6 @@ func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseAndRecv() (*ap func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseSend() error { ret := _m.Called() - if len(ret) == 0 { - panic("no return value specified for CloseSend") - } - var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -69,10 +58,6 @@ func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseSend() error { func (_m *RepoServerService_GenerateManifestWithFilesClient) Context() context.Context { ret := _m.Called() - if len(ret) == 0 { - panic("no return value specified for Context") - } - var r0 context.Context if rf, ok := ret.Get(0).(func() context.Context); ok { r0 = rf() @@ -89,15 +74,7 @@ func (_m *RepoServerService_GenerateManifestWithFilesClient) Context() context.C func (_m *RepoServerService_GenerateManifestWithFilesClient) Header() (metadata.MD, error) { ret := _m.Called() - if len(ret) == 0 { - panic("no return value specified for Header") - } - var r0 metadata.MD - var r1 error - if rf, ok := ret.Get(0).(func() (metadata.MD, error)); ok { - return rf() - } if rf, ok := ret.Get(0).(func() metadata.MD); ok { r0 = rf() } else { @@ -106,6 +83,7 @@ func (_m *RepoServerService_GenerateManifestWithFilesClient) Header() (metadata. } } + var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -119,10 +97,6 @@ func (_m *RepoServerService_GenerateManifestWithFilesClient) Header() (metadata. func (_m *RepoServerService_GenerateManifestWithFilesClient) RecvMsg(m interface{}) error { ret := _m.Called(m) - if len(ret) == 0 { - panic("no return value specified for RecvMsg") - } - var r0 error if rf, ok := ret.Get(0).(func(interface{}) error); ok { r0 = rf(m) @@ -137,10 +111,6 @@ func (_m *RepoServerService_GenerateManifestWithFilesClient) RecvMsg(m interface func (_m *RepoServerService_GenerateManifestWithFilesClient) Send(_a0 *apiclient.ManifestRequestWithFiles) error { ret := _m.Called(_a0) - if len(ret) == 0 { - panic("no return value specified for Send") - } - var r0 error if rf, ok := ret.Get(0).(func(*apiclient.ManifestRequestWithFiles) error); ok { r0 = rf(_a0) @@ -155,10 +125,6 @@ func (_m *RepoServerService_GenerateManifestWithFilesClient) Send(_a0 *apiclient func (_m *RepoServerService_GenerateManifestWithFilesClient) SendMsg(m interface{}) error { ret := _m.Called(m) - if len(ret) == 0 { - panic("no return value specified for SendMsg") - } - var r0 error if rf, ok := ret.Get(0).(func(interface{}) error); ok { r0 = rf(m) @@ -173,10 +139,6 @@ func (_m *RepoServerService_GenerateManifestWithFilesClient) SendMsg(m interface func (_m *RepoServerService_GenerateManifestWithFilesClient) Trailer() metadata.MD { ret := _m.Called() - if len(ret) == 0 { - panic("no return value specified for Trailer") - } - var r0 metadata.MD if rf, ok := ret.Get(0).(func() metadata.MD); ok { r0 = rf() @@ -189,12 +151,13 @@ func (_m *RepoServerService_GenerateManifestWithFilesClient) Trailer() metadata. return r0 } -// 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. -// The first argument is typically a *testing.T value. -func NewRepoServerService_GenerateManifestWithFilesClient(t interface { +type mockConstructorTestingTNewRepoServerService_GenerateManifestWithFilesClient interface { mock.TestingT Cleanup(func()) -}) *RepoServerService_GenerateManifestWithFilesClient { +} + +// 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) diff --git a/reposerver/apiclient/repository.pb.go b/reposerver/apiclient/repository.pb.go index 61c14068bdd18..914a967db3dfc 100644 --- a/reposerver/apiclient/repository.pb.go +++ b/reposerver/apiclient/repository.pb.go @@ -43,10 +43,8 @@ type ManifestRequest struct { // 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 is the Kubernetes API version from the destination cluster. - KubeVersion string `protobuf:"bytes,14,opt,name=kubeVersion,proto3" json:"kubeVersion,omitempty"` - // ApiVersions is the list of API versions from the destination cluster, used for rendering Helm charts. - ApiVersions []string `protobuf:"bytes,15,rep,name=apiVersions,proto3" json:"apiVersions,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"` @@ -59,12 +57,10 @@ type ManifestRequest struct { // 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"` - // argocd.argoproj.io/manifest-generate-paths annotation value of the Application to allow optimize which resources propagated to cmpserver - AnnotationManifestGeneratePaths string `protobuf:"bytes,26,opt,name=AnnotationManifestGeneratePaths,proto3" json:"AnnotationManifestGeneratePaths,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + 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{} } @@ -254,13 +250,6 @@ func (m *ManifestRequest) GetProjectName() string { return "" } -func (m *ManifestRequest) GetAnnotationManifestGeneratePaths() string { - if m != nil { - return m.AnnotationManifestGeneratePaths - } - return "" -} - type ManifestRequestWithFiles struct { // Types that are valid to be assigned to Part: // *ManifestRequestWithFiles_Request @@ -568,7 +557,6 @@ type ResolveRevisionRequest struct { Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` App *v1alpha1.Application `protobuf:"bytes,2,opt,name=app,proto3" json:"app,omitempty"` AmbiguousRevision string `protobuf:"bytes,3,opt,name=ambiguousRevision,proto3" json:"ambiguousRevision,omitempty"` - SourceIndex int64 `protobuf:"varint,4,opt,name=sourceIndex,proto3" json:"sourceIndex,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -628,13 +616,6 @@ func (m *ResolveRevisionRequest) GetAmbiguousRevision() string { return "" } -func (m *ResolveRevisionRequest) GetSourceIndex() int64 { - if m != nil { - return m.SourceIndex - } - return 0 -} - // ResolveRevisionResponse type ResolveRevisionResponse struct { // returns the resolved revision @@ -700,9 +681,7 @@ type ManifestResponse struct { Revision string `protobuf:"bytes,4,opt,name=revision,proto3" json:"revision,omitempty"` SourceType string `protobuf:"bytes,6,opt,name=sourceType,proto3" json:"sourceType,omitempty"` // Raw response of git verify-commit operation (always the empty string for Helm) - VerifyResult string `protobuf:"bytes,7,opt,name=verifyResult,proto3" json:"verifyResult,omitempty"` - // Commands is the list of commands used to hydrate the manifests - Commands []string `protobuf:"bytes,8,rep,name=commands,proto3" json:"commands,omitempty"` + VerifyResult string `protobuf:"bytes,7,opt,name=verifyResult,proto3" json:"verifyResult,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -783,13 +762,6 @@ func (m *ManifestResponse) GetVerifyResult() string { return "" } -func (m *ManifestResponse) GetCommands() []string { - if m != nil { - return m.Commands - } - return nil -} - type ListRefsRequest struct { Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -1939,7 +1911,6 @@ type GitFilesRequest struct { 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"` - VerifyCommit bool `protobuf:"varint,7,opt,name=verifyCommit,proto3" json:"verifyCommit,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2020,13 +1991,6 @@ func (m *GitFilesRequest) GetNoRevisionCache() bool { return false } -func (m *GitFilesRequest) GetVerifyCommit() bool { - if m != nil { - return m.VerifyCommit - } - 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"` @@ -2080,7 +2044,6 @@ type GitDirectoriesRequest struct { 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"` - VerifyCommit bool `protobuf:"varint,5,opt,name=verifyCommit,proto3" json:"verifyCommit,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2147,13 +2110,6 @@ func (m *GitDirectoriesRequest) GetNoRevisionCache() bool { return false } -func (m *GitDirectoriesRequest) GetVerifyCommit() bool { - if m != nil { - return m.VerifyCommit - } - return false -} - type GitDirectoriesResponse struct { // A set of directory paths Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"` @@ -2202,212 +2158,6 @@ func (m *GitDirectoriesResponse) GetPaths() []string { return nil } -type UpdateRevisionForPathsRequest struct { - Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` - AppLabelKey string `protobuf:"bytes,2,opt,name=appLabelKey,proto3" json:"appLabelKey,omitempty"` - AppName string `protobuf:"bytes,3,opt,name=appName,proto3" json:"appName,omitempty"` - Namespace string `protobuf:"bytes,4,opt,name=namespace,proto3" json:"namespace,omitempty"` - ApplicationSource *v1alpha1.ApplicationSource `protobuf:"bytes,5,opt,name=applicationSource,proto3" json:"applicationSource,omitempty"` - TrackingMethod string `protobuf:"bytes,6,opt,name=trackingMethod,proto3" json:"trackingMethod,omitempty"` - RefSources map[string]*v1alpha1.RefTarget `protobuf:"bytes,7,rep,name=refSources,proto3" json:"refSources,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - KubeVersion string `protobuf:"bytes,8,opt,name=kubeVersion,proto3" json:"kubeVersion,omitempty"` - ApiVersions []string `protobuf:"bytes,9,rep,name=apiVersions,proto3" json:"apiVersions,omitempty"` - HasMultipleSources bool `protobuf:"varint,10,opt,name=hasMultipleSources,proto3" json:"hasMultipleSources,omitempty"` - SyncedRevision string `protobuf:"bytes,11,opt,name=syncedRevision,proto3" json:"syncedRevision,omitempty"` - Revision string `protobuf:"bytes,12,opt,name=revision,proto3" json:"revision,omitempty"` - Paths []string `protobuf:"bytes,13,rep,name=paths,proto3" json:"paths,omitempty"` - NoRevisionCache bool `protobuf:"varint,14,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateRevisionForPathsRequest) Reset() { *m = UpdateRevisionForPathsRequest{} } -func (m *UpdateRevisionForPathsRequest) String() string { return proto.CompactTextString(m) } -func (*UpdateRevisionForPathsRequest) ProtoMessage() {} -func (*UpdateRevisionForPathsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_dd8723cfcc820480, []int{31} -} -func (m *UpdateRevisionForPathsRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *UpdateRevisionForPathsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_UpdateRevisionForPathsRequest.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 *UpdateRevisionForPathsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateRevisionForPathsRequest.Merge(m, src) -} -func (m *UpdateRevisionForPathsRequest) XXX_Size() int { - return m.Size() -} -func (m *UpdateRevisionForPathsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateRevisionForPathsRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateRevisionForPathsRequest proto.InternalMessageInfo - -func (m *UpdateRevisionForPathsRequest) GetRepo() *v1alpha1.Repository { - if m != nil { - return m.Repo - } - return nil -} - -func (m *UpdateRevisionForPathsRequest) GetAppLabelKey() string { - if m != nil { - return m.AppLabelKey - } - return "" -} - -func (m *UpdateRevisionForPathsRequest) GetAppName() string { - if m != nil { - return m.AppName - } - return "" -} - -func (m *UpdateRevisionForPathsRequest) GetNamespace() string { - if m != nil { - return m.Namespace - } - return "" -} - -func (m *UpdateRevisionForPathsRequest) GetApplicationSource() *v1alpha1.ApplicationSource { - if m != nil { - return m.ApplicationSource - } - return nil -} - -func (m *UpdateRevisionForPathsRequest) GetTrackingMethod() string { - if m != nil { - return m.TrackingMethod - } - return "" -} - -func (m *UpdateRevisionForPathsRequest) GetRefSources() map[string]*v1alpha1.RefTarget { - if m != nil { - return m.RefSources - } - return nil -} - -func (m *UpdateRevisionForPathsRequest) GetKubeVersion() string { - if m != nil { - return m.KubeVersion - } - return "" -} - -func (m *UpdateRevisionForPathsRequest) GetApiVersions() []string { - if m != nil { - return m.ApiVersions - } - return nil -} - -func (m *UpdateRevisionForPathsRequest) GetHasMultipleSources() bool { - if m != nil { - return m.HasMultipleSources - } - return false -} - -func (m *UpdateRevisionForPathsRequest) GetSyncedRevision() string { - if m != nil { - return m.SyncedRevision - } - return "" -} - -func (m *UpdateRevisionForPathsRequest) GetRevision() string { - if m != nil { - return m.Revision - } - return "" -} - -func (m *UpdateRevisionForPathsRequest) GetPaths() []string { - if m != nil { - return m.Paths - } - return nil -} - -func (m *UpdateRevisionForPathsRequest) GetNoRevisionCache() bool { - if m != nil { - return m.NoRevisionCache - } - return false -} - -type UpdateRevisionForPathsResponse struct { - Changes bool `protobuf:"varint,1,opt,name=changes,proto3" json:"changes,omitempty"` - Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateRevisionForPathsResponse) Reset() { *m = UpdateRevisionForPathsResponse{} } -func (m *UpdateRevisionForPathsResponse) String() string { return proto.CompactTextString(m) } -func (*UpdateRevisionForPathsResponse) ProtoMessage() {} -func (*UpdateRevisionForPathsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_dd8723cfcc820480, []int{32} -} -func (m *UpdateRevisionForPathsResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *UpdateRevisionForPathsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_UpdateRevisionForPathsResponse.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 *UpdateRevisionForPathsResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateRevisionForPathsResponse.Merge(m, src) -} -func (m *UpdateRevisionForPathsResponse) XXX_Size() int { - return m.Size() -} -func (m *UpdateRevisionForPathsResponse) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateRevisionForPathsResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateRevisionForPathsResponse proto.InternalMessageInfo - -func (m *UpdateRevisionForPathsResponse) GetChanges() bool { - if m != nil { - return m.Changes - } - return false -} - -func (m *UpdateRevisionForPathsResponse) GetRevision() string { - if m != nil { - return m.Revision - } - return "" -} - func init() { proto.RegisterType((*ManifestRequest)(nil), "repository.ManifestRequest") proto.RegisterMapType((map[string]bool)(nil), "repository.ManifestRequest.EnabledSourceTypesEntry") @@ -2448,9 +2198,6 @@ func init() { proto.RegisterMapType((map[string][]byte)(nil), "repository.GitFilesResponse.MapEntry") proto.RegisterType((*GitDirectoriesRequest)(nil), "repository.GitDirectoriesRequest") proto.RegisterType((*GitDirectoriesResponse)(nil), "repository.GitDirectoriesResponse") - proto.RegisterType((*UpdateRevisionForPathsRequest)(nil), "repository.UpdateRevisionForPathsRequest") - proto.RegisterMapType((map[string]*v1alpha1.RefTarget)(nil), "repository.UpdateRevisionForPathsRequest.RefSourcesEntry") - proto.RegisterType((*UpdateRevisionForPathsResponse)(nil), "repository.UpdateRevisionForPathsResponse") } func init() { @@ -2458,154 +2205,140 @@ func init() { } var fileDescriptor_dd8723cfcc820480 = []byte{ - // 2352 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x1a, 0x4d, 0x73, 0x1c, 0x47, - 0x55, 0xfb, 0xa9, 0xdd, 0x27, 0xeb, 0xab, 0x6d, 0xcb, 0xe3, 0x8d, 0x2d, 0x94, 0x01, 0xbb, 0x1c, - 0x3b, 0x59, 0x95, 0xe5, 0x4a, 0x0c, 0x4e, 0x08, 0xa5, 0x28, 0xb6, 0xe4, 0xd8, 0xb2, 0xc5, 0xd8, - 0x09, 0x65, 0x30, 0x50, 0xbd, 0xb3, 0xad, 0xd9, 0x89, 0xe6, 0xa3, 0x3d, 0xd3, 0xa3, 0xb0, 0xae, - 0xe2, 0x04, 0xc5, 0x85, 0x3b, 0x07, 0xae, 0xfc, 0x05, 0x28, 0x8e, 0x1c, 0x28, 0x0a, 0x8e, 0x14, - 0x17, 0xaa, 0xb8, 0x40, 0xf9, 0x97, 0x50, 0xfd, 0x31, 0x9f, 0x3b, 0xbb, 0x52, 0x58, 0x59, 0x01, - 0x2e, 0xd2, 0xf4, 0xeb, 0xd7, 0xef, 0xbd, 0x7e, 0x5f, 0xfd, 0x5e, 0xf7, 0xc2, 0xd5, 0x80, 0x50, - 0x3f, 0x24, 0xc1, 0x21, 0x09, 0xd6, 0xc5, 0xa7, 0xcd, 0xfc, 0x60, 0x98, 0xf9, 0xec, 0xd2, 0xc0, - 0x67, 0x3e, 0x82, 0x14, 0xd2, 0x79, 0x68, 0xd9, 0x6c, 0x10, 0xf5, 0xba, 0xa6, 0xef, 0xae, 0xe3, - 0xc0, 0xf2, 0x69, 0xe0, 0x7f, 0x2e, 0x3e, 0xde, 0x31, 0xfb, 0xeb, 0x87, 0x1b, 0xeb, 0xf4, 0xc0, - 0x5a, 0xc7, 0xd4, 0x0e, 0xd7, 0x31, 0xa5, 0x8e, 0x6d, 0x62, 0x66, 0xfb, 0xde, 0xfa, 0xe1, 0x4d, - 0xec, 0xd0, 0x01, 0xbe, 0xb9, 0x6e, 0x11, 0x8f, 0x04, 0x98, 0x91, 0xbe, 0xa4, 0xdc, 0x79, 0xc3, - 0xf2, 0x7d, 0xcb, 0x21, 0xeb, 0x62, 0xd4, 0x8b, 0xf6, 0xd7, 0x89, 0x4b, 0x99, 0x62, 0xab, 0xff, - 0x76, 0x1e, 0x16, 0x77, 0xb1, 0x67, 0xef, 0x93, 0x90, 0x19, 0xe4, 0x45, 0x44, 0x42, 0x86, 0x9e, - 0x43, 0x9d, 0x0b, 0xa3, 0x55, 0xd6, 0x2a, 0xd7, 0xe6, 0x36, 0x76, 0xba, 0xa9, 0x34, 0xdd, 0x58, - 0x1a, 0xf1, 0xf1, 0x63, 0xb3, 0xdf, 0x3d, 0xdc, 0xe8, 0xd2, 0x03, 0xab, 0xcb, 0xa5, 0xe9, 0x66, - 0xa4, 0xe9, 0xc6, 0xd2, 0x74, 0x8d, 0x64, 0x5b, 0x86, 0xa0, 0x8a, 0x3a, 0xd0, 0x0a, 0xc8, 0xa1, - 0x1d, 0xda, 0xbe, 0xa7, 0x55, 0xd7, 0x2a, 0xd7, 0xda, 0x46, 0x32, 0x46, 0x1a, 0xcc, 0x7a, 0xfe, - 0x16, 0x36, 0x07, 0x44, 0xab, 0xad, 0x55, 0xae, 0xb5, 0x8c, 0x78, 0x88, 0xd6, 0x60, 0x0e, 0x53, - 0xfa, 0x10, 0xf7, 0x88, 0xf3, 0x80, 0x0c, 0xb5, 0xba, 0x58, 0x98, 0x05, 0xf1, 0xb5, 0x98, 0xd2, - 0x47, 0xd8, 0x25, 0x5a, 0x43, 0xcc, 0xc6, 0x43, 0x74, 0x09, 0xda, 0x1e, 0x76, 0x49, 0x48, 0xb1, - 0x49, 0xb4, 0x96, 0x98, 0x4b, 0x01, 0xe8, 0xa7, 0xb0, 0x9c, 0x11, 0xfc, 0x89, 0x1f, 0x05, 0x26, - 0xd1, 0x40, 0x6c, 0xfd, 0xf1, 0x74, 0x5b, 0xdf, 0x2c, 0x92, 0x35, 0x46, 0x39, 0xa1, 0x1f, 0x41, - 0x43, 0x58, 0x5e, 0x9b, 0x5b, 0xab, 0x9d, 0xa8, 0xb6, 0x25, 0x59, 0xe4, 0xc1, 0x2c, 0x75, 0x22, - 0xcb, 0xf6, 0x42, 0xed, 0x8c, 0xe0, 0xf0, 0x74, 0x3a, 0x0e, 0x5b, 0xbe, 0xb7, 0x6f, 0x5b, 0xbb, - 0xd8, 0xc3, 0x16, 0x71, 0x89, 0xc7, 0xf6, 0x04, 0x71, 0x23, 0x66, 0x82, 0x5e, 0xc2, 0xd2, 0x41, - 0x14, 0x32, 0xdf, 0xb5, 0x5f, 0x92, 0xc7, 0x94, 0xaf, 0x0d, 0xb5, 0x79, 0xa1, 0xcd, 0x47, 0xd3, - 0x31, 0x7e, 0x50, 0xa0, 0x6a, 0x8c, 0xf0, 0xe1, 0x4e, 0x72, 0x10, 0xf5, 0xc8, 0x67, 0x24, 0x10, - 0xde, 0xb5, 0x20, 0x9d, 0x24, 0x03, 0x92, 0x6e, 0x64, 0xab, 0x51, 0xa8, 0x2d, 0xae, 0xd5, 0xa4, - 0x1b, 0x25, 0x20, 0x74, 0x0d, 0x16, 0x0f, 0x49, 0x60, 0xef, 0x0f, 0x9f, 0xd8, 0x96, 0x87, 0x59, - 0x14, 0x10, 0x6d, 0x49, 0xb8, 0x62, 0x11, 0x8c, 0x5c, 0x98, 0x1f, 0x10, 0xc7, 0xe5, 0x2a, 0xdf, - 0x0a, 0x48, 0x3f, 0xd4, 0x96, 0x85, 0x7e, 0xb7, 0xa7, 0xb7, 0xa0, 0x20, 0x67, 0xe4, 0xa9, 0x73, - 0xc1, 0x3c, 0xdf, 0x50, 0x91, 0x22, 0x63, 0x04, 0x49, 0xc1, 0x0a, 0x60, 0x74, 0x15, 0x16, 0x58, - 0x80, 0xcd, 0x03, 0xdb, 0xb3, 0x76, 0x09, 0x1b, 0xf8, 0x7d, 0xed, 0xac, 0xd0, 0x44, 0x01, 0x8a, - 0x4c, 0x40, 0xc4, 0xc3, 0x3d, 0x87, 0xf4, 0xa5, 0x2f, 0x3e, 0x1d, 0x52, 0x12, 0x6a, 0xe7, 0xc4, - 0x2e, 0x6e, 0x75, 0x33, 0x19, 0xaa, 0x90, 0x20, 0xba, 0x77, 0x47, 0x56, 0xdd, 0xf5, 0x58, 0x30, - 0x34, 0x4a, 0xc8, 0xa1, 0x03, 0x98, 0xe3, 0xfb, 0x88, 0x5d, 0xe1, 0xbc, 0x70, 0x85, 0xfb, 0xd3, - 0xe9, 0x68, 0x27, 0x25, 0x68, 0x64, 0xa9, 0xa3, 0x2e, 0xa0, 0x01, 0x0e, 0x77, 0x23, 0x87, 0xd9, - 0xd4, 0x21, 0x52, 0x8c, 0x50, 0x5b, 0x11, 0x6a, 0x2a, 0x99, 0x41, 0x0f, 0x00, 0x02, 0xb2, 0x1f, - 0xe3, 0x5d, 0x10, 0x3b, 0xbf, 0x31, 0x69, 0xe7, 0x46, 0x82, 0x2d, 0x77, 0x9c, 0x59, 0xce, 0x99, - 0xf3, 0x6d, 0x10, 0x93, 0xa9, 0x68, 0x17, 0x61, 0xad, 0x09, 0x17, 0x2b, 0x99, 0xe1, 0xbe, 0xa8, - 0xa0, 0x22, 0x69, 0x5d, 0x94, 0xde, 0x9a, 0x01, 0xa1, 0x1d, 0xf8, 0xda, 0xa6, 0xe7, 0xf9, 0x4c, - 0x6c, 0x3f, 0x16, 0x65, 0x5b, 0xa5, 0xf7, 0x3d, 0xcc, 0x06, 0xa1, 0xd6, 0x11, 0xab, 0x8e, 0x42, - 0xeb, 0xdc, 0x85, 0x0b, 0x63, 0x8c, 0x86, 0x96, 0xa0, 0x76, 0x40, 0x86, 0x22, 0xd9, 0xb7, 0x0d, - 0xfe, 0x89, 0xce, 0x41, 0xe3, 0x10, 0x3b, 0x11, 0x11, 0xe9, 0xb9, 0x65, 0xc8, 0xc1, 0x9d, 0xea, - 0x37, 0x2b, 0x9d, 0x5f, 0x54, 0x60, 0xb1, 0xa0, 0x82, 0x92, 0xf5, 0x3f, 0xcc, 0xae, 0x3f, 0x81, - 0x80, 0xd8, 0x7f, 0x8a, 0x03, 0x8b, 0xb0, 0x8c, 0x20, 0xfa, 0xdf, 0x2a, 0xa0, 0x15, 0x6c, 0xf3, - 0x3d, 0x9b, 0x0d, 0xee, 0xd9, 0x0e, 0x09, 0xd1, 0x6d, 0x98, 0x0d, 0x24, 0x4c, 0x1d, 0x61, 0x6f, - 0x4c, 0x30, 0xe9, 0xce, 0x8c, 0x11, 0x63, 0xa3, 0x0f, 0xa1, 0xe5, 0x12, 0x86, 0xfb, 0x98, 0x61, - 0x25, 0xfb, 0x5a, 0xd9, 0x4a, 0xce, 0x65, 0x57, 0xe1, 0xed, 0xcc, 0x18, 0xc9, 0x1a, 0xf4, 0x2e, - 0x34, 0xcc, 0x41, 0xe4, 0x1d, 0x88, 0xc3, 0x6b, 0x6e, 0xe3, 0xf2, 0xb8, 0xc5, 0x5b, 0x1c, 0x69, - 0x67, 0xc6, 0x90, 0xd8, 0x1f, 0x35, 0xa1, 0x4e, 0x71, 0xc0, 0xf4, 0x7b, 0x70, 0xae, 0x8c, 0x05, - 0x3f, 0x31, 0xcd, 0x01, 0x31, 0x0f, 0xc2, 0xc8, 0x55, 0x6a, 0x4e, 0xc6, 0x08, 0x41, 0x3d, 0xb4, - 0x5f, 0x4a, 0x55, 0xd7, 0x0c, 0xf1, 0xad, 0xbf, 0x05, 0xcb, 0x23, 0xdc, 0xb8, 0x51, 0xa5, 0x6c, - 0x9c, 0xc2, 0x19, 0xc5, 0x5a, 0x8f, 0xe0, 0xfc, 0x53, 0xa1, 0x8b, 0xe4, 0xd8, 0x38, 0x8d, 0x1a, - 0x40, 0xdf, 0x81, 0x95, 0x22, 0xdb, 0x90, 0xfa, 0x5e, 0x48, 0x78, 0x10, 0x89, 0x3c, 0x6b, 0x93, - 0x7e, 0x3a, 0x2b, 0xa4, 0x68, 0x19, 0x25, 0x33, 0xfa, 0x6f, 0xaa, 0xb0, 0x62, 0x90, 0xd0, 0x77, - 0x0e, 0x49, 0x9c, 0x04, 0x4f, 0xa7, 0x8c, 0xf9, 0x01, 0xd4, 0x30, 0xa5, 0xca, 0x4d, 0xee, 0x9f, - 0x58, 0xa1, 0x60, 0x70, 0xaa, 0xe8, 0x6d, 0x58, 0xc6, 0x6e, 0xcf, 0xb6, 0x22, 0x3f, 0x0a, 0xe3, - 0x6d, 0x09, 0xa7, 0x6a, 0x1b, 0xa3, 0x13, 0x3c, 0x91, 0x84, 0x22, 0x22, 0xef, 0x7b, 0x7d, 0xf2, - 0x13, 0x51, 0x1b, 0xd5, 0x8c, 0x2c, 0x48, 0x37, 0xe1, 0xc2, 0x88, 0x92, 0x94, 0xc2, 0xb3, 0xe5, - 0x58, 0xa5, 0x50, 0x8e, 0x95, 0x8a, 0x51, 0x1d, 0x23, 0x86, 0xfe, 0xaa, 0x02, 0x4b, 0x69, 0x70, - 0x29, 0xf2, 0x97, 0xa0, 0xed, 0x2a, 0x58, 0xa8, 0x55, 0x44, 0x2e, 0x4c, 0x01, 0xf9, 0xca, 0xac, - 0x5a, 0xac, 0xcc, 0x56, 0xa0, 0x29, 0x0b, 0x67, 0xb5, 0x75, 0x35, 0xca, 0x89, 0x5c, 0x2f, 0x88, - 0xbc, 0x0a, 0x10, 0x26, 0x19, 0x4e, 0x6b, 0x8a, 0xd9, 0x0c, 0x04, 0xe9, 0x70, 0x46, 0x9e, 0xe3, - 0x06, 0x09, 0x23, 0x87, 0x69, 0xb3, 0x02, 0x23, 0x07, 0x13, 0xf1, 0xe6, 0xbb, 0x2e, 0xf6, 0xfa, - 0xa1, 0xd6, 0x12, 0x22, 0x27, 0x63, 0xdd, 0x87, 0xc5, 0x87, 0x36, 0xdf, 0xdf, 0x7e, 0x78, 0x3a, - 0xa1, 0xf2, 0x1e, 0xd4, 0x39, 0x33, 0x2e, 0x54, 0x2f, 0xc0, 0x9e, 0x39, 0x20, 0xb1, 0x1e, 0x93, - 0x31, 0x4f, 0x02, 0x0c, 0x5b, 0xa1, 0x56, 0x15, 0x70, 0xf1, 0xad, 0xff, 0xbe, 0x2a, 0x25, 0xdd, - 0xa4, 0x34, 0xfc, 0xea, 0x0b, 0xfb, 0xf2, 0x52, 0xa3, 0x36, 0x5a, 0x6a, 0x14, 0x44, 0xfe, 0x32, - 0xa5, 0xc6, 0x09, 0x1d, 0x72, 0x7a, 0x04, 0xb3, 0x9b, 0x94, 0x72, 0x41, 0xd0, 0x4d, 0xa8, 0x63, - 0x4a, 0xa5, 0xc2, 0x0b, 0xf9, 0x5c, 0xa1, 0xf0, 0xff, 0x4a, 0x24, 0x81, 0xda, 0xb9, 0x0d, 0xed, - 0x04, 0x74, 0x14, 0xdb, 0x76, 0x96, 0xed, 0x1a, 0x80, 0xac, 0xa5, 0xef, 0x7b, 0xfb, 0x3e, 0x37, - 0x29, 0x0f, 0x04, 0xb5, 0x54, 0x7c, 0xeb, 0x77, 0x62, 0x0c, 0x21, 0xdb, 0xdb, 0xd0, 0xb0, 0x19, - 0x71, 0x63, 0xe1, 0x56, 0xb2, 0xc2, 0xa5, 0x84, 0x0c, 0x89, 0xa4, 0xff, 0xb9, 0x05, 0x17, 0xb9, - 0xc5, 0x9e, 0x88, 0x10, 0xda, 0xa4, 0xf4, 0x63, 0xc2, 0xb0, 0xed, 0x84, 0xdf, 0x8d, 0x48, 0x30, - 0x7c, 0xcd, 0x8e, 0x61, 0x41, 0x53, 0x46, 0xa0, 0xca, 0x96, 0x27, 0xde, 0x56, 0x29, 0xf2, 0x69, - 0x2f, 0x55, 0x7b, 0x3d, 0xbd, 0x54, 0x59, 0x6f, 0x53, 0x3f, 0xa5, 0xde, 0x66, 0x7c, 0x7b, 0x9b, - 0x69, 0x9a, 0x9b, 0xf9, 0xa6, 0xb9, 0xa4, 0x65, 0x98, 0x3d, 0x6e, 0xcb, 0xd0, 0x2a, 0x6d, 0x19, - 0xdc, 0xd2, 0x38, 0x6e, 0x0b, 0x75, 0x7f, 0x3b, 0xeb, 0x81, 0x63, 0x7d, 0x6d, 0x9a, 0xe6, 0x01, - 0x5e, 0x6b, 0xf3, 0xf0, 0x69, 0xae, 0x19, 0x90, 0xed, 0xf8, 0xbb, 0xc7, 0xdb, 0xd3, 0x84, 0xb6, - 0xe0, 0xff, 0xae, 0xf4, 0xfe, 0xb9, 0xa8, 0xb8, 0xa8, 0x9f, 0xea, 0x20, 0x39, 0xec, 0xf9, 0x39, - 0xc4, 0x8f, 0x5d, 0x95, 0xb4, 0xf8, 0x37, 0xba, 0x01, 0x75, 0xae, 0x64, 0x55, 0x12, 0x5f, 0xc8, - 0xea, 0x93, 0x5b, 0x62, 0x93, 0xd2, 0x27, 0x94, 0x98, 0x86, 0x40, 0x42, 0x77, 0xa0, 0x9d, 0x38, - 0xbe, 0x8a, 0xac, 0x4b, 0xd9, 0x15, 0x49, 0x9c, 0xc4, 0xcb, 0x52, 0x74, 0xbe, 0xb6, 0x6f, 0x07, - 0xc4, 0x14, 0x05, 0x63, 0x63, 0x74, 0xed, 0xc7, 0xf1, 0x64, 0xb2, 0x36, 0x41, 0x47, 0x37, 0xa1, - 0x29, 0xef, 0x2f, 0x44, 0x04, 0xcd, 0x6d, 0x5c, 0x1c, 0x4d, 0xa6, 0xf1, 0x2a, 0x85, 0xa8, 0xff, - 0xa9, 0x02, 0x6f, 0xa6, 0x0e, 0x11, 0x47, 0x53, 0x5c, 0xb3, 0x7f, 0xf5, 0x27, 0xee, 0x55, 0x58, - 0x10, 0x4d, 0x42, 0x7a, 0x8d, 0x21, 0x6f, 0xd4, 0x0a, 0x50, 0xfd, 0x77, 0x15, 0xb8, 0x32, 0xba, - 0x8f, 0xad, 0x01, 0x0e, 0x58, 0x62, 0xde, 0xd3, 0xd8, 0x4b, 0x7c, 0xe0, 0x55, 0xd3, 0x03, 0x2f, - 0xb7, 0xbf, 0x5a, 0x7e, 0x7f, 0xfa, 0x1f, 0xaa, 0x30, 0x97, 0x71, 0xa0, 0xb2, 0x03, 0x93, 0x17, - 0x83, 0xc2, 0x6f, 0x45, 0x5b, 0x28, 0x0e, 0x85, 0xb6, 0x91, 0x81, 0xa0, 0x03, 0x00, 0x8a, 0x03, - 0xec, 0x12, 0x46, 0x02, 0x9e, 0xc9, 0x79, 0xc4, 0x3f, 0x98, 0x3e, 0xbb, 0xec, 0xc5, 0x34, 0x8d, - 0x0c, 0x79, 0x5e, 0xcd, 0x0a, 0xd6, 0xa1, 0xca, 0xdf, 0x6a, 0x84, 0xbe, 0x80, 0x85, 0x7d, 0xdb, - 0x21, 0x7b, 0xa9, 0x20, 0x4d, 0x21, 0xc8, 0xe3, 0xe9, 0x05, 0xb9, 0x97, 0xa5, 0x6b, 0x14, 0xd8, - 0xe8, 0xd7, 0x61, 0xa9, 0x18, 0x4f, 0x5c, 0x48, 0xdb, 0xc5, 0x56, 0xa2, 0x2d, 0x35, 0xd2, 0x11, - 0x2c, 0x15, 0xe3, 0x47, 0xff, 0x67, 0x15, 0xce, 0x27, 0xe4, 0x36, 0x3d, 0xcf, 0x8f, 0x3c, 0x53, - 0x5c, 0x09, 0x96, 0xda, 0xe2, 0x1c, 0x34, 0x98, 0xcd, 0x9c, 0xa4, 0xf0, 0x11, 0x03, 0x7e, 0x76, - 0x31, 0xdf, 0x77, 0x98, 0x4d, 0x95, 0x81, 0xe3, 0xa1, 0xb4, 0xfd, 0x8b, 0xc8, 0x0e, 0x48, 0x5f, - 0x64, 0x82, 0x96, 0x91, 0x8c, 0xf9, 0x1c, 0xaf, 0x6a, 0x44, 0x89, 0x2f, 0x95, 0x99, 0x8c, 0x85, - 0xdf, 0xfb, 0x8e, 0x43, 0x4c, 0xae, 0x8e, 0x4c, 0x13, 0x50, 0x80, 0x8a, 0xe6, 0x82, 0x05, 0xb6, - 0x67, 0xa9, 0x16, 0x40, 0x8d, 0xb8, 0x9c, 0x38, 0x08, 0xf0, 0x50, 0x55, 0xfe, 0x72, 0x80, 0x3e, - 0x80, 0x9a, 0x8b, 0xa9, 0x3a, 0xe8, 0xae, 0xe7, 0xb2, 0x43, 0x99, 0x06, 0xba, 0xbb, 0x98, 0xca, - 0x93, 0x80, 0x2f, 0xeb, 0xbc, 0x07, 0xad, 0x18, 0xf0, 0xa5, 0x4a, 0xc2, 0xcf, 0x61, 0x3e, 0x97, - 0x7c, 0xd0, 0x33, 0x58, 0x49, 0x3d, 0x2a, 0xcb, 0x50, 0x15, 0x81, 0x6f, 0x1e, 0x29, 0x99, 0x31, - 0x86, 0x80, 0xfe, 0x02, 0x96, 0xb9, 0xcb, 0x88, 0xc0, 0x3f, 0xa5, 0xd6, 0xe6, 0x7d, 0x68, 0x27, - 0x2c, 0x4b, 0x7d, 0xa6, 0x03, 0xad, 0xc3, 0xf8, 0xaa, 0x56, 0xf6, 0x36, 0xc9, 0x58, 0xdf, 0x04, - 0x94, 0x95, 0x57, 0x9d, 0x40, 0x37, 0xf2, 0x45, 0xf1, 0xf9, 0xe2, 0x71, 0x23, 0xd0, 0xe3, 0x9a, - 0xf8, 0xef, 0x55, 0x58, 0xdc, 0xb6, 0xc5, 0x1d, 0xc9, 0x29, 0x25, 0xb9, 0xeb, 0xb0, 0x14, 0x46, - 0x3d, 0xd7, 0xef, 0x47, 0x0e, 0x51, 0x45, 0x81, 0x3a, 0xe9, 0x47, 0xe0, 0x93, 0x92, 0x1f, 0x57, - 0x16, 0xc5, 0x6c, 0xa0, 0xba, 0x5f, 0xf1, 0x8d, 0x3e, 0x80, 0x8b, 0x8f, 0xc8, 0x17, 0x6a, 0x3f, - 0xdb, 0x8e, 0xdf, 0xeb, 0xd9, 0x9e, 0x15, 0x33, 0x69, 0x08, 0x26, 0xe3, 0x11, 0xca, 0x4a, 0xc5, - 0x66, 0x79, 0xa9, 0x98, 0x74, 0xd0, 0x5b, 0xbe, 0xeb, 0xda, 0x4c, 0x55, 0x94, 0x39, 0x98, 0xfe, - 0xb3, 0x0a, 0x2c, 0xa5, 0x9a, 0x55, 0xb6, 0xb9, 0x2d, 0x63, 0x48, 0x5a, 0xe6, 0x4a, 0xd6, 0x32, - 0x45, 0xd4, 0xff, 0x3c, 0x7c, 0xce, 0x64, 0xc3, 0xe7, 0x97, 0x55, 0x38, 0xbf, 0x6d, 0xb3, 0x38, - 0x71, 0xd9, 0xff, 0x6b, 0x56, 0x2e, 0xb1, 0x49, 0xfd, 0x78, 0x36, 0x69, 0x94, 0xd8, 0xa4, 0x0b, - 0x2b, 0x45, 0x65, 0x28, 0xc3, 0x9c, 0x83, 0x06, 0x15, 0x97, 0xc9, 0xf2, 0x5e, 0x41, 0x0e, 0xf4, - 0x7f, 0x34, 0xe1, 0xf2, 0xa7, 0xb4, 0x8f, 0x59, 0x72, 0x67, 0x74, 0xcf, 0x0f, 0xc4, 0x6d, 0xf2, - 0xe9, 0x68, 0xb1, 0xf0, 0xe2, 0x57, 0x9d, 0xf8, 0xe2, 0x57, 0x9b, 0xf0, 0xe2, 0x57, 0x3f, 0xd6, - 0x8b, 0x5f, 0xe3, 0xd4, 0x5e, 0xfc, 0x46, 0x7b, 0xad, 0x66, 0x69, 0xaf, 0xf5, 0x2c, 0xd7, 0x8f, - 0xcc, 0x8a, 0xb0, 0xf9, 0x56, 0x36, 0x6c, 0x26, 0x5a, 0x67, 0xe2, 0x53, 0x45, 0xe1, 0xa1, 0xac, - 0x75, 0xe4, 0x43, 0x59, 0x7b, 0xf4, 0xa1, 0xac, 0xfc, 0xad, 0x05, 0xc6, 0xbe, 0xb5, 0x5c, 0x85, - 0x85, 0x70, 0xe8, 0x99, 0xa4, 0x9f, 0xdc, 0x24, 0xce, 0xc9, 0x6d, 0xe7, 0xa1, 0xb9, 0x88, 0x38, - 0x53, 0x88, 0x88, 0xc4, 0x53, 0xe7, 0x33, 0x9e, 0x5a, 0x16, 0x27, 0x0b, 0xa5, 0x71, 0xf2, 0xdf, - 0xd3, 0x44, 0x7d, 0x06, 0xab, 0xe3, 0xac, 0xa7, 0x82, 0x52, 0x83, 0x59, 0x73, 0x80, 0x3d, 0x4b, - 0x5c, 0xf7, 0x89, 0xae, 0x5e, 0x0d, 0x27, 0x55, 0xfd, 0x1b, 0x7f, 0x04, 0x58, 0x4e, 0xab, 0x79, - 0xfe, 0xd7, 0x36, 0x09, 0x7a, 0x0c, 0x4b, 0xf1, 0x73, 0x50, 0x7c, 0x41, 0x8b, 0x26, 0xbd, 0x89, - 0x74, 0x2e, 0x95, 0x4f, 0x4a, 0xd1, 0xf4, 0x19, 0x64, 0xc2, 0xc5, 0x22, 0xc1, 0xf4, 0xf9, 0xe5, - 0x1b, 0x13, 0x28, 0x27, 0x58, 0x47, 0xb1, 0xb8, 0x56, 0x41, 0xcf, 0x60, 0x21, 0xff, 0x48, 0x80, - 0x72, 0xe5, 0x4d, 0xe9, 0xbb, 0x45, 0x47, 0x9f, 0x84, 0x92, 0xc8, 0xff, 0x9c, 0xbb, 0x41, 0xee, - 0x3e, 0x1c, 0xe9, 0xf9, 0x4e, 0xbf, 0xec, 0x45, 0xa1, 0xf3, 0xf5, 0x89, 0x38, 0x09, 0xf5, 0xf7, - 0xa1, 0x15, 0xdf, 0x11, 0xe7, 0xd5, 0x5c, 0xb8, 0x39, 0xee, 0x2c, 0xe5, 0xe9, 0xed, 0x87, 0xfa, - 0x0c, 0xfa, 0x50, 0x2e, 0xde, 0xa4, 0xb4, 0x64, 0x71, 0xe6, 0x66, 0xb4, 0x73, 0xb6, 0xe4, 0x36, - 0x52, 0x9f, 0x41, 0xdf, 0x81, 0x39, 0xfe, 0xb5, 0xa7, 0x9e, 0xe3, 0x57, 0xba, 0xf2, 0xd7, 0x1f, - 0xdd, 0xf8, 0xd7, 0x1f, 0xdd, 0xbb, 0x2e, 0x65, 0xc3, 0x4e, 0xc9, 0x75, 0xa1, 0x22, 0xf0, 0x1c, - 0xe6, 0xb7, 0x09, 0x4b, 0xbb, 0x7b, 0x74, 0xe5, 0x58, 0x77, 0x20, 0x1d, 0xbd, 0x88, 0x36, 0x7a, - 0x41, 0xa0, 0xcf, 0xa0, 0x5f, 0x55, 0xe0, 0xec, 0x36, 0x61, 0xc5, 0x7e, 0x19, 0xbd, 0x53, 0xce, - 0x64, 0x4c, 0x5f, 0xdd, 0x79, 0x34, 0x6d, 0x4c, 0xe6, 0xc9, 0xea, 0x33, 0xe8, 0xd7, 0x15, 0xb8, - 0x90, 0x11, 0x2c, 0xdb, 0x00, 0xa3, 0x9b, 0x93, 0x85, 0x2b, 0x69, 0x96, 0x3b, 0x9f, 0x4c, 0xf9, - 0x2b, 0x8b, 0x0c, 0x49, 0x7d, 0x06, 0xed, 0x09, 0x9b, 0xa4, 0xf5, 0x2e, 0xba, 0x5c, 0x5a, 0xd8, - 0x26, 0xdc, 0x57, 0xc7, 0x4d, 0x27, 0x76, 0xf8, 0x04, 0xe6, 0xb6, 0x09, 0x8b, 0x0b, 0xaf, 0xbc, - 0xa7, 0x15, 0x6a, 0xe2, 0x7c, 0xa8, 0x16, 0x6b, 0x35, 0xe1, 0x31, 0xcb, 0x92, 0x56, 0xa6, 0xb8, - 0xc8, 0xc7, 0x6a, 0x69, 0x15, 0x96, 0xf7, 0x98, 0xf2, 0xda, 0x44, 0x9f, 0x41, 0x2f, 0x60, 0xa5, - 0x3c, 0x55, 0xa2, 0xb7, 0x8e, 0x7d, 0x18, 0x76, 0xae, 0x1f, 0x07, 0x35, 0x66, 0xf9, 0xd1, 0xe6, - 0x5f, 0x5e, 0xad, 0x56, 0xfe, 0xfa, 0x6a, 0xb5, 0xf2, 0xaf, 0x57, 0xab, 0x95, 0xef, 0xdf, 0x3a, - 0xe2, 0xd7, 0x58, 0x99, 0x1f, 0x78, 0x61, 0x6a, 0x9b, 0x8e, 0x4d, 0x3c, 0xd6, 0x6b, 0x8a, 0x78, - 0xbb, 0xf5, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0xed, 0x52, 0xaa, 0xcc, 0xff, 0x25, 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. @@ -2646,8 +2379,6 @@ type RepoServerServiceClient interface { 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) - // UpdateRevisionForPaths will compare two revisions and update the cache with the new revision if no changes are detected in the provided paths - UpdateRevisionForPaths(ctx context.Context, in *UpdateRevisionForPathsRequest, opts ...grpc.CallOption) (*UpdateRevisionForPathsResponse, error) } type repoServerServiceClient struct { @@ -2800,15 +2531,6 @@ func (c *repoServerServiceClient) GetGitDirectories(ctx context.Context, in *Git return out, nil } -func (c *repoServerServiceClient) UpdateRevisionForPaths(ctx context.Context, in *UpdateRevisionForPathsRequest, opts ...grpc.CallOption) (*UpdateRevisionForPathsResponse, error) { - out := new(UpdateRevisionForPathsResponse) - err := c.cc.Invoke(ctx, "/repository.RepoServerService/UpdateRevisionForPaths", 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 @@ -2837,8 +2559,6 @@ type RepoServerServiceServer interface { GetGitFiles(context.Context, *GitFilesRequest) (*GitFilesResponse, error) // GetGitDirectories returns a set of directory paths for the given repo GetGitDirectories(context.Context, *GitDirectoriesRequest) (*GitDirectoriesResponse, error) - // UpdateRevisionForPaths will compare two revisions and update the cache with the new revision if no changes are detected in the provided paths - UpdateRevisionForPaths(context.Context, *UpdateRevisionForPathsRequest) (*UpdateRevisionForPathsResponse, error) } // UnimplementedRepoServerServiceServer can be embedded to have forward compatible implementations. @@ -2884,9 +2604,6 @@ func (*UnimplementedRepoServerServiceServer) GetGitFiles(ctx context.Context, re func (*UnimplementedRepoServerServiceServer) GetGitDirectories(ctx context.Context, req *GitDirectoriesRequest) (*GitDirectoriesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetGitDirectories not implemented") } -func (*UnimplementedRepoServerServiceServer) UpdateRevisionForPaths(ctx context.Context, req *UpdateRevisionForPathsRequest) (*UpdateRevisionForPathsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateRevisionForPaths not implemented") -} func RegisterRepoServerServiceServer(s *grpc.Server, srv RepoServerServiceServer) { s.RegisterService(&_RepoServerService_serviceDesc, srv) @@ -3134,24 +2851,6 @@ func _RepoServerService_GetGitDirectories_Handler(srv interface{}, ctx context.C return interceptor(ctx, in, info, handler) } -func _RepoServerService_UpdateRevisionForPaths_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateRevisionForPathsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RepoServerServiceServer).UpdateRevisionForPaths(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/repository.RepoServerService/UpdateRevisionForPaths", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RepoServerServiceServer).UpdateRevisionForPaths(ctx, req.(*UpdateRevisionForPathsRequest)) - } - return interceptor(ctx, in, info, handler) -} - var _RepoServerService_serviceDesc = grpc.ServiceDesc{ ServiceName: "repository.RepoServerService", HandlerType: (*RepoServerServiceServer)(nil), @@ -3204,10 +2903,6 @@ var _RepoServerService_serviceDesc = grpc.ServiceDesc{ MethodName: "GetGitDirectories", Handler: _RepoServerService_GetGitDirectories_Handler, }, - { - MethodName: "UpdateRevisionForPaths", - Handler: _RepoServerService_UpdateRevisionForPaths_Handler, - }, }, Streams: []grpc.StreamDesc{ { @@ -3243,15 +2938,6 @@ func (m *ManifestRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.AnnotationManifestGeneratePaths) > 0 { - i -= len(m.AnnotationManifestGeneratePaths) - copy(dAtA[i:], m.AnnotationManifestGeneratePaths) - i = encodeVarintRepository(dAtA, i, uint64(len(m.AnnotationManifestGeneratePaths))) - i-- - dAtA[i] = 0x1 - i-- - dAtA[i] = 0xd2 - } if len(m.ProjectName) > 0 { i -= len(m.ProjectName) copy(dAtA[i:], m.ProjectName) @@ -3792,11 +3478,6 @@ func (m *ResolveRevisionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.SourceIndex != 0 { - i = encodeVarintRepository(dAtA, i, uint64(m.SourceIndex)) - i-- - dAtA[i] = 0x20 - } if len(m.AmbiguousRevision) > 0 { i -= len(m.AmbiguousRevision) copy(dAtA[i:], m.AmbiguousRevision) @@ -3896,15 +3577,6 @@ func (m *ManifestResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Commands) > 0 { - for iNdEx := len(m.Commands) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Commands[iNdEx]) - copy(dAtA[i:], m.Commands[iNdEx]) - i = encodeVarintRepository(dAtA, i, uint64(len(m.Commands[iNdEx]))) - i-- - dAtA[i] = 0x42 - } - } if len(m.VerifyResult) > 0 { i -= len(m.VerifyResult) copy(dAtA[i:], m.VerifyResult) @@ -5023,16 +4695,6 @@ func (m *GitFilesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.VerifyCommit { - i-- - if m.VerifyCommit { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x38 - } if m.NoRevisionCache { i-- if m.NoRevisionCache { @@ -5164,16 +4826,6 @@ func (m *GitDirectoriesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.VerifyCommit { - i-- - if m.VerifyCommit { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x28 - } if m.NoRevisionCache { i-- if m.NoRevisionCache { @@ -5252,234 +4904,26 @@ func (m *GitDirectoriesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func (m *UpdateRevisionForPathsRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err +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++ } - return dAtA[:n], nil -} - -func (m *UpdateRevisionForPathsRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) + dAtA[offset] = uint8(v) + return base } - -func (m *UpdateRevisionForPathsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i +func (m *ManifestRequest) Size() (n int) { + if m == nil { + return 0 + } 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] = 0x70 - } - 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] = 0x6a - } - } - 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] = 0x62 - } - if len(m.SyncedRevision) > 0 { - i -= len(m.SyncedRevision) - copy(dAtA[i:], m.SyncedRevision) - i = encodeVarintRepository(dAtA, i, uint64(len(m.SyncedRevision))) - i-- - dAtA[i] = 0x5a - } - if m.HasMultipleSources { - i-- - if m.HasMultipleSources { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x50 - } - if len(m.ApiVersions) > 0 { - for iNdEx := len(m.ApiVersions) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.ApiVersions[iNdEx]) - copy(dAtA[i:], m.ApiVersions[iNdEx]) - i = encodeVarintRepository(dAtA, i, uint64(len(m.ApiVersions[iNdEx]))) - i-- - dAtA[i] = 0x4a - } - } - if len(m.KubeVersion) > 0 { - i -= len(m.KubeVersion) - copy(dAtA[i:], m.KubeVersion) - i = encodeVarintRepository(dAtA, i, uint64(len(m.KubeVersion))) - i-- - dAtA[i] = 0x42 - } - if len(m.RefSources) > 0 { - for k := range m.RefSources { - v := m.RefSources[k] - baseI := i - if v != nil { - { - size, err := v.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintRepository(dAtA, i, uint64(size)) - } - 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] = 0x3a - } - } - if len(m.TrackingMethod) > 0 { - i -= len(m.TrackingMethod) - copy(dAtA[i:], m.TrackingMethod) - i = encodeVarintRepository(dAtA, i, uint64(len(m.TrackingMethod))) - i-- - dAtA[i] = 0x32 - } - if m.ApplicationSource != nil { - { - size, err := m.ApplicationSource.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintRepository(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x2a - } - if len(m.Namespace) > 0 { - i -= len(m.Namespace) - copy(dAtA[i:], m.Namespace) - i = encodeVarintRepository(dAtA, i, uint64(len(m.Namespace))) - i-- - dAtA[i] = 0x22 - } - if len(m.AppName) > 0 { - i -= len(m.AppName) - copy(dAtA[i:], m.AppName) - i = encodeVarintRepository(dAtA, i, uint64(len(m.AppName))) - i-- - dAtA[i] = 0x1a - } - if len(m.AppLabelKey) > 0 { - i -= len(m.AppLabelKey) - copy(dAtA[i:], m.AppLabelKey) - i = encodeVarintRepository(dAtA, i, uint64(len(m.AppLabelKey))) - 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 *UpdateRevisionForPathsResponse) 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 *UpdateRevisionForPathsResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *UpdateRevisionForPathsResponse) 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] = 0x12 - } - if m.Changes { - i-- - if m.Changes { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x8 - } - 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)) + if m.Repo != nil { + l = m.Repo.Size() + n += 1 + l + sovRepository(uint64(l)) } l = len(m.Revision) if l > 0 { @@ -5584,10 +5028,6 @@ func (m *ManifestRequest) Size() (n int) { if l > 0 { n += 2 + l + sovRepository(uint64(l)) } - l = len(m.AnnotationManifestGeneratePaths) - if l > 0 { - n += 2 + l + sovRepository(uint64(l)) - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5729,9 +5169,6 @@ func (m *ResolveRevisionRequest) Size() (n int) { if l > 0 { n += 1 + l + sovRepository(uint64(l)) } - if m.SourceIndex != 0 { - n += 1 + sovRepository(uint64(m.SourceIndex)) - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5790,12 +5227,6 @@ func (m *ManifestResponse) Size() (n int) { if l > 0 { n += 1 + l + sovRepository(uint64(l)) } - if len(m.Commands) > 0 { - for _, s := range m.Commands { - l = len(s) - n += 1 + l + sovRepository(uint64(l)) - } - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6294,9 +5725,6 @@ func (m *GitFilesRequest) Size() (n int) { if m.NoRevisionCache { n += 2 } - if m.VerifyCommit { - n += 2 - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6347,9 +5775,6 @@ func (m *GitDirectoriesRequest) Size() (n int) { if m.NoRevisionCache { n += 2 } - if m.VerifyCommit { - n += 2 - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6374,119 +5799,21 @@ func (m *GitDirectoriesResponse) Size() (n int) { return n } -func (m *UpdateRevisionForPathsRequest) 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.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)) - } - l = len(m.TrackingMethod) - if l > 0 { - n += 1 + l + sovRepository(uint64(l)) - } - if len(m.RefSources) > 0 { - for k, v := range m.RefSources { - _ = k - _ = v - l = 0 - if v != nil { - l = v.Size() - l += 1 + sovRepository(uint64(l)) - } - mapEntrySize := 1 + len(k) + sovRepository(uint64(len(k))) + l - n += mapEntrySize + 1 + sovRepository(uint64(mapEntrySize)) - } - } - 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.HasMultipleSources { - n += 2 - } - l = len(m.SyncedRevision) - if l > 0 { - n += 1 + l + sovRepository(uint64(l)) - } - l = len(m.Revision) - if l > 0 { - n += 1 + l + sovRepository(uint64(l)) - } - if len(m.Paths) > 0 { - for _, s := range m.Paths { - l = len(s) - 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 *UpdateRevisionForPathsResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Changes { - n += 2 - } - 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 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 +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 @@ -7365,38 +6692,6 @@ func (m *ManifestRequest) Unmarshal(dAtA []byte) error { } m.ProjectName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 26: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AnnotationManifestGeneratePaths", 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.AnnotationManifestGeneratePaths = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) @@ -8053,25 +7348,6 @@ func (m *ResolveRevisionRequest) Unmarshal(dAtA []byte) error { } m.AmbiguousRevision = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field SourceIndex", wireType) - } - m.SourceIndex = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRepository - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.SourceIndex |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) @@ -8430,38 +7706,6 @@ func (m *ManifestResponse) Unmarshal(dAtA []byte) error { } m.VerifyResult = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 8: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Commands", 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.Commands = append(m.Commands, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) @@ -11692,26 +10936,6 @@ func (m *GitFilesRequest) Unmarshal(dAtA []byte) error { } } m.NoRevisionCache = bool(v != 0) - case 7: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field VerifyCommit", 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.VerifyCommit = bool(v != 0) default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) @@ -12050,26 +11274,6 @@ func (m *GitDirectoriesRequest) Unmarshal(dAtA []byte) error { } } m.NoRevisionCache = bool(v != 0) - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field VerifyCommit", 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.VerifyCommit = bool(v != 0) default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) @@ -12175,689 +11379,6 @@ func (m *GitDirectoriesResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateRevisionForPathsRequest) 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: UpdateRevisionForPathsRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateRevisionForPathsRequest: 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 != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AppLabelKey", 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.AppLabelKey = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AppName", 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.AppName = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - 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 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.Namespace = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ApplicationSource", 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.ApplicationSource == nil { - m.ApplicationSource = &v1alpha1.ApplicationSource{} - } - if err := m.ApplicationSource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TrackingMethod", 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.TrackingMethod = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RefSources", 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.RefSources == nil { - m.RefSources = make(map[string]*v1alpha1.RefTarget) - } - var mapkey string - var mapvalue *v1alpha1.RefTarget - 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 mapmsglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRepository - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - mapmsglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if mapmsglen < 0 { - return ErrInvalidLengthRepository - } - postmsgIndex := iNdEx + mapmsglen - if postmsgIndex < 0 { - return ErrInvalidLengthRepository - } - if postmsgIndex > l { - return io.ErrUnexpectedEOF - } - mapvalue = &v1alpha1.RefTarget{} - if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { - return err - } - iNdEx = postmsgIndex - } 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.RefSources[mapkey] = mapvalue - iNdEx = postIndex - case 8: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field KubeVersion", 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.KubeVersion = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 9: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ApiVersions", 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.ApiVersions = append(m.ApiVersions, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 10: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field HasMultipleSources", 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.HasMultipleSources = bool(v != 0) - case 11: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SyncedRevision", 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.SyncedRevision = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 12: - 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 13: - 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 - case 14: - 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 *UpdateRevisionForPathsResponse) 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: UpdateRevisionForPathsResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateRevisionForPathsResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Changes", 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.Changes = bool(v != 0) - case 2: - 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 - 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/askpass/common.go b/reposerver/askpass/common.go index c9757f5878956..b4cadb2470214 100644 --- a/reposerver/askpass/common.go +++ b/reposerver/askpass/common.go @@ -4,17 +4,12 @@ import ( "github.com/argoproj/argo-cd/v2/util/env" ) -var SocketPath = "/tmp/reposerver-ask-pass.sock" - -const ( - // ASKPASS_NONCE_ENV is the environment variable that is used to pass the nonce to the askpass script - ASKPASS_NONCE_ENV = "ARGOCD_GIT_ASKPASS_NONCE" - // AKSPASS_SOCKET_PATH_ENV is the environment variable that is used to pass the socket path to the askpass script - AKSPASS_SOCKET_PATH_ENV = "ARGOCD_ASK_PASS_SOCK" +var ( + SocketPath = "/tmp/reposerver-ask-pass.sock" ) func init() { - SocketPath = env.StringFromEnv(AKSPASS_SOCKET_PATH_ENV, SocketPath) + SocketPath = env.StringFromEnv("ARGOCD_ASK_PASS_SOCK", SocketPath) } type Creds struct { diff --git a/reposerver/askpass/server.go b/reposerver/askpass/server.go index 2eb9f89869776..c34e3c332890d 100644 --- a/reposerver/askpass/server.go +++ b/reposerver/askpass/server.go @@ -2,7 +2,6 @@ package askpass import ( "context" - "fmt" "net" "os" "sync" @@ -23,16 +22,14 @@ type Server interface { } type server struct { - lock sync.Mutex - creds map[string]Creds - socketPath string + lock sync.Mutex + creds map[string]Creds } // NewServer returns a new server -func NewServer(socketPath string) *server { +func NewServer() *server { return &server{ - creds: make(map[string]Creds), - socketPath: socketPath, + creds: make(map[string]Creds), } } @@ -61,8 +58,8 @@ func (s *server) Start(path string) (io.Closer, error) { return io.NewCloser(listener.Close), nil } -func (s *server) Run() error { - _, err := s.Start(s.socketPath) +func (s *server) Run(path string) error { + _, err := s.Start(path) return err } @@ -91,14 +88,3 @@ func (s *server) getCreds(id string) (*Creds, bool) { creds, ok := s.creds[id] return &creds, ok } - -// Environ returns the environment variables that should be set when invoking git. -func (s *server) Environ(id string) []string { - return []string{ - "GIT_ASKPASS=argocd", - fmt.Sprintf("%s=%s", ASKPASS_NONCE_ENV, id), - "GIT_TERMINAL_PROMPT=0", - "ARGOCD_BINARY_NAME=argocd-git-ask-pass", - fmt.Sprintf("%s=%s", AKSPASS_SOCKET_PATH_ENV, s.socketPath), - } -} diff --git a/reposerver/askpass/server_test.go b/reposerver/askpass/server_test.go index 980575a669670..311592d7f0aa7 100644 --- a/reposerver/askpass/server_test.go +++ b/reposerver/askpass/server_test.go @@ -7,7 +7,7 @@ import ( ) func TestAdd(t *testing.T) { - s := NewServer(SocketPath) + s := NewServer() nonce := s.Add("foo", "bar") assert.Equal(t, "foo", s.creds[nonce].Username) @@ -15,7 +15,7 @@ func TestAdd(t *testing.T) { } func TestRemove(t *testing.T) { - s := NewServer(SocketPath) + s := NewServer() s.creds["some-id"] = Creds{Username: "foo"} s.Remove("some-id") diff --git a/reposerver/cache/cache.go b/reposerver/cache/cache.go index fde5a81748ab0..79d3a02b62750 100644 --- a/reposerver/cache/cache.go +++ b/reposerver/cache/cache.go @@ -3,7 +3,6 @@ package cache import ( "encoding/base64" "encoding/json" - "errors" "fmt" "hash/fnv" "math" @@ -13,6 +12,7 @@ import ( "github.com/argoproj/gitops-engine/pkg/utils/text" "github.com/go-git/go-git/v5/plumbing" + "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -24,16 +24,12 @@ import ( "github.com/argoproj/argo-cd/v2/util/hash" ) -var ( - ErrCacheMiss = cacheutil.ErrCacheMiss - ErrCacheKeyLocked = cacheutil.ErrCacheKeyLocked -) +var ErrCacheMiss = cacheutil.ErrCacheMiss type Cache struct { - cache *cacheutil.Cache - repoCacheExpiration time.Duration - revisionCacheExpiration time.Duration - revisionCacheLockTimeout time.Duration + cache *cacheutil.Cache + repoCacheExpiration time.Duration + revisionCacheExpiration time.Duration } // ClusterRuntimeInfo holds cluster runtime information @@ -44,18 +40,16 @@ type ClusterRuntimeInfo interface { GetKubeVersion() string } -func NewCache(cache *cacheutil.Cache, repoCacheExpiration time.Duration, revisionCacheExpiration time.Duration, revisionCacheLockTimeout time.Duration) *Cache { - return &Cache{cache, repoCacheExpiration, revisionCacheExpiration, revisionCacheLockTimeout} +func NewCache(cache *cacheutil.Cache, repoCacheExpiration time.Duration, revisionCacheExpiration time.Duration) *Cache { + return &Cache{cache, repoCacheExpiration, revisionCacheExpiration} } -func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...cacheutil.Options) func() (*Cache, error) { +func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) 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...) @@ -64,7 +58,7 @@ func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...cacheutil.Options) func() (* if err != nil { return nil, fmt.Errorf("error adding cache flags to cmd: %w", err) } - return NewCache(cache, repoCacheExpiration, revisionCacheExpiration, revisionCacheLockTimeout), nil + return NewCache(cache, repoCacheExpiration, revisionCacheExpiration), nil } } @@ -152,13 +146,7 @@ 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, - &cacheutil.CacheActionOpts{ - Expiration: c.repoCacheExpiration, - Delete: apps == nil, - }) + return c.cache.SetItem(listApps(repoUrl, revision), apps, c.repoCacheExpiration, apps == nil) } func helmIndexRefsKey(repo string) string { @@ -167,14 +155,7 @@ func helmIndexRefsKey(repo string) string { // SetHelmIndex stores helm repository index.yaml content to cache func (c *Cache) SetHelmIndex(repo string, indexData []byte) error { - 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}) + return c.cache.SetItem(helmIndexRefsKey(repo), indexData, c.revisionCacheExpiration, false) } // GetHelmIndex retrieves helm repository index.yaml content from cache @@ -192,100 +173,21 @@ 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, &cacheutil.CacheActionOpts{Expiration: c.revisionCacheExpiration}) -} - -// 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) + return c.cache.SetItem(gitRefsKey(repo), input, c.revisionCacheExpiration, false) } -// Retrieves the cache item for git repo references. Returns foundLockId, error -func (c *Cache) GetGitReferences(repo string, references *[]*plumbing.Reference) (string, error) { +// GetGitReferences retrieves resolved Git repository references from cache +func (c *Cache) GetGitReferences(repo string, references *[]*plumbing.Reference) error { var input [][2]string - err := c.cache.GetItem(gitRefsKey(repo), &input) - valueExists := len(input) > 0 && len(input[0]) > 0 - switch { - // Unexpected Error - case err != nil && !errors.Is(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 - } -} - -// 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) - } - // 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") + if err := c.cache.GetItem(gitRefsKey(repo), &input); err != nil { + return err } - // 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}) + var res []*plumbing.Reference + for i := range input { + res = append(res, plumbing.NewReferenceFromStrings(input[i][0], input[i][1])) } - return err + *references = res + return nil } // refSourceCommitSHAs is a list of resolved revisions for each ref source. This allows us to invalidate the cache @@ -323,21 +225,16 @@ func LogDebugManifestCacheKeyFields(message string, reason string, revision stri } } -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 } hash, err := res.generateCacheEntryHash() if err != nil { - return fmt.Errorf("Unable to generate hash value: %w", err) + return fmt.Errorf("Unable to generate hash value: %s", err) } // If cached result does not have manifests or the expected hash of the cache entry does not match the actual hash value... @@ -348,7 +245,7 @@ func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, s 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, %w", err) + return fmt.Errorf("Unable to delete manifest after hash mismatch, %v", err) } // Treat hash mismatches as cache misses, so that the underlying resource is reacquired @@ -358,11 +255,6 @@ func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, s // The expected hash matches the actual hash, so remove the hash from the returned value res.CacheEntryHash = "" - if res.ManifestResponse != nil { - // cached manifest response might be reused across different revisions, so we need to assume that the revision is the one we are looking for - res.ManifestResponse.Revision = revision - } - return nil } @@ -372,25 +264,16 @@ func (c *Cache) SetManifests(revision string, appSrc *appv1.ApplicationSource, s res = res.shallowCopy() hash, err := res.generateCacheEntryHash() if err != nil { - return fmt.Errorf("Unable to generate hash value: %w", err) + return fmt.Errorf("Unable to generate hash value: %s", err) } res.CacheEntryHash = hash } - return c.cache.SetItem( - manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs), - res, - &cacheutil.CacheActionOpts{ - Expiration: c.repoCacheExpiration, - Delete: res == nil, - }) + return c.cache.SetItem(manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs), res, c.repoCacheExpiration, res == nil) } 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}) + return c.cache.SetItem(manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs), "", c.repoCacheExpiration, true) } func appDetailsCacheKey(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, trackingMethod appv1.TrackingMethod, refSourceCommitSHAs ResolvedRevisions) string { @@ -405,13 +288,7 @@ func (c *Cache) GetAppDetails(revision string, appSrc *appv1.ApplicationSource, } 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, - }) + return c.cache.SetItem(appDetailsCacheKey(revision, appSrc, srcRefs, trackingMethod, refSourceCommitSHAs), res, c.repoCacheExpiration, res == nil) } func revisionMetadataKey(repoURL, revision string) string { @@ -424,10 +301,7 @@ 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, - &cacheutil.CacheActionOpts{Expiration: c.repoCacheExpiration}) + return c.cache.SetItem(revisionMetadataKey(repoURL, revision), item, c.repoCacheExpiration, false) } func revisionChartDetailsKey(repoURL, chart, revision string) string { @@ -440,10 +314,7 @@ func (c *Cache) GetRevisionChartDetails(repoURL, chart, revision string) (*appv1 } 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}) + return c.cache.SetItem(revisionChartDetailsKey(repoURL, chart, revision), item, c.repoCacheExpiration, false) } func gitFilesKey(repoURL, revision, pattern string) string { @@ -451,10 +322,7 @@ func gitFilesKey(repoURL, revision, pattern string) string { } 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}) + return c.cache.SetItem(gitFilesKey(repoURL, revision, pattern), &files, c.repoCacheExpiration, false) } func (c *Cache) GetGitFiles(repoURL, revision, pattern string) (map[string][]byte, error) { @@ -467,10 +335,7 @@ func gitDirectoriesKey(repoURL, revision string) string { } func (c *Cache) SetGitDirectories(repoURL, revision string, directories []string) error { - return c.cache.SetItem( - gitDirectoriesKey(repoURL, revision), - &directories, - &cacheutil.CacheActionOpts{Expiration: c.repoCacheExpiration}) + return c.cache.SetItem(gitDirectoriesKey(repoURL, revision), &directories, c.repoCacheExpiration, false) } func (c *Cache) GetGitDirectories(repoURL, revision string) ([]string, error) { @@ -494,6 +359,7 @@ func (cmr *CachedManifestResponse) shallowCopy() *CachedManifestResponse { } func (cmr *CachedManifestResponse) generateCacheEntryHash() (string, error) { + // Copy, then remove the old hash copy := cmr.shallowCopy() copy.CacheEntryHash = "" @@ -510,11 +376,13 @@ func (cmr *CachedManifestResponse) generateCacheEntryHash() (string, error) { } fnvHash := h.Sum(nil) return base64.URLEncoding.EncodeToString(fnvHash), nil + } // CachedManifestResponse represents a cached result of a previous manifest generation operation, including the caching // of a manifest generation error, plus additional information on previous failures type CachedManifestResponse struct { + // NOTE: When adding fields to this struct, you MUST also update shallowCopy() CacheEntryHash string `json:"cacheEntryHash"` diff --git a/reposerver/cache/cache_test.go b/reposerver/cache/cache_test.go index 82e7fd556ad16..190ddfc78fe09 100644 --- a/reposerver/cache/cache_test.go +++ b/reposerver/cache/cache_test.go @@ -3,52 +3,38 @@ package cache import ( "encoding/json" "errors" - "fmt" + "strings" "testing" "time" - "github.com/go-git/go-git/v5/plumbing" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" . "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" ) -type MockedCache struct { - mock.Mock - *Cache -} - type fixtures struct { - mockCache *mocks.MockRepoCache - cache *MockedCache + *Cache } func newFixtures() *fixtures { - 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}} + return &fixtures{NewCache( + cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)), + 1*time.Minute, + 1*time.Minute, + )} } func TestCache_GetRevisionMetadata(t *testing.T) { - fixtures := newFixtures() - t.Cleanup(fixtures.mockCache.StopRedisCallback) - cache := fixtures.cache - mockCache := fixtures.mockCache + cache := newFixtures().Cache // 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"}) - require.NoError(t, err) + assert.NoError(t, err) // cache miss _, err = cache.GetRevisionMetadata("other-repo-url", "my-revision") assert.Equal(t, ErrCacheMiss, err) @@ -57,22 +43,18 @@ func TestCache_GetRevisionMetadata(t *testing.T) { assert.Equal(t, ErrCacheMiss, err) // cache hit value, err := cache.GetRevisionMetadata("my-repo-url", "my-revision") - require.NoError(t, err) + 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) { - fixtures := newFixtures() - t.Cleanup(fixtures.mockCache.StopRedisCallback) - cache := fixtures.cache - mockCache := fixtures.mockCache + cache := newFixtures().Cache // cache miss _, err := cache.ListApps("my-repo-url", "my-revision") assert.Equal(t, ErrCacheMiss, err) // populate cache err = cache.SetApps("my-repo-url", "my-revision", map[string]string{"foo": "bar"}) - require.NoError(t, err) + assert.NoError(t, err) // cache miss _, err = cache.ListApps("other-repo-url", "my-revision") assert.Equal(t, ErrCacheMiss, err) @@ -81,16 +63,12 @@ func TestCache_ListApps(t *testing.T) { assert.Equal(t, ErrCacheMiss, err) // cache hit value, err := cache.ListApps("my-repo-url", "my-revision") - require.NoError(t, err) + 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) { - fixtures := newFixtures() - t.Cleanup(fixtures.mockCache.StopRedisCallback) - cache := fixtures.cache - mockCache := fixtures.mockCache + cache := newFixtures().Cache // cache miss q := &apiclient.ManifestRequest{} value := &CachedManifestResponse{} @@ -99,7 +77,7 @@ func TestCache_GetManifests(t *testing.T) { // 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, nil) - require.NoError(t, err) + 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, nil) assert.Equal(t, ErrCacheMiss, err) @@ -125,25 +103,14 @@ func TestCache_GetManifests(t *testing.T) { assert.Equal(t, ErrCacheMiss, err) }) t.Run("expect cache hit", func(t *testing.T) { - err = cache.SetManifests( - "my-revision1", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", - &CachedManifestResponse{ManifestResponse: &apiclient.ManifestResponse{SourceType: "my-source-type", Revision: "my-revision2"}}, nil) - require.NoError(t, err) - - err = cache.GetManifests("my-revision1", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil) - require.NoError(t, err) - - assert.Equal(t, "my-source-type", value.ManifestResponse.SourceType) - assert.Equal(t, "my-revision1", value.ManifestResponse.Revision) + 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: 2, ExternalGets: 8}) } func TestCache_GetAppDetails(t *testing.T) { - fixtures := newFixtures() - t.Cleanup(fixtures.mockCache.StopRedisCallback) - cache := fixtures.cache - mockCache := fixtures.mockCache + cache := newFixtures().Cache // cache miss value := &apiclient.RepoAppDetailsResponse{} emptyRefSources := map[string]*RefTarget{} @@ -151,34 +118,33 @@ func TestCache_GetAppDetails(t *testing.T) { assert.Equal(t, ErrCacheMiss, err) res := &apiclient.RepoAppDetailsResponse{Type: "my-type"} err = cache.SetAppDetails("my-revision", &ApplicationSource{}, emptyRefSources, res, "", nil) - require.NoError(t, err) - // cache miss + assert.NoError(t, err) + //cache miss err = cache.GetAppDetails("other-revision", &ApplicationSource{}, emptyRefSources, value, "", nil) assert.Equal(t, ErrCacheMiss, err) - // cache miss + //cache miss 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, "", nil) - require.NoError(t, err) + 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) { cache, err := AddCacheFlagsToCmd(&cobra.Command{})() - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, 24*time.Hour, cache.repoCacheExpiration) } func TestCachedManifestResponse_HashBehavior(t *testing.T) { + inMemCache := cacheutil.NewInMemoryCache(1 * time.Hour) repoCache := NewCache( cacheutil.NewCache(inMemCache), 1*time.Minute, 1*time.Minute, - 10*time.Second, ) response := apiclient.ManifestResponse{ @@ -208,9 +174,10 @@ func TestCachedManifestResponse_HashBehavior(t *testing.T) { var cacheKey string var cmr *CachedManifestResponse { + items := getInMemoryCacheContents(t, inMemCache) - assert.Len(t, items, 1) + assert.Equal(t, len(items), 1) for key, val := range items { cmr = val @@ -247,17 +214,19 @@ func TestCachedManifestResponse_HashBehavior(t *testing.T) { if err != nil { t.Fatal(err) } + } // 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, nil) - assert.Equal(t, err, cacheutil.ErrCacheMiss) + assert.True(t, err == cacheutil.ErrCacheMiss) // Verify that the hash mismatch item has been deleted items := getInMemoryCacheContents(t, inMemCache) - assert.Empty(t, items) + assert.Equal(t, len(items), 0) + } func getInMemoryCacheContents(t *testing.T, inMemCache *cacheutil.InMemoryCache) map[string]*CachedManifestResponse { @@ -280,6 +249,7 @@ func getInMemoryCacheContents(t *testing.T, inMemCache *cacheutil.InMemoryCache) } func TestCachedManifestResponse_ShallowCopy(t *testing.T) { + pre := &CachedManifestResponse{ CacheEntryHash: "value", FirstFailureTimestamp: 1, @@ -308,6 +278,7 @@ func TestCachedManifestResponse_ShallowCopy(t *testing.T) { } func TestCachedManifestResponse_ShallowCopyExpectedFields(t *testing.T) { + // Attempt to ensure that the developer updated CachedManifestResponse.shallowCopy(), by doing a sanity test of the structure here val := &CachedManifestResponse{} @@ -325,10 +296,8 @@ func TestCachedManifestResponse_ShallowCopyExpectedFields(t *testing.T) { return } - expectedFields := []string{ - "cacheEntryHash", "manifestResponse", "mostRecentError", "firstFailureTimestamp", - "numberOfConsecutiveFailures", "numberOfCachedResponsesReturned", - } + expectedFields := []string{"cacheEntryHash", "manifestResponse", "mostRecentError", "firstFailureTimestamp", + "numberOfConsecutiveFailures", "numberOfCachedResponsesReturned"} assert.Equal(t, len(jsonMap), len(expectedFields)) @@ -336,431 +305,7 @@ func TestCachedManifestResponse_ShallowCopyExpectedFields(t *testing.T) { // go do that first :) for _, expectedField := range expectedFields { - assert.Containsf(t, string(str), "\""+expectedField+"\"", "Missing field: %s", expectedField) + assert.Truef(t, strings.Contains(string(str), "\""+expectedField+"\""), "Missing field: %s", expectedField) } -} - -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) - require.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) - require.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"}})) - require.NoError(t, err) - var references []*plumbing.Reference - lockOwner, err := cache.GetGitReferences("test-repo", &references) - require.NoError(t, err) - assert.Equal(t, "", lockOwner, "Lock owner should be empty") - assert.Len(t, references, 1) - 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) - require.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.Empty(t, references, "No data should be handled gracefully by returning an empty slice") - references = *GitRefCacheItemToReferences([][2]string{{"", ""}}) - assert.Empty(t, references, "Empty data should be discarded") - references = *GitRefCacheItemToReferences([][2]string{{"test", ""}}) - assert.Len(t, references, 1, "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.Len(t, references, 1, "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.Len(t, references, 1, "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.Len(t, references, 2, "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}) - require.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}) - require.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}) - require.NoError(t, err) - err = utilCache.GetItem(key, &output) - fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 2, ExternalGets: 4}) - require.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}) - require.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}) - require.NoError(t, err) - err = utilCache.GetItem(key, &output) - require.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) - require.NoError(t, err) - assert.Equal(t, "test-lock-id", lockId) - 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"}})) - require.NoError(t, err) - var references []*plumbing.Reference - lockId, err := cache.GetOrLockGitReferences("test-repo", "test-lock-id", &references) - require.NoError(t, err) - assert.NotEqual(t, "test-lock-id", lockId) - 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, - }) - require.NoError(t, err) - var references []*plumbing.Reference - lockId, err := cache.GetOrLockGitReferences("test-repo", "test-lock-id", &references) - require.NoError(t, err) - assert.NotEqual(t, "test-lock-id", lockId) - 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"}})) - require.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) - require.NoError(t, err) - assert.NotEqual(t, "test-lock-id", lockId) - 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"}})) - require.NoError(t, err) - cache.revisionCacheLockTimeout = -1 * time.Second - var references []*plumbing.Reference - lockId, err := cache.GetOrLockGitReferences("test-repo", "test-lock-id", &references) - require.NoError(t, err) - assert.Equal(t, "test-lock-id", lockId) - 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) - require.NoError(t, err) - assert.Equal(t, "test-lock-id", lockId) - 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", "") - require.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) - require.NoError(t, err) - assert.Equal(t, "test-lock-id", lockId) - assert.NotEqual(t, "", lockId, "Lock id should be set") - // Release lock - err = cache.UnlockGitReferences("test-repo", lockId) - require.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")) - require.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) - require.Error(t, err, "nil data should not be cached") - var indexData []byte - err = fixtures.cache.GetHelmIndex("test-repo", &indexData) - require.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") - require.ErrorIs(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}) - require.NoError(t, err) - details, err := fixtures.cache.GetRevisionChartDetails("test-repo", "test-revision", "v1.0.0") - require.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}) - require.NoError(t, err) - details, err := fixtures.cache.GetRevisionChartDetails("test-repo", "test-revision", "v1.0.0") - require.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) - require.NoError(t, err) - details, err := fixtures.cache.GetRevisionChartDetails("test-repo", "test-revision", "v1.0.0") - require.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") - require.ErrorIs(t, err, ErrCacheMiss) - assert.Empty(t, 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}) - require.NoError(t, err) - directories, err := fixtures.cache.GetGitDirectories("test-repo", "test-revision") - require.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}) - require.NoError(t, err) - directories, err := fixtures.cache.GetGitDirectories("test-repo", "test-revision") - require.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) - require.NoError(t, err) - directories, err := fixtures.cache.GetGitDirectories("test-repo", "test-revision") - require.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") - require.ErrorIs(t, err, ErrCacheMiss) - assert.Empty(t, 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}) - require.NoError(t, err) - files, err := fixtures.cache.GetGitFiles("test-repo", "test-revision", "*.json") - require.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) - require.NoError(t, err) - files, err := fixtures.cache.GetGitFiles("test-repo", "test-revision", "*.json") - require.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 index f26bd8bccac43..0e49b5816178e 100644 --- a/reposerver/cache/mocks/reposervercache.go +++ b/reposerver/cache/mocks/reposervercache.go @@ -5,11 +5,10 @@ import ( "time" "github.com/alicebob/miniredis/v2" - "github.com/redis/go-redis/v9" - "github.com/stretchr/testify/mock" - 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 @@ -36,7 +35,6 @@ type CacheCallCounts struct { ExternalSets int ExternalGets int ExternalDeletes int - ExternalRenames int } // Checks that the cache was called the expected number of times @@ -44,14 +42,12 @@ func (mockCache *MockRepoCache) AssertCacheCalledTimes(t *testing.T, calls *Cach mockCache.RedisClient.AssertNumberOfCalls(t, "Get", calls.ExternalGets) mockCache.RedisClient.AssertNumberOfCalls(t, "Set", calls.ExternalSets) mockCache.RedisClient.AssertNumberOfCalls(t, "Delete", calls.ExternalDeletes) - mockCache.RedisClient.AssertNumberOfCalls(t, "Rename", calls.ExternalRenames) } 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) - mockCache.RedisClient.On("Rename", mock.Anything, mock.Anything, mock.Anything).Return(nil) } func NewInMemoryRedis() (*redis.Client, func()) { @@ -68,8 +64,7 @@ func NewMockRepoCache(cacheOpts *MockCacheOptions) *MockRepoCache { redisCacheClient := &cacheutilmocks.MockCacheClient{ ReadDelay: cacheOpts.ReadDelay, WriteDelay: cacheOpts.WriteDelay, - BaseCache: cacheutil.NewRedisCache(redisClient, cacheOpts.RepoCacheExpiration, cacheutil.RedisCompressionNone), - } + 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 5b43d6a24ac76..9c2c9be790813 100644 --- a/reposerver/gpgwatcher.go +++ b/reposerver/gpgwatcher.go @@ -19,7 +19,7 @@ func StartGPGWatcher(sourcePath string) error { forceSync := false watcher, err := fsnotify.NewWatcher() if err != nil { - return fmt.Errorf("failed to create fsnotify Watcher: %w", err) + return err } defer func(watcher *fsnotify.Watcher) { if err = watcher.Close(); err != nil { @@ -83,7 +83,7 @@ func StartGPGWatcher(sourcePath string) error { err = watcher.Add(sourcePath) if err != nil { - return fmt.Errorf("failed to add a new source to the watcher: %w", err) + return err } <-done return fmt.Errorf("Abnormal termination of GPG watcher, refusing to continue.") diff --git a/reposerver/metrics/githandlers_test.go b/reposerver/metrics/githandlers_test.go deleted file mode 100644 index 6eaeeca82cc36..0000000000000 --- a/reposerver/metrics/githandlers_test.go +++ /dev/null @@ -1,122 +0,0 @@ -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 4fd9cde3ba60a..e629b75e63d3c 100644 --- a/reposerver/metrics/metrics.go +++ b/reposerver/metrics/metrics.go @@ -12,8 +12,6 @@ import ( type MetricsServer struct { handler http.Handler - gitFetchFailCounter *prometheus.CounterVec - gitLsRemoteFailCounter *prometheus.CounterVec gitRequestCounter *prometheus.CounterVec gitRequestHistogram *prometheus.HistogramVec repoPendingRequestsGauge *prometheus.GaugeVec @@ -34,24 +32,6 @@ 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) - - gitLsRemoteFailCounter := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "argocd_git_lsremote_fail_total", - Help: "Number of git ls-remote requests failures by repo server", - }, - []string{"repo", "revision"}, - ) - registry.MustRegister(gitLsRemoteFailCounter) - gitRequestCounter := prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "argocd_git_request_total", @@ -101,8 +81,6 @@ func NewMetricsServer() *MetricsServer { return &MetricsServer{ handler: promhttp.HandlerFor(registry, promhttp.HandlerOpts{}), - gitFetchFailCounter: gitFetchFailCounter, - gitLsRemoteFailCounter: gitLsRemoteFailCounter, gitRequestCounter: gitRequestCounter, gitRequestHistogram: gitRequestHistogram, repoPendingRequestsGauge: repoPendingRequestsGauge, @@ -115,14 +93,6 @@ func (m *MetricsServer) GetHandler() http.Handler { return m.handler } -func (m *MetricsServer) IncGitFetchFail(repo string, revision string) { - m.gitFetchFailCounter.WithLabelValues(repo, revision).Inc() -} - -func (m *MetricsServer) IncGitLsRemoteFail(repo string, revision string) { - m.gitLsRemoteFailCounter.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 index c1ad7855049d3..f4bcf48fba569 100644 --- a/reposerver/repository/chart.go +++ b/reposerver/repository/chart.go @@ -4,9 +4,8 @@ import ( "fmt" "strings" - "sigs.k8s.io/yaml" - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "sigs.k8s.io/yaml" ) func getChartDetails(chartYAML string) (*v1alpha1.ChartDetails, error) { diff --git a/reposerver/repository/chart_test.go b/reposerver/repository/chart_test.go index 3e1bccfa46a07..b22e7c21bede5 100644 --- a/reposerver/repository/chart_test.go +++ b/reposerver/repository/chart_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func Test_getChartDetailsNotSet(t *testing.T) { @@ -13,10 +12,10 @@ name: mychart version: 0.0.0` cd, err := getChartDetails(chart1) - require.NoError(t, err) - assert.Equal(t, "", cd.Description) + assert.NoError(t, err) + assert.Equal(t, cd.Description, "") assert.Equal(t, cd.Maintainers, []string(nil)) - assert.Equal(t, "", cd.Home) + assert.Equal(t, cd.Home, "") } func Test_getChartDetailsSet(t *testing.T) { @@ -31,10 +30,10 @@ maintainers: ` cd, err := getChartDetails(chart1) - require.NoError(t, err) - assert.Equal(t, "a good chart", cd.Description) - assert.Equal(t, []string{"alex "}, cd.Maintainers) - assert.Equal(t, "https://example.com", cd.Home) + 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 @@ -45,8 +44,8 @@ maintainers: - name: alex ` cd, err = getChartDetails(chart1) - require.NoError(t, err) - assert.Equal(t, []string{"alex"}, cd.Maintainers) + assert.NoError(t, err) + assert.Equal(t, cd.Maintainers, []string{"alex"}) } func Test_getChartDetailsBad(t *testing.T) { @@ -59,6 +58,6 @@ maintainers: alex ` cd, err := getChartDetails(chart1) - require.Error(t, err) + assert.Error(t, err) assert.Nil(t, cd) } diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index 7115c1bedd9aa..be64c4598e3b6 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -16,6 +16,16 @@ import ( "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" @@ -23,7 +33,6 @@ import ( "github.com/argoproj/pkg/sync" jsonpatch "github.com/evanphx/json-patch" 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" @@ -31,35 +40,28 @@ 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" "github.com/argoproj/argo-cd/v2/reposerver/metrics" "github.com/argoproj/argo-cd/v2/util/app/discovery" - apppathutil "github.com/argoproj/argo-cd/v2/util/app/path" 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" - "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" ) @@ -72,12 +74,7 @@ const ( ociPrefix = "oci://" ) -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) -) +var ErrExceededMaxCombinedManifestFileSize = errors.New("exceeded max combined manifest file size") // Service implements ManifestService interface type Service struct { @@ -91,8 +88,8 @@ type Service struct { parallelismLimitSemaphore *semaphore.Weighted metricsServer *metrics.MetricsServer resourceTracking argo.ResourceTracking - newGitClient func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, proxy string, noProxy string, opts ...git.ClientOpts) (git.Client, error) - newHelmClient func(repoURL string, creds helm.Creds, enableOci bool, proxy string, noProxy string, opts ...helm.ClientOpts) helm.Client + newGitClient func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, proxy string, opts ...git.ClientOpts) (git.Client, error) + newHelmClient func(repoURL string, creds helm.Creds, enableOci bool, proxy string, opts ...helm.ClientOpts) helm.Client initConstants RepoServerInitConstants // now is usually just time.Now, but may be replaced by unit tests for testing purposes now func() time.Time @@ -112,8 +109,6 @@ type RepoServerInitConstants struct { HelmManifestMaxExtractedSize int64 HelmRegistryMaxIndexSize int64 DisableHelmManifestMaxExtractedSize bool - IncludeHiddenDirectories bool - CMPUseManifestGeneratePaths bool } // NewService returns a new instance of the Manifest service @@ -132,8 +127,8 @@ func NewService(metricsServer *metrics.MetricsServer, cache *cache.Cache, initCo metricsServer: metricsServer, newGitClient: git.NewClientExt, resourceTracking: resourceTracking, - newHelmClient: func(repoURL string, creds helm.Creds, enableOci bool, proxy string, noProxy string, opts ...helm.ClientOpts) helm.Client { - return helm.NewClientWithLock(repoURL, creds, sync.NewKeyLock(), enableOci, proxy, noProxy, opts...) + newHelmClient: func(repoURL string, creds helm.Creds, enableOci bool, proxy string, opts ...helm.ClientOpts) helm.Client { + return helm.NewClientWithLock(repoURL, creds, sync.NewKeyLock(), enableOci, proxy, opts...) }, initConstants: initConstants, now: time.Now, @@ -148,11 +143,11 @@ func NewService(metricsServer *metrics.MetricsServer, cache *cache.Cache, initCo func (s *Service) Init() error { _, err := os.Stat(s.rootDir) if os.IsNotExist(err) { - return os.MkdirAll(s.rootDir, 0o300) + return os.MkdirAll(s.rootDir, 0300) } if err == nil { // give itself read permissions to list previously written directories - err = os.Chmod(s.rootDir, 0o700) + err = os.Chmod(s.rootDir, 0700) } var dirEntries []fs.DirEntry if err == nil { @@ -177,7 +172,7 @@ func (s *Service) Init() error { io.Close(closer) } // remove read permissions since no-one should be able to list the directories - return os.Chmod(s.rootDir, 0o300) + return os.Chmod(s.rootDir, 0300) } // ListRefs List a subset of the refs (currently, branches and tags) of a git repo @@ -220,12 +215,13 @@ func (s *Service) ListApps(ctx context.Context, q *apiclient.ListAppsRequest) (* closer, err := s.repoLock.Lock(gitClient.Root(), commitSHA, true, func() (goio.Closer, error) { return s.checkoutRevision(gitClient, commitSHA, s.initConstants.SubmoduleEnabled) }) + if err != nil { return nil, fmt.Errorf("error acquiring repository lock: %w", err) } defer io.Close(closer) - apps, err := discovery.Discover(ctx, gitClient.Root(), gitClient.Root(), q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs, []string{}) + apps, err := discovery.Discover(ctx, gitClient.Root(), gitClient.Root(), q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs) if err != nil { return nil, fmt.Errorf("error discovering applications: %w", err) } @@ -267,6 +263,7 @@ type operationSettings struct { // operationContext contains request values which are generated by runRepoOperation (on demand) by a call to the // provided operationContextSrc function. type operationContext struct { + // application path or helm chart path appPath string @@ -294,8 +291,8 @@ func (s *Service) runRepoOperation( operation func(repoRoot, commitSHA, cacheKey string, ctxSrc operationContextSrc) error, settings operationSettings, hasMultipleSources bool, - refSources map[string]*v1alpha1.RefTarget, -) error { + refSources map[string]*v1alpha1.RefTarget) error { + if sanitizer, ok := grpc.SanitizerFromContext(ctx); ok { // make sure a randomized path replaced with '.' in the error message sanitizer.AddRegexReplacement(getRepoSanitizerRegex(s.rootDir), "") @@ -343,7 +340,7 @@ func (s *Service) runRepoOperation( if source.IsHelm() { if settings.noCache { - err = helmClient.CleanChartCache(source.Chart, revision, repo.Project) + err = helmClient.CleanChartCache(source.Chart, revision) if err != nil { return err } @@ -352,7 +349,7 @@ func (s *Service) runRepoOperation( if source.Helm != nil { helmPassCredentials = source.Helm.PassCredentials } - chartPath, closer, err := helmClient.ExtractChart(source.Chart, revision, repo.Project, helmPassCredentials, s.initConstants.HelmManifestMaxExtractedSize, s.initConstants.DisableHelmManifestMaxExtractedSize) + chartPath, closer, err := helmClient.ExtractChart(source.Chart, revision, helmPassCredentials, s.initConstants.HelmManifestMaxExtractedSize, s.initConstants.DisableHelmManifestMaxExtractedSize) if err != nil { return err } @@ -381,6 +378,7 @@ func (s *Service) runRepoOperation( closer, err := s.repoLock.Lock(gitClient.Root(), revision, settings.allowConcurrent, func() (goio.Closer, error) { return s.checkoutRevision(gitClient, revision, s.initConstants.SubmoduleEnabled) }) + if err != nil { return err } @@ -473,13 +471,7 @@ func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.Applicat return repoRefs, nil } - refFileParams := make([]string, 0) - for _, fileParam := range source.FileParameters { - refFileParams = append(refFileParams, fileParam.Path) - } - refCandidates := append(source.ValueFiles, refFileParams...) - - for _, valueFile := range refCandidates { + for _, valueFile := range source.ValueFiles { if strings.HasPrefix(valueFile, "$") { refVar := strings.Split(valueFile, "/")[0] @@ -518,7 +510,7 @@ func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestReq var err error // Skip this path for ref only sources - if q.HasMultipleSources && q.ApplicationSource.Path == "" && !q.ApplicationSource.IsHelm() && q.ApplicationSource.IsRef() { + 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{ @@ -597,6 +589,7 @@ func (s *Service) GenerateManifestWithFiles(stream apiclient.RepoServerService_G }() req, metadata, err := manifeststream.ReceiveManifestFileStream(stream.Context(), stream, workDir, s.initConstants.StreamedManifestMaxTarSize, s.initConstants.StreamedManifestMaxExtractedSize) + if err != nil { return fmt.Errorf("error receiving manifest file stream: %w", err) } @@ -676,6 +669,7 @@ type generateManifestCh struct { // - or, NoCache is true // Returns a ManifestResponse, or an error, but not both func (s *Service) runManifestGen(ctx context.Context, repoRoot, commitSHA, cacheKey string, opContextSrc operationContextSrc, q *apiclient.ManifestRequest) *ManifestResponsePromise { + responseCh := make(chan *apiclient.ManifestResponse) tarDoneCh := make(chan bool) errCh := make(chan error) @@ -717,14 +711,9 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, // check whether they should be replicated in resolveReferencedSources. if q.HasMultipleSources { if q.ApplicationSource.Helm != nil { - refFileParams := make([]string, 0) - for _, fileParam := range q.ApplicationSource.Helm.FileParameters { - refFileParams = append(refFileParams, fileParam.Path) - } - refCandidates := append(q.ApplicationSource.Helm.ValueFiles, refFileParams...) // Checkout every one of the referenced sources to the target revision before generating Manifests - for _, valueFile := range refCandidates { + for _, valueFile := range q.ApplicationSource.Helm.ValueFiles { if strings.HasPrefix(valueFile, "$") { refVar := strings.Split(valueFile, "/")[0] @@ -806,7 +795,7 @@ 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), WithCMPUseManifestGeneratePaths(s.initConstants.CMPUseManifestGeneratePaths)) + 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 { @@ -828,7 +817,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, // 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, refSourceCommitSHAs) - if cacheErr != nil && !errors.Is(cacheErr, cache.ErrCacheMiss) { + if cacheErr != nil && cacheErr != cache.ErrCacheMiss { logCtx.Warnf("manifest cache get error %s: %v", appSourceCopy.String(), cacheErr) ch.errCh <- cacheErr return @@ -846,12 +835,12 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, innerRes.NumberOfConsecutiveFailures++ innerRes.MostRecentError = err.Error() cacheErr = s.cache.SetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, innerRes, refSourceCommitSHAs) - if cacheErr != nil { logCtx.Warnf("manifest cache set error %s: %v", appSourceCopy.String(), cacheErr) ch.errCh <- cacheErr return } + } ch.errCh <- err return @@ -888,14 +877,18 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe res := cache.CachedManifestResponse{} 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 // If caching of manifest generation errors is enabled, and res is a cached manifest generation error... if s.initConstants.PauseGenerationAfterFailedGenerationAttempts > 0 && res.FirstFailureTimestamp > 0 { + // If we are already in the 'manifest generation caching' state, due to too many consecutive failures... if res.NumberOfConsecutiveFailures >= s.initConstants.PauseGenerationAfterFailedGenerationAttempts { + // Check if enough time has passed to try generation again (e.g. to exit the 'manifest generation caching' state) if s.initConstants.PauseGenerationOnFailureForMinutes > 0 { + elapsedTimeInMinutes := int((s.now().Unix() - res.FirstFailureTimestamp) / 60) // After X minutes, reset the cache and retry the operation (e.g. perhaps the error is ephemeral and has passed) @@ -914,6 +907,7 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe // Check if enough cached responses have been returned to try generation again (e.g. to exit the 'manifest generation caching' state) 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, refSourceCommitSHAs) @@ -945,6 +939,7 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe } return true, nil, cachedErrorResponse + } // Otherwise we are not yet in the manifest generation error state, and not enough consecutive errors have @@ -957,7 +952,7 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe return true, res.ManifestResponse, nil } - if !errors.Is(err, cache.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) @@ -1001,9 +996,7 @@ func getHelmRepos(appPath string, repositories []*v1alpha1.Repository, helmRepoC // 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 the repo is OCI, don't match the repository URL exactly, but only as a dependent repository prefix just like in the getRepoCredential function - // see https://github.com/argoproj/argo-cd/issues/12436 - if _, err := url.Parse("oci://" + dep.Repo); err == nil && cred.EnableOCI && strings.HasPrefix(dep.Repo, cred.Repo) { + 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 @@ -1095,7 +1088,7 @@ func runHelmBuild(appPath string, h helm.Helm) error { if err != nil { return fmt.Errorf("error building helm chart dependencies: %w", err) } - return os.WriteFile(markerFile, []byte("marker"), 0o644) + return os.WriteFile(markerFile, []byte("marker"), 0644) } func isSourcePermitted(url string, repos []string) bool { @@ -1103,8 +1096,8 @@ func isSourcePermitted(url string, repos []string) bool { 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, string, error) { - concurrencyAllowed := helmConcurrencyDefault || isConcurrencyAllowed(appPath) +func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclient.ManifestRequest, isLocal bool, gitRepoPaths io.TempPaths) ([]*unstructured.Unstructured, error) { + concurrencyAllowed := isConcurrencyAllowed(appPath) if !concurrencyAllowed { manifestGenerateLock.Lock(appPath) defer manifestGenerateLock.Unlock(appPath) @@ -1118,9 +1111,9 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie templateOpts := &helm.TemplateOpts{ Name: appName, - Namespace: q.ApplicationSource.GetNamespaceOrDefault(q.Namespace), - KubeVersion: text.SemVer(q.ApplicationSource.GetKubeVersionOrDefault(q.KubeVersion)), - APIVersions: q.ApplicationSource.GetAPIVersionsOrDefault(q.ApiVersions), + Namespace: q.Namespace, + KubeVersion: text.SemVer(q.KubeVersion), + APIVersions: q.ApiVersions, Set: map[string]string{}, SetString: map[string]string{}, SetFile: map[string]pathutil.ResolvedFilePath{}, @@ -1139,7 +1132,7 @@ 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, "", fmt.Errorf("error resolving helm value files: %w", err) + return nil, fmt.Errorf("error resolving helm value files: %w", err) } templateOpts.Values = resolvedValueFiles @@ -1147,7 +1140,7 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie if !appHelm.ValuesIsEmpty() { rand, err := uuid.NewRandom() if err != nil { - return nil, "", fmt.Errorf("error generating random filename for Helm values file: %w", err) + return nil, fmt.Errorf("error generating random filename for Helm values file: %w", err) } p := path.Join(os.TempDir(), rand.String()) defer func() { @@ -1156,11 +1149,11 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie _ = os.RemoveAll(p) } }() - err = os.WriteFile(p, appHelm.ValuesYAML(), 0o644) + err = os.WriteFile(p, appHelm.ValuesYAML(), 0644) if err != nil { - return nil, "", fmt.Errorf("error writing helm values file: %w", err) + return nil, fmt.Errorf("error writing helm values file: %w", err) } - templateOpts.ExtraValues = pathutil.ResolvedFilePath(p) + templateOpts.Values = append(templateOpts.Values, pathutil.ResolvedFilePath(p)) } for _, p := range appHelm.Parameters { @@ -1171,19 +1164,9 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie } } for _, p := range appHelm.FileParameters { - var resolvedPath pathutil.ResolvedFilePath - referencedSource := getReferencedSource(p.Path, q.RefSources) - if referencedSource != nil { - // If the $-prefixed path appears to reference another source, do env substitution _after_ resolving the source - resolvedPath, err = getResolvedRefValueFile(p.Path, env, q.GetValuesFileSchemes(), referencedSource.Repo.Repo, gitRepoPaths, referencedSource.Repo.Project) - if err != nil { - return nil, "", fmt.Errorf("error resolving set-file path: %w", err) - } - } else { - resolvedPath, _, err = pathutil.ResolveValueFilePathOrUrl(appPath, repoRoot, env.Envsubst(p.Path), q.GetValuesFileSchemes()) - if err != nil { - return nil, "", fmt.Errorf("error resolving helm value file path: %w", err) - } + resolvedPath, _, err := pathutil.ResolveValueFilePathOrUrl(appPath, repoRoot, env.Envsubst(p.Path), q.GetValuesFileSchemes()) + if err != nil { + return nil, fmt.Errorf("error resolving helm value file path: %w", err) } templateOpts.SetFile[p.Name] = resolvedPath } @@ -1207,20 +1190,24 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie helmRepos, err := getHelmRepos(appPath, q.Repos, q.HelmRepoCreds) if err != nil { - return nil, "", fmt.Errorf("error getting helm repos: %w", err) + return nil, fmt.Errorf("error getting helm repos: %w", err) } - h, err := helm.NewHelmApp(appPath, helmRepos, isLocal, version, proxy, q.Repo.NoProxy, passCredentials) + h, err := helm.NewHelmApp(appPath, helmRepos, isLocal, version, proxy, passCredentials) if err != nil { - return nil, "", fmt.Errorf("error initializing helm app object: %w", err) + return nil, fmt.Errorf("error initializing helm app object: %w", err) } defer h.Dispose() + err = h.Init() + if err != nil { + return nil, fmt.Errorf("error initializing helm app: %w", err) + } - out, command, err := h.Template(templateOpts) + out, err := h.Template(templateOpts) if err != nil { if !helm.IsMissingDependencyErr(err) { - return nil, "", err + return nil, err } if concurrencyAllowed { @@ -1245,39 +1232,18 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie } 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, status.Errorf(codes.PermissionDenied, "helm repos %s are not permitted in project '%s'", strings.Join(reposNotPermitted, ", "), q.ProjectName) } - return nil, "", err + return nil, err } - out, command, err = h.Template(templateOpts) + out, err = h.Template(templateOpts) if err != nil { - return nil, "", err + return nil, err } } - objs, err := kube.SplitYAML([]byte(out)) - - redactedCommand := redactPaths(command, gitRepoPaths, templateOpts.ExtraValues) - - return objs, redactedCommand, err -} - -// redactPaths removes temp repo paths, since those paths are randomized (and therefore not helpful for the user) and -// sensitive (so not suitable for logging). It also replaces the path of the randomly-named values file which is used -// to hold the `spec.source.helm.values` or `valuesObject` contents. -func redactPaths(s string, paths io.TempPaths, extraValuesPath pathutil.ResolvedFilePath) string { - if paths == nil { - return s - } - for _, p := range paths.GetPaths() { - s = strings.ReplaceAll(s, p, ".") - } - if extraValuesPath != "" { - // Replace with a placeholder so that the user knows what this values file was for. - s = strings.ReplaceAll(s, string(extraValuesPath), "") - } - return s + return kube.SplitYAML([]byte(out)) } func getResolvedValueFiles( @@ -1292,19 +1258,19 @@ func getResolvedValueFiles( ) ([]pathutil.ResolvedFilePath, error) { var resolvedValueFiles []pathutil.ResolvedFilePath for _, rawValueFile := range rawValueFiles { - isRemote := false + var isRemote = false var resolvedPath pathutil.ResolvedFilePath var err error referencedSource := getReferencedSource(rawValueFile, refSources) if referencedSource != nil { // 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, referencedSource.Repo.Project) + resolvedPath, err = getResolvedRefValueFile(rawValueFile, env, allowedValueFilesSchemas, referencedSource.Repo.Repo, gitRepoPaths) if err != nil { return nil, fmt.Errorf("error resolving value file path: %w", err) } } else { - // This will resolve val to an absolute path (or a URL) + // 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, fmt.Errorf("error resolving value file path: %w", err) @@ -1332,15 +1298,9 @@ func getResolvedRefValueFile( allowedValueFilesSchemas []string, refSourceRepo string, gitRepoPaths io.TempPaths, - project string, ) (pathutil.ResolvedFilePath, error) { pathStrings := strings.Split(rawValueFile, "/") - - keyData, err := json.Marshal(map[string]string{"url": git.NormalizeGitURL(refSourceRepo), "project": project}) - if err != nil { - return "", err - } - repoPath := gitRepoPaths.GetPathIfExists(string(keyData)) + repoPath := gitRepoPaths.GetPathIfExists(git.NormalizeGitURL(refSourceRepo)) if repoPath == "" { return "", fmt.Errorf("failed to find repo %q", refSourceRepo) } @@ -1374,14 +1334,11 @@ func getRepoCredential(repoCredentials []*v1alpha1.RepoCreds, repoURL string) *v return nil } -type ( - GenerateManifestOpt func(*generateManifestOpt) - generateManifestOpt struct { - cmpTarDoneCh chan<- bool - cmpTarExcludedGlobs []string - cmpUseManifestGeneratePaths bool - } -) +type GenerateManifestOpt func(*generateManifestOpt) +type generateManifestOpt struct { + cmpTarDoneCh chan<- bool + cmpTarExcludedGlobs []string +} func newGenerateManifestOpt(opts ...GenerateManifestOpt) *generateManifestOpt { o := &generateManifestOpt{} @@ -1408,14 +1365,6 @@ func WithCMPTarExcludedGlobs(excludedGlobs []string) GenerateManifestOpt { } } -// WithCMPUseManifestGeneratePaths enables or disables the use of the -// 'argocd.argoproj.io/manifest-generate-paths' annotation for manifest generation instead of transmit the whole repository. -func WithCMPUseManifestGeneratePaths(enabled bool) GenerateManifestOpt { - return func(o *generateManifestOpt) { - o.cmpUseManifestGeneratePaths = enabled - } -} - // GenerateManifests generates manifests from a path. Overrides are applied as a side effect on the given ApplicationSource. 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...) @@ -1423,9 +1372,7 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, resourceTracking := argo.NewResourceTracking() - env := newEnv(q, revision) - - appSourceType, err := GetAppSourceType(ctx, q.ApplicationSource, appPath, repoRoot, q.AppName, q.EnabledSourceTypes, opt.cmpTarExcludedGlobs, env.Environ()) + appSourceType, err := GetAppSourceType(ctx, q.ApplicationSource, appPath, repoRoot, q.AppName, q.EnabledSourceTypes, opt.cmpTarExcludedGlobs) if err != nil { return nil, fmt.Errorf("error getting app source type: %w", err) } @@ -1433,31 +1380,25 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, if q.Repo != nil { repoURL = q.Repo.Repo } - - var commands []string + env := newEnv(q, revision) switch appSourceType { case v1alpha1.ApplicationSourceTypeHelm: - var command string - targetObjs, command, err = helmTemplate(appPath, repoRoot, env, q, isLocal, gitRepoPaths) - commands = append(commands, command) + targetObjs, err = helmTemplate(appPath, repoRoot, env, q, isLocal, gitRepoPaths) case v1alpha1.ApplicationSourceTypeKustomize: kustomizeBinary := "" if q.KustomizeOptions != nil { kustomizeBinary = q.KustomizeOptions.BinaryPath } - k := kustomize.NewKustomizeApp(repoRoot, appPath, q.Repo.GetGitCreds(gitCredsStore), repoURL, kustomizeBinary, q.Repo.Proxy, q.Repo.NoProxy) - targetObjs, _, commands, err = k.Build(q.ApplicationSource.Kustomize, q.KustomizeOptions, env, &kustomize.BuildOpts{ - KubeVersion: text.SemVer(q.ApplicationSource.GetKubeVersionOrDefault(q.KubeVersion)), - APIVersions: q.ApplicationSource.GetAPIVersionsOrDefault(q.ApiVersions), - }) + k := kustomize.NewKustomizeApp(repoRoot, appPath, q.Repo.GetGitCreds(gitCredsStore), repoURL, kustomizeBinary) + targetObjs, _, err = k.Build(q.ApplicationSource.Kustomize, q.KustomizeOptions, env) case v1alpha1.ApplicationSourceTypePlugin: 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, opt.cmpTarDoneCh, opt.cmpTarExcludedGlobs, opt.cmpUseManifestGeneratePaths) + 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()) } @@ -1516,42 +1457,25 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, return &apiclient.ManifestResponse{ Manifests: manifests, SourceType: string(appSourceType), - Commands: commands, }, nil } func newEnv(q *apiclient.ManifestRequest, revision string) *v1alpha1.Env { - shortRevision := shortenRevision(revision, 7) - shortRevision8 := shortenRevision(revision, 8) + 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_REVISION_SHORT_8", Value: shortRevision8}, &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 shortenRevision(revision string, length int) string { - if len(revision) > length { - return revision[:length] - } - return revision -} - -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. // @@ -1565,7 +1489,7 @@ func mergeSourceParameters(source *v1alpha1.ApplicationSource, path, appName str overrides = append(overrides, filepath.Join(path, fmt.Sprintf(appSourceFile, appName))) } - merged := *source.DeepCopy() + var merged = *source.DeepCopy() for _, filename := range overrides { info, err := os.Stat(filename) @@ -1580,23 +1504,23 @@ func mergeSourceParameters(source *v1alpha1.ApplicationSource, path, appName str data, err := json.Marshal(merged) if err != nil { - return fmt.Errorf("%s: %w", filename, err) + return fmt.Errorf("%s: %v", filename, err) } patch, err := os.ReadFile(filename) if err != nil { - return fmt.Errorf("%s: %w", filename, err) + return fmt.Errorf("%s: %v", filename, err) } patch, err = yaml.YAMLToJSON(patch) if err != nil { - return fmt.Errorf("%s: %w", filename, err) + return fmt.Errorf("%s: %v", filename, err) } data, err = jsonpatch.MergePatch(data, patch) if err != nil { - return fmt.Errorf("%s: %w", filename, err) + return fmt.Errorf("%s: %v", filename, err) } err = json.Unmarshal(data, &merged) if err != nil { - return fmt.Errorf("%s: %w", filename, err) + return fmt.Errorf("%s: %v", filename, err) } } @@ -1611,10 +1535,10 @@ 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, appPath, repoPath, appName string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string, env []string) (v1alpha1.ApplicationSourceType, error) { +func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, appPath, repoPath, appName string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (v1alpha1.ApplicationSourceType, error) { err := mergeSourceParameters(source, appPath, appName) if err != nil { - return "", fmt.Errorf("error while parsing source parameters: %w", err) + return "", fmt.Errorf("error while parsing source parameters: %v", err) } appSourceType, err := source.ExplicitType() @@ -1628,9 +1552,9 @@ func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, a } return *appSourceType, nil } - appType, err := discovery.AppType(ctx, appPath, repoPath, enableGenerateManifests, tarExcludedGlobs, env) + appType, err := discovery.AppType(ctx, appPath, repoPath, enableGenerateManifests, tarExcludedGlobs) if err != nil { - return "", fmt.Errorf("error getting app source type: %w", err) + return "", fmt.Errorf("error getting app source type: %v", err) } return v1alpha1.ApplicationSourceType(appType), nil } @@ -1762,10 +1686,10 @@ func splitYAMLOrJSON(reader goio.Reader) ([]*unstructured.Unstructured, error) { for { u := &unstructured.Unstructured{} if err := d.Decode(&u); err != nil { - if errors.Is(err, goio.EOF) { + if err == goio.EOF { break } - return objs, fmt.Errorf("failed to unmarshal manifest: %w", err) + return objs, fmt.Errorf("failed to unmarshal manifest: %v", err) } if u == nil { continue @@ -1796,7 +1720,7 @@ func getPotentiallyValidManifestFile(path string, f os.FileInfo, appPath, repoRo } // If the file is a symlink, these will be overridden with the destination file's info. - relRealPath := relPath + var relRealPath = relPath realFileInfo = f if files.IsSymlink(f) { @@ -1851,7 +1775,7 @@ type potentiallyValidManifest struct { // and 2) the combined file size of the potentially-valid manifest files does not exceed the limit. func getPotentiallyValidManifests(logCtx *log.Entry, appPath string, repoRoot string, recurse bool, include string, exclude string, maxCombinedManifestQuantity resource.Quantity) ([]potentiallyValidManifest, error) { maxCombinedManifestFileSize := maxCombinedManifestQuantity.Value() - currentCombinedManifestFileSize := int64(0) + var currentCombinedManifestFileSize = int64(0) var potentiallyValidManifests []potentiallyValidManifest err := filepath.Walk(appPath, func(path string, f os.FileInfo, err error) error { @@ -1897,6 +1821,7 @@ func getPotentiallyValidManifests(logCtx *log.Entry, appPath string, repoRoot st } func makeJsonnetVm(appPath string, repoRoot string, sourceJsonnet v1alpha1.ApplicationSourceJsonnet, env *v1alpha1.Env) (*jsonnet.VM, error) { + vm := jsonnet.MakeVM() for i, j := range sourceJsonnet.TLAs { sourceJsonnet.TLAs[i].Value = env.Envsubst(j.Value) @@ -1974,7 +1899,7 @@ 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, tarDoneCh chan<- bool, tarExcludedGlobs []string, useManifestGeneratePaths bool) ([]*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) if err != nil { @@ -1988,17 +1913,10 @@ func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath, p } defer io.Close(conn) - rootPath := repoPath - if useManifestGeneratePaths { - // Transmit the files under the common root path for all paths related to the manifest generate paths annotation. - rootPath = getApplicationRootPath(q, appPath, repoPath) - log.Debugf("common root path calculated for application %s: %s", q.AppName, rootPath) - } - // generate manifests using commands provided in plugin config file in detected cmp-server sidecar - cmpManifests, err := generateManifestsCMP(ctx, appPath, rootPath, env, cmpClient, tarDoneCh, tarExcludedGlobs) + cmpManifests, err := generateManifestsCMP(ctx, appPath, repoPath, env, cmpClient, tarDoneCh, tarExcludedGlobs) if err != nil { - return nil, fmt.Errorf("error generating manifests in cmp: %w", err) + return nil, fmt.Errorf("error generating manifests in cmp: %s", err) } var manifests []*unstructured.Unstructured for _, manifestString := range cmpManifests.Manifests { @@ -2019,7 +1937,7 @@ func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath, p // generateManifestsCMP will send the appPath files to the cmp-server over a gRPC stream. // The cmp-server will generate the manifests. Returns a response object with the generated // manifests. -func generateManifestsCMP(ctx context.Context, appPath, rootPath string, env []string, cmpClient pluginclient.ConfigManagementPluginServiceClient, tarDoneCh chan<- bool, tarExcludedGlobs []string) (*pluginclient.ManifestResponse, error) { +func generateManifestsCMP(ctx context.Context, appPath, repoPath string, env []string, cmpClient pluginclient.ConfigManagementPluginServiceClient, tarDoneCh chan<- bool, tarExcludedGlobs []string) (*pluginclient.ManifestResponse, error) { generateManifestStream, err := cmpClient.GenerateManifest(ctx, grpc_retry.Disable()) if err != nil { return nil, fmt.Errorf("error getting generateManifestStream: %w", err) @@ -2028,9 +1946,9 @@ func generateManifestsCMP(ctx context.Context, appPath, rootPath string, env []s cmp.WithTarDoneChan(tarDoneCh), } - err = cmp.SendRepoStream(generateManifestStream.Context(), appPath, rootPath, generateManifestStream, env, tarExcludedGlobs, opts...) + err = cmp.SendRepoStream(generateManifestStream.Context(), appPath, repoPath, generateManifestStream, env, tarExcludedGlobs, opts...) if err != nil { - return nil, fmt.Errorf("error sending file to cmp-server: %w", err) + return nil, fmt.Errorf("error sending file to cmp-server: %s", err) } return generateManifestStream.CloseAndRecv() @@ -2046,9 +1964,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD return err } - env := newEnvRepoQuery(q, revision) - - appSourceType, err := GetAppSourceType(ctx, q.Source, opContext.appPath, repoRoot, q.AppName, q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs, env.Environ()) + appSourceType, err := GetAppSourceType(ctx, q.Source, opContext.appPath, repoRoot, q.AppName, q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs) if err != nil { return err } @@ -2065,7 +1981,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD return err } case v1alpha1.ApplicationSourceTypePlugin: - if err := populatePluginAppDetails(ctx, res, opContext.appPath, repoRoot, q, s.initConstants.CMPTarExcludedGlobs); err != nil { + if err := populatePluginAppDetails(ctx, res, opContext.appPath, repoRoot, q, s.gitCredsStore, s.initConstants.CMPTarExcludedGlobs); err != nil { return fmt.Errorf("failed to populate plugin app details: %w", err) } } @@ -2074,7 +1990,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD } 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, len(q.RefSources) > 0, q.RefSources) + err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, false, cacheFn, operation, settings, false, nil) return res, err } @@ -2087,7 +2003,7 @@ func (s *Service) createGetAppDetailsCacheHandler(res *apiclient.RepoAppDetailsR return true, nil } - if !errors.Is(err, cache.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) @@ -2122,11 +2038,15 @@ func populateHelmAppDetails(res *apiclient.RepoAppDetailsResponse, appPath strin if err != nil { return err } - h, err := helm.NewHelmApp(appPath, helmRepos, false, version, q.Repo.Proxy, q.Repo.NoProxy, passCredentials) + h, err := helm.NewHelmApp(appPath, helmRepos, false, version, q.Repo.Proxy, passCredentials) if err != nil { return err } defer h.Dispose() + err = h.Init() + if err != nil { + return err + } if resolvedValuesPath, _, err := pathutil.ResolveValueFilePathOrUrl(appPath, repoRoot, "values.yaml", []string{}); err == nil { if err := loadFileIntoIfExists(resolvedValuesPath, &res.Helm.Values); err != nil { @@ -2179,6 +2099,7 @@ func loadFileIntoIfExists(path pathutil.ResolvedFilePath, destination *string) e func walkHelmValueFilesInPath(root string, valueFiles *[]string) filepath.WalkFunc { return func(path string, info os.FileInfo, err error) error { + if err != nil { return fmt.Errorf("error reading helm values file from %s: %w", path, err) } @@ -2203,7 +2124,7 @@ func populateKustomizeAppDetails(res *apiclient.RepoAppDetailsResponse, q *apicl if q.KustomizeOptions != nil { kustomizeBinary = q.KustomizeOptions.BinaryPath } - k := kustomize.NewKustomizeApp(repoRoot, appPath, q.Repo.GetGitCreds(credsStore), q.Repo.Repo, kustomizeBinary, q.Repo.Proxy, q.Repo.NoProxy) + 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 @@ -2211,7 +2132,7 @@ func populateKustomizeAppDetails(res *apiclient.RepoAppDetailsResponse, q *apicl ApplicationSource: q.Source, } env := newEnv(&fakeManifestRequest, reversion) - _, images, _, err := k.Build(q.Source.Kustomize, q.KustomizeOptions, env, nil) + _, images, err := k.Build(q.Source.Kustomize, q.KustomizeOptions, env) if err != nil { return err } @@ -2219,7 +2140,7 @@ func populateKustomizeAppDetails(res *apiclient.RepoAppDetailsResponse, q *apicl return nil } -func populatePluginAppDetails(ctx context.Context, res *apiclient.RepoAppDetailsResponse, appPath string, repoPath string, q *apiclient.RepoServerAppDetailsQuery, tarExcludedGlobs []string) error { +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{} envVars := []string{ @@ -2252,12 +2173,12 @@ func populatePluginAppDetails(ctx context.Context, res *apiclient.RepoAppDetails err = cmp.SendRepoStream(parametersAnnouncementStream.Context(), appPath, repoPath, parametersAnnouncementStream, env, tarExcludedGlobs) if err != nil { - return fmt.Errorf("error sending file to cmp-server: %w", err) + return fmt.Errorf("error sending file to cmp-server: %s", err) } announcement, err := parametersAnnouncementStream.CloseAndRecv() if err != nil { - return fmt.Errorf("failed to get parameter announcement: %w", err) + return fmt.Errorf("failed to get parameter anouncement: %w", err) } res.Plugin = &apiclient.PluginAppSpec{ @@ -2287,7 +2208,7 @@ func (s *Service) GetRevisionMetadata(ctx context.Context, q *apiclient.RepoServ return metadata, nil } } else { - if !errors.Is(err, cache.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) @@ -2305,6 +2226,7 @@ func (s *Service) GetRevisionMetadata(ctx context.Context, q *apiclient.RepoServ closer, err := s.repoLock.Lock(gitClient.Root(), q.Revision, true, func() (goio.Closer, error) { return s.checkoutRevision(gitClient, q.Revision, s.initConstants.SubmoduleEnabled) }) + if err != nil { return nil, fmt.Errorf("error acquiring repo lock: %w", err) } @@ -2349,7 +2271,7 @@ func (s *Service) GetRevisionChartDetails(ctx context.Context, q *apiclient.Repo log.Infof("revision chart details cache hit: %s/%s/%s", q.Repo.Repo, q.Name, q.Revision) return details, nil } else { - if errors.Is(err, cache.ErrCacheMiss) { + 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) @@ -2357,25 +2279,25 @@ func (s *Service) GetRevisionChartDetails(ctx context.Context, q *apiclient.Repo } helmClient, revision, err := s.newHelmClientResolveRevision(q.Repo, q.Revision, q.Name, true) if err != nil { - return nil, fmt.Errorf("helm client error: %w", err) + return nil, fmt.Errorf("helm client error: %v", err) } - chartPath, closer, err := helmClient.ExtractChart(q.Name, revision, q.Repo.Project, false, s.initConstants.HelmManifestMaxExtractedSize, s.initConstants.DisableHelmManifestMaxExtractedSize) + 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: %w", err) + return nil, fmt.Errorf("error extracting chart: %v", err) } defer io.Close(closer) - helmCmd, err := helm.NewCmdWithVersion(chartPath, q.Repo.EnableOCI, q.Repo.Proxy, q.Repo.NoProxy) + helmCmd, err := helm.NewCmdWithVersion(chartPath, helm.HelmV3, q.Repo.EnableOCI, q.Repo.Proxy) if err != nil { - return nil, fmt.Errorf("error creating helm cmd: %w", err) + 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: %w", err) + return nil, fmt.Errorf("error inspecting chart: %v", err) } details, err = getChartDetails(helmDetails) if err != nil { - return nil, fmt.Errorf("error getting chart details: %w", err) + return nil, fmt.Errorf("error getting chart details: %v", err) } _ = s.cache.SetRevisionChartDetails(q.Repo.Repo, q.Name, q.Revision, details) return details, nil @@ -2389,16 +2311,12 @@ func fileParameters(q *apiclient.RepoServerAppDetailsQuery) []v1alpha1.HelmFileP } func (s *Service) newClient(repo *v1alpha1.Repository, opts ...git.ClientOpts) (git.Client, error) { - keyData, err := json.Marshal(map[string]string{"url": git.NormalizeGitURL(repo.Repo), "project": repo.Project}) - if err != nil { - return nil, err - } - repoPath, err := s.gitRepoPaths.GetPath(string(keyData)) + repoPath, err := s.gitRepoPaths.GetPath(git.NormalizeGitURL(repo.Repo)) if err != nil { return nil, err } opts = append(opts, git.WithEventHandlers(metrics.NewGitClientEventHandlers(s.metricsServer))) - return s.newGitClient(repo.Repo, repoPath, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.EnableLFS, repo.Proxy, repo.NoProxy, opts...) + return s.newGitClient(repo.Repo, repoPath, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.EnableLFS, repo.Proxy, opts...) } // newClientResolveRevision is a helper to perform the common task of instantiating a git client @@ -2410,7 +2328,6 @@ func (s *Service) newClientResolveRevision(repo *v1alpha1.Repository, revision s } commitSHA, err := gitClient.LsRemote(revision) if err != nil { - s.metricsServer.IncGitLsRemoteFail(gitClient.Root(), revision) return nil, "", err } return gitClient, commitSHA, nil @@ -2418,24 +2335,24 @@ func (s *Service) newClientResolveRevision(repo *v1alpha1.Repository, revision s func (s *Service) newHelmClientResolveRevision(repo *v1alpha1.Repository, revision string, chart string, noRevisionCache bool) (helm.Client, string, error) { enableOCI := repo.EnableOCI || helm.IsHelmOciRepo(repo.Repo) - helmClient := s.newHelmClient(repo.Repo, repo.GetHelmCreds(), enableOCI, repo.Proxy, repo.NoProxy, helm.WithIndexCache(s.cache), helm.WithChartPaths(s.chartPaths)) + helmClient := s.newHelmClient(repo.Repo, repo.GetHelmCreds(), enableOCI, repo.Proxy, helm.WithIndexCache(s.cache), helm.WithChartPaths(s.chartPaths)) if helm.IsVersion(revision) { return helmClient, revision, nil } constraints, err := semver.NewConstraint(revision) if err != nil { - return nil, "", fmt.Errorf("invalid revision '%s': %w", revision, err) + return nil, "", fmt.Errorf("invalid revision '%s': %v", revision, err) } if enableOCI { tags, err := helmClient.GetTags(chart, noRevisionCache) if err != nil { - return nil, "", fmt.Errorf("unable to get tags: %w", err) + return nil, "", fmt.Errorf("unable to get tags: %v", err) } version, err := tags.MaxVersion(constraints) if err != nil { - return nil, "", fmt.Errorf("no version for constraints: %w", err) + return nil, "", fmt.Errorf("no version for constraints: %v", err) } return helmClient, version.String(), nil } @@ -2459,7 +2376,7 @@ func (s *Service) newHelmClientResolveRevision(repo *v1alpha1.Repository, revisi // a function that can be used to remove all permissions. func directoryPermissionInitializer(rootPath string) goio.Closer { if _, err := os.Stat(rootPath); err == nil { - if err := os.Chmod(rootPath, 0o700); err != nil { + if err := os.Chmod(rootPath, 0700); err != nil { log.Warnf("Failed to restore read/write/execute permissions on %s: %v", rootPath, err) } else { log.Debugf("Successfully restored read/write/execute permissions on %s", rootPath) @@ -2467,7 +2384,7 @@ func directoryPermissionInitializer(rootPath string) goio.Closer { } return io.NewCloser(func() error { - if err := os.Chmod(rootPath, 0o000); err != nil { + if err := os.Chmod(rootPath, 0000); err != nil { log.Warnf("Failed to remove permissions on %s: %v", rootPath, err) } else { log.Debugf("Successfully removed permissions on %s", rootPath) @@ -2481,11 +2398,7 @@ 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()) - err := checkoutRevision(gitClient, revision, submoduleEnabled) - if err != nil { - s.metricsServer.IncGitFetchFail(gitClient.Root(), revision) - } - return closer, err + return closer, checkoutRevision(gitClient, revision, submoduleEnabled) } func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bool) error { @@ -2494,19 +2407,10 @@ func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bo return status.Errorf(codes.Internal, "Failed to initialize git repo: %v", err) } - revisionPresent := gitClient.IsRevisionPresent(revision) - - log.WithFields(map[string]interface{}{ - "skipFetch": revisionPresent, - }).Debugf("Checking out revision %v", revision) - - // Fetching can be skipped if the revision is already present locally. - if !revisionPresent { - // Fetching with no revision first. Fetching with an explicit version can cause repo bloat. https://github.com/argoproj/argo-cd/issues/8845 - err = gitClient.Fetch("") - if err != nil { - return status.Errorf(codes.Internal, "Failed to fetch default: %v", err) - } + // Fetching with no revision first. Fetching with an explicit version can cause repo bloat. https://github.com/argoproj/argo-cd/issues/8845 + err = gitClient.Fetch("") + if err != nil { + return status.Errorf(codes.Internal, "Failed to fetch default: %v", err) } err = gitClient.Checkout(revision, submoduleEnabled) @@ -2531,7 +2435,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, q.Repo.NoProxy, helm.WithIndexCache(s.cache), helm.WithChartPaths(s.chartPaths)).GetIndex(true, s.initConstants.HelmRegistryMaxIndexSize) + 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 } @@ -2556,17 +2460,17 @@ func (s *Service) TestRepository(ctx context.Context, q *apiclient.TestRepositor } checks := map[string]func() error{ "git": func() error { - return git.TestRepo(repo.Repo, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy, repo.NoProxy) + return git.TestRepo(repo.Repo, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy) }, "helm": func() error { if repo.EnableOCI { if !helm.IsHelmOciRepo(repo.Repo) { return errors.New("OCI Helm repository URL should include hostname and port only") } - _, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy, repo.NoProxy).TestHelmOCI() + _, 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, repo.NoProxy).GetIndex(false, s.initConstants.HelmRegistryMaxIndexSize) + _, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false, s.initConstants.HelmRegistryMaxIndexSize) return err } }, @@ -2582,13 +2486,15 @@ func (s *Service) TestRepository(ctx context.Context, q *apiclient.TestRepositor // ResolveRevision resolves the revision/ambiguousRevision specified in the ResolveRevisionRequest request into a concrete revision. func (s *Service) ResolveRevision(ctx context.Context, q *apiclient.ResolveRevisionRequest) (*apiclient.ResolveRevisionResponse, error) { + repo := q.Repo app := q.App ambiguousRevision := q.AmbiguousRevision var revision string - source := app.Spec.GetSourcePtrByIndex(int(q.SourceIndex)) + var source = app.Spec.GetSource() if source.IsHelm() { _, revision, err := s.newHelmClientResolveRevision(repo, ambiguousRevision, source.Chart, true) + if err != nil { return &apiclient.ResolveRevisionResponse{Revision: "", AmbiguousRevision: ""}, err } @@ -2597,13 +2503,12 @@ func (s *Service) ResolveRevision(ctx context.Context, q *apiclient.ResolveRevis AmbiguousRevision: fmt.Sprintf("%v (%v)", ambiguousRevision, revision), }, nil } else { - gitClient, err := git.NewClient(repo.Repo, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy, repo.NoProxy) + gitClient, err := git.NewClient(repo.Repo, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy) if err != nil { return &apiclient.ResolveRevisionResponse{Revision: "", AmbiguousRevision: ""}, err } revision, err = gitClient.LsRemote(ambiguousRevision) if err != nil { - s.metricsServer.IncGitLsRemoteFail(gitClient.Root(), revision) return &apiclient.ResolveRevisionResponse{Revision: "", AmbiguousRevision: ""}, err } return &apiclient.ResolveRevisionResponse{ @@ -2632,10 +2537,6 @@ func (s *Service) GetGitFiles(_ context.Context, request *apiclient.GitFilesRequ return nil, status.Errorf(codes.Internal, "unable to resolve git revision %s: %v", revision, err) } - if err := verifyCommitSignature(request.VerifyCommit, gitClient, revision, repo); err != nil { - return nil, 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) @@ -2681,28 +2582,6 @@ func (s *Service) GetGitFiles(_ context.Context, request *apiclient.GitFilesRequ }, nil } -func verifyCommitSignature(verifyCommit bool, gitClient git.Client, revision string, repo *v1alpha1.Repository) error { - if gpg.IsGPGEnabled() && verifyCommit { - cs, err := gitClient.VerifyCommitSignature(revision) - if err != nil { - log.Errorf("error verifying signature of commit '%s' in repo '%s': %v", revision, repo.Repo, err) - return err - } - - if cs == "" { - return fmt.Errorf("revision %s is not signed", revision) - } else { - vr := gpg.ParseGitCommitVerification(cs) - if vr.Result == gpg.VerifyResultUnknown { - return fmt.Errorf("UNKNOWN signature: %s", vr.Message) - } else { - log.Debugf("%s signature from %s key %s", vr.Result, vr.Cipher, gpg.KeyID(vr.KeyID)) - } - } - } - return nil -} - func (s *Service) GetGitDirectories(_ context.Context, request *apiclient.GitDirectoriesRequest) (*apiclient.GitDirectoriesResponse, error) { repo := request.GetRepo() revision := request.GetRevision() @@ -2716,10 +2595,6 @@ func (s *Service) GetGitDirectories(_ context.Context, request *apiclient.GitDir return nil, status.Errorf(codes.Internal, "unable to resolve git revision %s: %v", revision, err) } - if err := verifyCommitSignature(request.VerifyCommit, gitClient, revision, repo); err != nil { - return nil, 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) @@ -2750,8 +2625,9 @@ func (s *Service) GetGitDirectories(_ context.Context, request *apiclient.GitDir return nil } - if !s.initConstants.IncludeHiddenDirectories && strings.HasPrefix(entry.Name(), ".") { - return filepath.SkipDir // Skip hidden directory + fname := entry.Name() + if strings.HasPrefix(fname, ".") { // Skip all folders starts with "." + return filepath.SkipDir } relativePath, err := filepath.Rel(repoRoot, path) @@ -2780,116 +2656,3 @@ func (s *Service) GetGitDirectories(_ context.Context, request *apiclient.GitDir Paths: paths, }, nil } - -// UpdateRevisionForPaths compares two git revisions and checks if the files in the given paths have changed -// If no files were changed, it will store the already cached manifest to the key corresponding to the old revision, avoiding an unnecessary generation. -// Example: cache has key "a1a1a1" with manifest "x", and the files for that manifest have not changed, -// "x" will be stored again with the new revision "b2b2b2". -func (s *Service) UpdateRevisionForPaths(_ context.Context, request *apiclient.UpdateRevisionForPathsRequest) (*apiclient.UpdateRevisionForPathsResponse, error) { - logCtx := log.WithFields(log.Fields{"application": request.AppName, "appNamespace": request.Namespace}) - - repo := request.GetRepo() - revision := request.GetRevision() - syncedRevision := request.GetSyncedRevision() - refreshPaths := request.GetPaths() - - if repo == nil { - return nil, status.Error(codes.InvalidArgument, "must pass a valid repo") - } - - if len(refreshPaths) == 0 { - // Always refresh if path is not specified - return &apiclient.UpdateRevisionForPathsResponse{}, nil - } - - gitClientOpts := git.WithCache(s.cache, !request.NoRevisionCache) - gitClient, revision, err := s.newClientResolveRevision(repo, revision, gitClientOpts) - if err != nil { - return nil, status.Errorf(codes.Internal, "unable to resolve git revision %s: %v", revision, err) - } - - syncedRevision, err = gitClient.LsRemote(syncedRevision) - if err != nil { - s.metricsServer.IncGitLsRemoteFail(gitClient.Root(), revision) - return nil, status.Errorf(codes.Internal, "unable to resolve git revision %s: %v", revision, err) - } - - // No need to compare if it is the same revision - if revision == syncedRevision { - return &apiclient.UpdateRevisionForPathsResponse{ - Revision: revision, - }, nil - } - - s.metricsServer.IncPendingRepoRequest(repo.Repo) - defer s.metricsServer.DecPendingRepoRequest(repo.Repo) - - closer, err := s.repoLock.Lock(gitClient.Root(), revision, true, func() (goio.Closer, error) { - return s.checkoutRevision(gitClient, revision, false) - }) - 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) - - files, err := gitClient.ChangedFiles(syncedRevision, revision) - if err != nil { - return nil, status.Errorf(codes.Internal, "unable to get changed files for repo %s with revision %s: %v", repo.Repo, revision, err) - } - - changed := false - if len(files) != 0 { - changed = apppathutil.AppFilesHaveChanged(refreshPaths, files) - } - - if !changed { - logCtx.Debugf("no changes found for application %s in repo %s from revision %s to revision %s", request.AppName, repo.Repo, syncedRevision, revision) - - err := s.updateCachedRevision(logCtx, syncedRevision, revision, request, gitClientOpts) - if err != nil { - // Only warn with the error, no need to block anything if there is a caching error. - logCtx.Warnf("error updating cached revision for repo %s with revision %s: %v", repo.Repo, revision, err) - return &apiclient.UpdateRevisionForPathsResponse{ - Revision: revision, - }, nil - } - - return &apiclient.UpdateRevisionForPathsResponse{ - Revision: revision, - }, nil - } - - logCtx.Debugf("changes found for application %s in repo %s from revision %s to revision %s", request.AppName, repo.Repo, syncedRevision, revision) - return &apiclient.UpdateRevisionForPathsResponse{ - Revision: revision, - Changes: true, - }, nil -} - -func (s *Service) updateCachedRevision(logCtx *log.Entry, oldRev string, newRev string, request *apiclient.UpdateRevisionForPathsRequest, gitClientOpts git.ClientOpts) error { - repoRefs := make(map[string]string) - if request.HasMultipleSources && request.ApplicationSource.Helm != nil { - var err error - repoRefs, err = resolveReferencedSources(true, request.ApplicationSource.Helm, request.RefSources, s.newClientResolveRevision, gitClientOpts) - if err != nil { - return fmt.Errorf("failed to get repo refs for application %s in repo %s from revision %s: %w", request.AppName, request.GetRepo().Repo, request.Revision, err) - } - - // Update revision in refSource - for normalizedURL := range repoRefs { - repoRefs[normalizedURL] = newRev - } - } - - err := s.cache.SetNewRevisionManifests(newRev, oldRev, request.ApplicationSource, request.RefSources, request, request.Namespace, request.TrackingMethod, request.AppLabelKey, request.AppName, repoRefs) - if err != nil { - if errors.Is(err, cache.ErrCacheMiss) { - logCtx.Debugf("manifest cache miss during comparison for application %s in repo %s from revision %s", request.AppName, request.GetRepo().Repo, oldRev) - return nil - } - return fmt.Errorf("manifest cache move error for %s: %w", request.AppName, err) - } - - logCtx.Debugf("manifest cache updated for application %s in repo %s from revision %s to revision %s", request.AppName, request.GetRepo().Repo, oldRev, newRev) - return nil -} diff --git a/reposerver/repository/repository.proto b/reposerver/repository/repository.proto index 631ab2787171a..de061122e2586 100644 --- a/reposerver/repository/repository.proto +++ b/reposerver/repository/repository.proto @@ -21,9 +21,7 @@ message ManifestRequest { // 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; - // KubeVersion is the Kubernetes API version from the destination cluster. string kubeVersion = 14; - // ApiVersions is the list of API versions from the destination cluster, used for rendering Helm charts. repeated string apiVersions = 15; // Request to verify the signature when generating the manifests (only for Git repositories) bool verifySignature = 16; @@ -38,8 +36,6 @@ message ManifestRequest { repeated string projectSourceRepos = 24; // This is used to surface "source not permitted" errors for Helm repositories string projectName = 25; - // argocd.argoproj.io/manifest-generate-paths annotation value of the Application to allow optimize which resources propagated to cmpserver - string AnnotationManifestGeneratePaths = 26; } message ManifestRequestWithFiles { @@ -77,7 +73,6 @@ message ResolveRevisionRequest { github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repo = 1; github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Application app = 2; string ambiguousRevision = 3; - int64 sourceIndex = 4; } // ResolveRevisionResponse @@ -96,8 +91,6 @@ message ManifestResponse { string sourceType = 6; // Raw response of git verify-commit operation (always the empty string for Helm) string verifyResult = 7; - // Commands is the list of commands used to hydrate the manifests - repeated string commands = 8; } message ListRefsRequest { @@ -244,7 +237,6 @@ message GitFilesRequest { string path = 4; bool NewGitFileGlobbingEnabled = 5; bool noRevisionCache = 6; - bool verifyCommit = 7; } message GitFilesResponse { @@ -257,7 +249,6 @@ message GitDirectoriesRequest { bool submoduleEnabled = 2; string revision = 3; bool noRevisionCache = 4; - bool verifyCommit = 5; } message GitDirectoriesResponse { @@ -265,31 +256,6 @@ message GitDirectoriesResponse { repeated string paths = 1; } -message UpdateRevisionForPathsRequest { - github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repo = 1; - - string appLabelKey = 2; - string appName = 3; - string namespace = 4; - github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSource applicationSource = 5; - string trackingMethod = 6; - map refSources = 7; - string kubeVersion = 8; - repeated string apiVersions = 9; - bool hasMultipleSources = 10; - - string syncedRevision = 11; - string revision = 12; - repeated string paths = 13; - - bool noRevisionCache = 14; -} - -message UpdateRevisionForPathsResponse { - bool changes = 1; - string revision = 2; -} - // ManifestService service RepoServerService { @@ -344,8 +310,4 @@ service RepoServerService { // GetGitDirectories returns a set of directory paths for the given repo rpc GetGitDirectories(GitDirectoriesRequest) returns (GitDirectoriesResponse) { } - - // UpdateRevisionForPaths will compare two revisions and update the cache with the new revision if no changes are detected in the provided paths - rpc UpdateRevisionForPaths(UpdateRevisionForPathsRequest) returns (UpdateRevisionForPathsResponse) { - } } diff --git a/reposerver/repository/repository_test.go b/reposerver/repository/repository_test.go index 0c11553e5d7f4..74a842bcb6faa 100644 --- a/reposerver/repository/repository_test.go +++ b/reposerver/repository/repository_test.go @@ -13,18 +13,14 @@ import ( "path" "path/filepath" "regexp" - "slices" "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" - "k8s.io/apimachinery/pkg/util/intstr" - - cacheutil "github.com/argoproj/argo-cd/v2/util/cache" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -34,7 +30,6 @@ import ( "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" @@ -43,6 +38,7 @@ import ( "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" + 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" "github.com/argoproj/argo-cd/v2/util/helm" @@ -81,10 +77,6 @@ type newGitRepoOptions struct { } 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, @@ -94,7 +86,7 @@ func newCacheMocksWithOpts(repoCacheExpiration, revisionCacheExpiration, revisio cacheutilCache := cacheutil.NewCache(mockRepoCache.RedisClient) return &repoCacheMocks{ cacheutilCache: cacheutilCache, - cache: cache.NewCache(cacheutilCache, repoCacheExpiration, revisionCacheExpiration, revisionCacheLockTimeout), + cache: cache.NewCache(cacheutilCache, 1*time.Minute, 1*time.Minute), mockCache: mockRepoCache, } } @@ -106,7 +98,6 @@ func newServiceWithMocks(t *testing.T, root string, signed bool) (*Service, *git } return newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { gitClient.On("Init").Return(nil) - gitClient.On("IsRevisionPresent", mock.Anything).Return(false) 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) @@ -126,16 +117,15 @@ func newServiceWithMocks(t *testing.T, root string, signed bool) (*Service, *git chart: {{Version: "1.0.0"}, {Version: version}}, oobChart: {{Version: "1.0.0"}, {Version: version}}, }}, nil) - helmClient.On("ExtractChart", chart, version, "", false, int64(0), false).Return("./testdata/my-chart", io.NopCloser, nil) - helmClient.On("ExtractChart", oobChart, version, "", false, int64(0), false).Return("./testdata2/out-of-bounds-chart", io.NopCloser, nil) - helmClient.On("CleanChartCache", chart, version, "").Return(nil) - helmClient.On("CleanChartCache", oobChart, version, "").Return(nil) + helmClient.On("ExtractChart", chart, version).Return("./testdata/my-chart", io.NopCloser, nil) + helmClient.On("ExtractChart", oobChart, version).Return("./testdata2/out-of-bounds-chart", io.NopCloser, nil) + helmClient.On("CleanChartCache", chart, version).Return(nil) + helmClient.On("CleanChartCache", oobChart, version).Return(nil) helmClient.On("DependencyBuild").Return(nil) paths.On("Add", mock.Anything, mock.Anything).Return(root, nil) paths.On("GetPath", mock.Anything).Return(root, nil) paths.On("GetPathIfExists", mock.Anything).Return(root, nil) - paths.On("GetPaths").Return(map[string]string{"fake-nonce": root}) }, root) } @@ -148,10 +138,10 @@ func newServiceWithOpt(t *testing.T, cf clientFunc, root string) (*Service, *git 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, proxy string, noProxy 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, noProxy string, opts ...helm.ClientOpts) helm.Client { + service.newHelmClient = func(repoURL string, creds helm.Creds, enableOci bool, proxy string, opts ...helm.ClientOpts) helm.Client { return helmClient } service.gitRepoInitializer = func(rootPath string) goio.Closer { @@ -181,7 +171,6 @@ func newServiceWithCommitSHA(t *testing.T, root, revision string) *Service { service, gitClient, _ := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { gitClient.On("Init").Return(nil) - gitClient.On("IsRevisionPresent", mock.Anything).Return(false) gitClient.On("Fetch", mock.Anything).Return(nil) gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) gitClient.On("LsRemote", revision).Return(revision, revisionErr) @@ -191,7 +180,7 @@ func newServiceWithCommitSHA(t *testing.T, root, revision string) *Service { paths.On("GetPathIfExists", mock.Anything).Return(root, nil) }, root) - service.newGitClient = func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, proxy string, noProxy 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 } @@ -214,13 +203,13 @@ func TestGenerateYamlManifestInDir(t *testing.T) { res1, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) - assert.Len(t, res1.Manifests, countOfManifests) + assert.NoError(t, err) + assert.Equal(t, countOfManifests, len(res1.Manifests)) // this will test concatenated manifests to verify we split YAMLs correctly res2, err := GenerateManifests(context.Background(), "./testdata/concatenated", "/", "", &q, false, &git.NoopCredsStore{}, resource.MustParse("0"), nil) - require.NoError(t, err) - assert.Len(t, res2.Manifests, 3) + assert.NoError(t, err) + assert.Equal(t, 3, len(res2.Manifests)) } func Test_GenerateManifests_NoOutOfBoundsAccess(t *testing.T) { @@ -261,22 +250,20 @@ func Test_GenerateManifests_NoOutOfBoundsAccess(t *testing.T) { outOfBoundsDir := t.TempDir() outOfBoundsFile := path.Join(outOfBoundsDir, testCaseCopy.outOfBoundsFilename) - err := os.WriteFile(outOfBoundsFile, []byte(testCaseCopy.outOfBoundsFileContents), os.FileMode(0o444)) + err := os.WriteFile(outOfBoundsFile, []byte(testCaseCopy.outOfBoundsFileContents), os.FileMode(0444)) require.NoError(t, err) repoDir := t.TempDir() err = os.Symlink(outOfBoundsFile, path.Join(repoDir, testCaseCopy.outOfBoundsFilename)) require.NoError(t, err) - mustNotContain := testCaseCopy.outOfBoundsFileContents + var mustNotContain = testCaseCopy.outOfBoundsFileContents if testCaseCopy.mustNotContain != "" { mustNotContain = testCaseCopy.mustNotContain } - q := apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{}, ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + 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) @@ -291,10 +278,8 @@ 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{}, ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + 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) } @@ -311,20 +296,20 @@ func TestGenerateManifests_K8SAPIResetCache(t *testing.T) { ProjectSourceRepos: []string{"*"}, } - cachedFakeResponse := &apiclient.ManifestResponse{Manifests: []string{"Fake"}, Revision: mock.Anything} + cachedFakeResponse := &apiclient.ManifestResponse{Manifests: []string{"Fake"}} err := service.cache.SetManifests(mock.Anything, &src, q.RefSources, &q, "", "", "", "", &cache.CachedManifestResponse{ManifestResponse: cachedFakeResponse}, nil) - require.NoError(t, err) + assert.NoError(t, err) res, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, cachedFakeResponse, res) q.KubeVersion = "v1.17.0" res, err = service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) + assert.NoError(t, err) assert.NotEqual(t, cachedFakeResponse, res) - assert.Greater(t, len(res.Manifests), 1) + assert.True(t, len(res.Manifests) > 1) } func TestGenerateManifests_EmptyCache(t *testing.T) { @@ -339,16 +324,15 @@ func TestGenerateManifests_EmptyCache(t *testing.T) { } err := service.cache.SetManifests(mock.Anything, &src, q.RefSources, &q, "", "", "", "", &cache.CachedManifestResponse{ManifestResponse: nil}, nil) - require.NoError(t, err) + assert.NoError(t, err) res, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) - assert.NotEmpty(t, res.Manifests) + assert.NoError(t, err) + assert.True(t, len(res.Manifests) > 0) mockCache.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ ExternalSets: 2, ExternalGets: 2, - ExternalDeletes: 1, - }) + ExternalDeletes: 1}) gitMocks.AssertCalled(t, "LsRemote", mock.Anything) gitMocks.AssertCalled(t, "Fetch", mock.Anything) } @@ -363,7 +347,7 @@ func TestGenerateManifest_RefOnlyShortCircuit(t *testing.T) { 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, noProxy 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) { 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() { @@ -377,7 +361,7 @@ func TestGenerateManifest_RefOnlyShortCircuit(t *testing.T) { } }, })) - gitClient, err := git.NewClientExt(rawRepoURL, root, creds, insecure, enableLfs, proxy, noProxy, opts...) + gitClient, err := git.NewClientExt(rawRepoURL, root, creds, insecure, enableLfs, proxy, opts...) return gitClient, err } revision := initGitRepo(t, newGitRepoOptions{ @@ -399,14 +383,13 @@ func TestGenerateManifest_RefOnlyShortCircuit(t *testing.T) { ProjectSourceRepos: []string{"*"}, } _, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) + assert.NoError(t, err) cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ - ExternalSets: 2, - ExternalGets: 2, - }) + ExternalSets: 1, + ExternalGets: 1}) assert.True(t, lsremoteCalled, "ls-remote should be called when the source is ref only") var revisions [][2]string - require.NoError(t, cacheMocks.cacheutilCache.GetItem(fmt.Sprintf("git-refs|%s", repoRemote), &revisions)) + 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) } @@ -420,7 +403,7 @@ func TestGenerateManifestsHelmWithRefs_CachedNoLsRemote(t *testing.T) { err := filepath.WalkDir(dir, func(path string, di fs.DirEntry, err error) error { if err == nil { - return os.Chmod(path, 0o777) + return os.Chmod(path, 0777) } return err }) @@ -431,7 +414,7 @@ func TestGenerateManifestsHelmWithRefs_CachedNoLsRemote(t *testing.T) { 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, noProxy 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) { 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() { @@ -440,7 +423,7 @@ func TestGenerateManifestsHelmWithRefs_CachedNoLsRemote(t *testing.T) { } }, })) - gitClient, err = git.NewClientExt(rawRepoURL, root, creds, insecure, enableLfs, proxy, noProxy, opts...) + gitClient, err = git.NewClientExt(rawRepoURL, root, creds, insecure, enableLfs, proxy, opts...) return gitClient, err } repoRemote := fmt.Sprintf("file://%s", repopath) @@ -451,8 +434,7 @@ func TestGenerateManifestsHelmWithRefs_CachedNoLsRemote(t *testing.T) { helmChartOptions: newGitRepoHelmChartOptions{ chartName: "my-chart", chartVersion: "v1.0.0", - valuesFiles: map[string]map[string]string{"test.yaml": {"testval": "test"}}, - }, + 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"}, @@ -469,14 +451,13 @@ func TestGenerateManifestsHelmWithRefs_CachedNoLsRemote(t *testing.T) { 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) - require.NoError(t, err) + err = cacheMocks.cacheutilCache.SetItem(fmt.Sprintf("git-refs|%s", repoRemote), [][2]string{{"HEAD", revision}}, 30*time.Second, false) + assert.NoError(t, err) _, err = service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) + assert.NoError(t, err) cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ ExternalSets: 2, - ExternalGets: 5, - }) + ExternalGets: 5}) } // ensure we can use a semver constraint range (>= 1.0.0) and get back the correct chart (1.0.0) @@ -484,12 +465,10 @@ func TestHelmManifestFromChartRepo(t *testing.T) { 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, ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} response, err := service.GenerateManifest(context.Background(), request) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, response) assert.Equal(t, &apiclient.ManifestResponse{ Manifests: []string{"{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"my-map\"}}"}, @@ -497,12 +476,10 @@ func TestHelmManifestFromChartRepo(t *testing.T) { Server: "", Revision: "1.1.0", SourceType: "Helm", - Commands: []string{`helm template . --name-template "" --include-crds`}, }, response) mockCache.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ ExternalSets: 1, - ExternalGets: 0, - }) + ExternalGets: 0}) gitMocks.AssertNotCalled(t, "LsRemote", mock.Anything) } @@ -516,18 +493,16 @@ func TestHelmChartReferencingExternalValues(t *testing.T) { {Ref: "ref", RepoURL: "https://git.example.com/test/repo"}, }, } - refSources, err := argo.GetRefSources(context.Background(), spec.Sources, spec.Project, func(ctx context.Context, url string, project string) (*argoappv1.Repository, error) { - return &argoappv1.Repository{ - Repo: "https://git.example.com/test/repo", - }, nil - }, []string{}, false) + repoDB := &dbmocks.ArgoDB{} + repoDB.On("GetRepository", context.Background(), "https://git.example.com/test/repo").Return(&argoappv1.Repository{ + Repo: "https://git.example.com/test/repo", + }, 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, ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + 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) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, response) assert.Equal(t, &apiclient.ManifestResponse{ Manifests: []string{"{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"my-map\"}}"}, @@ -535,87 +510,23 @@ func TestHelmChartReferencingExternalValues(t *testing.T) { Server: "", Revision: "1.1.0", SourceType: "Helm", - Commands: []string{`helm template . --name-template "" --values ./testdata/my-chart/my-chart-values.yaml --include-crds`}, }, response) } -func TestHelmChartReferencingExternalValues_InvalidRefs(t *testing.T) { - spec := argoappv1.ApplicationSpec{ - Sources: []argoappv1.ApplicationSource{ - {RepoURL: "https://helm.example.com", Chart: "my-chart", TargetRevision: ">= 1.0.0", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"$ref/testdata/my-chart/my-chart-values.yaml"}, - }}, - {RepoURL: "https://git.example.com/test/repo"}, - }, - } - - // Empty refsource - service := newService(t, ".") - - getRepository := func(ctx context.Context, url string, project string) (*argoappv1.Repository, error) { - return &argoappv1.Repository{ - Repo: "https://git.example.com/test/repo", - }, nil - } - - refSources, err := argo.GetRefSources(context.Background(), spec.Sources, spec.Project, getRepository, []string{}, false) - require.NoError(t, err) - - 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) - require.Error(t, err) - assert.Nil(t, response) - - // Invalid ref - service = newService(t, ".") - - spec.Sources[1].Ref = "Invalid" - refSources, err = argo.GetRefSources(context.Background(), spec.Sources, spec.Project, getRepository, []string{}, false) - require.NoError(t, err) - - 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) - require.Error(t, err) - assert.Nil(t, response) - - // Helm chart as ref (unsupported) - service = newService(t, ".") - - spec.Sources[1].Ref = "ref" - spec.Sources[1].Chart = "helm-chart" - refSources, err = argo.GetRefSources(context.Background(), spec.Sources, spec.Project, getRepository, []string{}, false) - require.NoError(t, err) - - 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) - require.Error(t, err) - assert.Nil(t, response) -} - func TestHelmChartReferencingExternalValues_OutOfBounds_Symlink(t *testing.T) { service := newService(t, ".") - err := os.Mkdir("testdata/oob-symlink", 0o755) + err := os.Mkdir("testdata/oob-symlink", 0755) require.NoError(t, err) t.Cleanup(func() { err = os.RemoveAll("testdata/oob-symlink") require.NoError(t, err) }) - // Create a symlink to a file outside the repo + // Create a symlink to a file outside of the repo err = os.Symlink("../../../values.yaml", "./testdata/oob-symlink/oob-symlink.yaml") // Create a regular file to reference from another source - err = os.WriteFile("./testdata/oob-symlink/values.yaml", []byte("foo: bar"), 0o644) + err = os.WriteFile("./testdata/oob-symlink/values.yaml", []byte("foo: bar"), 0644) require.NoError(t, err) spec := argoappv1.ApplicationSpec{ - Project: "default", Sources: []argoappv1.ApplicationSource{ {RepoURL: "https://helm.example.com", Chart: "my-chart", TargetRevision: ">= 1.0.0", Helm: &argoappv1.ApplicationSourceHelm{ // Reference `ref` but do not use the oob symlink. The mere existence of the link should be enough to @@ -625,15 +536,15 @@ func TestHelmChartReferencingExternalValues_OutOfBounds_Symlink(t *testing.T) { {Ref: "ref", RepoURL: "https://git.example.com/test/repo"}, }, } - refSources, err := argo.GetRefSources(context.Background(), spec.Sources, spec.Project, func(ctx context.Context, url string, project string) (*argoappv1.Repository, error) { - return &argoappv1.Repository{ - Repo: "https://git.example.com/test/repo", - }, nil - }, []string{}, false) + repoDB := &dbmocks.ArgoDB{} + repoDB.On("GetRepository", context.Background(), "https://git.example.com/test/repo").Return(&argoappv1.Repository{ + Repo: "https://git.example.com/test/repo", + }, 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} _, err = service.GenerateManifest(context.Background(), request) - require.Error(t, err) + assert.Error(t, err) } func TestGenerateManifestsUseExactRevision(t *testing.T) { @@ -641,15 +552,13 @@ func TestGenerateManifestsUseExactRevision(t *testing.T) { src := argoappv1.ApplicationSource{Path: "./testdata/recurse", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} - q := apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, ApplicationSource: &src, Revision: "abc", ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, Revision: "abc", ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res1, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) - assert.Len(t, res1.Manifests, 2) - assert.Equal(t, "abc", gitClient.Calls[0].Arguments[0]) + assert.Nil(t, err) + assert.Equal(t, 2, len(res1.Manifests)) + assert.Equal(t, gitClient.Calls[0].Arguments[0], "abc") } func TestRecurseManifestsInDir(t *testing.T) { @@ -657,14 +566,12 @@ func TestRecurseManifestsInDir(t *testing.T) { src := argoappv1.ApplicationSource{Path: "./testdata/recurse", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} - q := apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, ApplicationSource: &src, ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res1, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) - assert.Len(t, res1.Manifests, 2) + assert.Nil(t, err) + assert.Equal(t, 2, len(res1.Manifests)) } func TestInvalidManifestsInDir(t *testing.T) { @@ -675,7 +582,7 @@ func TestInvalidManifestsInDir(t *testing.T) { q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src} _, err := service.GenerateManifest(context.Background(), &q) - require.Error(t, err) + assert.NotNil(t, err) } func TestInvalidMetadata(t *testing.T) { @@ -684,8 +591,8 @@ func TestInvalidMetadata(t *testing.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) - require.Error(t, err) - assert.Contains(t, err.Error(), "contains non-string value in the map under key \"invalid\"") + assert.Error(t, err) + assert.Contains(t, err.Error(), "contains non-string key in the map") } func TestNilMetadataAccessors(t *testing.T) { @@ -695,8 +602,8 @@ func TestNilMetadataAccessors(t *testing.T) { 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) - require.NoError(t, err) - assert.Len(t, res.Manifests, 1) + assert.NoError(t, err) + assert.Equal(t, len(res.Manifests), 1) assert.Equal(t, expected, res.Manifests[0]) } @@ -719,8 +626,8 @@ func TestGenerateJsonnetManifestInDir(t *testing.T) { ProjectSourceRepos: []string{"*"}, } res1, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) - assert.Len(t, res1.Manifests, 2) + assert.Nil(t, err) + assert.Equal(t, 2, len(res1.Manifests)) } func TestGenerateJsonnetManifestInRootDir(t *testing.T) { @@ -742,8 +649,8 @@ func TestGenerateJsonnetManifestInRootDir(t *testing.T) { ProjectSourceRepos: []string{"*"}, } res1, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) - assert.Len(t, res1.Manifests, 2) + assert.Nil(t, err) + assert.Equal(t, 2, len(res1.Manifests)) } func TestGenerateJsonnetLibOutside(t *testing.T) { @@ -768,6 +675,7 @@ func TestGenerateJsonnetLibOutside(t *testing.T) { } func TestManifestGenErrorCacheByNumRequests(t *testing.T) { + // Returns the state of the manifest generation cache, by querying the cache for the previously set result getRecentCachedEntry := func(service *Service, manifestRequest *apiclient.ManifestRequest) *cache.CachedManifestResponse { assert.NotNil(t, service) @@ -775,7 +683,7 @@ func TestManifestGenErrorCacheByNumRequests(t *testing.T) { cachedManifestResponse := &cache.CachedManifestResponse{} err := service.cache.GetManifests(mock.Anything, manifestRequest.ApplicationSource, manifestRequest.RefSources, manifestRequest, manifestRequest.Namespace, "", manifestRequest.AppLabelKey, manifestRequest.AppName, cachedManifestResponse, nil) - require.NoError(t, err) + assert.Nil(t, err) return cachedManifestResponse } @@ -845,9 +753,9 @@ func TestManifestGenErrorCacheByNumRequests(t *testing.T) { // Verify invariant: res != nil xor err != nil if err != nil { - assert.Nil(t, res, "both err and res are non-nil res: %v err: %v", res, err) + assert.True(t, res == nil, "both err and res are non-nil res: %v err: %v", res, err) } else { - assert.NotNil(t, res, "both err and res are nil") + assert.True(t, res != nil, "both err and res are nil") } cachedManifestResponse := getRecentCachedEntry(service, manifestRequest) @@ -862,13 +770,14 @@ func TestManifestGenErrorCacheByNumRequests(t *testing.T) { // nolint:staticcheck assert.Nil(t, cachedManifestResponse.ManifestResponse) // nolint:staticcheck - assert.NotEqual(t, 0, cachedManifestResponse.FirstFailureTimestamp) + assert.True(t, cachedManifestResponse.FirstFailureTimestamp != 0) // Internal cache consec failures value should increase with invocations, cached response should stay the same, // nolint:staticcheck - assert.Equal(t, cachedManifestResponse.NumberOfConsecutiveFailures, adjustedInvocation+1) + assert.True(t, cachedManifestResponse.NumberOfConsecutiveFailures == adjustedInvocation+1) // nolint:staticcheck - assert.Equal(t, 0, cachedManifestResponse.NumberOfCachedResponsesReturned) + assert.True(t, cachedManifestResponse.NumberOfCachedResponsesReturned == 0) + } else { // GenerateManifest SHOULD return cached errors for the next X responses, where X is the // PauseGenerationOnFailureForRequests constant @@ -877,13 +786,13 @@ func TestManifestGenErrorCacheByNumRequests(t *testing.T) { // nolint:staticcheck assert.Nil(t, cachedManifestResponse.ManifestResponse) // nolint:staticcheck - assert.NotEqual(t, 0, cachedManifestResponse.FirstFailureTimestamp) + assert.True(t, cachedManifestResponse.FirstFailureTimestamp != 0) // Internal cache values should update correctly based on number of return cache entries, consecutive failures should stay the same // nolint:staticcheck - assert.Equal(t, cachedManifestResponse.NumberOfConsecutiveFailures, service.initConstants.PauseGenerationAfterFailedGenerationAttempts) + assert.True(t, cachedManifestResponse.NumberOfConsecutiveFailures == service.initConstants.PauseGenerationAfterFailedGenerationAttempts) // nolint:staticcheck - assert.Equal(t, cachedManifestResponse.NumberOfCachedResponsesReturned, (adjustedInvocation - service.initConstants.PauseGenerationAfterFailedGenerationAttempts + 1)) + assert.True(t, cachedManifestResponse.NumberOfCachedResponsesReturned == (adjustedInvocation-service.initConstants.PauseGenerationAfterFailedGenerationAttempts+1)) } } }) @@ -891,6 +800,7 @@ func TestManifestGenErrorCacheByNumRequests(t *testing.T) { } func TestManifestGenErrorCacheFileContentsChange(t *testing.T) { + tmpDir := t.TempDir() service := newService(t, tmpDir) @@ -903,6 +813,7 @@ func TestManifestGenErrorCacheFileContentsChange(t *testing.T) { } for step := 0; step < 3; step++ { + // step 1) Attempt to generate manifests against invalid helm chart (should return uncached error) // step 2) Attempt to generate manifest against valid helm chart (should succeed and return valid response) // step 3) Attempt to generate manifest against invalid helm chart (should return cached value from step 2) @@ -911,17 +822,18 @@ func TestManifestGenErrorCacheFileContentsChange(t *testing.T) { // Ensure that the target directory will succeed or fail, so we can verify the cache correctly handles it err := os.RemoveAll(tmpDir) - require.NoError(t, err) - err = os.MkdirAll(tmpDir, 0o777) - require.NoError(t, err) + assert.NoError(t, err) + err = os.MkdirAll(tmpDir, 0777) + assert.NoError(t, err) if errorExpected { // Copy invalid helm chart into temporary directory, ensuring manifest generation will fail err = fileutil.CopyDir("./testdata/invalid-helm", tmpDir) - require.NoError(t, err) + assert.NoError(t, err) + } else { // Copy valid helm chart into temporary directory, ensuring generation will succeed err = fileutil.CopyDir("./testdata/my-chart", tmpDir) - require.NoError(t, err) + assert.NoError(t, err) } res, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ @@ -939,23 +851,19 @@ func TestManifestGenErrorCacheFileContentsChange(t *testing.T) { fmt.Println(" res: ", res) if step < 2 { - if errorExpected { - require.Error(t, err, "error return value and error expected did not match") - assert.Nil(t, res, "GenerateManifest return value and expected value did not match") - } else { - require.NoError(t, err, "error return value and error expected did not match") - assert.NotNil(t, res, "GenerateManifest return value and expected value did not match") - } + assert.True(t, (err != nil) == errorExpected, "error return value and error expected did not match") + assert.True(t, (res != nil) == !errorExpected, "GenerateManifest return value and expected value did not match") } if step == 2 { - require.NoError(t, err, "error ret val was non-nil on step 3") + assert.NoError(t, err, "error ret val was non-nil on step 3") assert.NotNil(t, res, "GenerateManifest ret val was nil on step 3") } } } func TestManifestGenErrorCacheByMinutesElapsed(t *testing.T) { + tests := []struct { // Test with a range of pause expiration thresholds PauseGenerationOnFailureForMinutes int @@ -996,6 +904,7 @@ func TestManifestGenErrorCacheByMinutesElapsed(t *testing.T) { if x == 1 { assert.True(t, strings.HasPrefix(err.Error(), cachedManifestGenerationPrefix)) } + } // 2) Jump forward X-1 minutes in time, where X is the expiration boundary @@ -1025,12 +934,15 @@ func TestManifestGenErrorCacheByMinutesElapsed(t *testing.T) { // 5) Ensure that the service no longer returns a cached copy of the last error assert.True(t, err != nil && res == nil) - assert.False(t, strings.HasPrefix(err.Error(), cachedManifestGenerationPrefix)) + assert.True(t, !strings.HasPrefix(err.Error(), cachedManifestGenerationPrefix)) + }) } + } func TestManifestGenErrorCacheRespectsNoCache(t *testing.T) { + service := newService(t, ".") service.initConstants = RepoServerInitConstants{ @@ -1070,7 +982,7 @@ func TestManifestGenErrorCacheRespectsNoCache(t *testing.T) { // 3) Ensure that the cache returns a new generation attempt, rather than a previous cached error assert.True(t, err != nil && res == nil) - assert.False(t, strings.HasPrefix(err.Error(), cachedManifestGenerationPrefix)) + assert.True(t, !strings.HasPrefix(err.Error(), cachedManifestGenerationPrefix)) // 4) Call generateManifest res, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ @@ -1084,6 +996,7 @@ func TestManifestGenErrorCacheRespectsNoCache(t *testing.T) { // 5) Ensure that the subsequent invocation, after nocache, is cached assert.True(t, err != nil && res == nil) assert.True(t, strings.HasPrefix(err.Error(), cachedManifestGenerationPrefix)) + } func TestGenerateHelmWithValues(t *testing.T) { @@ -1103,23 +1016,24 @@ func TestGenerateHelmWithValues(t *testing.T) { ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) + assert.NoError(t, err) replicasVerified := false for _, src := range res.Manifests { obj := unstructured.Unstructured{} err = json.Unmarshal([]byte(src), &obj) - require.NoError(t, err) + assert.NoError(t, err) if obj.GetKind() == "Deployment" && obj.GetName() == "test-redis-slave" { var dep v1.Deployment err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &dep) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, int32(2), *dep.Spec.Replicas) replicasVerified = true } } assert.True(t, replicasVerified) + } func TestHelmWithMissingValueFiles(t *testing.T) { @@ -1141,13 +1055,13 @@ func TestHelmWithMissingValueFiles(t *testing.T) { // Should fail since we're passing a non-existent values file, and error should indicate that _, err := service.GenerateManifest(context.Background(), req) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), fmt.Sprintf("%s: no such file or directory", missingValuesFile)) // Should template without error even if defining a non-existent values file req.ApplicationSource.Helm.IgnoreMissingValueFiles = true _, err = service.GenerateManifest(context.Background(), req) - require.NoError(t, err) + assert.NoError(t, err) } func TestGenerateHelmWithEnvVars(t *testing.T) { @@ -1166,18 +1080,18 @@ func TestGenerateHelmWithEnvVars(t *testing.T) { ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) + assert.NoError(t, err) replicasVerified := false for _, src := range res.Manifests { obj := unstructured.Unstructured{} err = json.Unmarshal([]byte(src), &obj) - require.NoError(t, err) + assert.NoError(t, err) if obj.GetKind() == "Deployment" && obj.GetName() == "production-redis-slave" { var dep v1.Deployment err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &dep) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, int32(3), *dep.Spec.Replicas) replicasVerified = true } @@ -1202,7 +1116,7 @@ func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) + assert.NoError(t, err) // Test the case where the path is "." service = newService(t, "./testdata") @@ -1215,7 +1129,7 @@ func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) + assert.NoError(t, err) } func TestChartRepoWithOutOfBoundsSymlink(t *testing.T) { @@ -1242,10 +1156,9 @@ func TestHelmManifestFromChartRepoWithValueFile(t *testing.T) { ApplicationSource: source, NoCache: true, ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + ProjectSourceRepos: []string{"*"}} response, err := service.GenerateManifest(context.Background(), request) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, response) assert.Equal(t, &apiclient.ManifestResponse{ Manifests: []string{"{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"my-map\"}}"}, @@ -1253,7 +1166,6 @@ func TestHelmManifestFromChartRepoWithValueFile(t *testing.T) { Server: "", Revision: "1.1.0", SourceType: "Helm", - Commands: []string{`helm template . --name-template "" --values ./testdata/my-chart/my-chart-values.yaml --include-crds`}, }, response) } @@ -1270,7 +1182,7 @@ func TestHelmManifestFromChartRepoWithValueFileOutsideRepo(t *testing.T) { } request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true} _, err := service.GenerateManifest(context.Background(), request) - require.Error(t, err) + assert.Error(t, err) } func TestHelmManifestFromChartRepoWithValueFileLinks(t *testing.T) { @@ -1283,12 +1195,10 @@ func TestHelmManifestFromChartRepoWithValueFileLinks(t *testing.T) { ValueFiles: []string{"my-chart-link.yaml"}, }, } - request := &apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true, ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} _, err := service.GenerateManifest(context.Background(), request) - require.NoError(t, err) + assert.NoError(t, err) }) } @@ -1309,7 +1219,7 @@ func TestGenerateHelmWithURL(t *testing.T) { ProjectSourceRepos: []string{"*"}, HelmOptions: &argoappv1.HelmOptions{ValuesFileSchemes: []string{"https"}}, }) - require.NoError(t, err) + assert.NoError(t, err) } // The requested value file (`../minio/values.yaml`) is outside the repo directory @@ -1330,7 +1240,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "outside repository root") }) @@ -1349,7 +1259,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("Values file with absolute path stays within repo root", func(t *testing.T) { @@ -1367,7 +1277,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("Values file with absolute path using back-references outside repo root", func(t *testing.T) { @@ -1385,7 +1295,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "outside repository root") }) @@ -1404,7 +1314,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "is not allowed") }) @@ -1423,7 +1333,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "s3://my-bucket/my-chart-values.yaml: no such file or directory") }) } @@ -1433,13 +1343,13 @@ func TestGenerateHelmWithAbsoluteFileParameter(t *testing.T) { service := newService(t, "../..") file, err := os.CreateTemp("", "external-secret.txt") - require.NoError(t, err) + assert.NoError(t, err) externalSecretPath := file.Name() defer func() { _ = os.RemoveAll(externalSecretPath) }() expectedFileContent, err := os.ReadFile("../../util/helm/testdata/external/external-secret.txt") - require.NoError(t, err) - err = os.WriteFile(externalSecretPath, expectedFileContent, 0o644) - require.NoError(t, err) + assert.NoError(t, err) + err = os.WriteFile(externalSecretPath, expectedFileContent, 0644) + assert.NoError(t, err) defer func() { if err = file.Close(); err != nil { panic(err) @@ -1463,7 +1373,7 @@ func TestGenerateHelmWithAbsoluteFileParameter(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.Error(t, err) + assert.Error(t, err) } // The requested file parameter (`../external/external-secret.txt`) is outside the app path @@ -1491,7 +1401,7 @@ func TestGenerateHelmWithFileParameter(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, res.Manifests[6], `"replicas":2`, "ValuesObject should override Values") } @@ -1506,8 +1416,8 @@ func TestGenerateNullList(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) - assert.Len(t, res1.Manifests, 1) + assert.Nil(t, err) + assert.Equal(t, len(res1.Manifests), 1) assert.Contains(t, res1.Manifests[0], "prometheus-operator-operator") }) @@ -1519,8 +1429,8 @@ func TestGenerateNullList(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) - assert.Len(t, res1.Manifests, 1) + assert.Nil(t, err) + assert.Equal(t, len(res1.Manifests), 1) assert.Contains(t, res1.Manifests[0], "prometheus-operator-operator") }) @@ -1532,22 +1442,22 @@ func TestGenerateNullList(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) + assert.Nil(t, err) assert.Len(t, res1.Manifests, 2) }) } func TestIdentifyAppSourceTypeByAppDirWithKustomizations(t *testing.T) { - sourceType, err := GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yaml", "./testdata", "testapp", map[string]bool{}, []string{}, []string{}) - require.NoError(t, err) + sourceType, err := GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yaml", "./testdata", "testapp", map[string]bool{}, []string{}) + assert.Nil(t, err) assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType) - sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yml", "./testdata", "testapp", map[string]bool{}, []string{}, []string{}) - require.NoError(t, err) + sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yml", "./testdata", "testapp", map[string]bool{}, []string{}) + assert.Nil(t, err) assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType) - sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/Kustomization", "./testdata", "testapp", map[string]bool{}, []string{}, []string{}) - require.NoError(t, err) + sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/Kustomization", "./testdata", "testapp", map[string]bool{}, []string{}) + assert.Nil(t, err) assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType) } @@ -1559,15 +1469,15 @@ func TestGenerateFromUTF16(t *testing.T) { ProjectSourceRepos: []string{"*"}, } res1, err := GenerateManifests(context.Background(), "./testdata/utf-16", "/", "", &q, false, &git.NoopCredsStore{}, resource.MustParse("0"), nil) - require.NoError(t, err) - assert.Len(t, res1.Manifests, 2) + assert.Nil(t, err) + assert.Equal(t, 2, len(res1.Manifests)) } func TestListApps(t *testing.T) { service := newService(t, "./testdata") res, err := service.ListApps(context.Background(), &apiclient.ListAppsRequest{Repo: &argoappv1.Repository{}}) - require.NoError(t, err) + assert.NoError(t, err) expectedApps := map[string]string{ "Kustomization": "Kustomize", @@ -1587,8 +1497,6 @@ func TestListApps(t *testing.T) { "values-files": "Helm", "helm-with-dependencies": "Helm", "helm-with-dependencies-alias": "Helm", - "helm-with-local-dependency": "Helm", - "simple-chart": "Helm", } assert.Equal(t, expectedApps, res.Apps) } @@ -1603,7 +1511,7 @@ func TestGetAppDetailsHelm(t *testing.T) { }, }) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, res.Helm) assert.Equal(t, "Helm", res.Type) @@ -1620,7 +1528,7 @@ func TestGetAppDetailsHelmUsesCache(t *testing.T) { }, }) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, res.Helm) assert.Equal(t, "Helm", res.Type) @@ -1637,7 +1545,7 @@ func TestGetAppDetailsHelm_WithNoValuesFile(t *testing.T) { }, }) - require.NoError(t, err) + assert.NoError(t, err) assert.NotNil(t, res.Helm) assert.Equal(t, "Helm", res.Type) @@ -1655,7 +1563,7 @@ func TestGetAppDetailsKustomize(t *testing.T) { }, }) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "Kustomize", res.Type) assert.NotNil(t, res.Kustomize) @@ -1671,7 +1579,7 @@ func TestGetHelmCharts(t *testing.T) { return res.Items[i].Name < res.Items[j].Name }) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, res.Items, 2) item := res.Items[0] @@ -1700,7 +1608,7 @@ func TestGetRevisionMetadata(t *testing.T) { CheckSignature: true, }) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "test", res.Message) assert.Equal(t, now, res.Date.Time) assert.Equal(t, "author", res.Author) @@ -1714,7 +1622,7 @@ func TestGetRevisionMetadata(t *testing.T) { CheckSignature: true, }) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "test", res.Message) assert.Equal(t, now, res.Date.Time) assert.Equal(t, "author", res.Author) @@ -1727,7 +1635,7 @@ func TestGetRevisionMetadata(t *testing.T) { Revision: "c0b400fc458875d925171398f9ba9eabd5529923", CheckSignature: false, }) - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, res.SignatureInfo) // Enforce cache miss - signature info should not be in result @@ -1736,7 +1644,7 @@ func TestGetRevisionMetadata(t *testing.T) { Revision: "da52afd3b2df1ec49470603d8bbb46954dab1091", CheckSignature: false, }) - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, res.SignatureInfo) // Cache hit on previous entry that did not have signature info @@ -1745,7 +1653,7 @@ func TestGetRevisionMetadata(t *testing.T) { Revision: "da52afd3b2df1ec49470603d8bbb46954dab1091", CheckSignature: true, }) - require.NoError(t, err) + assert.NoError(t, err) assert.NotEmpty(t, res.SignatureInfo) } @@ -1764,7 +1672,7 @@ func TestGetSignatureVerificationResult(t *testing.T) { } res, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, testSignature, res.VerifyResult) } // Commit with signature and verification not requested @@ -1772,13 +1680,11 @@ func TestGetSignatureVerificationResult(t *testing.T) { service := newServiceWithSignature(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} - q := apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, ApplicationSource: &src, ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, res.VerifyResult) } // Commit without signature and verification requested @@ -1786,13 +1692,11 @@ func TestGetSignatureVerificationResult(t *testing.T) { service := newService(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} - q := apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true, ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, res.VerifyResult) } // Commit without signature and verification not requested @@ -1800,13 +1704,11 @@ func TestGetSignatureVerificationResult(t *testing.T) { service := newService(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} - q := apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true, ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res, err := service.GenerateManifest(context.Background(), &q) - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, res.VerifyResult) } } @@ -1817,7 +1719,6 @@ func Test_newEnv(t *testing.T) { &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_REVISION_SHORT_8", Value: "my-revis"}, &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"}, @@ -1925,7 +1826,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }, AppName: "broken", }) - require.Error(t, err) + assert.Error(t, err) }) }) } @@ -1972,7 +1873,9 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { for _, manifest := range manifests.Manifests { var un unstructured.Unstructured err := yaml.Unmarshal([]byte(manifest), &un) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } resourceByKindName[fmt.Sprintf("%s/%s", un.GetKind(), un.GetName())] = &un } deployment, ok := resourceByKindName["Deployment/guestbook-ui"] @@ -2001,7 +1904,9 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { for _, manifest := range manifests.Manifests { var un unstructured.Unstructured err := yaml.Unmarshal([]byte(manifest), &un) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } resourceByKindName[fmt.Sprintf("%s/%s", un.GetKind(), un.GetName())] = &un } deployment, ok := resourceByKindName["Deployment/guestbook-ui"] @@ -2031,7 +1936,9 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { for _, manifest := range manifests.Manifests { var un unstructured.Unstructured err := yaml.Unmarshal([]byte(manifest), &un) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } resourceByKindName[fmt.Sprintf("%s/%s", un.GetKind(), un.GetName())] = &un } deployment, ok := resourceByKindName["Deployment/guestbook-ui"] @@ -2059,7 +1966,7 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { ProjectSourceRepos: []string{"*"}, HasMultipleSources: true, }) - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, manifests.Manifests) assert.NotEmpty(t, manifests.Revision) }) @@ -2082,7 +1989,9 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { for _, manifest := range manifests.Manifests { var un unstructured.Unstructured err := yaml.Unmarshal([]byte(manifest), &un) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } resourceByKindName[fmt.Sprintf("%s/%s", un.GetKind(), un.GetName())] = &un } deployment, ok := resourceByKindName["Deployment/guestbook-ui"] @@ -2109,13 +2018,13 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { ProjectName: "something", ProjectSourceRepos: []string{"*"}, }) - require.NoError(t, err) + 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, nil) - require.NoError(t, err) + assert.NoError(t, err) }) }) } @@ -2195,6 +2104,7 @@ func TestGenerateManifestWithAnnotatedAndRegularGitTagHashes(t *testing.T) { t.Errorf("expected an error but did not throw one") } } + }) } } @@ -2237,72 +2147,6 @@ func TestGenerateManifestWithAnnotatedTagsAndMultiSourceApp(t *testing.T) { } } -func TestGenerateMultiSourceHelmWithFileParameter(t *testing.T) { - expectedFileContent, err := os.ReadFile("../../util/helm/testdata/external/external-secret.txt") - require.NoError(t, err) - - service := newService(t, "../../util/helm/testdata") - - testCases := []struct { - name string - refSources map[string]*argoappv1.RefTarget - expectedContent string - expectedErr bool - }{{ - name: "Successfully resolve multi-source ref for helm set-file", - refSources: map[string]*argoappv1.RefTarget{ - "$global": { - TargetRevision: "HEAD", - }, - }, - expectedContent: string(expectedFileContent), - expectedErr: false, - }, { - name: "Failed to resolve multi-source ref for helm set-file", - refSources: map[string]*argoappv1.RefTarget{}, - expectedContent: "DOES-NOT-EXIST", - expectedErr: true, - }} - - for i := range testCases { - tc := testCases[i] - t.Run(tc.name, func(t *testing.T) { - manifestRequest := &apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, - ApplicationSource: &argoappv1.ApplicationSource{ - Ref: "$global", - Path: "./redis", - TargetRevision: "HEAD", - Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"$global/redis/values-production.yaml"}, - FileParameters: []argoappv1.HelmFileParameter{{ - Name: "passwordContent", - Path: "$global/external/external-secret.txt", - }}, - }, - }, - HasMultipleSources: true, - NoCache: true, - RefSources: tc.refSources, - } - - res, err := service.GenerateManifest(context.Background(), manifestRequest) - - if !tc.expectedErr { - require.NoError(t, err) - - // Check that any of the manifests contains the secret - idx := slices.IndexFunc(res.Manifests, func(content string) bool { - return strings.Contains(content, tc.expectedContent) - }) - assert.GreaterOrEqual(t, idx, 0, "No manifest contains the value set with the helm fileParameters") - } else { - assert.Error(t, err) - } - }) - } -} - func TestFindResources(t *testing.T) { testCases := []struct { name string @@ -2342,7 +2186,9 @@ func TestFindResources(t *testing.T) { Include: tc.include, Exclude: tc.exclude, }, map[string]bool{}, resource.MustParse("0")) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } var names []string for i := range objs { names = append(names, objs[i].GetName()) @@ -2358,8 +2204,9 @@ func TestFindManifests_Exclude(t *testing.T) { Exclude: "subdir/deploymentSub.yaml", }, map[string]bool{}, resource.MustParse("0")) - require.NoError(t, err) - require.Len(t, objs, 1) + if !assert.NoError(t, err) || !assert.Len(t, objs, 1) { + return + } assert.Equal(t, "nginx-deployment", objs[0].GetName()) } @@ -2370,8 +2217,9 @@ func TestFindManifests_Exclude_NothingMatches(t *testing.T) { Exclude: "nothing.yaml", }, map[string]bool{}, resource.MustParse("0")) - require.NoError(t, err) - require.Len(t, objs, 2) + if !assert.NoError(t, err) || !assert.Len(t, objs, 2) { + return + } assert.ElementsMatch(t, []string{"nginx-deployment", "nginx-deployment-sub"}, []string{objs[0].GetName(), objs[1].GetName()}) @@ -2392,7 +2240,7 @@ func tempDir(t *testing.T) string { } func walkFor(t *testing.T, root string, testPath string, run func(info fs.FileInfo)) { - hitExpectedPath := false + var hitExpectedPath = false err := filepath.Walk(root, func(path string, info fs.FileInfo, err error) error { if path == testPath { require.NoError(t, err) @@ -2415,7 +2263,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { t.Run("non-JSON/YAML is skipped with an empty ignore message", func(t *testing.T) { appDir := tempDir(t) filePath := filepath.Join(appDir, "not-json-or-yaml") - file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0o644) + file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0644) require.NoError(t, err) err = file.Close() require.NoError(t, err) @@ -2424,7 +2272,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { realFileInfo, ignoreMessage, err := getPotentiallyValidManifestFile(filePath, info, appDir, appDir, "", "") assert.Nil(t, realFileInfo) assert.Empty(t, ignoreMessage) - require.NoError(t, err) + assert.NoError(t, err) }) }) @@ -2458,7 +2306,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { realFileInfo, ignoreMessage, err := getPotentiallyValidManifestFile(aPath, info, appDir, appDir, "", "") assert.Nil(t, realFileInfo) assert.NotEmpty(t, ignoreMessage) - require.NoError(t, err) + assert.NoError(t, err) }) }) @@ -2481,7 +2329,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { appDir := tempDir(t) dirPath := filepath.Join(appDir, "test.dir") - err := os.MkdirAll(dirPath, 0o644) + err := os.MkdirAll(dirPath, 0644) require.NoError(t, err) linkPath := filepath.Join(appDir, "test.json") err = os.Symlink(dirPath, linkPath) @@ -2491,7 +2339,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { realFileInfo, ignoreMessage, err := getPotentiallyValidManifestFile(linkPath, info, appDir, appDir, "", "") assert.Nil(t, realFileInfo) assert.Contains(t, ignoreMessage, "non-regular file") - require.NoError(t, err) + assert.NoError(t, err) }) }) @@ -2499,7 +2347,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { appDir := tempDir(t) filePath := filepath.Join(appDir, "not-included.yaml") - file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0o644) + file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0644) require.NoError(t, err) err = file.Close() require.NoError(t, err) @@ -2508,7 +2356,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { realFileInfo, ignoreMessage, err := getPotentiallyValidManifestFile(filePath, info, appDir, appDir, "*.json", "") assert.Nil(t, realFileInfo) assert.Empty(t, ignoreMessage) - require.NoError(t, err) + assert.NoError(t, err) }) }) @@ -2516,7 +2364,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { appDir := tempDir(t) filePath := filepath.Join(appDir, "excluded.json") - file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0o644) + file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0644) require.NoError(t, err) err = file.Close() require.NoError(t, err) @@ -2525,7 +2373,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { realFileInfo, ignoreMessage, err := getPotentiallyValidManifestFile(filePath, info, appDir, appDir, "", "excluded.*") assert.Nil(t, realFileInfo) assert.Empty(t, ignoreMessage) - require.NoError(t, err) + assert.NoError(t, err) }) }) @@ -2533,7 +2381,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { appDir := tempDir(t) filePath := filepath.Join(appDir, "regular-file") - file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0o644) + file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0644) require.NoError(t, err) err = file.Close() require.NoError(t, err) @@ -2546,7 +2394,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { realFileInfo, ignoreMessage, err := getPotentiallyValidManifestFile(linkPath, info, appDir, appDir, "", "") assert.NotNil(t, realFileInfo) assert.Empty(t, ignoreMessage) - require.NoError(t, err) + assert.NoError(t, err) }) }) @@ -2554,7 +2402,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { appDir := tempDir(t) filePath := filepath.Join(appDir, "regular-file.json") - file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0o644) + file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0644) require.NoError(t, err) err = file.Close() require.NoError(t, err) @@ -2563,7 +2411,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { realFileInfo, ignoreMessage, err := getPotentiallyValidManifestFile(filePath, info, appDir, appDir, "", "") assert.NotNil(t, realFileInfo) assert.Empty(t, ignoreMessage) - require.NoError(t, err) + assert.NoError(t, err) }) }) @@ -2571,7 +2419,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { appDir := tempDir(t) filePath := filepath.Join(appDir, "regular-file") - file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0o644) + file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0644) require.NoError(t, err) err = file.Close() require.NoError(t, err) @@ -2585,7 +2433,7 @@ func Test_getPotentiallyValidManifestFile(t *testing.T) { assert.NotNil(t, realFileInfo) assert.Equal(t, filepath.Base(filePath), realFileInfo.Name()) assert.Empty(t, ignoreMessage) - require.NoError(t, err) + assert.NoError(t, err) }) }) } @@ -2599,17 +2447,17 @@ func Test_getPotentiallyValidManifests(t *testing.T) { t.Run("unreadable file throws error", func(t *testing.T) { appDir := t.TempDir() unreadablePath := filepath.Join(appDir, "unreadable.json") - err := os.WriteFile(unreadablePath, []byte{}, 0o666) + err := os.WriteFile(unreadablePath, []byte{}, 0666) require.NoError(t, err) - err = os.Chmod(appDir, 0o000) + err = os.Chmod(appDir, 0000) require.NoError(t, err) manifests, err := getPotentiallyValidManifests(logCtx, appDir, appDir, false, "", "", resource.MustParse("0")) assert.Empty(t, manifests) - require.Error(t, err) + assert.Error(t, err) // allow cleanup - err = os.Chmod(appDir, 0o777) + err = os.Chmod(appDir, 0777) if err != nil { panic(err) } @@ -2618,19 +2466,19 @@ func Test_getPotentiallyValidManifests(t *testing.T) { t.Run("no recursion when recursion is disabled", func(t *testing.T) { manifests, err := getPotentiallyValidManifests(logCtx, "./testdata/recurse", "./testdata/recurse", false, "", "", resource.MustParse("0")) assert.Len(t, manifests, 1) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("recursion when recursion is enabled", func(t *testing.T) { manifests, err := getPotentiallyValidManifests(logCtx, "./testdata/recurse", "./testdata/recurse", true, "", "", resource.MustParse("0")) assert.Len(t, manifests, 2) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("non-JSON/YAML is skipped", func(t *testing.T) { manifests, err := getPotentiallyValidManifests(logCtx, "./testdata/non-manifest-file", "./testdata/non-manifest-file", false, "", "", resource.MustParse("0")) assert.Empty(t, manifests) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("circular link should throw an error", func(t *testing.T) { @@ -2642,14 +2490,14 @@ func Test_getPotentiallyValidManifests(t *testing.T) { defer os.Remove(path.Join(testDir, "b.json")) manifests, err := getPotentiallyValidManifests(logCtx, "./testdata/circular-link", "./testdata/circular-link", false, "", "", resource.MustParse("0")) assert.Empty(t, manifests) - require.Error(t, err) + assert.Error(t, err) }) t.Run("out-of-bounds symlink should throw an error", func(t *testing.T) { require.DirExists(t, "./testdata/out-of-bounds-link") manifests, err := getPotentiallyValidManifests(logCtx, "./testdata/out-of-bounds-link", "./testdata/out-of-bounds-link", false, "", "", resource.MustParse("0")) assert.Empty(t, manifests) - require.Error(t, err) + assert.Error(t, err) }) t.Run("symlink to a regular file works", func(t *testing.T) { @@ -2659,13 +2507,13 @@ func Test_getPotentiallyValidManifests(t *testing.T) { require.NoError(t, err) manifests, err := getPotentiallyValidManifests(logCtx, appPath, repoRoot, false, "", "", resource.MustParse("0")) assert.Len(t, manifests, 1) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("symlink to nowhere should be ignored", func(t *testing.T) { manifests, err := getPotentiallyValidManifests(logCtx, "./testdata/link-to-nowhere", "./testdata/link-to-nowhere", false, "", "", resource.MustParse("0")) assert.Empty(t, manifests) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("link to over-sized manifest fails", func(t *testing.T) { @@ -2683,7 +2531,7 @@ func Test_getPotentiallyValidManifests(t *testing.T) { // There is a total of 10 files, ech file being 10 bytes. manifests, err := getPotentiallyValidManifests(logCtx, "./testdata/several-files", "./testdata/several-files", false, "", "", resource.MustParse("365")) assert.Len(t, manifests, 10) - require.NoError(t, err) + assert.NoError(t, err) manifests, err = getPotentiallyValidManifests(logCtx, "./testdata/several-files", "./testdata/several-files", false, "", "", resource.MustParse("100")) assert.Empty(t, manifests) @@ -2698,17 +2546,17 @@ func Test_findManifests(t *testing.T) { t.Run("unreadable file throws error", func(t *testing.T) { appDir := t.TempDir() unreadablePath := filepath.Join(appDir, "unreadable.json") - err := os.WriteFile(unreadablePath, []byte{}, 0o666) + err := os.WriteFile(unreadablePath, []byte{}, 0666) require.NoError(t, err) - err = os.Chmod(appDir, 0o000) + err = os.Chmod(appDir, 0000) require.NoError(t, err) manifests, err := findManifests(logCtx, appDir, appDir, nil, noRecurse, nil, resource.MustParse("0")) assert.Empty(t, manifests) - require.Error(t, err) + assert.Error(t, err) // allow cleanup - err = os.Chmod(appDir, 0o777) + err = os.Chmod(appDir, 0777) if err != nil { panic(err) } @@ -2717,20 +2565,20 @@ func Test_findManifests(t *testing.T) { t.Run("no recursion when recursion is disabled", func(t *testing.T) { manifests, err := findManifests(logCtx, "./testdata/recurse", "./testdata/recurse", nil, noRecurse, nil, resource.MustParse("0")) assert.Len(t, manifests, 2) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("recursion when recursion is enabled", func(t *testing.T) { recurse := argoappv1.ApplicationSourceDirectory{Recurse: true} manifests, err := findManifests(logCtx, "./testdata/recurse", "./testdata/recurse", nil, recurse, nil, resource.MustParse("0")) assert.Len(t, manifests, 4) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("non-JSON/YAML is skipped", func(t *testing.T) { manifests, err := findManifests(logCtx, "./testdata/non-manifest-file", "./testdata/non-manifest-file", nil, noRecurse, nil, resource.MustParse("0")) assert.Empty(t, manifests) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("circular link should throw an error", func(t *testing.T) { @@ -2742,14 +2590,14 @@ func Test_findManifests(t *testing.T) { defer os.Remove(path.Join(testDir, "b.json")) manifests, err := findManifests(logCtx, "./testdata/circular-link", "./testdata/circular-link", nil, noRecurse, nil, resource.MustParse("0")) assert.Empty(t, manifests) - require.Error(t, err) + assert.Error(t, err) }) t.Run("out-of-bounds symlink should throw an error", func(t *testing.T) { require.DirExists(t, "./testdata/out-of-bounds-link") manifests, err := findManifests(logCtx, "./testdata/out-of-bounds-link", "./testdata/out-of-bounds-link", nil, noRecurse, nil, resource.MustParse("0")) assert.Empty(t, manifests) - require.Error(t, err) + assert.Error(t, err) }) t.Run("symlink to a regular file works", func(t *testing.T) { @@ -2759,13 +2607,13 @@ func Test_findManifests(t *testing.T) { require.NoError(t, err) manifests, err := findManifests(logCtx, appPath, repoRoot, nil, noRecurse, nil, resource.MustParse("0")) assert.Len(t, manifests, 1) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("symlink to nowhere should be ignored", func(t *testing.T) { manifests, err := findManifests(logCtx, "./testdata/link-to-nowhere", "./testdata/link-to-nowhere", nil, noRecurse, nil, resource.MustParse("0")) assert.Empty(t, manifests) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("link to over-sized manifest fails", func(t *testing.T) { @@ -2783,7 +2631,7 @@ func Test_findManifests(t *testing.T) { // There is a total of 10 files, each file being 10 bytes. manifests, err := findManifests(logCtx, "./testdata/several-files", "./testdata/several-files", nil, noRecurse, nil, resource.MustParse("365")) assert.Len(t, manifests, 10) - require.NoError(t, err) + assert.NoError(t, err) manifests, err = findManifests(logCtx, "./testdata/several-files", "./testdata/several-files", nil, noRecurse, nil, resource.MustParse("364")) assert.Empty(t, manifests) @@ -2794,7 +2642,7 @@ func Test_findManifests(t *testing.T) { // Each file is 36 bytes. Only the 36-byte json file should be counted against the limit. manifests, err := findManifests(logCtx, "./testdata/jsonnet-and-json", "./testdata/jsonnet-and-json", nil, noRecurse, nil, resource.MustParse("36")) assert.Len(t, manifests, 2) - require.NoError(t, err) + assert.NoError(t, err) manifests, err = findManifests(logCtx, "./testdata/jsonnet-and-json", "./testdata/jsonnet-and-json", nil, noRecurse, nil, resource.MustParse("35")) assert.Empty(t, manifests) @@ -2805,46 +2653,46 @@ func Test_findManifests(t *testing.T) { require.DirExists(t, "./testdata/partially-valid-yaml") manifests, err := findManifests(logCtx, "./testdata/partially-valid-yaml", "./testdata/partially-valid-yaml", nil, noRecurse, nil, resource.MustParse("0")) assert.Empty(t, manifests) - require.Error(t, err) + assert.Error(t, err) }) t.Run("invalid manifest throws an error", func(t *testing.T) { require.DirExists(t, "./testdata/invalid-manifests") manifests, err := findManifests(logCtx, "./testdata/invalid-manifests", "./testdata/invalid-manifests", nil, noRecurse, nil, resource.MustParse("0")) assert.Empty(t, manifests) - require.Error(t, err) + assert.Error(t, err) }) t.Run("irrelevant YAML gets skipped, relevant YAML gets parsed", func(t *testing.T) { manifests, err := findManifests(logCtx, "./testdata/irrelevant-yaml", "./testdata/irrelevant-yaml", nil, noRecurse, nil, resource.MustParse("0")) assert.Len(t, manifests, 1) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("multiple JSON objects in one file throws an error", func(t *testing.T) { require.DirExists(t, "./testdata/json-list") manifests, err := findManifests(logCtx, "./testdata/json-list", "./testdata/json-list", nil, noRecurse, nil, resource.MustParse("0")) assert.Empty(t, manifests) - require.Error(t, err) + assert.Error(t, err) }) t.Run("invalid JSON throws an error", func(t *testing.T) { require.DirExists(t, "./testdata/invalid-json") manifests, err := findManifests(logCtx, "./testdata/invalid-json", "./testdata/invalid-json", nil, noRecurse, nil, resource.MustParse("0")) assert.Empty(t, manifests) - require.Error(t, err) + assert.Error(t, err) }) t.Run("valid JSON returns manifest and no error", func(t *testing.T) { manifests, err := findManifests(logCtx, "./testdata/valid-json", "./testdata/valid-json", nil, noRecurse, nil, resource.MustParse("0")) assert.Len(t, manifests, 1) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("YAML with an empty document doesn't throw an error", func(t *testing.T) { manifests, err := findManifests(logCtx, "./testdata/yaml-with-empty-document", "./testdata/yaml-with-empty-document", nil, noRecurse, nil, resource.MustParse("0")) assert.Len(t, manifests, 1) - require.NoError(t, err) + assert.NoError(t, err) }) } @@ -2866,13 +2714,14 @@ func Test_getHelmDependencyRepos(t *testing.T) { repo2 := "https://eventstore.github.io/EventStore.Charts" repos, err := getHelmDependencyRepos("../../util/helm/testdata/dependency") - require.NoError(t, err) - assert.Len(t, repos, 2) + assert.NoError(t, err) + assert.Equal(t, len(repos), 2) assert.Equal(t, repos[0].Repo, repo1) assert.Equal(t, repos[1].Repo, repo2) } func TestResolveRevision(t *testing.T) { + service := newService(t, ".") repo := &argoappv1.Repository{Repo: "https://github.com/argoproj/argo-cd"} app := &argoappv1.Application{Spec: argoappv1.ApplicationSpec{Source: &argoappv1.ApplicationSource{}}} @@ -2888,11 +2737,13 @@ func TestResolveRevision(t *testing.T) { } assert.NotNil(t, resolveRevisionResponse.Revision) - require.NoError(t, err) + assert.Nil(t, err) assert.Equal(t, expectedResolveRevisionResponse, resolveRevisionResponse) + } func TestResolveRevisionNegativeScenarios(t *testing.T) { + service := newService(t, ".") repo := &argoappv1.Repository{Repo: "https://github.com/argoproj/argo-cd"} app := &argoappv1.Application{Spec: argoappv1.ApplicationSpec{Source: &argoappv1.ApplicationSource{}}} @@ -2908,8 +2759,9 @@ func TestResolveRevisionNegativeScenarios(t *testing.T) { } assert.NotNil(t, resolveRevisionResponse.Revision) - require.Error(t, err) + assert.NotNil(t, err) assert.Equal(t, expectedResolveRevisionResponse, resolveRevisionResponse) + } func TestDirectoryPermissionInitializer(t *testing.T) { @@ -2920,12 +2772,12 @@ func TestDirectoryPermissionInitializer(t *testing.T) { io.Close(file) // remove read permissions - require.NoError(t, os.Chmod(dir, 0o000)) + assert.NoError(t, os.Chmod(dir, 0000)) // Remember to restore permissions when the test finishes so dir can // be removed properly. t.Cleanup(func() { - require.NoError(t, os.Chmod(dir, 0o777)) + require.NoError(t, os.Chmod(dir, 0777)) }) // make sure permission are restored @@ -2940,43 +2792,43 @@ func TestDirectoryPermissionInitializer(t *testing.T) { } func addHelmToGitRepo(t *testing.T, options newGitRepoOptions) { - err := os.WriteFile(filepath.Join(options.path, "Chart.yaml"), []byte("name: test\nversion: v1.0.0"), 0o777) - require.NoError(t, err) + 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) - require.NoError(t, err) - err = os.WriteFile(filepath.Join(options.path, valuesFileName), valuesFileContents, 0o777) - require.NoError(t, err) + assert.NoError(t, err) + err = os.WriteFile(filepath.Join(options.path, valuesFileName), valuesFileContents, 0777) + assert.NoError(t, err) } - require.NoError(t, err) + assert.NoError(t, err) cmd := exec.Command("git", "add", "-A") cmd.Dir = options.path - require.NoError(t, cmd.Run()) + assert.NoError(t, cmd.Run()) cmd = exec.Command("git", "commit", "-m", "Initial commit") cmd.Dir = options.path - require.NoError(t, cmd.Run()) + assert.NoError(t, cmd.Run()) } func initGitRepo(t *testing.T, options newGitRepoOptions) (revision string) { if options.createPath { - require.NoError(t, os.Mkdir(options.path, 0o755)) + assert.NoError(t, os.Mkdir(options.path, 0755)) } cmd := exec.Command("git", "init", "-b", "main", options.path) cmd.Dir = options.path - require.NoError(t, cmd.Run()) + assert.NoError(t, cmd.Run()) if options.remote != "" { cmd = exec.Command("git", "remote", "add", "origin", options.path) cmd.Dir = options.path - require.NoError(t, cmd.Run()) + assert.NoError(t, cmd.Run()) } commitAdded := options.addEmptyCommit || options.helmChartOptions.chartName != "" if options.addEmptyCommit { cmd = exec.Command("git", "commit", "-m", "Initial commit", "--allow-empty") cmd.Dir = options.path - require.NoError(t, cmd.Run()) + assert.NoError(t, cmd.Run()) } else if options.helmChartOptions.chartName != "" { addHelmToGitRepo(t, options) } @@ -2986,7 +2838,7 @@ func initGitRepo(t *testing.T, options newGitRepoOptions) (revision string) { cmd = exec.Command("git", "rev-parse", "HEAD", options.path) cmd.Dir = options.path cmd.Stdout = &revB - require.NoError(t, cmd.Run()) + assert.NoError(t, cmd.Run()) revision = strings.Split(revB.String(), "\n")[0] } return revision @@ -2998,7 +2850,7 @@ func TestInit(t *testing.T) { // service.Init sets permission to 0300. Restore permissions when the test // finishes so dir can be removed properly. t.Cleanup(func() { - require.NoError(t, os.Chmod(dir, 0o777)) + require.NoError(t, os.Chmod(dir, 0777)) }) repoPath := path.Join(dir, "repo1") @@ -3037,42 +2889,17 @@ func TestCheckoutRevisionCanGetNonstandardRefs(t *testing.T) { destRepoPath, err := os.MkdirTemp(rootPath, "") require.NoError(t, err) - gitClient, err := git.NewClientExt("file://"+sourceRepoPath, destRepoPath, &git.NopCreds{}, true, false, "", "") + gitClient, err := git.NewClientExt("file://"+sourceRepoPath, destRepoPath, &git.NopCreds{}, true, false, "") require.NoError(t, err) pullSha, err := gitClient.LsRemote("refs/pull/123/head") require.NoError(t, err) err = checkoutRevision(gitClient, "does-not-exist", false) - require.Error(t, err) + assert.Error(t, err) err = checkoutRevision(gitClient, pullSha, false) - require.NoError(t, err) -} - -func TestCheckoutRevisionPresentSkipFetch(t *testing.T) { - revision := "0123456789012345678901234567890123456789" - - gitClient := &gitmocks.Client{} - gitClient.On("Init").Return(nil) - gitClient.On("IsRevisionPresent", revision).Return(true) - gitClient.On("Checkout", revision, mock.Anything).Return(nil) - - err := checkoutRevision(gitClient, revision, false) - require.NoError(t, err) -} - -func TestCheckoutRevisionNotPresentCallFetch(t *testing.T) { - revision := "0123456789012345678901234567890123456789" - - gitClient := &gitmocks.Client{} - gitClient.On("Init").Return(nil) - gitClient.On("IsRevisionPresent", revision).Return(false) - gitClient.On("Fetch", "").Return(nil) - gitClient.On("Checkout", revision, mock.Anything).Return(nil) - - err := checkoutRevision(gitClient, revision, false) - require.NoError(t, err) + assert.NoError(t, err) } // runGit runs a git command in the given working directory. If the command succeeds, it returns the combined standard @@ -3091,14 +2918,14 @@ func Test_walkHelmValueFilesInPath(t *testing.T) { var files []string root := "/obviously/does/not/exist" err := filepath.Walk(root, walkHelmValueFilesInPath(root, &files)) - require.Error(t, err) + assert.Error(t, err) assert.Empty(t, files) }) t.Run("values files", func(t *testing.T) { var files []string root := "./testdata/values-files" err := filepath.Walk(root, walkHelmValueFilesInPath(root, &files)) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, files, 5) }) t.Run("unrelated root", func(t *testing.T) { @@ -3106,12 +2933,12 @@ func Test_walkHelmValueFilesInPath(t *testing.T) { root := "./testdata/values-files" unrelated_root := "/different/root/path" err := filepath.Walk(root, walkHelmValueFilesInPath(unrelated_root, &files)) - require.Error(t, err) + assert.Error(t, err) }) } func Test_populateHelmAppDetails(t *testing.T) { - emptyTempPaths := io.NewRandomizedTempPaths(t.TempDir()) + var emptyTempPaths = io.NewRandomizedTempPaths(t.TempDir()) res := apiclient.RepoAppDetailsResponse{} q := apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -3128,7 +2955,7 @@ func Test_populateHelmAppDetails(t *testing.T) { } func Test_populateHelmAppDetails_values_symlinks(t *testing.T) { - emptyTempPaths := io.NewRandomizedTempPaths(t.TempDir()) + var emptyTempPaths = io.NewRandomizedTempPaths(t.TempDir()) t.Run("inbound", func(t *testing.T) { res := apiclient.RepoAppDetailsResponse{} q := apiclient.RepoServerAppDetailsQuery{Repo: &argoappv1.Repository{}, Source: &argoappv1.ApplicationSource{}} @@ -3148,32 +2975,19 @@ func Test_populateHelmAppDetails_values_symlinks(t *testing.T) { }) } -func TestGetHelmRepos_OCIDependenciesWithHelmRepo(t *testing.T) { +func TestGetHelmRepos_OCIDependencies(t *testing.T) { src := argoappv1.ApplicationSource{Path: "."} - q := apiclient.ManifestRequest{Repos: []*argoappv1.Repository{}, ApplicationSource: &src, HelmRepoCreds: []*argoappv1.RepoCreds{ + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, HelmRepoCreds: []*argoappv1.RepoCreds{ {URL: "example.com", Username: "test", Password: "test", EnableOCI: true}, }} helmRepos, err := getHelmRepos("./testdata/oci-dependencies", q.Repos, q.HelmRepoCreds) - require.NoError(t, err) + assert.Nil(t, err) - assert.Len(t, helmRepos, 1) - assert.Equal(t, "test", helmRepos[0].Username) - assert.True(t, helmRepos[0].EnableOci) - assert.Equal(t, "example.com/myrepo", helmRepos[0].Repo) -} - -func TestGetHelmRepos_OCIDependenciesWithRepo(t *testing.T) { - src := argoappv1.ApplicationSource{Path: "."} - q := apiclient.ManifestRequest{Repos: []*argoappv1.Repository{{Repo: "example.com", Username: "test", Password: "test", EnableOCI: true}}, ApplicationSource: &src, HelmRepoCreds: []*argoappv1.RepoCreds{}} - - helmRepos, err := getHelmRepos("./testdata/oci-dependencies", q.Repos, q.HelmRepoCreds) - require.NoError(t, err) - - assert.Len(t, helmRepos, 1) - assert.Equal(t, "test", helmRepos[0].Username) - assert.True(t, helmRepos[0].EnableOci) - assert.Equal(t, "example.com/myrepo", helmRepos[0].Repo) + 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) { @@ -3185,11 +2999,11 @@ func TestGetHelmRepo_NamedRepos(t *testing.T) { }}} helmRepos, err := getHelmRepos("./testdata/helm-with-dependencies", q.Repos, q.HelmRepoCreds) - require.NoError(t, err) + assert.Nil(t, err) - assert.Len(t, helmRepos, 1) - assert.Equal(t, "test", helmRepos[0].Username) - assert.Equal(t, "https://example.com", helmRepos[0].Repo) + 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) { @@ -3201,19 +3015,17 @@ func TestGetHelmRepo_NamedReposAlias(t *testing.T) { }}} helmRepos, err := getHelmRepos("./testdata/helm-with-dependencies-alias", q.Repos, q.HelmRepoCreds) - require.NoError(t, err) + assert.Nil(t, err) - assert.Len(t, helmRepos, 1) - assert.Equal(t, "test-alias", helmRepos[0].Username) - assert.Equal(t, "https://example.com", helmRepos[0].Repo) + 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) { tempDir := t.TempDir() paths := io.NewRandomizedTempPaths(tempDir) - - key, _ := json.Marshal(map[string]string{"url": git.NormalizeGitURL("https://github.com/org/repo1"), "project": ""}) - paths.Add(string(key), path.Join(tempDir, "repo1")) + paths.Add(git.NormalizeGitURL("https://github.com/org/repo1"), path.Join(tempDir, "repo1")) testCases := []struct { name string @@ -3355,21 +3167,17 @@ func Test_getResolvedValueFiles(t *testing.T) { t.Parallel() resolvedPaths, err := getResolvedValueFiles(path.Join(tempDir, "main-repo"), path.Join(tempDir, "main-repo"), tcc.env, []string{}, []string{tcc.rawPath}, tcc.refSources, paths, false) if !tcc.expectedErr { - require.NoError(t, err) + assert.NoError(t, err) require.Len(t, resolvedPaths, 1) assert.Equal(t, tcc.expectedPath, string(resolvedPaths[0])) } else { - require.Error(t, err) + assert.Error(t, err) assert.Empty(t, resolvedPaths) } }) } } - func TestErrorGetGitDirectories(t *testing.T) { - // test not using the cache - root := "./testdata/git-files-dirs" - type fields struct { service *Service } @@ -3396,25 +3204,6 @@ func TestErrorGetGitDirectories(t *testing.T) { 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")) - gitClient.On("Root").Return(root) - 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}, - {name: "ErrorVerifyCommit", 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")) - gitClient.On("VerifyCommitSignature", mock.Anything).Return("", fmt.Errorf("revision %s is not signed", "sadfsadf")) - gitClient.On("Root").Return(root) paths.On("GetPath", mock.Anything).Return(".", nil) paths.On("GetPathIfExists", mock.Anything).Return(".", nil) }, ".") @@ -3425,7 +3214,6 @@ func TestErrorGetGitDirectories(t *testing.T) { Repo: &argoappv1.Repository{Repo: "not-a-valid-url"}, SubmoduleEnabled: false, Revision: "sadfsadf", - VerifyCommit: true, }, }, want: nil, wantErr: assert.Error}, } @@ -3446,7 +3234,6 @@ func TestGetGitDirectories(t *testing.T) { 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("IsRevisionPresent", mock.Anything).Return(false) gitClient.On("Fetch", mock.Anything).Return(nil) gitClient.On("Checkout", mock.Anything, mock.Anything).Once().Return(nil) gitClient.On("LsRemote", "HEAD").Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil) @@ -3460,13 +3247,13 @@ func TestGetGitDirectories(t *testing.T) { Revision: "HEAD", } directories, err := s.GetGitDirectories(context.TODO(), dirRequest) - require.NoError(t, err) + 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) - require.NoError(t, err) + 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, @@ -3474,44 +3261,7 @@ func TestGetGitDirectories(t *testing.T) { }) } -func TestGetGitDirectoriesWithHiddenDirSupported(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("IsRevisionPresent", mock.Anything).Return(false) - 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) - s.initConstants.IncludeHiddenDirectories = true - dirRequest := &apiclient.GitDirectoriesRequest{ - Repo: &argoappv1.Repository{Repo: "a-url.com"}, - SubmoduleEnabled: false, - Revision: "HEAD", - } - directories, err := s.GetGitDirectories(context.TODO(), dirRequest) - require.NoError(t, err) - assert.ElementsMatch(t, directories.GetPaths(), []string{"app", "app/bar", "app/foo/bar", "somedir", "app/foo", "app/bar/.hidden"}) - - // 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) - require.NoError(t, err) - assert.ElementsMatch(t, []string{"app", "app/bar", "app/foo/bar", "somedir", "app/foo", "app/bar/.hidden"}, directories.GetPaths()) - cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ - ExternalSets: 1, - ExternalGets: 2, - }) -} - func TestErrorGetGitFiles(t *testing.T) { - // test not using the cache - root := "" - type fields struct { service *Service } @@ -3538,7 +3288,6 @@ func TestErrorGetGitFiles(t *testing.T) { 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")) - gitClient.On("Root").Return(root) paths.On("GetPath", mock.Anything).Return(".", nil) paths.On("GetPathIfExists", mock.Anything).Return(".", nil) }, ".") @@ -3566,14 +3315,11 @@ func TestErrorGetGitFiles(t *testing.T) { 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", - } + 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("IsRevisionPresent", mock.Anything).Return(false) gitClient.On("Fetch", mock.Anything).Return(nil) gitClient.On("Checkout", mock.Anything, mock.Anything).Once().Return(nil) gitClient.On("LsRemote", "HEAD").Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil) @@ -3592,18 +3338,18 @@ func TestGetGitFiles(t *testing.T) { expected := make(map[string][]byte) for _, filePath := range files { fileContents, err := os.ReadFile(filePath) - require.NoError(t, err) + assert.Nil(t, err) expected[filePath] = fileContents } fileResponse, err := s.GetGitFiles(context.TODO(), filesRequest) - require.NoError(t, err) - assert.Equal(t, expected, fileResponse.GetMap()) + 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) - require.NoError(t, err) + assert.Nil(t, err) assert.Equal(t, expected, fileResponse.GetMap()) cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ ExternalSets: 1, @@ -3611,277 +3357,6 @@ func TestGetGitFiles(t *testing.T) { }) } -func TestErrorUpdateRevisionForPaths(t *testing.T) { - // test not using the cache - root := "" - - type fields struct { - service *Service - } - type args struct { - ctx context.Context - request *apiclient.UpdateRevisionForPathsRequest - } - tests := []struct { - name string - fields fields - args args - want *apiclient.UpdateRevisionForPathsResponse - wantErr assert.ErrorAssertionFunc - }{ - {name: "InvalidRepo", fields: fields{service: newService(t, ".")}, args: args{ - ctx: context.TODO(), - request: &apiclient.UpdateRevisionForPathsRequest{ - Repo: nil, - Revision: "HEAD", - SyncedRevision: "sadfsadf", - }, - }, 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")) - gitClient.On("Root").Return(root) - paths.On("GetPath", mock.Anything).Return(".", nil) - paths.On("GetPathIfExists", mock.Anything).Return(".", nil) - }, ".") - return s - }()}, args: args{ - ctx: context.TODO(), - request: &apiclient.UpdateRevisionForPathsRequest{ - Repo: &argoappv1.Repository{Repo: "not-a-valid-url"}, - Revision: "sadfsadf", - SyncedRevision: "HEAD", - Paths: []string{"."}, - }, - }, want: nil, wantErr: assert.Error}, - {name: "InvalidResolveSyncedRevision", 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", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil) - gitClient.On("LsRemote", mock.Anything).Return("", fmt.Errorf("ah error")) - gitClient.On("Root").Return(root) - paths.On("GetPath", mock.Anything).Return(".", nil) - paths.On("GetPathIfExists", mock.Anything).Return(".", nil) - }, ".") - return s - }()}, args: args{ - ctx: context.TODO(), - request: &apiclient.UpdateRevisionForPathsRequest{ - Repo: &argoappv1.Repository{Repo: "not-a-valid-url"}, - Revision: "HEAD", - SyncedRevision: "sadfsadf", - Paths: []string{"."}, - }, - }, want: nil, wantErr: assert.Error}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := tt.fields.service - got, err := s.UpdateRevisionForPaths(tt.args.ctx, tt.args.request) - if !tt.wantErr(t, err, fmt.Sprintf("UpdateRevisionForPaths(%v, %v)", tt.args.ctx, tt.args.request)) { - return - } - assert.Equalf(t, tt.want, got, "UpdateRevisionForPaths(%v, %v)", tt.args.ctx, tt.args.request) - }) - } -} - -func TestUpdateRevisionForPaths(t *testing.T) { - type fields struct { - service *Service - cache *repoCacheMocks - } - type args struct { - ctx context.Context - request *apiclient.UpdateRevisionForPathsRequest - } - type cacheHit struct { - revision string - previousRevision string - } - tests := []struct { - name string - fields fields - args args - want *apiclient.UpdateRevisionForPathsResponse - wantErr assert.ErrorAssertionFunc - cacheHit *cacheHit - }{ - {name: "NoPathAbort", fields: func() fields { - s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { - gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) - }, ".") - return fields{ - service: s, - cache: c, - } - }(), args: args{ - ctx: context.TODO(), - request: &apiclient.UpdateRevisionForPathsRequest{ - Repo: &argoappv1.Repository{Repo: "a-url.com"}, - Paths: []string{}, - }, - }, want: &apiclient.UpdateRevisionForPathsResponse{}, wantErr: assert.NoError}, - {name: "SameResolvedRevisionAbort", fields: func() fields { - s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { - gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) - gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil) - gitClient.On("LsRemote", "SYNCEDHEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil) - paths.On("GetPath", mock.Anything).Return(".", nil) - paths.On("GetPathIfExists", mock.Anything).Return(".", nil) - }, ".") - return fields{ - service: s, - cache: c, - } - }(), args: args{ - ctx: context.TODO(), - request: &apiclient.UpdateRevisionForPathsRequest{ - Repo: &argoappv1.Repository{Repo: "a-url.com"}, - Revision: "HEAD", - SyncedRevision: "SYNCEDHEAD", - Paths: []string{"."}, - }, - }, want: &apiclient.UpdateRevisionForPathsResponse{ - Revision: "632039659e542ed7de0c170a4fcc1c571b288fc0", - }, wantErr: assert.NoError}, - {name: "ChangedFilesDoNothing", fields: func() fields { - s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { - gitClient.On("Init").Return(nil) - gitClient.On("IsRevisionPresent", mock.Anything).Return(false) - gitClient.On("Fetch", mock.Anything).Return(nil) - gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) - gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil) - gitClient.On("LsRemote", "SYNCEDHEAD").Once().Return("1e67a504d03def3a6a1125d934cb511680f72555", nil) - paths.On("GetPath", mock.Anything).Return(".", nil) - paths.On("GetPathIfExists", mock.Anything).Return(".", nil) - gitClient.On("Root").Return("") - gitClient.On("ChangedFiles", mock.Anything, mock.Anything).Return([]string{"app.yaml"}, nil) - }, ".") - return fields{ - service: s, - cache: c, - } - }(), args: args{ - ctx: context.TODO(), - request: &apiclient.UpdateRevisionForPathsRequest{ - Repo: &argoappv1.Repository{Repo: "a-url.com"}, - Revision: "HEAD", - SyncedRevision: "SYNCEDHEAD", - Paths: []string{"."}, - }, - }, want: &apiclient.UpdateRevisionForPathsResponse{ - Revision: "632039659e542ed7de0c170a4fcc1c571b288fc0", - Changes: true, - }, wantErr: assert.NoError}, - {name: "NoChangesUpdateCache", fields: func() fields { - s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { - gitClient.On("Init").Return(nil) - gitClient.On("IsRevisionPresent", mock.Anything).Return(false) - gitClient.On("Fetch", mock.Anything).Return(nil) - gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) - gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil) - gitClient.On("LsRemote", "SYNCEDHEAD").Once().Return("1e67a504d03def3a6a1125d934cb511680f72555", nil) - paths.On("GetPath", mock.Anything).Return(".", nil) - paths.On("GetPathIfExists", mock.Anything).Return(".", nil) - gitClient.On("Root").Return("") - gitClient.On("ChangedFiles", mock.Anything, mock.Anything).Return([]string{}, nil) - }, ".") - return fields{ - service: s, - cache: c, - } - }(), args: args{ - ctx: context.TODO(), - request: &apiclient.UpdateRevisionForPathsRequest{ - Repo: &argoappv1.Repository{Repo: "a-url.com"}, - Revision: "HEAD", - SyncedRevision: "SYNCEDHEAD", - Paths: []string{"."}, - - AppLabelKey: "app.kubernetes.io/name", - AppName: "no-change-update-cache", - Namespace: "default", - TrackingMethod: "annotation+label", - ApplicationSource: &argoappv1.ApplicationSource{Path: "."}, - KubeVersion: "v1.16.0", - }, - }, want: &apiclient.UpdateRevisionForPathsResponse{ - Revision: "632039659e542ed7de0c170a4fcc1c571b288fc0", - }, wantErr: assert.NoError, cacheHit: &cacheHit{ - previousRevision: "1e67a504d03def3a6a1125d934cb511680f72555", - revision: "632039659e542ed7de0c170a4fcc1c571b288fc0", - }}, - {name: "NoChangesHelmMultiSourceUpdateCache", fields: func() fields { - s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { - gitClient.On("Init").Return(nil) - gitClient.On("IsRevisionPresent", mock.Anything).Return(false) - gitClient.On("Fetch", mock.Anything).Return(nil) - gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) - gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil) - gitClient.On("LsRemote", "SYNCEDHEAD").Once().Return("1e67a504d03def3a6a1125d934cb511680f72555", nil) - paths.On("GetPath", mock.Anything).Return(".", nil) - paths.On("GetPathIfExists", mock.Anything).Return(".", nil) - gitClient.On("Root").Return("") - gitClient.On("ChangedFiles", mock.Anything, mock.Anything).Return([]string{}, nil) - }, ".") - return fields{ - service: s, - cache: c, - } - }(), args: args{ - ctx: context.TODO(), - request: &apiclient.UpdateRevisionForPathsRequest{ - Repo: &argoappv1.Repository{Repo: "a-url.com"}, - Revision: "HEAD", - SyncedRevision: "SYNCEDHEAD", - Paths: []string{"."}, - - AppLabelKey: "app.kubernetes.io/name", - AppName: "no-change-update-cache", - Namespace: "default", - TrackingMethod: "annotation+label", - ApplicationSource: &argoappv1.ApplicationSource{Path: ".", Helm: &argoappv1.ApplicationSourceHelm{ReleaseName: "test"}}, - KubeVersion: "v1.16.0", - - HasMultipleSources: true, - }, - }, want: &apiclient.UpdateRevisionForPathsResponse{ - Revision: "632039659e542ed7de0c170a4fcc1c571b288fc0", - }, wantErr: assert.NoError, cacheHit: &cacheHit{ - previousRevision: "1e67a504d03def3a6a1125d934cb511680f72555", - revision: "632039659e542ed7de0c170a4fcc1c571b288fc0", - }}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := tt.fields.service - cache := tt.fields.cache - - if tt.cacheHit != nil { - cache.mockCache.On("Rename", tt.cacheHit.previousRevision, tt.cacheHit.revision, mock.Anything).Return(nil) - } - - got, err := s.UpdateRevisionForPaths(tt.args.ctx, tt.args.request) - if !tt.wantErr(t, err, fmt.Sprintf("UpdateRevisionForPaths(%v, %v)", tt.args.ctx, tt.args.request)) { - return - } - assert.Equalf(t, tt.want, got, "UpdateRevisionForPaths(%v, %v)", tt.args.ctx, tt.args.request) - - if tt.cacheHit != nil { - cache.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ - ExternalRenames: 1, - }) - } else { - cache.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ - ExternalRenames: 0, - }) - } - }) - } -} - func Test_getRepoSanitizerRegex(t *testing.T) { r := getRepoSanitizerRegex("/tmp/_argocd-repo") msg := r.ReplaceAllString("error message containing /tmp/_argocd-repo/SENSITIVE and other stuff", "") @@ -3890,148 +3365,6 @@ func Test_getRepoSanitizerRegex(t *testing.T) { 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() - require.NoError(t, err) - assert.NotNil(t, refs) - assert.NotEmpty(t, 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() - require.NoError(t, err) - assert.NotNil(t, refs) - assert.NotEmpty(t, 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() - require.NoError(t, err) - assert.NotNil(t, refs) - assert.NotEmpty(t, 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() - require.NoError(t, err) - assert.NotNil(t, refs) - assert.NotEmpty(t, 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) - require.Error(t, err, "Should be a cache miss") - assert.Empty(t, 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() - require.NoError(t, err) - assert.NotNil(t, refs) -} - func TestGetRevisionChartDetails(t *testing.T) { t.Run("Test revision semvar", func(t *testing.T) { root := t.TempDir() @@ -4057,7 +3390,7 @@ func TestGetRevisionChartDetails(t *testing.T) { Home: "test-home", Maintainers: []string{"test-maintainer"}, }) - require.NoError(t, err) + assert.NoError(t, err) chartDetails, err := service.GetRevisionChartDetails(context.Background(), &apiclient.RepoServerRevisionChartDetailsRequest{ Repo: &v1alpha1.Repository{ Repo: fmt.Sprintf("file://%s", root), @@ -4067,246 +3400,9 @@ func TestGetRevisionChartDetails(t *testing.T) { Name: "my-chart", Revision: "1.1.0", }) - require.NoError(t, err) + 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) }) } - -func TestVerifyCommitSignature(t *testing.T) { - repo := &v1alpha1.Repository{ - Repo: "https://github.com/example/repo.git", - } - - t.Run("VerifyCommitSignature with valid signature", func(t *testing.T) { - t.Setenv("ARGOCD_GPG_ENABLED", "true") - mockGitClient := &gitmocks.Client{} - mockGitClient.On("VerifyCommitSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(testSignature, nil) - - err := verifyCommitSignature(true, mockGitClient, "abcd1234", repo) - require.NoError(t, err) - }) - - t.Run("VerifyCommitSignature with invalid signature", func(t *testing.T) { - t.Setenv("ARGOCD_GPG_ENABLED", "true") - mockGitClient := &gitmocks.Client{} - mockGitClient.On("VerifyCommitSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return("", nil) - - err := verifyCommitSignature(true, mockGitClient, "abcd1234", repo) - require.Error(t, err) - assert.Equal(t, "revision abcd1234 is not signed", err.Error()) - }) - - t.Run("VerifyCommitSignature with unknown signature", func(t *testing.T) { - t.Setenv("ARGOCD_GPG_ENABLED", "true") - mockGitClient := &gitmocks.Client{} - mockGitClient.On("VerifyCommitSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return("", fmt.Errorf("UNKNOWN signature: gpg: Unknown signature from ABCDEFGH")) - - err := verifyCommitSignature(true, mockGitClient, "abcd1234", repo) - require.Error(t, err) - assert.Equal(t, "UNKNOWN signature: gpg: Unknown signature from ABCDEFGH", err.Error()) - }) - - t.Run("VerifyCommitSignature with error verifying signature", func(t *testing.T) { - t.Setenv("ARGOCD_GPG_ENABLED", "true") - mockGitClient := &gitmocks.Client{} - mockGitClient.On("VerifyCommitSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return("", fmt.Errorf("error verifying signature of commit 'abcd1234' in repo 'https://github.com/example/repo.git': failed to verify signature")) - - err := verifyCommitSignature(true, mockGitClient, "abcd1234", repo) - require.Error(t, err) - assert.Equal(t, "error verifying signature of commit 'abcd1234' in repo 'https://github.com/example/repo.git': failed to verify signature", err.Error()) - }) - - t.Run("VerifyCommitSignature with signature verification disabled", func(t *testing.T) { - t.Setenv("ARGOCD_GPG_ENABLED", "false") - mockGitClient := &gitmocks.Client{} - err := verifyCommitSignature(false, mockGitClient, "abcd1234", repo) - require.NoError(t, err) - }) -} - -func Test_GenerateManifests_Commands(t *testing.T) { - t.Run("helm", func(t *testing.T) { - service := newService(t, "testdata/my-chart") - - // Fill the manifest request with as many parameters affecting Helm commands as possible. - q := apiclient.ManifestRequest{ - AppName: "test-app", - Namespace: "test-namespace", - KubeVersion: "1.2.3", - ApiVersions: []string{"v1/Test", "v2/Test"}, - Repo: &argoappv1.Repository{}, - ApplicationSource: &argoappv1.ApplicationSource{ - Path: ".", - Helm: &argoappv1.ApplicationSourceHelm{ - FileParameters: []argoappv1.HelmFileParameter{ - { - Name: "test-file-param-name", - Path: "test-file-param.yaml", - }, - }, - Parameters: []argoappv1.HelmParameter{ - { - Name: "test-param-name", - // Use build env var to test substitution. - Value: "test-value-$ARGOCD_APP_NAME", - ForceString: true, - }, - { - Name: "test-param-bool-name", - // Use build env var to test substitution. - Value: "false", - }, - }, - PassCredentials: true, - SkipCrds: true, - ValueFiles: []string{ - "my-chart-values.yaml", - }, - Values: "test: values", - }, - }, - ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } - - res, err := service.GenerateManifest(context.Background(), &q) - - require.NoError(t, err) - assert.Equal(t, []string{"helm template . --name-template test-app --namespace test-namespace --kube-version 1.2.3 --set test-param-bool-name=false --set-string test-param-name=test-value-test-app --set-file test-file-param-name=./test-file-param.yaml --values ./my-chart-values.yaml --values --api-versions v1/Test --api-versions v2/Test"}, res.Commands) - - t.Run("with overrides", func(t *testing.T) { - // These can be set explicitly instead of using inferred values. Make sure the overrides apply. - q.ApplicationSource.Helm.APIVersions = []string{"v3", "v4"} - q.ApplicationSource.Helm.KubeVersion = "5.6.7" - q.ApplicationSource.Helm.Namespace = "different-namespace" - q.ApplicationSource.Helm.ReleaseName = "different-release-name" - - res, err = service.GenerateManifest(context.Background(), &q) - - require.NoError(t, err) - assert.Equal(t, []string{"helm template . --name-template different-release-name --namespace different-namespace --kube-version 5.6.7 --set test-param-bool-name=false --set-string test-param-name=test-value-test-app --set-file test-file-param-name=./test-file-param.yaml --values ./my-chart-values.yaml --values --api-versions v3 --api-versions v4"}, res.Commands) - }) - }) - - t.Run("helm with dependencies", func(t *testing.T) { - // This test makes sure we still get commands, even if we hit the code path that has to run "helm dependency build." - // We don't actually return the "helm dependency build" command, because we expect that the user is able to read - // the "helm template" and figure out how to fix it. - t.Cleanup(func() { - err := os.Remove("testdata/helm-with-local-dependency/Chart.lock") - require.NoError(t, err) - err = os.RemoveAll("testdata/helm-with-local-dependency/charts") - require.NoError(t, err) - }) - - service := newService(t, "testdata/helm-with-local-dependency") - - q := apiclient.ManifestRequest{ - AppName: "test-app", - Namespace: "test-namespace", - Repo: &argoappv1.Repository{}, - ApplicationSource: &argoappv1.ApplicationSource{ - Path: ".", - }, - ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } - - res, err := service.GenerateManifest(context.Background(), &q) - - require.NoError(t, err) - assert.Equal(t, []string{"helm template . --name-template test-app --namespace test-namespace --include-crds"}, res.Commands) - }) - - t.Run("kustomize", func(t *testing.T) { - // Write test files to a temp dir, because the test mutates kustomization.yaml in place. - tempDir := t.TempDir() - err := os.WriteFile(path.Join(tempDir, "kustomization.yaml"), []byte(` -resources: -- guestbook.yaml -`), os.FileMode(0o600)) - require.NoError(t, err) - err = os.WriteFile(path.Join(tempDir, "guestbook.yaml"), []byte(` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: guestbook-ui -`), os.FileMode(0o400)) - require.NoError(t, err) - err = os.Mkdir(path.Join(tempDir, "component"), os.FileMode(0o700)) - require.NoError(t, err) - err = os.WriteFile(path.Join(tempDir, "component", "kustomization.yaml"), []byte(` -apiVersion: kustomize.config.k8s.io/v1alpha1 -kind: Component -images: -- name: old - newName: new -`), os.FileMode(0o400)) - require.NoError(t, err) - - service := newService(t, tempDir) - - // Fill the manifest request with as many parameters affecting Helm commands as possible. - q := apiclient.ManifestRequest{ - AppName: "test-app", - Namespace: "test-namespace", - KubeVersion: "1.2.3", - ApiVersions: []string{"v1/Test", "v2/Test"}, - Repo: &argoappv1.Repository{}, - ApplicationSource: &argoappv1.ApplicationSource{ - Path: ".", - Kustomize: &argoappv1.ApplicationSourceKustomize{ - APIVersions: []string{"v1", "v2"}, - CommonAnnotations: map[string]string{ - // Use build env var to test substitution. - "test": "annotation-$ARGOCD_APP_NAME", - }, - CommonAnnotationsEnvsubst: true, - CommonLabels: map[string]string{ - "test": "label", - }, - Components: []string{"component"}, - ForceCommonAnnotations: true, - ForceCommonLabels: true, - Images: argoappv1.KustomizeImages{ - "image=override", - }, - KubeVersion: "5.6.7", - LabelWithoutSelector: true, - NamePrefix: "test-prefix", - NameSuffix: "test-suffix", - Namespace: "override-namespace", - Replicas: argoappv1.KustomizeReplicas{ - { - Name: "guestbook-ui", - Count: intstr.Parse("1337"), - }, - }, - }, - }, - ProjectName: "something", - ProjectSourceRepos: []string{"*"}, - } - - res, err := service.GenerateManifest(context.Background(), &q) - - require.NoError(t, err) - assert.Equal(t, []string{ - "kustomize edit set nameprefix -- test-prefix", - "kustomize edit set namesuffix -- test-suffix", - "kustomize edit set image image=override", - "kustomize edit set replicas guestbook-ui=1337", - "kustomize edit add label --force --without-selector test:label", - "kustomize edit add annotation --force test:annotation-test-app", - "kustomize edit set namespace -- override-namespace", - "kustomize edit add component component", - "kustomize build .", - }, res.Commands) - }) -} diff --git a/reposerver/repository/testdata/helm-with-local-dependency/Chart.yaml b/reposerver/repository/testdata/helm-with-local-dependency/Chart.yaml deleted file mode 100644 index 5daf5d1d091c9..0000000000000 --- a/reposerver/repository/testdata/helm-with-local-dependency/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v2 -name: helm-with-dependencies -version: v1.0.0 -dependencies: - - name: simple-chart - repository: file://../simple-chart - version: v1.1.0 diff --git a/reposerver/repository/testdata/my-chart/test-file-param.yaml b/reposerver/repository/testdata/my-chart/test-file-param.yaml deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/reposerver/repository/testdata/simple-chart/Chart.yaml b/reposerver/repository/testdata/simple-chart/Chart.yaml deleted file mode 100644 index 00bfbfaf78f3e..0000000000000 --- a/reposerver/repository/testdata/simple-chart/Chart.yaml +++ /dev/null @@ -1,2 +0,0 @@ -name: simple-chart -version: 1.1.0 diff --git a/reposerver/repository/testdata/simple-chart/simple-chart-values.yaml b/reposerver/repository/testdata/simple-chart/simple-chart-values.yaml deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/reposerver/repository/testdata/simple-chart/templates/my-map.yaml b/reposerver/repository/testdata/simple-chart/templates/my-map.yaml deleted file mode 100644 index efbeb3b7b9393..0000000000000 --- a/reposerver/repository/testdata/simple-chart/templates/my-map.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: my-map \ No newline at end of file diff --git a/reposerver/repository/utils.go b/reposerver/repository/utils.go deleted file mode 100644 index d77bef728d92a..0000000000000 --- a/reposerver/repository/utils.go +++ /dev/null @@ -1,85 +0,0 @@ -package repository - -import ( - "path/filepath" - "strings" - - securejoin "github.com/cyphar/filepath-securejoin" - log "github.com/sirupsen/logrus" - - "github.com/argoproj/argo-cd/v2/reposerver/apiclient" - "github.com/argoproj/argo-cd/v2/util/io/files" -) - -// getApplicationRootPath returns the common root path (shortest shared structure between all paths) among a -// set of application-related paths for manifest generation. AppPath is the lower possible value -func getApplicationRootPath(q *apiclient.ManifestRequest, appPath, repoPath string) string { - paths := getPaths(q, appPath, repoPath) - - if len(paths) == 0 { - // backward compatibility, by default the root path is the repoPath - return repoPath - } - - // the app path must be the lower possible value - commonParts := strings.Split(appPath, string(filepath.Separator)) - - var disjoint bool - for _, path := range paths { - parts := strings.Split(path, string(filepath.Separator)) - // find the minimum length between the current common parts and the current path - minLen := func(a, b int) int { - if a < b { - return a - } - return b - }(len(commonParts), len(parts)) - - // check if diverge /disjoint in some point - for i := 0; i < minLen; i++ { - if commonParts[i] != parts[i] { - commonParts = commonParts[:i] - disjoint = true - break - } - } - - // for non-disjoint paths - if !disjoint && minLen < len(commonParts) { - commonParts = commonParts[:minLen] - } - } - return string(filepath.Separator) + filepath.Join(commonParts...) -} - -// getPaths retrieves all absolute paths associated with the generation of application manifests. -func getPaths(q *apiclient.ManifestRequest, appPath, repoPath string) []string { - var paths []string - for _, annotationPath := range strings.Split(q.AnnotationManifestGeneratePaths, ";") { - if annotationPath == "" { - continue - } - var err error - var path, unsafePath string - - if filepath.IsAbs(annotationPath) { - unsafePath = filepath.Clean(annotationPath) - } else { - appRelPath, err := files.RelativePath(appPath, repoPath) - if err != nil { - log.Errorf("error building app relative path: %v", err) - continue - } - unsafePath = filepath.Clean(filepath.Join(appRelPath, annotationPath)) - } - - path, err = securejoin.SecureJoin(repoPath, unsafePath) - if err != nil { - log.Errorf("error joining repoPath %q and absolute unsafePath %q: %v", repoPath, unsafePath, err) - continue - } - - paths = append(paths, path) - } - return paths -} diff --git a/reposerver/repository/utils_test.go b/reposerver/repository/utils_test.go deleted file mode 100644 index 3eb2428f09c03..0000000000000 --- a/reposerver/repository/utils_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package repository - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/argoproj/argo-cd/v2/reposerver/apiclient" -) - -func TestGetCommonRootPath(t *testing.T) { - t.Parallel() - - repoRoot := "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731" - - tests := []struct { - name string - annotation string - appPath string - expectedRootPath string - }{ - {"app path", ".", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/helloworld", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/helloworld"}, - {"app path and relative", "../../overlays;.", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/helloworld", repoRoot}, - {"app path and absolute path", "/services;.", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/helloworld", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services"}, - {"several relative paths", "../../;..;.", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/team/helloworld", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services"}, - // backward compatibility test - {"no annotation", "", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/helloworld", repoRoot}, - // appPath should be the lower calculated root path - {"relative subdir", "./manifests", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/team/helloworld", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/team/helloworld"}, - // glob pattern - {"glob", "/services/shared/*-secret.yaml", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/helloworld", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services"}, - {"relative glob", "../*", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/helloworld", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services"}, - {"duplicate slashes", "//services/shared/*-secret.yaml", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services/helloworld", "/tmp/_argocd-repo/7a58c52a-0030-4fd9-8cc5-35b2d8b4e731/services"}, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - req := &apiclient.ManifestRequest{AnnotationManifestGeneratePaths: tt.annotation} - rootPath := getApplicationRootPath(req, tt.appPath, repoRoot) - assert.Equal(t, tt.expectedRootPath, rootPath, "input and output should match") - }) - } -} diff --git a/reposerver/server.go b/reposerver/server.go index 5d280329deed3..1ba98dd6f5538 100644 --- a/reposerver/server.go +++ b/reposerver/server.go @@ -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, fmt.Errorf("failed to initialize the repo service: %w", err) + return nil, err } return &ArgoCDRepoServer{ diff --git a/resource_customizations/apps.kruise.io/DaemonSet/health.lua b/resource_customizations/apps.kruise.io/DaemonSet/health.lua index 30ccdc85da176..7705bcc3325e5 100644 --- a/resource_customizations/apps.kruise.io/DaemonSet/health.lua +++ b/resource_customizations/apps.kruise.io/DaemonSet/health.lua @@ -8,7 +8,7 @@ if obj.status ~= nil then hs.status = "Suspended" hs.message = "Daemonset is paused" return hs - elseif (obj.spec.updateStrategy.rollingUpdate.partition ~= nil) and (obj.spec.updateStrategy.rollingUpdate.partition ~= 0 and obj.metadata.generation > 1) then + 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" diff --git a/resource_customizations/apps.kruise.io/DaemonSet/health_test.yaml b/resource_customizations/apps.kruise.io/DaemonSet/health_test.yaml index a1d2579d2e9f8..0a8c8292672f3 100644 --- a/resource_customizations/apps.kruise.io/DaemonSet/health_test.yaml +++ b/resource_customizations/apps.kruise.io/DaemonSet/health_test.yaml @@ -11,10 +11,6 @@ tests: status: Progressing message: "Waiting for initialization" inputPath: testdata/unknown.yaml - - healthStatus: - status: Progressing - message: "Waiting for initialization" - inputPath: testdata/no-update-strategy-partition.yaml - healthStatus: status: Suspended message: "Daemonset is paused" diff --git a/resource_customizations/apps.kruise.io/DaemonSet/testdata/no-update-strategy-partition.yaml b/resource_customizations/apps.kruise.io/DaemonSet/testdata/no-update-strategy-partition.yaml deleted file mode 100644 index 765378b0c6078..0000000000000 --- a/resource_customizations/apps.kruise.io/DaemonSet/testdata/no-update-strategy-partition.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: apps.kruise.io/v1alpha1 -kind: DaemonSet -metadata: - name: rdma-device-plugin - namespace: nvidia-gpu - generation: 2 -spec: - selector: - matchLabels: - app-name: rdma-device-plugin-pod - template: - metadata: - labels: - app-name: rdma-device-plugin-pod - spec: - containers: - image: 'my-k8s-rdmaplugin' - imagePullPolicy: IfNotPresent - name: k8s-rdma-device-plugin - hostNetwork: true - updateStrategy: - rollingUpdate: - maxSurge: 0 - maxUnavailable: 50 - rollingUpdateType: Standard - type: RollingUpdate -status: - currentNumberScheduled: 0 - daemonSetHash: 5998d4d4d7 - desiredNumberScheduled: 0 - numberMisscheduled: 0 - numberReady: 0 - observedGeneration: 2 - updatedNumberScheduled: 0 diff --git a/resource_customizations/astra.netapp.io/AppVault/health.lua b/resource_customizations/astra.netapp.io/AppVault/health.lua deleted file mode 100644 index 7490ed2a89fd0..0000000000000 --- a/resource_customizations/astra.netapp.io/AppVault/health.lua +++ /dev/null @@ -1,13 +0,0 @@ -hs = { status = "Progressing", message = "No status available" } -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "available" or obj.status.state == "Available" then - hs.status = "Healthy" - hs.message = obj.kind .. " Available" - elseif obj.status.state == "failed" or obj.status.state == "Failed" then - hs.status = "Degraded" - hs.message = obj.kind .. " Failed" - end - end -end -return hs diff --git a/resource_customizations/astra.netapp.io/AppVault/health_test.yaml b/resource_customizations/astra.netapp.io/AppVault/health_test.yaml deleted file mode 100644 index 03918c3ecaa56..0000000000000 --- a/resource_customizations/astra.netapp.io/AppVault/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "No status available" - inputPath: testdata/progressing_nostatus.yaml - - healthStatus: - status: Healthy - message: "AppVault Available" - inputPath: testdata/healthy.yaml - - healthStatus: - status: Degraded - message: "AppVault Failed" - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/astra.netapp.io/AppVault/testdata/degraded.yaml b/resource_customizations/astra.netapp.io/AppVault/testdata/degraded.yaml deleted file mode 100644 index 0ece84574b9b2..0000000000000 --- a/resource_customizations/astra.netapp.io/AppVault/testdata/degraded.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: AppVault -metadata: - creationTimestamp: "2024-04-26T14:25:45Z" - generation: 1 - name: astra-gcp-backup-743cfd150129 - namespace: astra-connector - resourceVersion: "12094908" - uid: 12943b68-323a-4e8a-ba78-604da0801d11 -spec: - providerConfig: - bucketName: astra-gcp-backup-743cfd150129 - providerCredentials: - credentials: - valueFromSecret: - key: credentials.json - name: astra-gcp-backup-734ced050128-5rdt4 - providerType: gcp -status: - error: - 'failed to close GCP object "appVault.json" in bucket "astra-gcp-backup-743cfd150129": - googleapi: Error 404: The specified bucket does not exist., notFound' - state: failed diff --git a/resource_customizations/astra.netapp.io/AppVault/testdata/healthy.yaml b/resource_customizations/astra.netapp.io/AppVault/testdata/healthy.yaml deleted file mode 100644 index 3ea713e8ef74e..0000000000000 --- a/resource_customizations/astra.netapp.io/AppVault/testdata/healthy.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: AppVault -metadata: - creationTimestamp: "2024-04-11T21:28:27Z" - generation: 1 - name: astra-gcp-backup-743cfd150129 - namespace: astra-connector - resourceVersion: "70908" - uid: d1b552b2-5d8e-467b-829b-1e6af7240400 -spec: - providerConfig: - bucketName: astra-gcp-backup-743cfd150129 - providerCredentials: - credentials: - valueFromSecret: - key: credentials.json - name: astra-gcp-backup-743cfd150129-5rdt4 - providerType: gcp -status: - state: available - uid: c708262e-3944-49bf-af96-ad1c3eb6cafb diff --git a/resource_customizations/astra.netapp.io/AppVault/testdata/progressing_nostatus.yaml b/resource_customizations/astra.netapp.io/AppVault/testdata/progressing_nostatus.yaml deleted file mode 100644 index d6987da72c348..0000000000000 --- a/resource_customizations/astra.netapp.io/AppVault/testdata/progressing_nostatus.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: AppVault -metadata: - creationTimestamp: "2024-04-26T14:25:45Z" - generation: 1 - name: astra-gcp-backup-743cfd150129 - namespace: astra-connector - resourceVersion: "12094608" - uid: 12943b68-323a-4e8a-ba78-604da0801d11 -spec: - providerConfig: - bucketName: astra-gcp-backup-743cfd150129 - providerCredentials: - credentials: - valueFromSecret: - key: credentials.json - name: astra-gcp-backup-734ced050128-5rdt4 - providerType: gcp diff --git a/resource_customizations/astra.netapp.io/Application/health.lua b/resource_customizations/astra.netapp.io/Application/health.lua deleted file mode 100644 index 967400d8819b0..0000000000000 --- a/resource_customizations/astra.netapp.io/Application/health.lua +++ /dev/null @@ -1,17 +0,0 @@ -hs = { status = "Progressing", message = "No status available" } -if obj.status ~= nil then - if obj.status.conditions ~= nil then - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" and condition.status == "True" then - hs.status = "Healthy" - hs.message = "Astra Application Ready, protectionState: " .. obj.status.protectionState - return hs - elseif condition.type == "Ready" and condition.status == "False" then - hs.status = "Degraded" - hs.message = "Astra Application Degraded, message: " .. condition.message - return hs - end - end - end -end -return hs diff --git a/resource_customizations/astra.netapp.io/Application/health_test.yaml b/resource_customizations/astra.netapp.io/Application/health_test.yaml deleted file mode 100644 index d1c2bc9b769cb..0000000000000 --- a/resource_customizations/astra.netapp.io/Application/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "No status available" - inputPath: testdata/progressing.yaml - - healthStatus: - status: Healthy - message: "Astra Application Ready, protectionState: protected" - inputPath: testdata/healthy.yaml - - healthStatus: - status: Degraded - message: "Astra Application Degraded, message: namespace wordpress is in terminating state" - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/astra.netapp.io/Application/testdata/degraded.yaml b/resource_customizations/astra.netapp.io/Application/testdata/degraded.yaml deleted file mode 100644 index 9b25186fa9587..0000000000000 --- a/resource_customizations/astra.netapp.io/Application/testdata/degraded.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Application -metadata: - creationTimestamp: "2024-04-15T20:59:56Z" - finalizers: - - astra.netapp.io/finalizer - generation: 2 - name: wordpress - namespace: astra-connector - resourceVersion: "10484469" - uid: 5ab7cd7d-7a9b-4508-9da2-c7dcb10a69b3 -spec: - includedNamespaces: - - labelSelector: {} - namespace: wordpress -status: - conditions: - - lastTransitionTime: "2024-04-24T16:13:26Z" - message: namespace wordpress is in terminating state - reason: Ready - status: "False" - type: Ready - protectionState: partial - protectionStateDetails: - - Active backup schedule missing - - Application unavailable diff --git a/resource_customizations/astra.netapp.io/Application/testdata/healthy.yaml b/resource_customizations/astra.netapp.io/Application/testdata/healthy.yaml deleted file mode 100644 index f42f84b1a60ae..0000000000000 --- a/resource_customizations/astra.netapp.io/Application/testdata/healthy.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Application -metadata: - creationTimestamp: "2024-04-15T20:46:16Z" - finalizers: - - astra.netapp.io/finalizer - generation: 3 - labels: - argocd.argoproj.io/instance: ghost-demo - name: ghost - namespace: astra-connector - resourceVersion: "3235325" - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 -spec: - includedNamespaces: - - namespace: ghost -status: - conditions: - - lastTransitionTime: "2024-04-15T20:46:16Z" - message: "" - reason: Ready - status: "True" - type: Ready - protectionState: protected diff --git a/resource_customizations/astra.netapp.io/Application/testdata/progressing.yaml b/resource_customizations/astra.netapp.io/Application/testdata/progressing.yaml deleted file mode 100644 index 64450c1aebc8a..0000000000000 --- a/resource_customizations/astra.netapp.io/Application/testdata/progressing.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Application -metadata: - creationTimestamp: "2024-04-15T20:46:16Z" - finalizers: - - astra.netapp.io/finalizer - generation: 3 - labels: - argocd.argoproj.io/instance: ghost-demo - name: ghost - namespace: astra-connector - resourceVersion: "3235325" - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 -spec: - includedNamespaces: - - namespace: ghost diff --git a/resource_customizations/astra.netapp.io/Backup/health.lua b/resource_customizations/astra.netapp.io/Backup/health.lua deleted file mode 100644 index 39de4ac74eb68..0000000000000 --- a/resource_customizations/astra.netapp.io/Backup/health.lua +++ /dev/null @@ -1,16 +0,0 @@ -hs = { status = "Progressing", message = "No status available" } -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Completed" then - hs.status = "Healthy" - hs.message = obj.kind .. " Completed" - elseif obj.status.state == "Running" then - hs.status = "Progressing" - hs.message = obj.kind .. " Running" - else - hs.status = "Degraded" - hs.message = obj.status.state - end - end -end -return hs diff --git a/resource_customizations/astra.netapp.io/Backup/health_test.yaml b/resource_customizations/astra.netapp.io/Backup/health_test.yaml deleted file mode 100644 index 56385a102f681..0000000000000 --- a/resource_customizations/astra.netapp.io/Backup/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "No status available" - inputPath: testdata/progressing_nostatus.yaml - - healthStatus: - status: Progressing - message: "Backup Running" - inputPath: testdata/progressing_status.yaml - - healthStatus: - status: Healthy - message: "Backup Completed" - inputPath: testdata/healthy.yaml - - healthStatus: - status: Degraded - message: "Failed" - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/astra.netapp.io/Backup/testdata/degraded.yaml b/resource_customizations/astra.netapp.io/Backup/testdata/degraded.yaml deleted file mode 100644 index 8dbe9ca86c361..0000000000000 --- a/resource_customizations/astra.netapp.io/Backup/testdata/degraded.yaml +++ /dev/null @@ -1,79 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Backup -metadata: - creationTimestamp: "2024-04-24T19:54:18Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - name: backup-20240424193746 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - kind: Application - name: ghost - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 - resourceVersion: "10641332" - uid: ad301b6a-6536-4313-89c1-d10ad0275430 -spec: - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost -status: - conditions: - - lastTransitionTime: "2024-04-24T19:54:18Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppOwnerReferenceCreated - - lastTransitionTime: "2024-04-24T19:54:18Z" - message: Successfully reconciled - reason: Done - status: "True" - type: SourceSnapshotExists - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: - "Source snapshot failed with permanent error: reconcile timeout of 1h0m0s - exceeded" - reason: Failed - status: "False" - type: SourceSnapshotCompleted - - lastTransitionTime: "2024-04-24T19:54:18Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: SnapshotAppArchiveCopied - - lastTransitionTime: "2024-04-24T19:54:18Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: PreBackupExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T19:54:18Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: VolumeBackupsCompleted - - lastTransitionTime: "2024-04-24T19:54:18Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: PostBackupExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T19:54:18Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: TemporarySnapshotCleanedUp - - lastTransitionTime: "2024-04-24T19:54:18Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: Completed - - lastTransitionTime: "2024-04-24T19:54:18Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: OnFailurePostBackupExecHooksRunCompleted - error: - "Source snapshot failed with permanent error: reconcile timeout of 1h0m0s - exceeded" - progress: {} - sourceSnapshotName: backup-ad301b6a-6536-4313-89c1-d10ad0275430 - state: Failed diff --git a/resource_customizations/astra.netapp.io/Backup/testdata/healthy.yaml b/resource_customizations/astra.netapp.io/Backup/testdata/healthy.yaml deleted file mode 100644 index d3f32fbf93d20..0000000000000 --- a/resource_customizations/astra.netapp.io/Backup/testdata/healthy.yaml +++ /dev/null @@ -1,116 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Backup -metadata: - annotations: - astra.netapp.io/correlationid: 3c492b7e-8b1f-491a-af99-aa3fca9d54cf - created-by-astra-schedule-name: ghost-daily - created-by-astra-schedule-namespace: astra-connector - creationTimestamp: "2024-04-24T01:00:00Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - labels: - created-by-astra-schedule-uid: a2736922-6801-482c-a199-03ef8a3f35d7 - name: daily-a4587-20240424010000 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - kind: Application - name: ghost - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 - resourceVersion: "9965658" - uid: d4b61932-5c8e-4310-82a5-37a0b671aa2d -spec: - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - snapshotRef: daily-a4587-20240424010000 -status: - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/backups/daily-a4587-20240424010000_d4b61932-5c8e-4310-82a5-37a0b671aa2d - completionTimestamp: "2024-04-24T01:02:30Z" - conditions: - - lastTransitionTime: "2024-04-24T01:00:00Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppOwnerReferenceCreated - - lastTransitionTime: "2024-04-24T01:00:00Z" - message: Successfully reconciled - reason: Done - status: "True" - type: SourceSnapshotExists - - lastTransitionTime: "2024-04-24T01:00:30Z" - message: Successfully reconciled - reason: Done - status: "True" - type: SourceSnapshotCompleted - - lastTransitionTime: "2024-04-24T01:00:33Z" - message: Successfully reconciled - reason: Done - status: "True" - type: SnapshotAppArchiveCopied - - lastTransitionTime: "2024-04-24T01:00:34Z" - message: Successfully reconciled - reason: Done - status: "True" - type: PreBackupExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T01:02:30Z" - message: Successfully reconciled - reason: Done - status: "True" - type: VolumeBackupsCompleted - - lastTransitionTime: "2024-04-24T01:02:30Z" - message: Successfully reconciled - reason: Done - status: "True" - type: PostBackupExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T01:02:30Z" - message: Successfully reconciled - reason: Done - status: "True" - type: TemporarySnapshotCleanedUp - - lastTransitionTime: "2024-04-24T01:02:31Z" - message: Successfully reconciled - reason: Done - status: "True" - type: Completed - - lastTransitionTime: "2024-04-24T01:00:00Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: OnFailurePostBackupExecHooksRunCompleted - postBackupExecHooksRunResults: [] - postSnapshotExecHooksRunResults: [] - preBackupExecHooksRunResults: [] - preSnapshotExecHooksRunResults: [] - progress: - volumeBackups: - - completionTimestamp: "2024-04-24T01:02:30Z" - pvcUid: b9ff9e05-5049-4862-82c6-dea080c2fe0d - resticRepositoryPath: gs:astra-gcp-backup-743cfd150129://ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/restic/ghost/ghost_b9ff9e05-5049-4862-82c6-dea080c2fe0d - resticSnapshotID: 5d066ee6e4626ec2e3eff50d766f080ba90b2339df5b9f7baf46c281d0763da6 - resticVolumeBackupCompleted: true - resticVolumeBackupCreated: true - sourceVolumeSnapshot: - name: snapshot-71804332-e19d-42a0-bc02-56bd606b9f66-pvc-b9ff9e05-5049-4862-82c6-dea080c2fe0d - namespace: ghost - volumeSnapshotContentCopyName: backup-d4b61932-5c8e-4310-82a5-37a0b671aa2d-vsc-ab718bad-fa67-4159-a761-6d1eb5de5330 - volumeSnapshotCopied: true - volumeSnapshotCopyDeleted: true - volumeSnapshotCopyName: backup-d4b61932-5c8e-4310-82a5-37a0b671aa2d-vs-d55f9b97-11e5-4fb7-89c0-a2559eba753d - volumeSnapshotCopyReadyToUse: true - - completionTimestamp: "2024-04-24T01:02:30Z" - pvcUid: 38c468b3-eed6-48f2-b43b-15083dd1c030 - resticRepositoryPath: gs:astra-gcp-backup-743cfd150129://ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/restic/ghost/mysql-pv-claim_38c468b3-eed6-48f2-b43b-15083dd1c030 - resticSnapshotID: dc601d5db3ed78823b134326c6cc9607f1636530783707eb8cd02a018b244e07 - resticVolumeBackupCompleted: true - resticVolumeBackupCreated: true - sourceVolumeSnapshot: - name: snapshot-71804332-e19d-42a0-bc02-56bd606b9f66-pvc-38c468b3-eed6-48f2-b43b-15083dd1c030 - namespace: ghost - volumeSnapshotContentCopyName: backup-d4b61932-5c8e-4310-82a5-37a0b671aa2d-vsc-df43df62-1501-406b-b7ba-90aafcd763d5 - volumeSnapshotCopied: true - volumeSnapshotCopyDeleted: true - volumeSnapshotCopyName: backup-d4b61932-5c8e-4310-82a5-37a0b671aa2d-vs-ecf680cf-1665-4320-9f84-c99911b48a2b - volumeSnapshotCopyReadyToUse: true - sourceSnapshotName: daily-a4587-20240424010000 - state: Completed diff --git a/resource_customizations/astra.netapp.io/Backup/testdata/progressing_nostatus.yaml b/resource_customizations/astra.netapp.io/Backup/testdata/progressing_nostatus.yaml deleted file mode 100644 index 9cc87d827cb11..0000000000000 --- a/resource_customizations/astra.netapp.io/Backup/testdata/progressing_nostatus.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Backup -metadata: - annotations: - astra.netapp.io/correlationid: 3c492b7e-8b1f-491a-af99-aa3fca9d54cf - created-by-astra-schedule-name: ghost-daily - created-by-astra-schedule-namespace: astra-connector - creationTimestamp: "2024-04-24T01:00:00Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - labels: - created-by-astra-schedule-uid: a2736922-6801-482c-a199-03ef8a3f35d7 - name: daily-a4587-20240424010000 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - kind: Application - name: ghost - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 - resourceVersion: "9965658" - uid: d4b61932-5c8e-4310-82a5-37a0b671aa2d -spec: - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - snapshotRef: daily-a4587-20240424010000 diff --git a/resource_customizations/astra.netapp.io/Backup/testdata/progressing_status.yaml b/resource_customizations/astra.netapp.io/Backup/testdata/progressing_status.yaml deleted file mode 100644 index 38477b5a3f02c..0000000000000 --- a/resource_customizations/astra.netapp.io/Backup/testdata/progressing_status.yaml +++ /dev/null @@ -1,76 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Backup -metadata: - annotations: - astra.netapp.io/correlationid: cd272631-d0a8-4a61-9cde-6a7202074051 - creationTimestamp: "2024-04-24T19:39:34Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - name: backup-20240424193745 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - kind: Application - name: ghost - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 - resourceVersion: "10599529" - uid: fea5520e-553c-400d-8539-e9d2bbe5b762 -spec: - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost -status: - conditions: - - lastTransitionTime: "2024-04-24T19:39:34Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppOwnerReferenceCreated - - lastTransitionTime: "2024-04-24T19:39:34Z" - message: Successfully reconciled - reason: Done - status: "True" - type: SourceSnapshotExists - - lastTransitionTime: "2024-04-24T19:39:34Z" - message: Waiting for source Snapshot to complete - reason: Waiting - status: "False" - type: SourceSnapshotCompleted - - lastTransitionTime: "2024-04-24T19:39:34Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: SnapshotAppArchiveCopied - - lastTransitionTime: "2024-04-24T19:39:34Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: PreBackupExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T19:39:34Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: VolumeBackupsCompleted - - lastTransitionTime: "2024-04-24T19:39:34Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: PostBackupExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T19:39:34Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: TemporarySnapshotCleanedUp - - lastTransitionTime: "2024-04-24T19:39:34Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: Completed - - lastTransitionTime: "2024-04-24T19:39:34Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: OnFailurePostBackupExecHooksRunCompleted - progress: {} - sourceSnapshotName: backup-fea5520e-553c-400d-8539-e9d2bbe5b762 - state: Running diff --git a/resource_customizations/astra.netapp.io/ExecHook/health.lua b/resource_customizations/astra.netapp.io/ExecHook/health.lua deleted file mode 100644 index 6d7389ccf0704..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHook/health.lua +++ /dev/null @@ -1,13 +0,0 @@ -hs = { status = "Progressing", message = "No status available" } -if obj.spec ~= nil then - if obj.spec.enabled ~= nil then - if obj.spec.enabled == true then - hs.status = "Healthy" - hs.message = obj.kind .. " enabled" - elseif obj.spec.enabled == false then - hs.status = "Suspended" - hs.message = obj.kind .. " disabled" - end - end -end -return hs diff --git a/resource_customizations/astra.netapp.io/ExecHook/health_test.yaml b/resource_customizations/astra.netapp.io/ExecHook/health_test.yaml deleted file mode 100644 index abe46b3f6714e..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHook/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "No status available" - inputPath: testdata/progressing_nostatus.yaml - - healthStatus: - status: Healthy - message: "ExecHook enabled" - inputPath: testdata/healthy.yaml - - healthStatus: - status: Suspended - message: "ExecHook disabled" - inputPath: testdata/suspended.yaml diff --git a/resource_customizations/astra.netapp.io/ExecHook/testdata/healthy.yaml b/resource_customizations/astra.netapp.io/ExecHook/testdata/healthy.yaml deleted file mode 100644 index fd0e7ad1af15b..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHook/testdata/healthy.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ExecHook -metadata: - creationTimestamp: "2024-04-25T14:17:52Z" - generation: 1 - labels: - argocd.argoproj.io/instance: ghost-demo - name: pre-snapshot - namespace: astra-connector - resourceVersion: "11239151" - uid: 105679e3-4acc-4618-a3c2-53e0e5949f65 -spec: - action: snapshot - applicationRef: ghost - arguments: - - pre - enabled: true - hookSource: IyEvYmluL3NoCgojCiMgc3VjY2Vzc19zYW1wbGUuc2gKIwojIEEgc2ltcGxlIG5vb3Agc3VjY2VzcyBob29rIHNjcmlwdCBmb3IgdGVzdGluZyBwdXJwb3Nlcy4KIwojIGFyZ3M6IE5vbmUKIwoKCiMKIyBXcml0ZXMgdGhlIGdpdmVuIG1lc3NhZ2UgdG8gc3RhbmRhcmQgb3V0cHV0CiMKIyAkKiAtIFRoZSBtZXNzYWdlIHRvIHdyaXRlCiMKbXNnKCkgewogICAgZWNobyAiJCoiCn0KCgojCiMgV3JpdGVzIHRoZSBnaXZlbiBpbmZvcm1hdGlvbiBtZXNzYWdlIHRvIHN0YW5kYXJkIG91dHB1dAojCiMgJCogLSBUaGUgbWVzc2FnZSB0byB3cml0ZQojCmluZm8oKSB7CiAgICBtc2cgIklORk86ICQqIgp9CgojCiMgV3JpdGVzIHRoZSBnaXZlbiBlcnJvciBtZXNzYWdlIHRvIHN0YW5kYXJkIGVycm9yCiMKIyAkKiAtIFRoZSBtZXNzYWdlIHRvIHdyaXRlCiMKZXJyb3IoKSB7CiAgICBtc2cgIkVSUk9SOiAkKiIgMT4mMgp9CgoKIwojIG1haW4KIwoKIyBsb2cgc29tZXRoaW5nIHRvIHN0ZG91dAppbmZvICJydW5uaW5nIHN1Y2Nlc3Nfc2FtcGxlLnNoIgoKIyBleGl0IHdpdGggMCB0byBpbmRpY2F0ZSBzdWNjZXNzIAppbmZvICJleGl0IDAiCmV4aXQgMA== - matchingCriteria: - - type: containerImage - value: mysql - stage: pre - timeout: 25 diff --git a/resource_customizations/astra.netapp.io/ExecHook/testdata/progressing_nostatus.yaml b/resource_customizations/astra.netapp.io/ExecHook/testdata/progressing_nostatus.yaml deleted file mode 100644 index ba5af3f288bf4..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHook/testdata/progressing_nostatus.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ExecHook -metadata: - creationTimestamp: "2024-04-25T14:17:52Z" - generation: 3 - labels: - argocd.argoproj.io/instance: ghost-demo - name: pre-snapshot - namespace: astra-connector - resourceVersion: "11342335" - uid: 105679e3-4acc-4618-a3c2-53e0e5949f65 -spec: - action: snapshot - applicationRef: ghost - arguments: - - pre - hookSource: IyEvYmluL3NoCgojCiMgc3VjY2Vzc19zYW1wbGUuc2gKIwojIEEgc2ltcGxlIG5vb3Agc3VjY2VzcyBob29rIHNjcmlwdCBmb3IgdGVzdGluZyBwdXJwb3Nlcy4KIwojIGFyZ3M6IE5vbmUKIwoKCiMKIyBXcml0ZXMgdGhlIGdpdmVuIG1lc3NhZ2UgdG8gc3RhbmRhcmQgb3V0cHV0CiMKIyAkKiAtIFRoZSBtZXNzYWdlIHRvIHdyaXRlCiMKbXNnKCkgewogICAgZWNobyAiJCoiCn0KCgojCiMgV3JpdGVzIHRoZSBnaXZlbiBpbmZvcm1hdGlvbiBtZXNzYWdlIHRvIHN0YW5kYXJkIG91dHB1dAojCiMgJCogLSBUaGUgbWVzc2FnZSB0byB3cml0ZQojCmluZm8oKSB7CiAgICBtc2cgIklORk86ICQqIgp9CgojCiMgV3JpdGVzIHRoZSBnaXZlbiBlcnJvciBtZXNzYWdlIHRvIHN0YW5kYXJkIGVycm9yCiMKIyAkKiAtIFRoZSBtZXNzYWdlIHRvIHdyaXRlCiMKZXJyb3IoKSB7CiAgICBtc2cgIkVSUk9SOiAkKiIgMT4mMgp9CgoKIwojIG1haW4KIwoKIyBsb2cgc29tZXRoaW5nIHRvIHN0ZG91dAppbmZvICJydW5uaW5nIHN1Y2Nlc3Nfc2FtcGxlLnNoIgoKIyBleGl0IHdpdGggMCB0byBpbmRpY2F0ZSBzdWNjZXNzIAppbmZvICJleGl0IDAiCnNsZWVwIDMwMApleGl0IDA= - matchingCriteria: - - type: containerImage - value: mysql - stage: pre - timeout: 25 diff --git a/resource_customizations/astra.netapp.io/ExecHook/testdata/suspended.yaml b/resource_customizations/astra.netapp.io/ExecHook/testdata/suspended.yaml deleted file mode 100644 index 607b3df616164..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHook/testdata/suspended.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ExecHook -metadata: - creationTimestamp: "2024-04-25T14:17:52Z" - generation: 3 - labels: - argocd.argoproj.io/instance: ghost-demo - name: pre-snapshot - namespace: astra-connector - resourceVersion: "11342335" - uid: 105679e3-4acc-4618-a3c2-53e0e5949f65 -spec: - action: snapshot - applicationRef: ghost - arguments: - - pre - enabled: false - hookSource: IyEvYmluL3NoCgojCiMgc3VjY2Vzc19zYW1wbGUuc2gKIwojIEEgc2ltcGxlIG5vb3Agc3VjY2VzcyBob29rIHNjcmlwdCBmb3IgdGVzdGluZyBwdXJwb3Nlcy4KIwojIGFyZ3M6IE5vbmUKIwoKCiMKIyBXcml0ZXMgdGhlIGdpdmVuIG1lc3NhZ2UgdG8gc3RhbmRhcmQgb3V0cHV0CiMKIyAkKiAtIFRoZSBtZXNzYWdlIHRvIHdyaXRlCiMKbXNnKCkgewogICAgZWNobyAiJCoiCn0KCgojCiMgV3JpdGVzIHRoZSBnaXZlbiBpbmZvcm1hdGlvbiBtZXNzYWdlIHRvIHN0YW5kYXJkIG91dHB1dAojCiMgJCogLSBUaGUgbWVzc2FnZSB0byB3cml0ZQojCmluZm8oKSB7CiAgICBtc2cgIklORk86ICQqIgp9CgojCiMgV3JpdGVzIHRoZSBnaXZlbiBlcnJvciBtZXNzYWdlIHRvIHN0YW5kYXJkIGVycm9yCiMKIyAkKiAtIFRoZSBtZXNzYWdlIHRvIHdyaXRlCiMKZXJyb3IoKSB7CiAgICBtc2cgIkVSUk9SOiAkKiIgMT4mMgp9CgoKIwojIG1haW4KIwoKIyBsb2cgc29tZXRoaW5nIHRvIHN0ZG91dAppbmZvICJydW5uaW5nIHN1Y2Nlc3Nfc2FtcGxlLnNoIgoKIyBleGl0IHdpdGggMCB0byBpbmRpY2F0ZSBzdWNjZXNzIAppbmZvICJleGl0IDAiCnNsZWVwIDMwMApleGl0IDA= - matchingCriteria: - - type: containerImage - value: mysql - stage: pre - timeout: 25 diff --git a/resource_customizations/astra.netapp.io/ExecHooksRun/health.lua b/resource_customizations/astra.netapp.io/ExecHooksRun/health.lua deleted file mode 100644 index 39de4ac74eb68..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHooksRun/health.lua +++ /dev/null @@ -1,16 +0,0 @@ -hs = { status = "Progressing", message = "No status available" } -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Completed" then - hs.status = "Healthy" - hs.message = obj.kind .. " Completed" - elseif obj.status.state == "Running" then - hs.status = "Progressing" - hs.message = obj.kind .. " Running" - else - hs.status = "Degraded" - hs.message = obj.status.state - end - end -end -return hs diff --git a/resource_customizations/astra.netapp.io/ExecHooksRun/health_test.yaml b/resource_customizations/astra.netapp.io/ExecHooksRun/health_test.yaml deleted file mode 100644 index 52b629e5e7013..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHooksRun/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "No status available" - inputPath: testdata/progressing_nostatus.yaml - - healthStatus: - status: Progressing - message: "ExecHooksRun Running" - inputPath: testdata/progressing_status.yaml - - healthStatus: - status: Healthy - message: "ExecHooksRun Completed" - inputPath: testdata/healthy.yaml - - healthStatus: - status: Degraded - message: "Failed" - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/degraded.yaml b/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/degraded.yaml deleted file mode 100644 index d8822c311f449..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/degraded.yaml +++ /dev/null @@ -1,71 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ExecHooksRun -metadata: - annotations: - astra.netapp.io/correlationid: 1c47a636-f819-43f3-baee-054793424bb5 - creationTimestamp: "2024-04-25T17:00:50Z" - generation: 1 - name: post-snapshot-073d13d7-4a0c-4c5e-914f-331ef6d00de2 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Snapshot - name: argo-presync-20240425163524 - uid: 073d13d7-4a0c-4c5e-914f-331ef6d00de2 - resourceVersion: "11335239" - uid: 9bfcda95-2731-47dc-8eb2-6e83ae19da00 -spec: - action: snapshot - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240425163526_argo-presync-20240425163524_073d13d7-4a0c-4c5e-914f-331ef6d00de2 - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - completionTimeout: 0s - resourceFilter: {} - stage: post -status: - completionTimestamp: "2024-04-25T17:00:56Z" - conditions: - - lastTransitionTime: "2024-04-25T17:00:50Z" - message: failed to get application archive - reason: Done - status: "False" - type: RetrievedMatchingContainers - - lastTransitionTime: "2024-04-25T17:00:50Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: WaitForReadiness - - lastTransitionTime: "2024-04-25T17:00:56Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: ProcessMatchingContainers - - lastTransitionTime: "2024-04-25T17:00:56Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: ArchiveExecHooksUsed - - lastTransitionTime: "2024-04-25T17:00:56Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: Completed - - lastTransitionTime: "2024-04-25T17:00:50Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: OnFailureArchiveExecHooksUsed - matchingContainers: - - completionTimestamp: "2024-04-25T17:00:56Z" - containerImage: docker.io/bitnami/mysql:8.0.32-debian-11-r8 - containerName: mysql - execHookRef: post-snapshot - execHookUID: 2cafb1b4-2575-426c-8102-29437ebee48b - jobName: ehr-47223ea8dd0115ca18a986c77380aeb3 - namespace: ghost - podName: ghost-mysql-5bfb6bc8f5-stw4w - podUID: 15ddfce0-1565-4574-89a6-80662450aedd - startTimestamp: "2024-04-25T17:00:50Z" - state: Failed diff --git a/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/healthy.yaml b/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/healthy.yaml deleted file mode 100644 index 0d237f0eb75a5..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/healthy.yaml +++ /dev/null @@ -1,71 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ExecHooksRun -metadata: - annotations: - astra.netapp.io/correlationid: 1c47a636-f819-43f3-baee-054793424bb5 - creationTimestamp: "2024-04-25T17:00:50Z" - generation: 1 - name: post-snapshot-073d13d7-4a0c-4c5e-914f-331ef6d00de2 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Snapshot - name: argo-presync-20240425163524 - uid: 073d13d7-4a0c-4c5e-914f-331ef6d00de2 - resourceVersion: "11335239" - uid: 9bfcda95-2731-47dc-8eb2-6e83ae19da00 -spec: - action: snapshot - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240425163526_argo-presync-20240425163524_073d13d7-4a0c-4c5e-914f-331ef6d00de2 - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - completionTimeout: 0s - resourceFilter: {} - stage: post -status: - completionTimestamp: "2024-04-25T17:00:56Z" - conditions: - - lastTransitionTime: "2024-04-25T17:00:50Z" - message: Found 1 matching container/exechook pairs - reason: Done - status: "True" - type: RetrievedMatchingContainers - - lastTransitionTime: "2024-04-25T17:00:50Z" - message: Wait only needed on a restore - reason: Done - status: "True" - type: WaitForReadiness - - lastTransitionTime: "2024-04-25T17:00:56Z" - message: Successfully reconciled - reason: Done - status: "True" - type: ProcessMatchingContainers - - lastTransitionTime: "2024-04-25T17:00:56Z" - message: Successfully reconciled - reason: Done - status: "True" - type: ArchiveExecHooksUsed - - lastTransitionTime: "2024-04-25T17:00:56Z" - message: Successfully reconciled - reason: Done - status: "True" - type: Completed - - lastTransitionTime: "2024-04-25T17:00:50Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: OnFailureArchiveExecHooksUsed - matchingContainers: - - completionTimestamp: "2024-04-25T17:00:56Z" - containerImage: docker.io/bitnami/mysql:8.0.32-debian-11-r8 - containerName: mysql - execHookRef: post-snapshot - execHookUID: 2cafb1b4-2575-426c-8102-29437ebee48b - jobName: ehr-47223ea8dd0115ca18a986c77380aeb3 - namespace: ghost - podName: ghost-mysql-5bfb6bc8f5-stw4w - podUID: 15ddfce0-1565-4574-89a6-80662450aedd - startTimestamp: "2024-04-25T17:00:50Z" - state: Completed diff --git a/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/progressing_nostatus.yaml b/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/progressing_nostatus.yaml deleted file mode 100644 index 4d3b6704d4d20..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/progressing_nostatus.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ExecHooksRun -metadata: - annotations: - astra.netapp.io/correlationid: 1c47a636-f819-43f3-baee-054793424bb5 - creationTimestamp: "2024-04-25T16:35:34Z" - generation: 1 - name: pre-snapshot-073d13d7-4a0c-4c5e-914f-331ef6d00de2 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Snapshot - name: argo-presync-20240425163524 - uid: 073d13d7-4a0c-4c5e-914f-331ef6d00de2 - resourceVersion: "11320392" - uid: 064199e2-d540-4628-b4ec-5b417bb85128 -spec: - action: snapshot - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240425163526_argo-presync-20240425163524_073d13d7-4a0c-4c5e-914f-331ef6d00de2 - appVaultRef: astra-gcp-backup-734ced050128 - applicationRef: ghost - completionTimeout: 0s - resourceFilter: {} - stage: pre diff --git a/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/progressing_status.yaml b/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/progressing_status.yaml deleted file mode 100644 index 44578700d61dd..0000000000000 --- a/resource_customizations/astra.netapp.io/ExecHooksRun/testdata/progressing_status.yaml +++ /dev/null @@ -1,69 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ExecHooksRun -metadata: - annotations: - astra.netapp.io/correlationid: 1c47a636-f819-43f3-baee-054793424bb5 - creationTimestamp: "2024-04-25T16:35:34Z" - generation: 1 - name: pre-snapshot-073d13d7-4a0c-4c5e-914f-331ef6d00de2 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Snapshot - name: argo-presync-20240425163524 - uid: 073d13d7-4a0c-4c5e-914f-331ef6d00de2 - resourceVersion: "11320407" - uid: 064199e2-d540-4628-b4ec-5b417bb85128 -spec: - action: snapshot - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240425163526_argo-presync-20240425163524_073d13d7-4a0c-4c5e-914f-331ef6d00de2 - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - completionTimeout: 0s - resourceFilter: {} - stage: pre -status: - conditions: - - lastTransitionTime: "2024-04-25T16:35:34Z" - message: Found 1 matching container/exechook pairs - reason: Done - status: "True" - type: RetrievedMatchingContainers - - lastTransitionTime: "2024-04-25T16:35:34Z" - message: Wait only needed on a restore - reason: Done - status: "True" - type: WaitForReadiness - - lastTransitionTime: "2024-04-25T16:35:34Z" - message: Waiting - reason: Waiting - status: "False" - type: ProcessMatchingContainers - - lastTransitionTime: "2024-04-25T16:35:34Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: ArchiveExecHooksUsed - - lastTransitionTime: "2024-04-25T16:35:34Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: Completed - - lastTransitionTime: "2024-04-25T16:35:34Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: OnFailureArchiveExecHooksUsed - matchingContainers: - - containerImage: docker.io/bitnami/mysql:8.0.32-debian-11-r8 - containerName: mysql - execHookRef: pre-snapshot - execHookUID: 105679e3-4acc-4618-a3c2-53e0e5949f65 - jobName: ehr-ea0e89c8221790b54e94b4ac937aeac2 - namespace: ghost - podName: ghost-mysql-5bfb6bc8f5-stw4w - podUID: 15ddfce0-1565-4574-89a6-80662450aedd - startTimestamp: "2024-04-25T16:35:34Z" - state: Running diff --git a/resource_customizations/astra.netapp.io/ResourceBackup/health.lua b/resource_customizations/astra.netapp.io/ResourceBackup/health.lua deleted file mode 100644 index 39de4ac74eb68..0000000000000 --- a/resource_customizations/astra.netapp.io/ResourceBackup/health.lua +++ /dev/null @@ -1,16 +0,0 @@ -hs = { status = "Progressing", message = "No status available" } -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Completed" then - hs.status = "Healthy" - hs.message = obj.kind .. " Completed" - elseif obj.status.state == "Running" then - hs.status = "Progressing" - hs.message = obj.kind .. " Running" - else - hs.status = "Degraded" - hs.message = obj.status.state - end - end -end -return hs diff --git a/resource_customizations/astra.netapp.io/ResourceBackup/health_test.yaml b/resource_customizations/astra.netapp.io/ResourceBackup/health_test.yaml deleted file mode 100644 index 21668ca2006eb..0000000000000 --- a/resource_customizations/astra.netapp.io/ResourceBackup/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "No status available" - inputPath: testdata/progressing_nostatus.yaml - - healthStatus: - status: Progressing - message: "ResourceBackup Running" - inputPath: testdata/progressing_status.yaml - - healthStatus: - status: Healthy - message: "ResourceBackup Completed" - inputPath: testdata/healthy.yaml - - healthStatus: - status: Degraded - message: "Error" - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/astra.netapp.io/ResourceBackup/testdata/degraded.yaml b/resource_customizations/astra.netapp.io/ResourceBackup/testdata/degraded.yaml deleted file mode 100644 index dc8bcd087f06c..0000000000000 --- a/resource_customizations/astra.netapp.io/ResourceBackup/testdata/degraded.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ResourceBackup -metadata: - annotations: - astra.netapp.io/correlationid: 6094b54d-b02b-475a-b5db-136729841240 - creationTimestamp: "2024-04-24T19:54:19Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - name: snapshot-7b0d4f5e-53d0-4742-adec-15ef5d527865 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Snapshot - name: backup-ad301b6a-6536-4313-89c1-d10ad0275430 - uid: 7b0d4f5e-53d0-4742-adec-15ef5d527865 - resourceVersion: "10608354" - uid: 9f8505a1-29ac-4755-92b5-536e6d825c35 -spec: - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240424195419_backup-ad301b6a-6536-4313-89c1-d10ad0275430_7b0d4f5e-53d0-4742-adec-15ef5d527865 - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost -status: - conditions: - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppOwnerReferenceCreated - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: - 'unable to fetch appVault: AppVault.astra.netapp.io "astra-gcp-backup-743cfd150129" - not found' - reason: Error - status: "False" - type: JobCreated - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: JobCompleted - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: JobCleanedUp - error: - 'unable to fetch appVault: AppVault.astra.netapp.io "astra-gcp-backup-743cfd150129" - not found' - state: Error diff --git a/resource_customizations/astra.netapp.io/ResourceBackup/testdata/healthy.yaml b/resource_customizations/astra.netapp.io/ResourceBackup/testdata/healthy.yaml deleted file mode 100644 index 047ccbf583b5a..0000000000000 --- a/resource_customizations/astra.netapp.io/ResourceBackup/testdata/healthy.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ResourceBackup -metadata: - annotations: - astra.netapp.io/correlationid: 5b89a58c-9b7c-42e8-b426-c8f863e88f41 - creationTimestamp: "2024-04-18T02:00:00Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - name: snapshot-0b1c9d28-33bd-45ce-b75b-2a45721e7218 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Snapshot - name: daily-02c95-20240418020000 - uid: 0b1c9d28-33bd-45ce-b75b-2a45721e7218 - resourceVersion: "5060306" - uid: 28c08689-2f8d-4b1e-bfa4-ac8c8795adff -spec: - appArchivePath: wordpress_5ab7cd7d-7a9b-4508-9da2-c7dcb10a69b3/snapshots/20240418020000_daily-02c95-20240418020000_0b1c9d28-33bd-45ce-b75b-2a45721e7218 - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: wordpress -status: - appArchivePath: wordpress_5ab7cd7d-7a9b-4508-9da2-c7dcb10a69b3/snapshots/20240418020000_daily-02c95-20240418020000_0b1c9d28-33bd-45ce-b75b-2a45721e7218 - completionTimestamp: "2024-04-18T02:00:09Z" - conditions: - - lastTransitionTime: "2024-04-18T02:00:00Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppOwnerReferenceCreated - - lastTransitionTime: "2024-04-18T02:00:00Z" - message: Successfully reconciled - reason: Done - status: "True" - type: JobCreated - - lastTransitionTime: "2024-04-18T02:00:09Z" - message: Successfully reconciled - reason: Done - status: "True" - type: JobCompleted - - lastTransitionTime: "2024-04-18T02:00:10Z" - message: Successfully reconciled - reason: Done - status: "True" - type: JobCleanedUp - state: Completed diff --git a/resource_customizations/astra.netapp.io/ResourceBackup/testdata/progressing_nostatus.yaml b/resource_customizations/astra.netapp.io/ResourceBackup/testdata/progressing_nostatus.yaml deleted file mode 100644 index e4e5f9f6512d5..0000000000000 --- a/resource_customizations/astra.netapp.io/ResourceBackup/testdata/progressing_nostatus.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ResourceBackup -metadata: - annotations: - astra.netapp.io/correlationid: ee3baf3b-c470-486f-a327-47a6eada0722 - creationTimestamp: "2024-04-24T21:30:21Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - name: snapshot-0796d78d-e751-4835-a0d4-be61b9f9076a - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Snapshot - name: argo-presync-20240424213020 - uid: 0796d78d-e751-4835-a0d4-be61b9f9076a - resourceVersion: "10661760" - uid: 6ed660f0-95be-4369-b548-15cb094a44c2 -spec: - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240424213020_argo-presync-20240424213020_0796d78d-e751-4835-a0d4-be61b9f9076a - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost diff --git a/resource_customizations/astra.netapp.io/ResourceBackup/testdata/progressing_status.yaml b/resource_customizations/astra.netapp.io/ResourceBackup/testdata/progressing_status.yaml deleted file mode 100644 index ba27f3627f798..0000000000000 --- a/resource_customizations/astra.netapp.io/ResourceBackup/testdata/progressing_status.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ResourceBackup -metadata: - annotations: - astra.netapp.io/correlationid: ee3baf3b-c470-486f-a327-47a6eada0722 - creationTimestamp: "2024-04-24T21:30:21Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - name: snapshot-0796d78d-e751-4835-a0d4-be61b9f9076a - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Snapshot - name: argo-presync-20240424213020 - uid: 0796d78d-e751-4835-a0d4-be61b9f9076a - resourceVersion: "10661760" - uid: 6ed660f0-95be-4369-b548-15cb094a44c2 -spec: - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240424213020_argo-presync-20240424213020_0796d78d-e751-4835-a0d4-be61b9f9076a - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost -status: - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240424213020_argo-presync-20240424213020_0796d78d-e751-4835-a0d4-be61b9f9076a - conditions: - - lastTransitionTime: "2024-04-24T21:30:21Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppOwnerReferenceCreated - - lastTransitionTime: "2024-04-24T21:30:21Z" - message: Successfully reconciled - reason: Done - status: "True" - type: JobCreated - - lastTransitionTime: "2024-04-24T21:30:21Z" - message: waiting for resource backup job to complete - reason: Waiting - status: "False" - type: JobCompleted - - lastTransitionTime: "2024-04-24T21:30:21Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: JobCleanedUp - state: Running diff --git a/resource_customizations/astra.netapp.io/ResticVolumeBackup/health.lua b/resource_customizations/astra.netapp.io/ResticVolumeBackup/health.lua deleted file mode 100644 index 39de4ac74eb68..0000000000000 --- a/resource_customizations/astra.netapp.io/ResticVolumeBackup/health.lua +++ /dev/null @@ -1,16 +0,0 @@ -hs = { status = "Progressing", message = "No status available" } -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Completed" then - hs.status = "Healthy" - hs.message = obj.kind .. " Completed" - elseif obj.status.state == "Running" then - hs.status = "Progressing" - hs.message = obj.kind .. " Running" - else - hs.status = "Degraded" - hs.message = obj.status.state - end - end -end -return hs diff --git a/resource_customizations/astra.netapp.io/ResticVolumeBackup/health_test.yaml b/resource_customizations/astra.netapp.io/ResticVolumeBackup/health_test.yaml deleted file mode 100644 index 2038e85656a2f..0000000000000 --- a/resource_customizations/astra.netapp.io/ResticVolumeBackup/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "No status available" - inputPath: testdata/progressing_nostatus.yaml - - healthStatus: - status: Progressing - message: "ResticVolumeBackup Running" - inputPath: testdata/progressing_status.yaml - - healthStatus: - status: Healthy - message: "ResticVolumeBackup Completed" - inputPath: testdata/healthy.yaml - - healthStatus: - status: Degraded - message: "Failed" - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/degraded.yaml b/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/degraded.yaml deleted file mode 100644 index dd1e080791b2a..0000000000000 --- a/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/degraded.yaml +++ /dev/null @@ -1,99 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ResticVolumeBackup -metadata: - annotations: - astra.netapp.io/correlationid: 26d34f64-38cc-4775-881d-a2fa12437f4b - creationTimestamp: "2024-04-17T13:50:44Z" - deletionGracePeriodSeconds: 0 - deletionTimestamp: "2024-04-17T14:51:28Z" - finalizers: - - astra.netapp.io/finalizer - generation: 2 - name: backup-8f2ae7bd-82fc-4b4f-a22d-d08edc2e4e27-vs-54c8ec7f-42e8-48aa-b347-d4acab7b877b - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Backup - name: hourly-acde9-20240417135000 - uid: 8f2ae7bd-82fc-4b4f-a22d-d08edc2e4e27 - resourceVersion: "4675672" - uid: ba90a4f7-a68f-4978-bc04-86902281adc2 -spec: - clonePVC: - metadata: {} - spec: - accessModes: - - ReadWriteMany - resources: - requests: - storage: 100Gi - storageClassName: netapp-cvs-perf-premium - dataSourceRef: - apiGroup: snapshot.storage.k8s.io - kind: VolumeSnapshot - name: backup-8f2ae7bd-82fc-4b4f-a22d-d08edc2e4e27-vs-54c8ec7f-42e8-48aa-b347-d4acab7b877b - resticEnv: - - name: GOOGLE_PROJECT_ID - - name: GOOGLE_APPLICATION_CREDENTIALS - value: /var/run/secrets/neptune/astra-gcp-backup-743cfd150129-5rdt4/credentials.json - - name: RESTIC_PASSWORD - value: password - resticRepository: gs:astra-gcp-backup-743cfd150129://ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/restic/ghost/ghost_b9ff9e05-5049-4862-82c6-dea080c2fe0d - resticVolumeMounts: - - mount: - mountPath: /var/run/secrets/neptune/astra-gcp-backup-743cfd150129-5rdt4 - name: secret-astra-gcp-backup-743cfd150129-5rdt4 - readOnly: true - source: - items: - - key: credentials.json - path: credentials.json - secretName: astra-gcp-backup-743cfd150129-5rdt4 -status: - clonePVCName: restic-volume-backup-ba90a4f7-a68f-4978-bc04-86902281adc2 - clonePVName: "" - conditions: - - lastTransitionTime: "2024-04-17T13:50:44Z" - message: Successfully reconciled - reason: Done - status: "True" - type: SourcePVCExists - - lastTransitionTime: "2024-04-17T13:50:44Z" - message: Successfully reconciled - reason: Done - status: "True" - type: ResticJobCreated - - lastTransitionTime: "2024-04-17T13:50:44Z" - message: - "restic job restic-volume-backup-ba90a4f7-a68f-4978-bc04-86902281adc2 - failed: permanent error" - reason: Failed - status: "False" - type: ResticJobCompleted - - lastTransitionTime: "2024-04-17T13:50:44Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: ResticJobCleanedUp - - lastTransitionTime: "2024-04-17T13:50:44Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: TemporaryPVCCloneCleanedUp - - lastTransitionTime: "2024-04-17T13:50:44Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: TemporaryPVCloneCleanedUp - - lastTransitionTime: "2024-04-17T13:50:44Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: Completed - error: - "restic job restic-volume-backup-ba90a4f7-a68f-4978-bc04-86902281adc2 failed: - permanent error" - resticJobName: restic-volume-backup-ba90a4f7-a68f-4978-bc04-86902281adc2 - state: Failed diff --git a/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/healthy.yaml b/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/healthy.yaml deleted file mode 100644 index 55d5fdf49055e..0000000000000 --- a/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/healthy.yaml +++ /dev/null @@ -1,94 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ResticVolumeBackup -metadata: - annotations: - astra.netapp.io/correlationid: 2d54c3e9-2b18-4ce9-958e-4c307619e4e7 - creationTimestamp: "2024-04-25T20:30:15Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - name: backup-40b1dc7d-f1c0-4c3d-b34e-d7db5cc26d14-vs-78b36b0d-52db-4b24-afe4-ceec56209bbb - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Backup - name: hourly-acde9-20240425195000 - uid: 40b1dc7d-f1c0-4c3d-b34e-d7db5cc26d14 - resourceVersion: "11460297" - uid: f3424a57-862e-4609-88ce-e534a655a5d6 -spec: - clonePVC: - metadata: {} - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: netapp-cvs-perf-premium - dataSourceRef: - apiGroup: snapshot.storage.k8s.io - kind: VolumeSnapshot - name: backup-40b1dc7d-f1c0-4c3d-b34e-d7db5cc26d14-vs-78b36b0d-52db-4b24-afe4-ceec56209bbb - resticEnv: - - name: GOOGLE_PROJECT_ID - - name: GOOGLE_APPLICATION_CREDENTIALS - value: /var/run/secrets/neptune/astra-gcp-backup-743cfd150129-5rdt4/credentials.json - - name: RESTIC_PASSWORD - value: password - resticRepository: gs:astra-gcp-backup-743cfd150129://ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/restic/ghost/mysql-pv-claim_5749beb5-e09a-4286-8cb4-1af9750f6929 - resticVolumeMounts: - - mount: - mountPath: /var/run/secrets/neptune/astra-gcp-backup-743cfd150129-5rdt4 - name: secret-astra-gcp-backup-743cfd150129-5rdt4 - readOnly: true - source: - items: - - key: credentials.json - path: credentials.json - secretName: astra-gcp-backup-743cfd150129-5rdt4 -status: - clonePVCName: restic-volume-backup-f3424a57-862e-4609-88ce-e534a655a5d6 - clonePVName: pvc-90470af6-7d44-4500-80c1-99f925193654 - completionTimestamp: "2024-04-25T20:31:57Z" - conditions: - - lastTransitionTime: "2024-04-25T20:30:15Z" - message: Successfully reconciled - reason: Done - status: "True" - type: SourcePVCExists - - lastTransitionTime: "2024-04-25T20:30:15Z" - message: Successfully reconciled - reason: Done - status: "True" - type: ResticJobCreated - - lastTransitionTime: "2024-04-25T20:30:54Z" - message: Successfully reconciled - reason: Done - status: "True" - type: ResticJobCompleted - - lastTransitionTime: "2024-04-25T20:30:54Z" - message: Successfully reconciled - reason: Done - status: "True" - type: ResticJobCleanedUp - - lastTransitionTime: "2024-04-25T20:30:54Z" - message: Successfully reconciled - reason: Done - status: "True" - type: TemporaryPVCCloneCleanedUp - - lastTransitionTime: "2024-04-25T20:31:57Z" - message: Successfully reconciled - reason: Done - status: "True" - type: TemporaryPVCloneCleanedUp - - lastTransitionTime: "2024-04-25T20:31:57Z" - message: Successfully reconciled - reason: Done - status: "True" - type: Completed - resticJobName: restic-volume-backup-f3424a57-862e-4609-88ce-e534a655a5d6 - resticSnapshotID: 88c5684cf3e0cd73e57d96f11d20b1c6b03c913cf574cb73cb40da95078d6694 - state: Completed diff --git a/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/progressing_nostatus.yaml b/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/progressing_nostatus.yaml deleted file mode 100644 index b622e552015f1..0000000000000 --- a/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/progressing_nostatus.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ResticVolumeBackup -metadata: - creationTimestamp: "2024-04-25T20:30:15Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - name: backup-40b1dc7d-f1c0-4c3d-b34e-d7db5cc26d14-vs-78b36b0d-52db-4b24-afe4-ceec56209bbb - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Backup - name: hourly-acde9-20240425195000 - uid: 40b1dc7d-f1c0-4c3d-b34e-d7db5cc26d14 - resourceVersion: "11459172" - uid: f3424a57-862e-4609-88ce-e534a655a5d6 -spec: - clonePVC: - metadata: {} - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: netapp-cvs-perf-premium - dataSourceRef: - apiGroup: snapshot.storage.k8s.io - kind: VolumeSnapshot - name: backup-40b1dc7d-f1c0-4c3d-b34e-d7db5cc26d14-vs-78b36b0d-52db-4b24-afe4-ceec56209bbb - resticEnv: - - name: GOOGLE_PROJECT_ID - - name: GOOGLE_APPLICATION_CREDENTIALS - value: /var/run/secrets/neptune/astra-gcp-backup-743cfd150129-5rdt4/credentials.json - - name: RESTIC_PASSWORD - value: password - resticRepository: gs:astra-gcp-backup-743cfd150129://ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/restic/ghost/mysql-pv-claim_5749beb5-e09a-4286-8cb4-1af9750f6929 - resticVolumeMounts: - - mount: - mountPath: /var/run/secrets/neptune/astra-gcp-backup-743cfd150129-5rdt4 - name: secret-astra-gcp-backup-743cfd150129-5rdt4 - readOnly: true - source: - items: - - key: credentials.json - path: credentials.json - secretName: astra-gcp-backup-743cfd150129-5rdt4 diff --git a/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/progressing_status.yaml b/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/progressing_status.yaml deleted file mode 100644 index e0889bf955fdd..0000000000000 --- a/resource_customizations/astra.netapp.io/ResticVolumeBackup/testdata/progressing_status.yaml +++ /dev/null @@ -1,92 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: ResticVolumeBackup -metadata: - annotations: - astra.netapp.io/correlationid: 2d54c3e9-2b18-4ce9-958e-4c307619e4e7 - creationTimestamp: "2024-04-25T20:30:15Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - name: backup-40b1dc7d-f1c0-4c3d-b34e-d7db5cc26d14-vs-78b36b0d-52db-4b24-afe4-ceec56209bbb - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Backup - name: hourly-acde9-20240425195000 - uid: 40b1dc7d-f1c0-4c3d-b34e-d7db5cc26d14 - resourceVersion: "11459181" - uid: f3424a57-862e-4609-88ce-e534a655a5d6 -spec: - clonePVC: - metadata: {} - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: netapp-cvs-perf-premium - dataSourceRef: - apiGroup: snapshot.storage.k8s.io - kind: VolumeSnapshot - name: backup-40b1dc7d-f1c0-4c3d-b34e-d7db5cc26d14-vs-78b36b0d-52db-4b24-afe4-ceec56209bbb - resticEnv: - - name: GOOGLE_PROJECT_ID - - name: GOOGLE_APPLICATION_CREDENTIALS - value: /var/run/secrets/neptune/astra-gcp-backup-743cfd150129-5rdt4/credentials.json - - name: RESTIC_PASSWORD - value: password - resticRepository: gs:astra-gcp-backup-743cfd150129://ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/restic/ghost/mysql-pv-claim_5749beb5-e09a-4286-8cb4-1af9750f6929 - resticVolumeMounts: - - mount: - mountPath: /var/run/secrets/neptune/astra-gcp-backup-743cfd150129-5rdt4 - name: secret-astra-gcp-backup-743cfd150129-5rdt4 - readOnly: true - source: - items: - - key: credentials.json - path: credentials.json - secretName: astra-gcp-backup-743cfd150129-5rdt4 -status: - clonePVCName: restic-volume-backup-f3424a57-862e-4609-88ce-e534a655a5d6 - clonePVName: "" - conditions: - - lastTransitionTime: "2024-04-25T20:30:15Z" - message: Successfully reconciled - reason: Done - status: "True" - type: SourcePVCExists - - lastTransitionTime: "2024-04-25T20:30:15Z" - message: Successfully reconciled - reason: Done - status: "True" - type: ResticJobCreated - - lastTransitionTime: "2024-04-25T20:30:15Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: ResticJobCompleted - - lastTransitionTime: "2024-04-25T20:30:15Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: ResticJobCleanedUp - - lastTransitionTime: "2024-04-25T20:30:15Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: TemporaryPVCCloneCleanedUp - - lastTransitionTime: "2024-04-25T20:30:15Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: TemporaryPVCloneCleanedUp - - lastTransitionTime: "2024-04-25T20:30:15Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: Completed - resticJobName: restic-volume-backup-f3424a57-862e-4609-88ce-e534a655a5d6 - state: Running diff --git a/resource_customizations/astra.netapp.io/Schedule/health.lua b/resource_customizations/astra.netapp.io/Schedule/health.lua deleted file mode 100644 index 5d122593e5b08..0000000000000 --- a/resource_customizations/astra.netapp.io/Schedule/health.lua +++ /dev/null @@ -1,7 +0,0 @@ -hs = { status = "Healthy", message = "Protection policy not yet executed" } -if obj.status ~= nil then - if obj.status.lastScheduleTime ~= nil then - hs.message = "Protection policy lastScheduleTime: " .. obj.status.lastScheduleTime - end -end -return hs diff --git a/resource_customizations/astra.netapp.io/Schedule/health_test.yaml b/resource_customizations/astra.netapp.io/Schedule/health_test.yaml deleted file mode 100644 index 73414e0b58d5a..0000000000000 --- a/resource_customizations/astra.netapp.io/Schedule/health_test.yaml +++ /dev/null @@ -1,9 +0,0 @@ -tests: - - healthStatus: - status: Healthy - message: "Protection policy not yet executed" - inputPath: testdata/healthy_nostatus.yaml - - healthStatus: - status: Healthy - message: "Protection policy lastScheduleTime: 2024-04-24T01:00:00Z" - inputPath: testdata/healthy_status.yaml diff --git a/resource_customizations/astra.netapp.io/Schedule/testdata/healthy_nostatus.yaml b/resource_customizations/astra.netapp.io/Schedule/testdata/healthy_nostatus.yaml deleted file mode 100644 index 0456fb39fc0f6..0000000000000 --- a/resource_customizations/astra.netapp.io/Schedule/testdata/healthy_nostatus.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Schedule -metadata: - creationTimestamp: "2024-04-15T20:46:16Z" - generation: 2 - labels: - argocd.argoproj.io/instance: ghost-demo - name: ghost-monthly - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - kind: Application - name: ghost - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 - resourceVersion: "3231157" - uid: f75ebc6f-627c-4b34-ba36-e64ddc3948e3 -spec: - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - backupRetention: "1" - dayOfMonth: "1" - dayOfWeek: "" - enabled: true - granularity: monthly - hour: "2" - minute: "0" - recurrenceRule: "" - snapshotRetention: "1" diff --git a/resource_customizations/astra.netapp.io/Schedule/testdata/healthy_status.yaml b/resource_customizations/astra.netapp.io/Schedule/testdata/healthy_status.yaml deleted file mode 100644 index 71f99ddf23299..0000000000000 --- a/resource_customizations/astra.netapp.io/Schedule/testdata/healthy_status.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Schedule -metadata: - creationTimestamp: "2024-04-15T20:46:16Z" - generation: 2 - labels: - argocd.argoproj.io/instance: ghost-demo - name: ghost-daily - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - kind: Application - name: ghost - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 - resourceVersion: "9963815" - uid: a2736922-6801-482c-a199-03ef8a3f35d7 -spec: - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - backupRetention: "1" - dayOfMonth: "" - dayOfWeek: "" - enabled: true - granularity: daily - hour: "1" - minute: "0" - recurrenceRule: "" - snapshotRetention: "1" -status: - lastScheduleTime: "2024-04-24T01:00:00Z" diff --git a/resource_customizations/astra.netapp.io/Snapshot/health.lua b/resource_customizations/astra.netapp.io/Snapshot/health.lua deleted file mode 100644 index 39de4ac74eb68..0000000000000 --- a/resource_customizations/astra.netapp.io/Snapshot/health.lua +++ /dev/null @@ -1,16 +0,0 @@ -hs = { status = "Progressing", message = "No status available" } -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Completed" then - hs.status = "Healthy" - hs.message = obj.kind .. " Completed" - elseif obj.status.state == "Running" then - hs.status = "Progressing" - hs.message = obj.kind .. " Running" - else - hs.status = "Degraded" - hs.message = obj.status.state - end - end -end -return hs diff --git a/resource_customizations/astra.netapp.io/Snapshot/health_test.yaml b/resource_customizations/astra.netapp.io/Snapshot/health_test.yaml deleted file mode 100644 index c15b3d8b3a9f0..0000000000000 --- a/resource_customizations/astra.netapp.io/Snapshot/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "No status available" - inputPath: testdata/progressing_nostatus.yaml - - healthStatus: - status: Progressing - message: "Snapshot Running" - inputPath: testdata/progressing_status.yaml - - healthStatus: - status: Healthy - message: "Snapshot Completed" - inputPath: testdata/healthy.yaml - - healthStatus: - status: Degraded - message: "Failed" - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/astra.netapp.io/Snapshot/testdata/degraded.yaml b/resource_customizations/astra.netapp.io/Snapshot/testdata/degraded.yaml deleted file mode 100644 index 89851bbe5dc0b..0000000000000 --- a/resource_customizations/astra.netapp.io/Snapshot/testdata/degraded.yaml +++ /dev/null @@ -1,80 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Snapshot -metadata: - annotations: - astra.netapp.io/correlationid: 6094b54d-b02b-475a-b5db-136729841240 - creationTimestamp: "2024-04-24T19:54:18Z" - finalizers: - - astra.netapp.io/finalizer - generation: 1 - name: backup-ad301b6a-6536-4313-89c1-d10ad0275430 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - blockOwnerDeletion: true - controller: true - kind: Backup - name: backup-20240424193746 - uid: ad301b6a-6536-4313-89c1-d10ad0275430 - - apiVersion: astra.netapp.io/v1 - kind: Application - name: ghost - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 - resourceVersion: "10641329" - uid: 7b0d4f5e-53d0-4742-adec-15ef5d527865 -spec: - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - completionTimeout: 0s - volumeSnapshotsCreatedTimeout: 0s - volumeSnapshotsReadyToUseTimeout: 0s -status: - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240424195419_backup-ad301b6a-6536-4313-89c1-d10ad0275430_7b0d4f5e-53d0-4742-adec-15ef5d527865 - conditions: - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppOwnerReferenceCreated - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppArchivePathNameGenerated - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: reconcile timeout of 1h0m0s exceeded - reason: Timeout - status: "False" - type: ResourceBackupCompleted - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: PreSnapshotExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: VolumeSnapshotsCreated - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: PostSnapshotExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: VolumeSnapshotsReady - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: Completed - - lastTransitionTime: "2024-04-24T19:54:19Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: OnFailurePostSnapshotExecHooksRunCompleted - error: reconcile timeout of 1h0m0s exceeded - state: Failed diff --git a/resource_customizations/astra.netapp.io/Snapshot/testdata/healthy.yaml b/resource_customizations/astra.netapp.io/Snapshot/testdata/healthy.yaml deleted file mode 100644 index 7073f9c5147b5..0000000000000 --- a/resource_customizations/astra.netapp.io/Snapshot/testdata/healthy.yaml +++ /dev/null @@ -1,81 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Snapshot -metadata: - annotations: - astra.netapp.io/correlationid: 87091676-6489-4c76-8728-6b81bf4936b0 - creationTimestamp: "2024-04-24T14:23:18Z" - finalizers: - - astra.netapp.io/finalizer - generation: 2 - name: argo-presync-20240424142317 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - kind: Application - name: ghost - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 - resourceVersion: "10421471" - uid: 152faab3-0374-4cef-bac9-6e7940b06aa9 -spec: - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - completionTimeout: 0s - volumeSnapshotsCreatedTimeout: 0s - volumeSnapshotsReadyToUseTimeout: 0s -status: - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240424142318_argo-presync-20240424142317_152faab3-0374-4cef-bac9-6e7940b06aa9 - completionTimestamp: "2024-04-24T14:23:43Z" - conditions: - - lastTransitionTime: "2024-04-24T14:23:18Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppOwnerReferenceCreated - - lastTransitionTime: "2024-04-24T14:23:18Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppArchivePathNameGenerated - - lastTransitionTime: "2024-04-24T14:23:30Z" - message: Successfully reconciled - reason: Done - status: "True" - type: ResourceBackupCompleted - - lastTransitionTime: "2024-04-24T14:23:31Z" - message: Successfully reconciled - reason: Done - status: "True" - type: PreSnapshotExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T14:23:41Z" - message: Successfully reconciled - reason: Done - status: "True" - type: VolumeSnapshotsCreated - - lastTransitionTime: "2024-04-24T14:23:42Z" - message: Successfully reconciled - reason: Done - status: "True" - type: PostSnapshotExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T14:23:43Z" - message: Successfully reconciled - reason: Done - status: "True" - type: VolumeSnapshotsReady - - lastTransitionTime: "2024-04-24T14:23:43Z" - message: Successfully reconciled - reason: Done - status: "True" - type: Completed - - lastTransitionTime: "2024-04-24T14:23:18Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: OnFailurePostSnapshotExecHooksRunCompleted - postSnapshotExecHooksRunResults: [] - preSnapshotExecHooksRunResults: [] - state: Completed - volumeSnapshots: - - name: snapshot-152faab3-0374-4cef-bac9-6e7940b06aa9-pvc-b9ff9e05-5049-4862-82c6-dea080c2fe0d - namespace: ghost - - name: snapshot-152faab3-0374-4cef-bac9-6e7940b06aa9-pvc-38c468b3-eed6-48f2-b43b-15083dd1c030 - namespace: ghost diff --git a/resource_customizations/astra.netapp.io/Snapshot/testdata/progressing_nostatus.yaml b/resource_customizations/astra.netapp.io/Snapshot/testdata/progressing_nostatus.yaml deleted file mode 100644 index 28501e28fd95f..0000000000000 --- a/resource_customizations/astra.netapp.io/Snapshot/testdata/progressing_nostatus.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Snapshot -metadata: - annotations: - astra.netapp.io/correlationid: de2315e9-4733-4733-91a0-1abec5f1e44e - creationTimestamp: "2024-04-24T21:17:04Z" - finalizers: - - astra.netapp.io/finalizer - generation: 2 - name: argo-presync-20240424211703 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - kind: Application - name: ghost - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 - resourceVersion: "10654224" - uid: b200db48-c186-4ae8-9748-1ba7bec23d6d -spec: - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - completionTimeout: 0s - volumeSnapshotsCreatedTimeout: 0s - volumeSnapshotsReadyToUseTimeout: 0s diff --git a/resource_customizations/astra.netapp.io/Snapshot/testdata/progressing_status.yaml b/resource_customizations/astra.netapp.io/Snapshot/testdata/progressing_status.yaml deleted file mode 100644 index 08951648c6d74..0000000000000 --- a/resource_customizations/astra.netapp.io/Snapshot/testdata/progressing_status.yaml +++ /dev/null @@ -1,73 +0,0 @@ -apiVersion: astra.netapp.io/v1 -kind: Snapshot -metadata: - annotations: - astra.netapp.io/correlationid: de2315e9-4733-4733-91a0-1abec5f1e44e - creationTimestamp: "2024-04-24T21:17:04Z" - finalizers: - - astra.netapp.io/finalizer - generation: 2 - name: argo-presync-20240424211703 - namespace: astra-connector - ownerReferences: - - apiVersion: astra.netapp.io/v1 - kind: Application - name: ghost - uid: 0af10ee8-772b-4367-8334-44f9e4ad2849 - resourceVersion: "10654224" - uid: b200db48-c186-4ae8-9748-1ba7bec23d6d -spec: - appVaultRef: astra-gcp-backup-743cfd150129 - applicationRef: ghost - completionTimeout: 0s - volumeSnapshotsCreatedTimeout: 0s - volumeSnapshotsReadyToUseTimeout: 0s -status: - appArchivePath: ghost_0af10ee8-772b-4367-8334-44f9e4ad2849/snapshots/20240424211704_argo-presync-20240424211703_b200db48-c186-4ae8-9748-1ba7bec23d6d - conditions: - - lastTransitionTime: "2024-04-24T21:17:04Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppOwnerReferenceCreated - - lastTransitionTime: "2024-04-24T21:17:04Z" - message: Successfully reconciled - reason: Done - status: "True" - type: AppArchivePathNameGenerated - - lastTransitionTime: "2024-04-24T21:17:04Z" - message: Waiting for ResourceBackup to complete - reason: Waiting - status: "False" - type: ResourceBackupCompleted - - lastTransitionTime: "2024-04-24T21:17:04Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: PreSnapshotExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T21:17:04Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: VolumeSnapshotsCreated - - lastTransitionTime: "2024-04-24T21:17:04Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: PostSnapshotExecHooksRunCompleted - - lastTransitionTime: "2024-04-24T21:17:04Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: VolumeSnapshotsReady - - lastTransitionTime: "2024-04-24T21:17:04Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: Completed - - lastTransitionTime: "2024-04-24T21:17:04Z" - message: Not yet reconciled - reason: Pending - status: Unknown - type: OnFailurePostSnapshotExecHooksRunCompleted - state: Running diff --git a/resource_customizations/batch/CronJob/actions/create-job/action.lua b/resource_customizations/batch/CronJob/actions/create-job/action.lua index aac90c4bf6719..a6f3253a5b757 100644 --- a/resource_customizations/batch/CronJob/actions/create-job/action.lua +++ b/resource_customizations/batch/CronJob/actions/create-job/action.lua @@ -36,7 +36,7 @@ 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.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 = {} diff --git a/resource_customizations/beat.k8s.elastic.co/Beat/health.lua b/resource_customizations/beat.k8s.elastic.co/Beat/health.lua deleted file mode 100644 index c7639dbbd94f0..0000000000000 --- a/resource_customizations/beat.k8s.elastic.co/Beat/health.lua +++ /dev/null @@ -1,31 +0,0 @@ -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 deleted file mode 100644 index fb44e998ffaf1..0000000000000 --- a/resource_customizations/beat.k8s.elastic.co/Beat/health_test.yaml +++ /dev/null @@ -1,29 +0,0 @@ -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 deleted file mode 100644 index 3eca183165a5c..0000000000000 --- a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/invalid.yaml +++ /dev/null @@ -1,12 +0,0 @@ -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 deleted file mode 100644 index b007ad72ae3fe..0000000000000 --- a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/progressing.yaml +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index 3f3c1866793d8..0000000000000 --- a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_green.yaml +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index fc2433c8076a8..0000000000000 --- a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_red.yaml +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 831ee281ef02d..0000000000000 --- a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow.yaml +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index d652b5a55d0ff..0000000000000 --- a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow_single_node.yaml +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index dbcca36c9e691..0000000000000 --- a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/unknown.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: beat.k8s.elastic.co/v1beta1 -kind: Beat -metadata: - name: quickstart -spec: - version: 8.8.8 - type: metricbeat -status: {} diff --git a/resource_customizations/camel.apache.org/Integration/health.lua b/resource_customizations/camel.apache.org/Integration/health.lua deleted file mode 100644 index d2c7494e36bc2..0000000000000 --- a/resource_customizations/camel.apache.org/Integration/health.lua +++ /dev/null @@ -1,24 +0,0 @@ -local hs = {} -if obj.status ~= nil then - if obj.status.conditions ~= nil then - for i, condition in ipairs(obj.status.conditions) do - -- Let's check if something is wrong with the CRD deployment - if condition.type == "Ready" and condition.status == "False" then - hs.status = "Degraded" - hs.message = condition.message - return hs - end - -- Let's check if things are healthy with the CRD deployment - if condition.type == "Ready" and condition.status == "True" then - hs.status = "Healthy" - hs.message = condition.message - return hs - end - end - end -end - --- Otherwise let's assume that we are still busy building/deploying the Integration -hs.status = "Progressing" -hs.message = "Waiting for Integration" -return hs diff --git a/resource_customizations/camel.apache.org/Integration/health_test.yaml b/resource_customizations/camel.apache.org/Integration/health_test.yaml deleted file mode 100644 index 44d6fb38f2507..0000000000000 --- a/resource_customizations/camel.apache.org/Integration/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: "Waiting for Integration" - inputPath: testdata/progressing.yaml -- healthStatus: - status: Healthy - message: "" - inputPath: testdata/healthy.yaml -- healthStatus: - status: Degraded - message: "back-off 40s restarting failed container=integration pod=camelk-example-deployment" - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/camel.apache.org/Integration/testdata/degraded.yaml b/resource_customizations/camel.apache.org/Integration/testdata/degraded.yaml deleted file mode 100644 index 40b6e69641f04..0000000000000 --- a/resource_customizations/camel.apache.org/Integration/testdata/degraded.yaml +++ /dev/null @@ -1,58 +0,0 @@ -apiVersion: camel.apache.org/v1 -kind: Integration -metadata: - annotations: - camel.apache.org/operator.id: camel-k - generation: 1 - name: camelk-example - namespace: default -spec: - sources: - - content: | - from('timer:tick?period=3000') - .setBody().constant('Hello world from Camel-K') - .to('log:info') - name: camelk-example.groovy - traits: {} -status: - conditions: - - firstTruthyTime: "2024-01-26T16:01:50Z" - lastTransitionTime: "2024-01-26T16:01:50Z" - lastUpdateTime: "2024-01-26T16:01:50Z" - message: camel-k/camel-k - reason: IntegrationPlatformAvailable - status: "True" - type: IntegrationPlatformAvailable - - firstTruthyTime: "2024-01-26T16:01:52Z" - lastTransitionTime: "2024-01-26T16:01:52Z" - lastUpdateTime: "2024-01-26T16:01:52Z" - message: kit-cmkkksgve68c73e60i60 - reason: IntegrationKitAvailable - status: "True" - type: IntegrationKitAvailable - - lastTransitionTime: "2024-01-26T16:01:52Z" - lastUpdateTime: "2024-01-26T16:01:52Z" - message: different controller strategy used (knative-service) - reason: CronJobNotAvailableReason - status: "False" - type: CronJobAvailable - - lastTransitionTime: "2024-01-26T16:01:52Z" - lastUpdateTime: "2024-01-26T16:01:52Z" - message: 'controller strategy: knative-service' - reason: DeploymentAvailable - status: "False" - type: DeploymentAvailable - - firstTruthyTime: "2024-01-26T16:01:52Z" - lastTransitionTime: "2024-01-26T16:01:52Z" - lastUpdateTime: "2024-01-26T16:01:52Z" - message: Knative service name is api-example - reason: KnativeServiceAvailable - status: "True" - type: KnativeServiceAvailable - - lastTransitionTime: "2024-01-26T16:01:52Z" - lastUpdateTime: "2024-01-26T16:02:55Z" - message: back-off 40s restarting failed container=integration pod=camelk-example-deployment - reason: Error - status: "False" - type: Ready - phase: Error diff --git a/resource_customizations/camel.apache.org/Integration/testdata/healthy.yaml b/resource_customizations/camel.apache.org/Integration/testdata/healthy.yaml deleted file mode 100644 index 6fb5e5e462110..0000000000000 --- a/resource_customizations/camel.apache.org/Integration/testdata/healthy.yaml +++ /dev/null @@ -1,58 +0,0 @@ -apiVersion: camel.apache.org/v1 -kind: Integration -metadata: - annotations: - camel.apache.org/operator.id: camel-k - generation: 1 - name: camelk-example - namespace: default -spec: - sources: - - content: | - from('timer:tick?period=3000') - .setBody().constant('Hello world from Camel-K') - .to('log:info') - name: camelk-example.groovy - traits: {} -status: - conditions: - - firstTruthyTime: "2024-01-26T09:13:16Z" - lastTransitionTime: "2024-01-26T09:13:16Z" - lastUpdateTime: "2024-01-26T09:13:16Z" - message: camel-k/camel-k - reason: IntegrationPlatformAvailable - status: "True" - type: IntegrationPlatformAvailable - - firstTruthyTime: "2024-01-26T09:13:19Z" - lastTransitionTime: "2024-01-26T09:13:19Z" - lastUpdateTime: "2024-01-26T09:13:19Z" - message: kit-cmkkksgve68c73e60i60 - reason: IntegrationKitAvailable - status: "True" - type: IntegrationKitAvailable - - lastTransitionTime: "2024-01-26T09:13:19Z" - lastUpdateTime: "2024-01-26T09:13:19Z" - message: different controller strategy used (knative-service) - reason: CronJobNotAvailableReason - status: "False" - type: CronJobAvailable - - lastTransitionTime: "2024-01-26T09:13:19Z" - lastUpdateTime: "2024-01-26T09:13:19Z" - message: 'controller strategy: knative-service' - reason: DeploymentAvailable - status: "False" - type: DeploymentAvailable - - firstTruthyTime: "2024-01-26T09:13:19Z" - lastTransitionTime: "2024-01-26T09:13:19Z" - lastUpdateTime: "2024-01-26T09:13:19Z" - message: Knative service name is camelk-example - reason: KnativeServiceAvailable - status: "True" - type: KnativeServiceAvailable - - firstTruthyTime: "2024-01-26T09:13:31Z" - lastTransitionTime: "2024-01-26T09:13:31Z" - lastUpdateTime: "2024-01-26T09:13:31Z" - reason: KnativeServiceReady - status: "True" - type: Ready - phase: Running diff --git a/resource_customizations/camel.apache.org/Integration/testdata/progressing.yaml b/resource_customizations/camel.apache.org/Integration/testdata/progressing.yaml deleted file mode 100644 index 58e7b4af64db6..0000000000000 --- a/resource_customizations/camel.apache.org/Integration/testdata/progressing.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: camel.apache.org/v1 -kind: Integration -metadata: - annotations: - camel.apache.org/operator.id: camel-k - generation: 1 - name: camelk-example - namespace: default -spec: - sources: - - content: | - from('timer:tick?period=3000') - .setBody().constant('Hello world from Camel-K') - .to('log:info') - name: camelk-example.groovy - traits: {} -status: - conditions: - - firstTruthyTime: "2024-01-26T09:13:16Z" - lastTransitionTime: "2024-01-26T09:13:16Z" - lastUpdateTime: "2024-01-26T09:13:16Z" - message: camel-k/camel-k - reason: IntegrationPlatformAvailable - status: "True" - type: IntegrationPlatformAvailable - - firstTruthyTime: "2024-01-26T09:13:19Z" - lastTransitionTime: "2024-01-26T09:13:19Z" - lastUpdateTime: "2024-01-26T09:13:19Z" - message: kit-cmkkksgve68c73e60i60 - reason: IntegrationKitAvailable - status: "True" - type: IntegrationKitAvailable - - lastTransitionTime: "2024-01-26T09:13:19Z" - lastUpdateTime: "2024-01-26T09:13:19Z" - message: different controller strategy used (knative-service) - reason: CronJobNotAvailableReason - status: "False" - type: CronJobAvailable - phase: Deploying \ No newline at end of file diff --git a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/health.lua b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/health.lua deleted file mode 100644 index 3e07226b3cf89..0000000000000 --- a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/health.lua +++ /dev/null @@ -1,42 +0,0 @@ -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 deleted file mode 100644 index 981a6000ecb88..0000000000000 --- a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -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 deleted file mode 100644 index 80ea7930574ac..0000000000000 --- a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/degraded_reconcileError.yaml +++ /dev/null @@ -1,96 +0,0 @@ -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 deleted file mode 100644 index 23d0287445e83..0000000000000 --- a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/healthy.yaml +++ /dev/null @@ -1,92 +0,0 @@ -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 deleted file mode 100644 index 3dbde7e040867..0000000000000 --- a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing.yaml +++ /dev/null @@ -1,92 +0,0 @@ -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 deleted file mode 100644 index 122ab330d593b..0000000000000 --- a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_creating.yaml +++ /dev/null @@ -1,92 +0,0 @@ -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 deleted file mode 100644 index 2985ec2dea657..0000000000000 --- a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noStatus.yaml +++ /dev/null @@ -1,82 +0,0 @@ -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 deleted file mode 100644 index 7a47b0f48eea7..0000000000000 --- a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noavailable.yaml +++ /dev/null @@ -1,88 +0,0 @@ -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 deleted file mode 100644 index d15713737ff72..0000000000000 --- a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/suspended.yaml +++ /dev/null @@ -1,94 +0,0 @@ -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/cluster.x-k8s.io/MachinePool/health.lua b/resource_customizations/cluster.x-k8s.io/MachinePool/health.lua deleted file mode 100644 index 521aa9a61161a..0000000000000 --- a/resource_customizations/cluster.x-k8s.io/MachinePool/health.lua +++ /dev/null @@ -1,48 +0,0 @@ --- Reference CRD can be found here: --- https://doc.crds.dev/github.com/kubernetes-sigs/cluster-api/cluster.x-k8s.io/MachinePool/v1beta1@v1.8.1 - -function getStatusBasedOnPhase(obj, hs) - -- Phases can be found here: - -- https://github.com/kubernetes-sigs/cluster-api/blob/release-1.8/exp/api/v1beta1/machinepool_types.go#L139-L182 - if obj.status ~= nil and obj.status.phase ~= nil then - hs.message = "MachinePool is " .. obj.status.phase - if obj.status.phase == "Running" then - hs.status = "Healthy" - end - if obj.status.phase == "Failed" or obj.status.phase == "Unknown" then - hs.status = "Degraded" - end - end - return hs -end - -function getConditionStatuses(obj, hs) - local extraInfo = "" - if obj.status ~= nil and obj.status.conditions ~= nil then - for i, condition in ipairs(obj.status.conditions) do - if condition.type ~= nil and condition.status == "False" then - if extraInfo ~= "" then - extraInfo = extraInfo .. ", " - end - extraInfo = extraInfo .. "Not " .. condition.type - if condition.reason ~= nil then - extraInfo = extraInfo .. " (" .. condition.reason .. ")" - end - end - end - end - if extraInfo ~= "" then - hs.message = hs.message .. ": " .. extraInfo - end - - return hs -end - -local hs = {} -hs.status = "Progressing" -hs.message = "" - -getStatusBasedOnPhase(obj, hs) -getConditionStatuses(obj, hs) - -return hs diff --git a/resource_customizations/cluster.x-k8s.io/MachinePool/health_test.yaml b/resource_customizations/cluster.x-k8s.io/MachinePool/health_test.yaml deleted file mode 100644 index 3ea490456e886..0000000000000 --- a/resource_customizations/cluster.x-k8s.io/MachinePool/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: 'MachinePool is Running' - inputPath: testdata/healthy_provisioned.yaml -- healthStatus: - status: Progressing - message: 'MachinePool is Provisioning: Not Ready (WaitingForInfrastructure), Not InfrastructureReady (WaitingForInfrastructure)' - inputPath: testdata/progressing_provisioning.yaml -- healthStatus: - status: Degraded - message: 'MachinePool is Failed' - inputPath: testdata/degraded_failed.yaml diff --git a/resource_customizations/cluster.x-k8s.io/MachinePool/testdata/degraded_failed.yaml b/resource_customizations/cluster.x-k8s.io/MachinePool/testdata/degraded_failed.yaml deleted file mode 100644 index 2079ebfdea584..0000000000000 --- a/resource_customizations/cluster.x-k8s.io/MachinePool/testdata/degraded_failed.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: cluster.x-k8s.io/v1beta1 -kind: MachinePool -metadata: - labels: - argocd.argoproj.io/instance: foo - cluster.x-k8s.io/cluster-name: foo - name: foo-pool - namespace: default -spec: - clusterName: foo - replicas: 3 - template: - metadata: {} - spec: - bootstrap: - dataSecretName: "" - clusterName: foo - infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 - kind: AWSManagedMachinePool - name: foo-pool - namespace: default - version: v1.30.0 -status: - phase: Failed diff --git a/resource_customizations/cluster.x-k8s.io/MachinePool/testdata/healthy_provisioned.yaml b/resource_customizations/cluster.x-k8s.io/MachinePool/testdata/healthy_provisioned.yaml deleted file mode 100644 index 02211d4950014..0000000000000 --- a/resource_customizations/cluster.x-k8s.io/MachinePool/testdata/healthy_provisioned.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: cluster.x-k8s.io/v1beta1 -kind: MachinePool -metadata: - labels: - argocd.argoproj.io/instance: foo - cluster.x-k8s.io/cluster-name: foo - name: foo-pool - namespace: default -spec: - clusterName: foo - replicas: 3 - template: - metadata: {} - spec: - bootstrap: - dataSecretName: "" - clusterName: foo - infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 - kind: AWSManagedMachinePool - name: foo-pool - namespace: default - version: v1.30.0 -status: - availableReplicas: 3 - bootstrapReady: true - conditions: - - lastTransitionTime: '2024-08-19T20:33:02Z' - status: 'True' - type: Ready - - lastTransitionTime: '2024-08-19T20:18:31Z' - status: 'True' - type: BootstrapReady - - lastTransitionTime: '2024-08-19T20:33:02Z' - status: 'True' - type: InfrastructureReady - - lastTransitionTime: '2024-08-19T20:18:31Z' - status: 'True' - type: ReplicasReady - infrastructureReady: true - nodeRefs: - - apiVersion: v1 - kind: Node - name: ip-18-232-50-123-ec2.internal - uid: e4b3a44f-1c2d-4fd3-bb9e-3b0e08787a5a - - apiVersion: v1 - kind: Node - name: ip-52-23-45-67-ec2.internal - uid: 2b9dabe5-3a1d-429a-985b-5e7ffb9649c6 - - apiVersion: v1 - kind: Node - name: ip-34-207-89-12-ec2.internal - uid: 6f94031a-d3e4-48f7-bc94-22bb9b687f5e - observedGeneration: 2 - phase: Running - readyReplicas: 3 - replicas: 3 diff --git a/resource_customizations/cluster.x-k8s.io/MachinePool/testdata/progressing_provisioning.yaml b/resource_customizations/cluster.x-k8s.io/MachinePool/testdata/progressing_provisioning.yaml deleted file mode 100644 index f287555ece532..0000000000000 --- a/resource_customizations/cluster.x-k8s.io/MachinePool/testdata/progressing_provisioning.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: cluster.x-k8s.io/v1beta1 -kind: MachinePool -metadata: - labels: - argocd.argoproj.io/instance: foo - cluster.x-k8s.io/cluster-name: foo - name: foo-pool - namespace: default -spec: - clusterName: foo - replicas: 3 - template: - metadata: {} - spec: - bootstrap: - dataSecretName: "" - clusterName: foo - infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 - kind: AWSManagedMachinePool - name: foo-pool - namespace: default - version: v1.30.0 -status: - bootstrapReady: true - conditions: - - lastTransitionTime: '2024-08-19T20:26:30Z' - reason: WaitingForInfrastructure - severity: Info - status: 'False' - type: Ready - - lastTransitionTime: '2024-08-19T20:26:30Z' - status: 'True' - type: BootstrapReady - - lastTransitionTime: '2024-08-19T20:26:30Z' - reason: WaitingForInfrastructure - severity: Info - status: 'False' - type: InfrastructureReady - - lastTransitionTime: '2024-08-19T20:26:30Z' - status: 'True' - type: ReplicasReady - observedGeneration: 1 - phase: Provisioning diff --git a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/health.lua b/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/health.lua deleted file mode 100644 index 4c975b9c21455..0000000000000 --- a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/health.lua +++ /dev/null @@ -1,47 +0,0 @@ -local health_status = { - status = "Progressing", - message = "Provisioning..." -} - --- If .status is nil or doesn't have conditions, then the control plane is not ready -if obj.status == nil or obj.status.conditions == nil then - return health_status -end - --- Accumulator for the error messages (could be multiple conditions in error state) -err_msg = "" - --- Iterate over the conditions to determine the health status -for i, condition in ipairs(obj.status.conditions) do - -- Check if the Ready condition is True, then the control plane is ready - if condition.type == "Ready" and condition.status == "True" then - health_status.status = "Healthy" - health_status.message = "Control plane is ready" - return health_status - end - - -- If we have a condition that is False and has an Error severity, then the control plane is in a degraded state - if condition.status == "False" and condition.severity == "Error" then - health_status.status = "Degraded" - err_msg = err_msg .. condition.message .. " " - end -end - --- If we have any error conditions, then the control plane is in a degraded state -if health_status.status == "Degraded" then - health_status.message = err_msg - return health_status -end - --- If .status.ready is False, then the control plane is not ready -if obj.status.ready == false then - health_status.status = "Progressing" - health_status.message = "Control plane is not ready (.status.ready is false)" - return health_status -end - --- If we reach this point, then the control plane is not ready and we don't have any error conditions -health_status.status = "Progressing" -health_status.message = "Control plane is not ready" - -return health_status diff --git a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/health_test.yaml b/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/health_test.yaml deleted file mode 100644 index 6b076583f1edb..0000000000000 --- a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: 'Control plane is ready' - inputPath: testdata/healthy.yaml -- healthStatus: - status: Progressing - message: 'Control plane is not ready (.status.ready is false)' - inputPath: testdata/progressing_ready_false.yaml -- healthStatus: - status: Progressing - message: 'Control plane is not ready' - inputPath: testdata/progressing_ready_true.yaml -- healthStatus: - status: Degraded - message: '7 of 10 completed failed reconciling OIDC provider for cluster: failed to create OIDC provider: error creating provider: LimitExceeded: Cannot exceed quota for OpenIdConnectProvidersPerAccount: 100 ' - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/degraded.yaml b/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/degraded.yaml deleted file mode 100644 index 5ddd95a6370ce..0000000000000 --- a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/degraded.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: controlplane.cluster.x-k8s.io/v1beta2 -kind: AWSManagedControlPlane -metadata: - name: test - namespace: ns-test - ownerReferences: - - apiVersion: cluster.x-k8s.io/v1beta1 - blockOwnerDeletion: true - controller: true - kind: Cluster - name: test -status: - conditions: - - lastTransitionTime: "2024-07-30T11:10:03Z" - status: "False" - severity: Error - message: "7 of 10 completed" - type: Ready - - lastTransitionTime: "2024-07-26T14:35:48Z" - status: "True" - type: ClusterSecurityGroupsReady - - lastTransitionTime: "2024-07-30T02:20:06Z" - status: "True" - type: EKSAddonsConfigured - - lastTransitionTime: "2024-07-26T14:43:57Z" - reason: created - severity: Info - status: "False" - type: EKSControlPlaneCreating - - lastTransitionTime: "2024-07-25T09:22:46Z" - message: "failed reconciling OIDC provider for cluster: failed to create OIDC provider: error creating provider: LimitExceeded: Cannot exceed quota for OpenIdConnectProvidersPerAccount: 100" - reason: EKSControlPlaneReconciliationFailed - severity: Error - status: "False" - - lastTransitionTime: "2024-07-30T11:10:03Z" - status: "True" - type: EKSControlPlaneReady - - lastTransitionTime: "2024-07-26T15:28:01Z" - status: "True" - type: IAMAuthenticatorConfigured - - lastTransitionTime: "2024-07-26T15:27:58Z" - status: "True" - type: IAMControlPlaneRolesReady - - lastTransitionTime: "2024-07-26T14:35:48Z" - status: "True" - type: SubnetsReady - - lastTransitionTime: "2024-07-26T14:35:46Z" - status: "True" - type: VpcReady - ready: true diff --git a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/healthy.yaml b/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/healthy.yaml deleted file mode 100644 index 4c27c83cc4cb5..0000000000000 --- a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/healthy.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: controlplane.cluster.x-k8s.io/v1beta2 -kind: AWSManagedControlPlane -metadata: - name: test - namespace: ns-test - ownerReferences: - - apiVersion: cluster.x-k8s.io/v1beta1 - blockOwnerDeletion: true - controller: true - kind: Cluster - name: test -status: - conditions: - - lastTransitionTime: "2024-07-30T11:10:03Z" - status: "True" - type: Ready - - lastTransitionTime: "2024-07-26T14:35:48Z" - status: "True" - type: ClusterSecurityGroupsReady - - lastTransitionTime: "2024-07-30T02:20:06Z" - status: "True" - type: EKSAddonsConfigured - - lastTransitionTime: "2024-07-26T14:43:57Z" - reason: created - severity: Info - status: "False" - type: EKSControlPlaneCreating - - lastTransitionTime: "2024-07-30T11:10:03Z" - status: "True" - type: EKSControlPlaneReady - - lastTransitionTime: "2024-07-30T13:05:45Z" - status: "True" - type: EKSIdentityProviderConfigured - - lastTransitionTime: "2024-07-26T15:28:01Z" - status: "True" - type: IAMAuthenticatorConfigured - - lastTransitionTime: "2024-07-26T15:27:58Z" - status: "True" - type: IAMControlPlaneRolesReady - - lastTransitionTime: "2024-07-26T14:35:48Z" - status: "True" - type: SubnetsReady - - lastTransitionTime: "2024-07-26T14:35:46Z" - status: "True" - type: VpcReady - ready: true diff --git a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/progressing_ready_false.yaml b/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/progressing_ready_false.yaml deleted file mode 100644 index 2c6199d55fbc3..0000000000000 --- a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/progressing_ready_false.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: controlplane.cluster.x-k8s.io/v1beta2 -kind: AWSManagedControlPlane -metadata: - name: test - namespace: ns-test - ownerReferences: - - apiVersion: cluster.x-k8s.io/v1beta1 - blockOwnerDeletion: true - controller: true - kind: Cluster - name: test -status: - conditions: - - lastTransitionTime: "2024-07-30T11:10:03Z" - status: "False" - type: Ready - - lastTransitionTime: "2024-07-26T14:35:48Z" - status: "True" - type: ClusterSecurityGroupsReady - - lastTransitionTime: "2024-07-30T02:20:06Z" - status: "True" - type: EKSAddonsConfigured - - lastTransitionTime: "2024-07-26T14:43:57Z" - reason: created - severity: Info - status: "False" - type: EKSControlPlaneCreating - - lastTransitionTime: "2024-07-30T11:10:03Z" - status: "True" - type: EKSControlPlaneReady - - lastTransitionTime: "2024-07-30T13:05:45Z" - status: "True" - type: EKSIdentityProviderConfigured - - lastTransitionTime: "2024-07-26T15:28:01Z" - status: "True" - type: IAMAuthenticatorConfigured - - lastTransitionTime: "2024-07-26T15:27:58Z" - status: "True" - type: IAMControlPlaneRolesReady - - lastTransitionTime: "2024-07-26T14:35:48Z" - status: "True" - type: SubnetsReady - - lastTransitionTime: "2024-07-26T14:35:46Z" - status: "True" - type: VpcReady - ready: false diff --git a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/progressing_ready_true.yaml b/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/progressing_ready_true.yaml deleted file mode 100644 index 09179d43e8f49..0000000000000 --- a/resource_customizations/controlplane.cluster.x-k8s.io/AWSManagedControlPlane/testdata/progressing_ready_true.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: controlplane.cluster.x-k8s.io/v1beta2 -kind: AWSManagedControlPlane -metadata: - name: test - namespace: ns-test - ownerReferences: - - apiVersion: cluster.x-k8s.io/v1beta1 - blockOwnerDeletion: true - controller: true - kind: Cluster - name: test -status: - conditions: - - lastTransitionTime: "2024-07-30T11:10:03Z" - status: "False" - type: Ready - - lastTransitionTime: "2024-07-26T14:35:48Z" - status: "True" - type: ClusterSecurityGroupsReady - - lastTransitionTime: "2024-07-30T02:20:06Z" - status: "True" - type: EKSAddonsConfigured - - lastTransitionTime: "2024-07-26T14:43:57Z" - reason: created - severity: Info - status: "False" - type: EKSControlPlaneCreating - - lastTransitionTime: "2024-07-30T11:10:03Z" - status: "True" - type: EKSControlPlaneReady - - lastTransitionTime: "2024-07-30T13:05:45Z" - status: "True" - type: EKSIdentityProviderConfigured - - lastTransitionTime: "2024-07-26T15:28:01Z" - status: "True" - type: IAMAuthenticatorConfigured - - lastTransitionTime: "2024-07-26T15:27:58Z" - status: "True" - type: IAMControlPlaneRolesReady - - lastTransitionTime: "2024-07-26T14:35:48Z" - status: "True" - type: SubnetsReady - - lastTransitionTime: "2024-07-26T14:35:46Z" - status: "True" - type: VpcReady - ready: true diff --git a/resource_customizations/core.humio.com/HumioAction/health.lua b/resource_customizations/core.humio.com/HumioAction/health.lua deleted file mode 100644 index a99a0eb2e0d59..0000000000000 --- a/resource_customizations/core.humio.com/HumioAction/health.lua +++ /dev/null @@ -1,30 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Exists" then - hs.status = "Healthy" - hs.message = "Component state: Exists." - end - if obj.status.state == "NotFound" then - hs.status = "Missing" - hs.message = "Component state: NotFound." - end - if obj.status.state == "ConfigError" then - hs.status = "Degraded" - hs.message = "Component state: ConfigError." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - end - return hs -end -return hs \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioAction/health_test.yaml b/resource_customizations/core.humio.com/HumioAction/health_test.yaml deleted file mode 100644 index f90baf9bccc9f..0000000000000 --- a/resource_customizations/core.humio.com/HumioAction/health_test.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "Component state: Exists." - inputPath: testdata/healthy.yaml -- healthStatus: - status: Missing - message: "Component state: NotFound." - inputPath: testdata/notfound.yaml -- healthStatus: - status: Degraded - message: "Component state: ConfigError." - inputPath: testdata/configerror.yaml -- healthStatus: - status: Unknown - message: "Component state: Unknown." - inputPath: testdata/unknown.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/progressing.yaml diff --git a/resource_customizations/core.humio.com/HumioAction/testdata/configerror.yaml b/resource_customizations/core.humio.com/HumioAction/testdata/configerror.yaml deleted file mode 100644 index 1c986292f21a4..0000000000000 --- a/resource_customizations/core.humio.com/HumioAction/testdata/configerror.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioAction -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: test-action-1 - namespace: humio - resourceVersion: '10768054' - uid: f339ddf1-3b3e-49e0-80cc-7f583dce532e -spec: - humioRepositoryProperties: - ingestTokenSource: - secretKeyRef: - key: test-token - name: token - managedClusterName: example-humiocluster - name: trigger_action - viewName: example-1 -status: - state: ConfigError diff --git a/resource_customizations/core.humio.com/HumioAction/testdata/healthy.yaml b/resource_customizations/core.humio.com/HumioAction/testdata/healthy.yaml deleted file mode 100644 index f6dfeffcf31a5..0000000000000 --- a/resource_customizations/core.humio.com/HumioAction/testdata/healthy.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioAction -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: test-action-1 - namespace: humio - resourceVersion: '10768054' - uid: f339ddf1-3b3e-49e0-80cc-7f583dce532e -spec: - humioRepositoryProperties: - ingestTokenSource: - secretKeyRef: - key: test-token - name: token - managedClusterName: example-humiocluster - name: trigger_action - viewName: example-1 -status: - state: Exists \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioAction/testdata/notfound.yaml b/resource_customizations/core.humio.com/HumioAction/testdata/notfound.yaml deleted file mode 100644 index 2743fb1b7b54b..0000000000000 --- a/resource_customizations/core.humio.com/HumioAction/testdata/notfound.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioAction -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: test-action-1 - namespace: humio - resourceVersion: '10768054' - uid: f339ddf1-3b3e-49e0-80cc-7f583dce532e -spec: - humioRepositoryProperties: - ingestTokenSource: - secretKeyRef: - key: test-token - name: token - managedClusterName: example-humiocluster - name: trigger_action - viewName: example-1 -status: - state: NotFound \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioAction/testdata/progressing.yaml b/resource_customizations/core.humio.com/HumioAction/testdata/progressing.yaml deleted file mode 100644 index 8a43ffc4fab01..0000000000000 --- a/resource_customizations/core.humio.com/HumioAction/testdata/progressing.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioAction -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: test-action-1 - namespace: humio - resourceVersion: '10768054' - uid: f339ddf1-3b3e-49e0-80cc-7f583dce532e -spec: - humioRepositoryProperties: - ingestTokenSource: - secretKeyRef: - key: test-token - name: token - managedClusterName: example-humiocluster - name: trigger_action - viewName: example-1 \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioAction/testdata/unknown.yaml b/resource_customizations/core.humio.com/HumioAction/testdata/unknown.yaml deleted file mode 100644 index e3ec98cd557b4..0000000000000 --- a/resource_customizations/core.humio.com/HumioAction/testdata/unknown.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioAction -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: test-action-1 - namespace: humio - resourceVersion: '10768054' - uid: f339ddf1-3b3e-49e0-80cc-7f583dce532e -spec: - humioRepositoryProperties: - ingestTokenSource: - secretKeyRef: - key: test-token - name: token - managedClusterName: example-humiocluster - name: trigger_action - viewName: example-1 -status: - state: Unknown diff --git a/resource_customizations/core.humio.com/HumioAlert/health.lua b/resource_customizations/core.humio.com/HumioAlert/health.lua deleted file mode 100644 index a99a0eb2e0d59..0000000000000 --- a/resource_customizations/core.humio.com/HumioAlert/health.lua +++ /dev/null @@ -1,30 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Exists" then - hs.status = "Healthy" - hs.message = "Component state: Exists." - end - if obj.status.state == "NotFound" then - hs.status = "Missing" - hs.message = "Component state: NotFound." - end - if obj.status.state == "ConfigError" then - hs.status = "Degraded" - hs.message = "Component state: ConfigError." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - end - return hs -end -return hs \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioAlert/health_test.yaml b/resource_customizations/core.humio.com/HumioAlert/health_test.yaml deleted file mode 100644 index f90baf9bccc9f..0000000000000 --- a/resource_customizations/core.humio.com/HumioAlert/health_test.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "Component state: Exists." - inputPath: testdata/healthy.yaml -- healthStatus: - status: Missing - message: "Component state: NotFound." - inputPath: testdata/notfound.yaml -- healthStatus: - status: Degraded - message: "Component state: ConfigError." - inputPath: testdata/configerror.yaml -- healthStatus: - status: Unknown - message: "Component state: Unknown." - inputPath: testdata/unknown.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/progressing.yaml diff --git a/resource_customizations/core.humio.com/HumioAlert/testdata/configerror.yaml b/resource_customizations/core.humio.com/HumioAlert/testdata/configerror.yaml deleted file mode 100644 index 043d08d6a2d17..0000000000000 --- a/resource_customizations/core.humio.com/HumioAlert/testdata/configerror.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioAlert -metadata: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 3 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-alert-1 - namespace: humio - resourceVersion: '10768150' - uid: eb138512-0661-47c1-a056-0e53f3b5fa1f -spec: - actions: - - move-to-alerts - description: Error counts - labels: - - test-label - managedClusterName: example-humiocluster - name: example-alert - query: - queryString: '#repo = humio | error = true | count() | _count > 0' - start: 24h - silenced: false - throttleTimeMillis: 60000 - viewName: example-view -status: - state: ConfigError diff --git a/resource_customizations/core.humio.com/HumioAlert/testdata/healthy.yaml b/resource_customizations/core.humio.com/HumioAlert/testdata/healthy.yaml deleted file mode 100644 index c62feb15a2943..0000000000000 --- a/resource_customizations/core.humio.com/HumioAlert/testdata/healthy.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioAlert -metadata: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 3 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-alert-1 - namespace: humio - resourceVersion: '10768150' - uid: eb138512-0661-47c1-a056-0e53f3b5fa1f -spec: - actions: - - move-to-alerts - description: Error counts - labels: - - test-label - managedClusterName: example-humiocluster - name: example-alert - query: - queryString: '#repo = humio | error = true | count() | _count > 0' - start: 24h - silenced: false - throttleTimeMillis: 60000 - viewName: example-view -status: - state: Exists \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioAlert/testdata/notfound.yaml b/resource_customizations/core.humio.com/HumioAlert/testdata/notfound.yaml deleted file mode 100644 index f694bf3788e5e..0000000000000 --- a/resource_customizations/core.humio.com/HumioAlert/testdata/notfound.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioAlert -metadata: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 3 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-alert-1 - namespace: humio - resourceVersion: '10768150' - uid: eb138512-0661-47c1-a056-0e53f3b5fa1f -spec: - actions: - - move-to-alerts - description: Error counts - labels: - - test-label - managedClusterName: example-humiocluster - name: example-alert - query: - queryString: '#repo = humio | error = true | count() | _count > 0' - start: 24h - silenced: false - throttleTimeMillis: 60000 - viewName: example-view -status: - state: NotFound \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioAlert/testdata/progressing.yaml b/resource_customizations/core.humio.com/HumioAlert/testdata/progressing.yaml deleted file mode 100644 index 2740b020a9311..0000000000000 --- a/resource_customizations/core.humio.com/HumioAlert/testdata/progressing.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioAlert -metadata: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 3 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-alert-1 - namespace: humio - resourceVersion: '10768150' - uid: eb138512-0661-47c1-a056-0e53f3b5fa1f -spec: - actions: - - move-to-alerts - description: Error counts - labels: - - test-label - managedClusterName: example-humiocluster - name: example-alert - query: - queryString: '#repo = humio | error = true | count() | _count > 0' - start: 24h - silenced: false - throttleTimeMillis: 60000 - viewName: example-view \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioAlert/testdata/unknown.yaml b/resource_customizations/core.humio.com/HumioAlert/testdata/unknown.yaml deleted file mode 100644 index edbf4b3355866..0000000000000 --- a/resource_customizations/core.humio.com/HumioAlert/testdata/unknown.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioAlert -metadata: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 3 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-alert-1 - namespace: humio - resourceVersion: '10768150' - uid: eb138512-0661-47c1-a056-0e53f3b5fa1f -spec: - actions: - - move-to-alerts - description: Error counts - labels: - - test-label - managedClusterName: example-humiocluster - name: example-alert - query: - queryString: '#repo = humio | error = true | count() | _count > 0' - start: 24h - silenced: false - throttleTimeMillis: 60000 - viewName: example-view -status: - state: Unknown diff --git a/resource_customizations/core.humio.com/HumioCluster/health.lua b/resource_customizations/core.humio.com/HumioCluster/health.lua deleted file mode 100644 index 95a13ce855cec..0000000000000 --- a/resource_customizations/core.humio.com/HumioCluster/health.lua +++ /dev/null @@ -1,67 +0,0 @@ - -hs = { - status = "Progressing", - message = "Update in progress" -} - -if obj.status == nil then - hs.status= "Progressing" - if obj.status.message ~= nil then - hs.message = obj.status.message - end -end - -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Running" then - hs.status = "Healthy" - if obj.status.message ~= nil then - hs.message = obj.status.message - else - hs.message = "Cluster is in a healthy running state" - end - end - if obj.status.state == "Restarting" then - hs.status = "Progressing" - if obj.status.message ~= nil then - hs.message = obj.status.message - else - hs.message = "Cluster pods are being restarted" - end - end - if obj.status.state == "Upgrading" then - hs.status = "Progressing" - if obj.status.message ~= nil then - hs.message = obj.status.message - else - hs.message = "Cluster pods are being upgraded" - end - end - if obj.status.state == "ConfigError" then - hs.status = "Degraded" - if obj.status.message ~= nil then - hs.message = obj.status.message - else - hs.message = "User-provided cluster specification resulted in a configuration error" - end - end - if obj.status.state == "Pending" then - hs.status = "Progressing" - if obj.status.message ~= nil then - hs.message = obj.status.message - else - hs.message = "Cluster is waiting on resources to be provisioned" - end - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - if obj.status.message ~= nil then - hs.message = obj.status.message - else - hs.message = "Component state: Unknown." - end - end - end - return hs -end -return hs \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioCluster/health_test.yaml b/resource_customizations/core.humio.com/HumioCluster/health_test.yaml deleted file mode 100644 index cb3696d2cff93..0000000000000 --- a/resource_customizations/core.humio.com/HumioCluster/health_test.yaml +++ /dev/null @@ -1,29 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "Cluster is in a healthy running state" - inputPath: testdata/healthy.yaml -- healthStatus: - status: Progressing - message: "Cluster pods are being restarted" - inputPath: testdata/restarting.yaml -- healthStatus: - status: Progressing - message: "Cluster pods are being upgraded" - inputPath: testdata/upgrading.yaml -- healthStatus: - status: Progressing - message: "Cluster is waiting on resources to be provisioned" - inputPath: testdata/pending.yaml -- healthStatus: - status: Degraded - message: 'Secret "example-humiocluster-license" not found' - inputPath: testdata/configerror_custom.yaml -- healthStatus: - status: Degraded - message: 'User-provided cluster specification resulted in a configuration error' - inputPath: testdata/configerror.yaml -- healthStatus: - status: Unknown - message: "Component state: Unknown." - inputPath: testdata/unknown.yaml \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioCluster/testdata/configerror.yaml b/resource_customizations/core.humio.com/HumioCluster/testdata/configerror.yaml deleted file mode 100644 index 0998dd7deac86..0000000000000 --- a/resource_customizations/core.humio.com/HumioCluster/testdata/configerror.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioCluster -metadata: - creationTimestamp: '2022-12-09T05:48:10Z' - generation: 1 - labels: - app: humio - app.kubernetes.io/instance: humio-cluster-failtest - name: example-humiocluster - namespace: failtes -spec: - dataVolumePersistentVolumeClaimSpecTemplate: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: longhorn - digestPartitionsCount: 2 - image: 'humio/humio-core:latest' - license: - secretKeyRef: - key: data - name: example-humiocluster-license - storagePartitionsCount: 2 - targetReplicationFactor: 1 - tls: - enabled: false -status: - state: ConfigError diff --git a/resource_customizations/core.humio.com/HumioCluster/testdata/configerror_custom.yaml b/resource_customizations/core.humio.com/HumioCluster/testdata/configerror_custom.yaml deleted file mode 100644 index 545b9f33ba4ea..0000000000000 --- a/resource_customizations/core.humio.com/HumioCluster/testdata/configerror_custom.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioCluster -metadata: - creationTimestamp: '2022-12-09T05:48:10Z' - generation: 1 - labels: - app: humio - app.kubernetes.io/instance: humio-cluster-failtest - name: example-humiocluster - namespace: failtes -spec: - dataVolumePersistentVolumeClaimSpecTemplate: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: longhorn - digestPartitionsCount: 2 - image: 'humio/humio-core:latest' - license: - secretKeyRef: - key: data - name: example-humiocluster-license - storagePartitionsCount: 2 - targetReplicationFactor: 1 - tls: - enabled: false -status: - licenseStatus: {} - message: Secret "example-humiocluster-license" not found - observedGeneration: '1' - state: ConfigError diff --git a/resource_customizations/core.humio.com/HumioCluster/testdata/healthy.yaml b/resource_customizations/core.humio.com/HumioCluster/testdata/healthy.yaml deleted file mode 100644 index 4620df81e3159..0000000000000 --- a/resource_customizations/core.humio.com/HumioCluster/testdata/healthy.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioCluster -metadata: - creationTimestamp: '2022-12-09T05:48:10Z' - generation: 1 - labels: - app: humio - app.kubernetes.io/instance: humio-cluster-failtest - name: example-humiocluster - namespace: failtes -spec: - dataVolumePersistentVolumeClaimSpecTemplate: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: longhorn - digestPartitionsCount: 2 - image: 'humio/humio-core:latest' - license: - secretKeyRef: - key: data - name: example-humiocluster-license - storagePartitionsCount: 2 - targetReplicationFactor: 1 - tls: - enabled: false -status: - state: Running diff --git a/resource_customizations/core.humio.com/HumioCluster/testdata/pending.yaml b/resource_customizations/core.humio.com/HumioCluster/testdata/pending.yaml deleted file mode 100644 index 9cb591f5e56d9..0000000000000 --- a/resource_customizations/core.humio.com/HumioCluster/testdata/pending.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioCluster -metadata: - creationTimestamp: '2022-12-09T05:48:10Z' - generation: 1 - labels: - app: humio - app.kubernetes.io/instance: humio-cluster-failtest - name: example-humiocluster - namespace: failtes -spec: - dataVolumePersistentVolumeClaimSpecTemplate: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: longhorn - digestPartitionsCount: 2 - image: 'humio/humio-core:latest' - license: - secretKeyRef: - key: data - name: example-humiocluster-license - storagePartitionsCount: 2 - targetReplicationFactor: 1 - tls: - enabled: false -status: - state: Pending diff --git a/resource_customizations/core.humio.com/HumioCluster/testdata/progressing.yaml b/resource_customizations/core.humio.com/HumioCluster/testdata/progressing.yaml deleted file mode 100644 index 78a6fd893dd1c..0000000000000 --- a/resource_customizations/core.humio.com/HumioCluster/testdata/progressing.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioCluster -metadata: - creationTimestamp: '2022-12-09T05:48:10Z' - generation: 1 - labels: - app: humio - app.kubernetes.io/instance: humio-cluster-failtest - name: example-humiocluster - namespace: failtes -spec: - dataVolumePersistentVolumeClaimSpecTemplate: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: longhorn - digestPartitionsCount: 2 - image: 'humio/humio-core:latest' - license: - secretKeyRef: - key: data - name: example-humiocluster-license - storagePartitionsCount: 2 - targetReplicationFactor: 1 - tls: - enabled: false diff --git a/resource_customizations/core.humio.com/HumioCluster/testdata/restarting.yaml b/resource_customizations/core.humio.com/HumioCluster/testdata/restarting.yaml deleted file mode 100644 index 84943fe0484c1..0000000000000 --- a/resource_customizations/core.humio.com/HumioCluster/testdata/restarting.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioCluster -metadata: - creationTimestamp: '2022-12-09T05:48:10Z' - generation: 1 - labels: - app: humio - app.kubernetes.io/instance: humio-cluster-failtest - name: example-humiocluster - namespace: failtes -spec: - dataVolumePersistentVolumeClaimSpecTemplate: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: longhorn - digestPartitionsCount: 2 - image: 'humio/humio-core:latest' - license: - secretKeyRef: - key: data - name: example-humiocluster-license - storagePartitionsCount: 2 - targetReplicationFactor: 1 - tls: - enabled: false -status: - state: Restarting diff --git a/resource_customizations/core.humio.com/HumioCluster/testdata/unknown.yaml b/resource_customizations/core.humio.com/HumioCluster/testdata/unknown.yaml deleted file mode 100644 index 1d08a2e885795..0000000000000 --- a/resource_customizations/core.humio.com/HumioCluster/testdata/unknown.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioCluster -metadata: - creationTimestamp: '2022-12-09T05:48:10Z' - generation: 1 - labels: - app: humio - app.kubernetes.io/instance: humio-cluster-failtest - name: example-humiocluster - namespace: failtes -spec: - dataVolumePersistentVolumeClaimSpecTemplate: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: longhorn - digestPartitionsCount: 2 - image: 'humio/humio-core:latest' - license: - secretKeyRef: - key: data - name: example-humiocluster-license - storagePartitionsCount: 2 - targetReplicationFactor: 1 - tls: - enabled: false -status: - state: Unknown - - - diff --git a/resource_customizations/core.humio.com/HumioCluster/testdata/upgrading.yaml b/resource_customizations/core.humio.com/HumioCluster/testdata/upgrading.yaml deleted file mode 100644 index 76a322288aab0..0000000000000 --- a/resource_customizations/core.humio.com/HumioCluster/testdata/upgrading.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioCluster -metadata: - creationTimestamp: '2022-12-09T05:48:10Z' - generation: 1 - labels: - app: humio - app.kubernetes.io/instance: humio-cluster-failtest - name: example-humiocluster - namespace: failtes -spec: - dataVolumePersistentVolumeClaimSpecTemplate: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi - storageClassName: longhorn - digestPartitionsCount: 2 - image: 'humio/humio-core:latest' - license: - secretKeyRef: - key: data - name: example-humiocluster-license - storagePartitionsCount: 2 - targetReplicationFactor: 1 - tls: - enabled: false -status: - state: Upgrading - - - diff --git a/resource_customizations/core.humio.com/HumioIngestToken/health.lua b/resource_customizations/core.humio.com/HumioIngestToken/health.lua deleted file mode 100644 index a99a0eb2e0d59..0000000000000 --- a/resource_customizations/core.humio.com/HumioIngestToken/health.lua +++ /dev/null @@ -1,30 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Exists" then - hs.status = "Healthy" - hs.message = "Component state: Exists." - end - if obj.status.state == "NotFound" then - hs.status = "Missing" - hs.message = "Component state: NotFound." - end - if obj.status.state == "ConfigError" then - hs.status = "Degraded" - hs.message = "Component state: ConfigError." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - end - return hs -end -return hs \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioIngestToken/health_test.yaml b/resource_customizations/core.humio.com/HumioIngestToken/health_test.yaml deleted file mode 100644 index f90baf9bccc9f..0000000000000 --- a/resource_customizations/core.humio.com/HumioIngestToken/health_test.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "Component state: Exists." - inputPath: testdata/healthy.yaml -- healthStatus: - status: Missing - message: "Component state: NotFound." - inputPath: testdata/notfound.yaml -- healthStatus: - status: Degraded - message: "Component state: ConfigError." - inputPath: testdata/configerror.yaml -- healthStatus: - status: Unknown - message: "Component state: Unknown." - inputPath: testdata/unknown.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/progressing.yaml diff --git a/resource_customizations/core.humio.com/HumioIngestToken/testdata/configerror.yaml b/resource_customizations/core.humio.com/HumioIngestToken/testdata/configerror.yaml deleted file mode 100644 index df1ed30141ded..0000000000000 --- a/resource_customizations/core.humio.com/HumioIngestToken/testdata/configerror.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioIngestToken -metadata: - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: test-token-1 - namespace: humio - resourceVersion: '10768058' - uid: f0a51e3d-8b64-483c-99fa-d7184a840707 -spec: - managedClusterName: example-humiocluster - name: test-token - parserName: json - repositoryName: example-1 - tokenSecretName: example-test-token-1 -status: - state: ConfigError diff --git a/resource_customizations/core.humio.com/HumioIngestToken/testdata/healthy.yaml b/resource_customizations/core.humio.com/HumioIngestToken/testdata/healthy.yaml deleted file mode 100644 index 3eb03b98569b8..0000000000000 --- a/resource_customizations/core.humio.com/HumioIngestToken/testdata/healthy.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioIngestToken -metadata: - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: test-token-1 - namespace: humio - resourceVersion: '10768058' - uid: f0a51e3d-8b64-483c-99fa-d7184a840707 -spec: - managedClusterName: example-humiocluster - name: test-token - parserName: json - repositoryName: example-1 - tokenSecretName: example-test-token-1 -status: - state: Exists diff --git a/resource_customizations/core.humio.com/HumioIngestToken/testdata/notfound.yaml b/resource_customizations/core.humio.com/HumioIngestToken/testdata/notfound.yaml deleted file mode 100644 index 6d36afdc1ef04..0000000000000 --- a/resource_customizations/core.humio.com/HumioIngestToken/testdata/notfound.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioIngestToken -metadata: - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: test-token-1 - namespace: humio - resourceVersion: '10768058' - uid: f0a51e3d-8b64-483c-99fa-d7184a840707 -spec: - managedClusterName: example-humiocluster - name: test-token - parserName: json - repositoryName: example-1 - tokenSecretName: example-test-token-1 -status: - state: NotFound \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioIngestToken/testdata/progressing.yaml b/resource_customizations/core.humio.com/HumioIngestToken/testdata/progressing.yaml deleted file mode 100644 index 5dd35fc65c62a..0000000000000 --- a/resource_customizations/core.humio.com/HumioIngestToken/testdata/progressing.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioIngestToken -metadata: - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: test-token-1 - namespace: humio - resourceVersion: '10768058' - uid: f0a51e3d-8b64-483c-99fa-d7184a840707 -spec: - managedClusterName: example-humiocluster - name: test-token - parserName: json - repositoryName: example-1 - tokenSecretName: example-test-token-1 \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioIngestToken/testdata/unknown.yaml b/resource_customizations/core.humio.com/HumioIngestToken/testdata/unknown.yaml deleted file mode 100644 index 3b5a50e620823..0000000000000 --- a/resource_customizations/core.humio.com/HumioIngestToken/testdata/unknown.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioIngestToken -metadata: - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: test-token-1 - namespace: humio - resourceVersion: '10768058' - uid: f0a51e3d-8b64-483c-99fa-d7184a840707 -spec: - managedClusterName: example-humiocluster - name: test-token - parserName: json - repositoryName: example-1 - tokenSecretName: example-test-token-1 -status: - state: Unknown diff --git a/resource_customizations/core.humio.com/HumioParser/health.lua b/resource_customizations/core.humio.com/HumioParser/health.lua deleted file mode 100644 index a99a0eb2e0d59..0000000000000 --- a/resource_customizations/core.humio.com/HumioParser/health.lua +++ /dev/null @@ -1,30 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Exists" then - hs.status = "Healthy" - hs.message = "Component state: Exists." - end - if obj.status.state == "NotFound" then - hs.status = "Missing" - hs.message = "Component state: NotFound." - end - if obj.status.state == "ConfigError" then - hs.status = "Degraded" - hs.message = "Component state: ConfigError." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - end - return hs -end -return hs \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioParser/health_test.yaml b/resource_customizations/core.humio.com/HumioParser/health_test.yaml deleted file mode 100644 index f90baf9bccc9f..0000000000000 --- a/resource_customizations/core.humio.com/HumioParser/health_test.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "Component state: Exists." - inputPath: testdata/healthy.yaml -- healthStatus: - status: Missing - message: "Component state: NotFound." - inputPath: testdata/notfound.yaml -- healthStatus: - status: Degraded - message: "Component state: ConfigError." - inputPath: testdata/configerror.yaml -- healthStatus: - status: Unknown - message: "Component state: Unknown." - inputPath: testdata/unknown.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/progressing.yaml diff --git a/resource_customizations/core.humio.com/HumioParser/testdata/configerror.yaml b/resource_customizations/core.humio.com/HumioParser/testdata/configerror.yaml deleted file mode 100644 index f1334b3110848..0000000000000 --- a/resource_customizations/core.humio.com/HumioParser/testdata/configerror.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioParser -metadata: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-1-parser - namespace: humio - resourceVersion: '10768079' - uid: 5641590d-b8e9-42e8-a544-d0673bf0e1a2 -spec: - managedClusterName: example-humiocluster - name: example-1 - parserScript: > - /(?\S+)\s+-\s+(?\S+)\s+\[(?<@timestamp>.*)\]\s+"((?\S+)\s+(?\S+)?\s+(?\S+)?|-)"\s+(?\d+)\s+(?\S+)\s+"(?[^"]*)"\s+"(?[^"]*)"\s*(?(\d|\.)+)?/ - | parseTimestamp(format="dd/MMM/yyyy:HH:mm:ss Z", field=@timestamp) - repositoryName: example-repo - tagFields: - - statuscode - - client - testData: - - >- - 4.4.4.4 - - [12/Dec/2015:18:25:11 +0100] "GET /administrator/ - HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) - Gecko/20100101 Firefox/34.0" "-" - - >- - 4.4.4.4 - - [12/Dec/2015:18:25:11 +0100] "POST - /administrator/index.php HTTP/1.1" 200 4494 - "http://github.com/administrator/" "Mozilla/5.0 (Windows NT 6.0; - rv:34.0) Gecko/20100101 Firefox/34.0" "-" - - >- - 4.4.4.4 - - [12/Dec/2015:18:31:08 +0100] "GET /administrator/ - HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) - Gecko/20100101 Firefox/34.0" "-" -status: - state: ConfigError diff --git a/resource_customizations/core.humio.com/HumioParser/testdata/healthy.yaml b/resource_customizations/core.humio.com/HumioParser/testdata/healthy.yaml deleted file mode 100644 index 9a4d92d25c75a..0000000000000 --- a/resource_customizations/core.humio.com/HumioParser/testdata/healthy.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioParser -metadata: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-1-parser - namespace: humio - resourceVersion: '10768079' - uid: 5641590d-b8e9-42e8-a544-d0673bf0e1a2 -spec: - managedClusterName: example-humiocluster - name: example-1 - parserScript: > - /(?\S+)\s+-\s+(?\S+)\s+\[(?<@timestamp>.*)\]\s+"((?\S+)\s+(?\S+)?\s+(?\S+)?|-)"\s+(?\d+)\s+(?\S+)\s+"(?[^"]*)"\s+"(?[^"]*)"\s*(?(\d|\.)+)?/ - | parseTimestamp(format="dd/MMM/yyyy:HH:mm:ss Z", field=@timestamp) - repositoryName: example-repo - tagFields: - - statuscode - - client - testData: - - >- - 4.4.4.4 - - [12/Dec/2015:18:25:11 +0100] "GET /administrator/ - HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) - Gecko/20100101 Firefox/34.0" "-" - - >- - 4.4.4.4 - - [12/Dec/2015:18:25:11 +0100] "POST - /administrator/index.php HTTP/1.1" 200 4494 - "http://github.com/administrator/" "Mozilla/5.0 (Windows NT 6.0; - rv:34.0) Gecko/20100101 Firefox/34.0" "-" - - >- - 4.4.4.4 - - [12/Dec/2015:18:31:08 +0100] "GET /administrator/ - HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) - Gecko/20100101 Firefox/34.0" "-" -status: - state: Exists diff --git a/resource_customizations/core.humio.com/HumioParser/testdata/notfound.yaml b/resource_customizations/core.humio.com/HumioParser/testdata/notfound.yaml deleted file mode 100644 index 87565368f42c5..0000000000000 --- a/resource_customizations/core.humio.com/HumioParser/testdata/notfound.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioParser -metadata: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-1-parser - namespace: humio - resourceVersion: '10768079' - uid: 5641590d-b8e9-42e8-a544-d0673bf0e1a2 -spec: - managedClusterName: example-humiocluster - name: example-1 - parserScript: > - /(?\S+)\s+-\s+(?\S+)\s+\[(?<@timestamp>.*)\]\s+"((?\S+)\s+(?\S+)?\s+(?\S+)?|-)"\s+(?\d+)\s+(?\S+)\s+"(?[^"]*)"\s+"(?[^"]*)"\s*(?(\d|\.)+)?/ - | parseTimestamp(format="dd/MMM/yyyy:HH:mm:ss Z", field=@timestamp) - repositoryName: example-repo - tagFields: - - statuscode - - client - testData: - - >- - 4.4.4.4 - - [12/Dec/2015:18:25:11 +0100] "GET /administrator/ - HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) - Gecko/20100101 Firefox/34.0" "-" - - >- - 4.4.4.4 - - [12/Dec/2015:18:25:11 +0100] "POST - /administrator/index.php HTTP/1.1" 200 4494 - "http://github.com/administrator/" "Mozilla/5.0 (Windows NT 6.0; - rv:34.0) Gecko/20100101 Firefox/34.0" "-" - - >- - 4.4.4.4 - - [12/Dec/2015:18:31:08 +0100] "GET /administrator/ - HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) - Gecko/20100101 Firefox/34.0" "-" -status: - state: NotFound \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioParser/testdata/progressing.yaml b/resource_customizations/core.humio.com/HumioParser/testdata/progressing.yaml deleted file mode 100644 index e46557b7931e2..0000000000000 --- a/resource_customizations/core.humio.com/HumioParser/testdata/progressing.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioParser -metadata: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-1-parser - namespace: humio - resourceVersion: '10768079' - uid: 5641590d-b8e9-42e8-a544-d0673bf0e1a2 -spec: - managedClusterName: example-humiocluster - name: example-1 - parserScript: > - /(?\S+)\s+-\s+(?\S+)\s+\[(?<@timestamp>.*)\]\s+"((?\S+)\s+(?\S+)?\s+(?\S+)?|-)"\s+(?\d+)\s+(?\S+)\s+"(?[^"]*)"\s+"(?[^"]*)"\s*(?(\d|\.)+)?/ - | parseTimestamp(format="dd/MMM/yyyy:HH:mm:ss Z", field=@timestamp) - repositoryName: example-repo - tagFields: - - statuscode - - client - testData: - - >- - 4.4.4.4 - - [12/Dec/2015:18:25:11 +0100] "GET /administrator/ - HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) - Gecko/20100101 Firefox/34.0" "-" - - >- - 4.4.4.4 - - [12/Dec/2015:18:25:11 +0100] "POST - /administrator/index.php HTTP/1.1" 200 4494 - "http://github.com/administrator/" "Mozilla/5.0 (Windows NT 6.0; - rv:34.0) Gecko/20100101 Firefox/34.0" "-" - - >- - 4.4.4.4 - - [12/Dec/2015:18:31:08 +0100] "GET /administrator/ - HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) - Gecko/20100101 Firefox/34.0" "-" \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioParser/testdata/unknown.yaml b/resource_customizations/core.humio.com/HumioParser/testdata/unknown.yaml deleted file mode 100644 index 9def4c7cbba62..0000000000000 --- a/resource_customizations/core.humio.com/HumioParser/testdata/unknown.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioParser -metadata: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-1-parser - namespace: humio - resourceVersion: '10768079' - uid: 5641590d-b8e9-42e8-a544-d0673bf0e1a2 -spec: - managedClusterName: example-humiocluster - name: example-1 - parserScript: > - /(?\S+)\s+-\s+(?\S+)\s+\[(?<@timestamp>.*)\]\s+"((?\S+)\s+(?\S+)?\s+(?\S+)?|-)"\s+(?\d+)\s+(?\S+)\s+"(?[^"]*)"\s+"(?[^"]*)"\s*(?(\d|\.)+)?/ - | parseTimestamp(format="dd/MMM/yyyy:HH:mm:ss Z", field=@timestamp) - repositoryName: example-repo - tagFields: - - statuscode - - client - testData: - - >- - 4.4.4.4 - - [12/Dec/2015:18:25:11 +0100] "GET /administrator/ - HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) - Gecko/20100101 Firefox/34.0" "-" - - >- - 4.4.4.4 - - [12/Dec/2015:18:25:11 +0100] "POST - /administrator/index.php HTTP/1.1" 200 4494 - "http://github.com/administrator/" "Mozilla/5.0 (Windows NT 6.0; - rv:34.0) Gecko/20100101 Firefox/34.0" "-" - - >- - 4.4.4.4 - - [12/Dec/2015:18:31:08 +0100] "GET /administrator/ - HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) - Gecko/20100101 Firefox/34.0" "-" -status: - state: Unknown diff --git a/resource_customizations/core.humio.com/HumioRepository/health.lua b/resource_customizations/core.humio.com/HumioRepository/health.lua deleted file mode 100644 index a99a0eb2e0d59..0000000000000 --- a/resource_customizations/core.humio.com/HumioRepository/health.lua +++ /dev/null @@ -1,30 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Exists" then - hs.status = "Healthy" - hs.message = "Component state: Exists." - end - if obj.status.state == "NotFound" then - hs.status = "Missing" - hs.message = "Component state: NotFound." - end - if obj.status.state == "ConfigError" then - hs.status = "Degraded" - hs.message = "Component state: ConfigError." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - end - return hs -end -return hs \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioRepository/health_test.yaml b/resource_customizations/core.humio.com/HumioRepository/health_test.yaml deleted file mode 100644 index f90baf9bccc9f..0000000000000 --- a/resource_customizations/core.humio.com/HumioRepository/health_test.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "Component state: Exists." - inputPath: testdata/healthy.yaml -- healthStatus: - status: Missing - message: "Component state: NotFound." - inputPath: testdata/notfound.yaml -- healthStatus: - status: Degraded - message: "Component state: ConfigError." - inputPath: testdata/configerror.yaml -- healthStatus: - status: Unknown - message: "Component state: Unknown." - inputPath: testdata/unknown.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/progressing.yaml diff --git a/resource_customizations/core.humio.com/HumioRepository/testdata/configerror.yaml b/resource_customizations/core.humio.com/HumioRepository/testdata/configerror.yaml deleted file mode 100644 index 01286b1f77b0b..0000000000000 --- a/resource_customizations/core.humio.com/HumioRepository/testdata/configerror.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioRepository -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 3 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-repo-1 - namespace: humio - resourceVersion: '10768154' - uid: bb626adb-1cdd-4db2-baa8-ae5e30132603 -spec: - description: example description - managedClusterName: example-humiocluster - name: example-repo - retention: - ingestSizeInGB: 4 - storageSizeInGB: 50 - timeInDays: 730 -status: - state: ConfigError diff --git a/resource_customizations/core.humio.com/HumioRepository/testdata/healthy.yaml b/resource_customizations/core.humio.com/HumioRepository/testdata/healthy.yaml deleted file mode 100644 index 2b2443047faf9..0000000000000 --- a/resource_customizations/core.humio.com/HumioRepository/testdata/healthy.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioRepository -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 3 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-repo-1 - namespace: humio - resourceVersion: '10768154' - uid: bb626adb-1cdd-4db2-baa8-ae5e30132603 -spec: - description: example description - managedClusterName: example-humiocluster - name: example-repo - retention: - ingestSizeInGB: 4 - storageSizeInGB: 50 - timeInDays: 730 -status: - state: Exists \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioRepository/testdata/notfound.yaml b/resource_customizations/core.humio.com/HumioRepository/testdata/notfound.yaml deleted file mode 100644 index 86a3c6cfa10ef..0000000000000 --- a/resource_customizations/core.humio.com/HumioRepository/testdata/notfound.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioRepository -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 3 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-repo-1 - namespace: humio - resourceVersion: '10768154' - uid: bb626adb-1cdd-4db2-baa8-ae5e30132603 -spec: - description: example description - managedClusterName: example-humiocluster - name: example-repo - retention: - ingestSizeInGB: 4 - storageSizeInGB: 50 - timeInDays: 730 -status: - state: NotFound \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioRepository/testdata/progressing.yaml b/resource_customizations/core.humio.com/HumioRepository/testdata/progressing.yaml deleted file mode 100644 index 9c40d176e131e..0000000000000 --- a/resource_customizations/core.humio.com/HumioRepository/testdata/progressing.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioRepository -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 3 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-repo-1 - namespace: humio - resourceVersion: '10768154' - uid: bb626adb-1cdd-4db2-baa8-ae5e30132603 -spec: - description: example description - managedClusterName: example-humiocluster - name: example-repo - retention: - ingestSizeInGB: 4 - storageSizeInGB: 50 - timeInDays: 730 \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioRepository/testdata/unknown.yaml b/resource_customizations/core.humio.com/HumioRepository/testdata/unknown.yaml deleted file mode 100644 index 22d343a44eec8..0000000000000 --- a/resource_customizations/core.humio.com/HumioRepository/testdata/unknown.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioRepository -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 3 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-repo-1 - namespace: humio - resourceVersion: '10768154' - uid: bb626adb-1cdd-4db2-baa8-ae5e30132603 -spec: - description: example description - managedClusterName: example-humiocluster - name: example-repo - retention: - ingestSizeInGB: 4 - storageSizeInGB: 50 - timeInDays: 730 -status: - state: Unknown diff --git a/resource_customizations/core.humio.com/HumioView/health.lua b/resource_customizations/core.humio.com/HumioView/health.lua deleted file mode 100644 index e11956922fb7a..0000000000000 --- a/resource_customizations/core.humio.com/HumioView/health.lua +++ /dev/null @@ -1,26 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} -if obj.status ~= nil then - if obj.status.state ~= nil then - if obj.status.state == "Exists" then - hs.status = "Healthy" - hs.message = "Component state: Exists." - end - if obj.status.state == "NotFound" then - hs.status = "Missing" - hs.message = "Component state: NotFound." - end - if obj.status.state == "ConfigError" then - hs.status = "Degraded" - hs.message = "Component state: ConfigError." - end - if obj.status.state == "Unknown" then - hs.status = "Unknown" - hs.message = "Component state: Unknown." - end - end - return hs -end -return hs diff --git a/resource_customizations/core.humio.com/HumioView/health_test.yaml b/resource_customizations/core.humio.com/HumioView/health_test.yaml deleted file mode 100644 index f90baf9bccc9f..0000000000000 --- a/resource_customizations/core.humio.com/HumioView/health_test.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "Component state: Exists." - inputPath: testdata/healthy.yaml -- healthStatus: - status: Missing - message: "Component state: NotFound." - inputPath: testdata/notfound.yaml -- healthStatus: - status: Degraded - message: "Component state: ConfigError." - inputPath: testdata/configerror.yaml -- healthStatus: - status: Unknown - message: "Component state: Unknown." - inputPath: testdata/unknown.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/progressing.yaml diff --git a/resource_customizations/core.humio.com/HumioView/testdata/configerror.yaml b/resource_customizations/core.humio.com/HumioView/testdata/configerror.yaml deleted file mode 100644 index 917c1c8b5eaab..0000000000000 --- a/resource_customizations/core.humio.com/HumioView/testdata/configerror.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioView -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-view - namespace: humio - resourceVersion: '10768121' - uid: 2c49ac77-1be2-4e2c-a473-44f3f0e03453 -spec: - connections: - - filter: '*' - repositoryName: example-1 - - filter: '*' - repositoryName: example-2 - managedClusterName: example-humiocluster - name: example-view -status: - state: ConfigError diff --git a/resource_customizations/core.humio.com/HumioView/testdata/healthy.yaml b/resource_customizations/core.humio.com/HumioView/testdata/healthy.yaml deleted file mode 100644 index 7c75082be60a0..0000000000000 --- a/resource_customizations/core.humio.com/HumioView/testdata/healthy.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioView -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-view - namespace: humio - resourceVersion: '10768121' - uid: 2c49ac77-1be2-4e2c-a473-44f3f0e03453 -spec: - connections: - - filter: '*' - repositoryName: example-1 - - filter: '*' - repositoryName: example-2 - managedClusterName: example-humiocluster - name: example-view -status: - state: Exists \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioView/testdata/notfound.yaml b/resource_customizations/core.humio.com/HumioView/testdata/notfound.yaml deleted file mode 100644 index 2d929ef2ae584..0000000000000 --- a/resource_customizations/core.humio.com/HumioView/testdata/notfound.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioView -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-view - namespace: humio - resourceVersion: '10768121' - uid: 2c49ac77-1be2-4e2c-a473-44f3f0e03453 -spec: - connections: - - filter: '*' - repositoryName: example-1 - - filter: '*' - repositoryName: example-2 - managedClusterName: example-humiocluster - name: example-view -status: - state: NotFound \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioView/testdata/progressing.yaml b/resource_customizations/core.humio.com/HumioView/testdata/progressing.yaml deleted file mode 100644 index 4b44ad4cbbf83..0000000000000 --- a/resource_customizations/core.humio.com/HumioView/testdata/progressing.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioView -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-view - namespace: humio - resourceVersion: '10768121' - uid: 2c49ac77-1be2-4e2c-a473-44f3f0e03453 -spec: - connections: - - filter: '*' - repositoryName: example-1 - - filter: '*' - repositoryName: example-2 - managedClusterName: example-humiocluster - name: example-view \ No newline at end of file diff --git a/resource_customizations/core.humio.com/HumioView/testdata/unknown.yaml b/resource_customizations/core.humio.com/HumioView/testdata/unknown.yaml deleted file mode 100644 index ac4453048e0e7..0000000000000 --- a/resource_customizations/core.humio.com/HumioView/testdata/unknown.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: core.humio.com/v1alpha1 -kind: HumioView -metadata: - annotations: - creationTimestamp: '2022-12-08T02:03:07Z' - finalizers: - - core.humio.com/finalizer - generation: 1 - labels: - app.kubernetes.io/instance: humio-deploy - name: example-view - namespace: humio - resourceVersion: '10768121' - uid: 2c49ac77-1be2-4e2c-a473-44f3f0e03453 -spec: - connections: - - filter: '*' - repositoryName: example-1 - - filter: '*' - repositoryName: example-2 - managedClusterName: example-humiocluster - name: example-view -status: - state: Unknown diff --git a/resource_customizations/gateway.solo.io/Gateway/health.lua b/resource_customizations/gateway.solo.io/Gateway/health.lua deleted file mode 100644 index 4e0930462f38a..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/health.lua +++ /dev/null @@ -1,54 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} - -function getStatus(status) - -- Accepted - if status.state == "Accepted" or status.state == 1 then - hs.status = "Healthy" - hs.message = "The resource has been validated" - return hs - end - - -- Warning - if status.state == "Warning" or status.state == 3 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - -- Pending - if status.state == "Pending" or status.state == 0 then - hs.status = "Suspended" - hs.message = "The resource has not yet been validated" - return hs - end - - -- Rejected - if status.state == "Rejected" or status.state == 2 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - return hs -end - -if obj.status ~= nil then - -- Namespaced version of status - if obj.status.statuses ~= nil then - for i, namespace in pairs(obj.status.statuses) do - hs = getStatus(namespace) - if hs.status ~= "Progressing" then - return hs - end - end - end - - -- Older non-namespaced version of status - if obj.status.state ~= nil then - hs = getStatus(obj.status) - end -end -return hs diff --git a/resource_customizations/gateway.solo.io/Gateway/health_test.yaml b/resource_customizations/gateway.solo.io/Gateway/health_test.yaml deleted file mode 100644 index bd7846b5019f1..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/gloo-rejected.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/non-namespaced-gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/non-namespaced-gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/non-namespaced-gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/non-namespaced-gloo-rejected.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/gloo-no-status.yaml diff --git a/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-accepted.yaml b/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-accepted.yaml deleted file mode 100644 index 973516d5e2d52..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-accepted.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: Gateway -status: - statuses: - gloo-system: - reportedBy: gateway - state: Accepted - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Accepted diff --git a/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-no-status.yaml b/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-no-status.yaml deleted file mode 100644 index 5dc9f8050b830..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-no-status.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: Gateway diff --git a/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-pending.yaml b/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-pending.yaml deleted file mode 100644 index 643701b928ae9..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: Gateway -status: - statuses: - gloo-system: - reportedBy: gateway - state: Pending - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Pending diff --git a/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-rejected.yaml b/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-rejected.yaml deleted file mode 100644 index 8499dd8301dc7..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-rejected.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: Gateway -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: Rejected - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Rejected diff --git a/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-warning.yaml b/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-warning.yaml deleted file mode 100644 index 76c85c21d3228..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/testdata/gloo-warning.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: Gateway -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: Warning - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Warning diff --git a/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-accepted.yaml b/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-accepted.yaml deleted file mode 100644 index aa9d5f0eace9e..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-accepted.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: Gateway -status: - reportedBy: gateway - state: 1 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 1 diff --git a/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-pending.yaml b/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-pending.yaml deleted file mode 100644 index 4092cb131dbeb..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: Gateway -status: - statuses: - gloo-system: - reportedBy: gateway - state: 0 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 0 diff --git a/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-rejected.yaml b/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-rejected.yaml deleted file mode 100644 index 633e115a2b934..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-rejected.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: Gateway -status: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: 2 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 2 diff --git a/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-warning.yaml b/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-warning.yaml deleted file mode 100644 index c1693e9eb8a5d..0000000000000 --- a/resource_customizations/gateway.solo.io/Gateway/testdata/non-namespaced-gloo-warning.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: Gateway -status: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: 3 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 3 diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/health.lua b/resource_customizations/gateway.solo.io/MatchableHttpGateway/health.lua deleted file mode 100644 index 4e0930462f38a..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/health.lua +++ /dev/null @@ -1,54 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} - -function getStatus(status) - -- Accepted - if status.state == "Accepted" or status.state == 1 then - hs.status = "Healthy" - hs.message = "The resource has been validated" - return hs - end - - -- Warning - if status.state == "Warning" or status.state == 3 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - -- Pending - if status.state == "Pending" or status.state == 0 then - hs.status = "Suspended" - hs.message = "The resource has not yet been validated" - return hs - end - - -- Rejected - if status.state == "Rejected" or status.state == 2 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - return hs -end - -if obj.status ~= nil then - -- Namespaced version of status - if obj.status.statuses ~= nil then - for i, namespace in pairs(obj.status.statuses) do - hs = getStatus(namespace) - if hs.status ~= "Progressing" then - return hs - end - end - end - - -- Older non-namespaced version of status - if obj.status.state ~= nil then - hs = getStatus(obj.status) - end -end -return hs diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/health_test.yaml b/resource_customizations/gateway.solo.io/MatchableHttpGateway/health_test.yaml deleted file mode 100644 index bd7846b5019f1..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/gloo-rejected.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/non-namespaced-gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/non-namespaced-gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/non-namespaced-gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/non-namespaced-gloo-rejected.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/gloo-no-status.yaml diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-accepted.yaml b/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-accepted.yaml deleted file mode 100644 index a733e4a76a063..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-accepted.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: MatchableHttpGateway -status: - statuses: - gloo-system: - reportedBy: gateway - state: Accepted - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Accepted diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-no-status.yaml b/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-no-status.yaml deleted file mode 100644 index 58e3356e93e88..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-no-status.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: MatchableHttpGateway diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-pending.yaml b/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-pending.yaml deleted file mode 100644 index 12726ea261f62..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: MatchableHttpGateway -status: - statuses: - gloo-system: - reportedBy: gateway - state: Pending - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Pending diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-rejected.yaml b/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-rejected.yaml deleted file mode 100644 index 0656c3d0e61c0..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-rejected.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: MatchableHttpGateway -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: Rejected - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Rejected diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-warning.yaml b/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-warning.yaml deleted file mode 100644 index eb0bf59fb8857..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/gloo-warning.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: MatchableHttpGateway -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: Warning - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Warning diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-accepted.yaml b/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-accepted.yaml deleted file mode 100644 index 29e342a33dcc0..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-accepted.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: MatchableHttpGateway -status: - reportedBy: gateway - state: 1 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 1 diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-pending.yaml b/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-pending.yaml deleted file mode 100644 index f2fb7b99dbe0c..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: MatchableHttpGateway -status: - statuses: - gloo-system: - reportedBy: gateway - state: 0 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 0 diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-rejected.yaml b/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-rejected.yaml deleted file mode 100644 index 579e767a2e3b5..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-rejected.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: MatchableHttpGateway -status: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: 2 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 2 diff --git a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-warning.yaml b/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-warning.yaml deleted file mode 100644 index f7f430cd4fb58..0000000000000 --- a/resource_customizations/gateway.solo.io/MatchableHttpGateway/testdata/non-namespaced-gloo-warning.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: MatchableHttpGateway -status: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: 3 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 3 diff --git a/resource_customizations/gateway.solo.io/RouteOption/health.lua b/resource_customizations/gateway.solo.io/RouteOption/health.lua deleted file mode 100644 index 4e0930462f38a..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/health.lua +++ /dev/null @@ -1,54 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} - -function getStatus(status) - -- Accepted - if status.state == "Accepted" or status.state == 1 then - hs.status = "Healthy" - hs.message = "The resource has been validated" - return hs - end - - -- Warning - if status.state == "Warning" or status.state == 3 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - -- Pending - if status.state == "Pending" or status.state == 0 then - hs.status = "Suspended" - hs.message = "The resource has not yet been validated" - return hs - end - - -- Rejected - if status.state == "Rejected" or status.state == 2 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - return hs -end - -if obj.status ~= nil then - -- Namespaced version of status - if obj.status.statuses ~= nil then - for i, namespace in pairs(obj.status.statuses) do - hs = getStatus(namespace) - if hs.status ~= "Progressing" then - return hs - end - end - end - - -- Older non-namespaced version of status - if obj.status.state ~= nil then - hs = getStatus(obj.status) - end -end -return hs diff --git a/resource_customizations/gateway.solo.io/RouteOption/health_test.yaml b/resource_customizations/gateway.solo.io/RouteOption/health_test.yaml deleted file mode 100644 index bd7846b5019f1..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/gloo-rejected.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/non-namespaced-gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/non-namespaced-gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/non-namespaced-gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/non-namespaced-gloo-rejected.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/gloo-no-status.yaml diff --git a/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-accepted.yaml b/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-accepted.yaml deleted file mode 100644 index 5d352df7fb5e9..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-accepted.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteOption -status: - statuses: - gloo-system: - reportedBy: gateway - state: Accepted - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Accepted diff --git a/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-no-status.yaml b/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-no-status.yaml deleted file mode 100644 index c2bae5789d605..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-no-status.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteOption diff --git a/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-pending.yaml b/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-pending.yaml deleted file mode 100644 index e7f4cee8f9f0a..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteOption -status: - statuses: - gloo-system: - reportedBy: gateway - state: Pending - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Pending diff --git a/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-rejected.yaml b/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-rejected.yaml deleted file mode 100644 index b2398a860adda..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-rejected.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteOption -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: Rejected - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Rejected diff --git a/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-warning.yaml b/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-warning.yaml deleted file mode 100644 index 19d5a84153551..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/testdata/gloo-warning.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteOption -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: Warning - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Warning diff --git a/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-accepted.yaml b/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-accepted.yaml deleted file mode 100644 index f39d4d7864ed9..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-accepted.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteOption -status: - reportedBy: gateway - state: 1 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 1 diff --git a/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-pending.yaml b/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-pending.yaml deleted file mode 100644 index 5743058f17b64..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteOption -status: - statuses: - gloo-system: - reportedBy: gateway - state: 0 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 0 diff --git a/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-rejected.yaml b/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-rejected.yaml deleted file mode 100644 index 23742e2d548c3..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-rejected.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteOption -status: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: 2 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 2 diff --git a/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-warning.yaml b/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-warning.yaml deleted file mode 100644 index 70313101a4b00..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteOption/testdata/non-namespaced-gloo-warning.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteOption -status: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: 3 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 3 diff --git a/resource_customizations/gateway.solo.io/RouteTable/health.lua b/resource_customizations/gateway.solo.io/RouteTable/health.lua deleted file mode 100644 index 4e0930462f38a..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/health.lua +++ /dev/null @@ -1,54 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} - -function getStatus(status) - -- Accepted - if status.state == "Accepted" or status.state == 1 then - hs.status = "Healthy" - hs.message = "The resource has been validated" - return hs - end - - -- Warning - if status.state == "Warning" or status.state == 3 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - -- Pending - if status.state == "Pending" or status.state == 0 then - hs.status = "Suspended" - hs.message = "The resource has not yet been validated" - return hs - end - - -- Rejected - if status.state == "Rejected" or status.state == 2 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - return hs -end - -if obj.status ~= nil then - -- Namespaced version of status - if obj.status.statuses ~= nil then - for i, namespace in pairs(obj.status.statuses) do - hs = getStatus(namespace) - if hs.status ~= "Progressing" then - return hs - end - end - end - - -- Older non-namespaced version of status - if obj.status.state ~= nil then - hs = getStatus(obj.status) - end -end -return hs diff --git a/resource_customizations/gateway.solo.io/RouteTable/health_test.yaml b/resource_customizations/gateway.solo.io/RouteTable/health_test.yaml deleted file mode 100644 index bd7846b5019f1..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/gloo-rejected.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/non-namespaced-gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/non-namespaced-gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/non-namespaced-gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/non-namespaced-gloo-rejected.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/gloo-no-status.yaml diff --git a/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-accepted.yaml b/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-accepted.yaml deleted file mode 100644 index 3f63118e2a95d..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-accepted.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteTable -status: - statuses: - gloo-system: - reportedBy: gateway - state: Accepted - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Accepted diff --git a/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-no-status.yaml b/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-no-status.yaml deleted file mode 100644 index 806a4d98d3695..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-no-status.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteTable diff --git a/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-pending.yaml b/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-pending.yaml deleted file mode 100644 index 80b3937d9de4b..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteTable -status: - statuses: - gloo-system: - reportedBy: gateway - state: Pending - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Pending diff --git a/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-rejected.yaml b/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-rejected.yaml deleted file mode 100644 index 90ff94075c164..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-rejected.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteTable -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: Rejected - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Rejected diff --git a/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-warning.yaml b/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-warning.yaml deleted file mode 100644 index 874de924b22de..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/testdata/gloo-warning.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteTable -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: Warning - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Warning diff --git a/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-accepted.yaml b/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-accepted.yaml deleted file mode 100644 index 76ab4680fe25d..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-accepted.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteTable -status: - reportedBy: gateway - state: 1 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 1 diff --git a/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-pending.yaml b/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-pending.yaml deleted file mode 100644 index 1acce91498614..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteTable -status: - statuses: - gloo-system: - reportedBy: gateway - state: 0 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 0 diff --git a/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-rejected.yaml b/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-rejected.yaml deleted file mode 100644 index 824dd6c808388..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-rejected.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteTable -status: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: 2 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 2 diff --git a/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-warning.yaml b/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-warning.yaml deleted file mode 100644 index 0399ce71faf7c..0000000000000 --- a/resource_customizations/gateway.solo.io/RouteTable/testdata/non-namespaced-gloo-warning.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: RouteTable -status: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: 3 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 3 diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/health.lua b/resource_customizations/gateway.solo.io/VirtualHostOption/health.lua deleted file mode 100644 index 4e0930462f38a..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/health.lua +++ /dev/null @@ -1,54 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} - -function getStatus(status) - -- Accepted - if status.state == "Accepted" or status.state == 1 then - hs.status = "Healthy" - hs.message = "The resource has been validated" - return hs - end - - -- Warning - if status.state == "Warning" or status.state == 3 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - -- Pending - if status.state == "Pending" or status.state == 0 then - hs.status = "Suspended" - hs.message = "The resource has not yet been validated" - return hs - end - - -- Rejected - if status.state == "Rejected" or status.state == 2 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - return hs -end - -if obj.status ~= nil then - -- Namespaced version of status - if obj.status.statuses ~= nil then - for i, namespace in pairs(obj.status.statuses) do - hs = getStatus(namespace) - if hs.status ~= "Progressing" then - return hs - end - end - end - - -- Older non-namespaced version of status - if obj.status.state ~= nil then - hs = getStatus(obj.status) - end -end -return hs diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/health_test.yaml b/resource_customizations/gateway.solo.io/VirtualHostOption/health_test.yaml deleted file mode 100644 index bd7846b5019f1..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/gloo-rejected.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/non-namespaced-gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/non-namespaced-gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/non-namespaced-gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/non-namespaced-gloo-rejected.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/gloo-no-status.yaml diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-accepted.yaml b/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-accepted.yaml deleted file mode 100644 index 1e90fcd65847d..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-accepted.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualHostOption -status: - statuses: - gloo-system: - reportedBy: gateway - state: Accepted - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Accepted diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-no-status.yaml b/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-no-status.yaml deleted file mode 100644 index a2ee1a9c818b6..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-no-status.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualHostOption diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-pending.yaml b/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-pending.yaml deleted file mode 100644 index e9c9844a969a7..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualHostOption -status: - statuses: - gloo-system: - reportedBy: gateway - state: Pending - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Pending diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-rejected.yaml b/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-rejected.yaml deleted file mode 100644 index bcc19457905df..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-rejected.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualHostOption -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: Rejected - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Rejected diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-warning.yaml b/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-warning.yaml deleted file mode 100644 index 10c4112a74376..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/gloo-warning.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualHostOption -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: Warning - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Warning diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-accepted.yaml b/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-accepted.yaml deleted file mode 100644 index e204609d6c66c..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-accepted.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualHostOption -status: - reportedBy: gateway - state: 1 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 1 diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-pending.yaml b/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-pending.yaml deleted file mode 100644 index 5fa8b8949ef9c..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualHostOption -status: - statuses: - gloo-system: - reportedBy: gateway - state: 0 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 0 diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-rejected.yaml b/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-rejected.yaml deleted file mode 100644 index ecfca65272feb..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-rejected.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualHostOption -status: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: 2 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 2 diff --git a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-warning.yaml b/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-warning.yaml deleted file mode 100644 index 0b02cb0856938..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualHostOption/testdata/non-namespaced-gloo-warning.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualHostOption -status: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: 3 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 3 diff --git a/resource_customizations/gateway.solo.io/VirtualService/health.lua b/resource_customizations/gateway.solo.io/VirtualService/health.lua deleted file mode 100644 index 4e0930462f38a..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/health.lua +++ /dev/null @@ -1,54 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} - -function getStatus(status) - -- Accepted - if status.state == "Accepted" or status.state == 1 then - hs.status = "Healthy" - hs.message = "The resource has been validated" - return hs - end - - -- Warning - if status.state == "Warning" or status.state == 3 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - -- Pending - if status.state == "Pending" or status.state == 0 then - hs.status = "Suspended" - hs.message = "The resource has not yet been validated" - return hs - end - - -- Rejected - if status.state == "Rejected" or status.state == 2 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - return hs -end - -if obj.status ~= nil then - -- Namespaced version of status - if obj.status.statuses ~= nil then - for i, namespace in pairs(obj.status.statuses) do - hs = getStatus(namespace) - if hs.status ~= "Progressing" then - return hs - end - end - end - - -- Older non-namespaced version of status - if obj.status.state ~= nil then - hs = getStatus(obj.status) - end -end -return hs diff --git a/resource_customizations/gateway.solo.io/VirtualService/health_test.yaml b/resource_customizations/gateway.solo.io/VirtualService/health_test.yaml deleted file mode 100644 index bd7846b5019f1..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/gloo-rejected.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/non-namespaced-gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/non-namespaced-gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/non-namespaced-gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/non-namespaced-gloo-rejected.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/gloo-no-status.yaml diff --git a/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-accepted.yaml b/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-accepted.yaml deleted file mode 100644 index 99b339dda7c06..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-accepted.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualService -status: - statuses: - gloo-system: - reportedBy: gateway - state: Accepted - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Accepted diff --git a/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-no-status.yaml b/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-no-status.yaml deleted file mode 100644 index 6567086648737..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-no-status.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualService diff --git a/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-pending.yaml b/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-pending.yaml deleted file mode 100644 index f3a31057d4299..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualService -status: - statuses: - gloo-system: - reportedBy: gateway - state: Pending - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Pending diff --git a/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-rejected.yaml b/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-rejected.yaml deleted file mode 100644 index 47575747ac596..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-rejected.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualService -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: Rejected - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Rejected diff --git a/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-warning.yaml b/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-warning.yaml deleted file mode 100644 index 27ba16faa5f2c..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/testdata/gloo-warning.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualService -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: Warning - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Warning diff --git a/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-accepted.yaml b/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-accepted.yaml deleted file mode 100644 index 7606a02c7a1f8..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-accepted.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualService -status: - reportedBy: gateway - state: 1 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 1 diff --git a/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-pending.yaml b/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-pending.yaml deleted file mode 100644 index 1e8f4dc517be4..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualService -status: - statuses: - gloo-system: - reportedBy: gateway - state: 0 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 0 diff --git a/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-rejected.yaml b/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-rejected.yaml deleted file mode 100644 index a3f204753a6f2..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-rejected.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualService -status: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: 2 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 2 diff --git a/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-warning.yaml b/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-warning.yaml deleted file mode 100644 index 1c641e62b190d..0000000000000 --- a/resource_customizations/gateway.solo.io/VirtualService/testdata/non-namespaced-gloo-warning.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualService -status: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: 3 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 3 diff --git a/resource_customizations/gloo.solo.io/Proxy/health.lua b/resource_customizations/gloo.solo.io/Proxy/health.lua deleted file mode 100644 index 4e0930462f38a..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/health.lua +++ /dev/null @@ -1,54 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} - -function getStatus(status) - -- Accepted - if status.state == "Accepted" or status.state == 1 then - hs.status = "Healthy" - hs.message = "The resource has been validated" - return hs - end - - -- Warning - if status.state == "Warning" or status.state == 3 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - -- Pending - if status.state == "Pending" or status.state == 0 then - hs.status = "Suspended" - hs.message = "The resource has not yet been validated" - return hs - end - - -- Rejected - if status.state == "Rejected" or status.state == 2 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - return hs -end - -if obj.status ~= nil then - -- Namespaced version of status - if obj.status.statuses ~= nil then - for i, namespace in pairs(obj.status.statuses) do - hs = getStatus(namespace) - if hs.status ~= "Progressing" then - return hs - end - end - end - - -- Older non-namespaced version of status - if obj.status.state ~= nil then - hs = getStatus(obj.status) - end -end -return hs diff --git a/resource_customizations/gloo.solo.io/Proxy/health_test.yaml b/resource_customizations/gloo.solo.io/Proxy/health_test.yaml deleted file mode 100644 index bd7846b5019f1..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/gloo-rejected.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/non-namespaced-gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/non-namespaced-gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/non-namespaced-gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/non-namespaced-gloo-rejected.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/gloo-no-status.yaml diff --git a/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-accepted.yaml b/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-accepted.yaml deleted file mode 100644 index 1a33b9dedeb7d..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-accepted.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Proxy -status: - statuses: - gloo-system: - reportedBy: gateway - state: Accepted - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Accepted diff --git a/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-no-status.yaml b/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-no-status.yaml deleted file mode 100644 index 5354698cf4acf..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-no-status.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Proxy diff --git a/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-pending.yaml b/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-pending.yaml deleted file mode 100644 index 8497064ec18ae..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Proxy -status: - statuses: - gloo-system: - reportedBy: gateway - state: Pending - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Pending diff --git a/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-rejected.yaml b/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-rejected.yaml deleted file mode 100644 index e93acfec72c47..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-rejected.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Proxy -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: Rejected - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Rejected diff --git a/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-warning.yaml b/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-warning.yaml deleted file mode 100644 index a65c3fa4957b1..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/testdata/gloo-warning.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Proxy -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: Warning - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Warning diff --git a/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-accepted.yaml b/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-accepted.yaml deleted file mode 100644 index dff6082232fa5..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-accepted.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Proxy -status: - reportedBy: gateway - state: 1 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 1 diff --git a/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-pending.yaml b/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-pending.yaml deleted file mode 100644 index 49afeb8e4be83..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Proxy -status: - statuses: - gloo-system: - reportedBy: gateway - state: 0 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 0 diff --git a/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-rejected.yaml b/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-rejected.yaml deleted file mode 100644 index 0f7ec50212811..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-rejected.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Proxy -status: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: 2 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 2 diff --git a/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-warning.yaml b/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-warning.yaml deleted file mode 100644 index cabff991640bb..0000000000000 --- a/resource_customizations/gloo.solo.io/Proxy/testdata/non-namespaced-gloo-warning.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Proxy -status: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: 3 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 3 diff --git a/resource_customizations/gloo.solo.io/Settings/health.lua b/resource_customizations/gloo.solo.io/Settings/health.lua deleted file mode 100644 index 4e0930462f38a..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/health.lua +++ /dev/null @@ -1,54 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} - -function getStatus(status) - -- Accepted - if status.state == "Accepted" or status.state == 1 then - hs.status = "Healthy" - hs.message = "The resource has been validated" - return hs - end - - -- Warning - if status.state == "Warning" or status.state == 3 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - -- Pending - if status.state == "Pending" or status.state == 0 then - hs.status = "Suspended" - hs.message = "The resource has not yet been validated" - return hs - end - - -- Rejected - if status.state == "Rejected" or status.state == 2 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - return hs -end - -if obj.status ~= nil then - -- Namespaced version of status - if obj.status.statuses ~= nil then - for i, namespace in pairs(obj.status.statuses) do - hs = getStatus(namespace) - if hs.status ~= "Progressing" then - return hs - end - end - end - - -- Older non-namespaced version of status - if obj.status.state ~= nil then - hs = getStatus(obj.status) - end -end -return hs diff --git a/resource_customizations/gloo.solo.io/Settings/health_test.yaml b/resource_customizations/gloo.solo.io/Settings/health_test.yaml deleted file mode 100644 index bd7846b5019f1..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/gloo-rejected.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/non-namespaced-gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/non-namespaced-gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/non-namespaced-gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/non-namespaced-gloo-rejected.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/gloo-no-status.yaml diff --git a/resource_customizations/gloo.solo.io/Settings/testdata/gloo-accepted.yaml b/resource_customizations/gloo.solo.io/Settings/testdata/gloo-accepted.yaml deleted file mode 100644 index 968a56607561c..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/testdata/gloo-accepted.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Settings -status: - statuses: - gloo-system: - reportedBy: gateway - state: Accepted - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Accepted diff --git a/resource_customizations/gloo.solo.io/Settings/testdata/gloo-no-status.yaml b/resource_customizations/gloo.solo.io/Settings/testdata/gloo-no-status.yaml deleted file mode 100644 index 82726a6c3099b..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/testdata/gloo-no-status.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Settings diff --git a/resource_customizations/gloo.solo.io/Settings/testdata/gloo-pending.yaml b/resource_customizations/gloo.solo.io/Settings/testdata/gloo-pending.yaml deleted file mode 100644 index bcfbd8a25a2da..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/testdata/gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Settings -status: - statuses: - gloo-system: - reportedBy: gateway - state: Pending - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Pending diff --git a/resource_customizations/gloo.solo.io/Settings/testdata/gloo-rejected.yaml b/resource_customizations/gloo.solo.io/Settings/testdata/gloo-rejected.yaml deleted file mode 100644 index 004ecf1ef4b14..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/testdata/gloo-rejected.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Settings -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: Rejected - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Rejected diff --git a/resource_customizations/gloo.solo.io/Settings/testdata/gloo-warning.yaml b/resource_customizations/gloo.solo.io/Settings/testdata/gloo-warning.yaml deleted file mode 100644 index d42784551beb4..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/testdata/gloo-warning.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Settings -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: Warning - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Warning diff --git a/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-accepted.yaml b/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-accepted.yaml deleted file mode 100644 index 99ff0026b4ade..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-accepted.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Settings -status: - reportedBy: gateway - state: 1 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 1 diff --git a/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-pending.yaml b/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-pending.yaml deleted file mode 100644 index fdec14fa04a6c..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Settings -status: - statuses: - gloo-system: - reportedBy: gateway - state: 0 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 0 diff --git a/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-rejected.yaml b/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-rejected.yaml deleted file mode 100644 index 5e62178cd182d..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-rejected.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Settings -status: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: 2 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 2 diff --git a/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-warning.yaml b/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-warning.yaml deleted file mode 100644 index 26ddf57424eea..0000000000000 --- a/resource_customizations/gloo.solo.io/Settings/testdata/non-namespaced-gloo-warning.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Settings -status: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: 3 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 3 diff --git a/resource_customizations/gloo.solo.io/Upstream/health.lua b/resource_customizations/gloo.solo.io/Upstream/health.lua deleted file mode 100644 index 4e0930462f38a..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/health.lua +++ /dev/null @@ -1,54 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} - -function getStatus(status) - -- Accepted - if status.state == "Accepted" or status.state == 1 then - hs.status = "Healthy" - hs.message = "The resource has been validated" - return hs - end - - -- Warning - if status.state == "Warning" or status.state == 3 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - -- Pending - if status.state == "Pending" or status.state == 0 then - hs.status = "Suspended" - hs.message = "The resource has not yet been validated" - return hs - end - - -- Rejected - if status.state == "Rejected" or status.state == 2 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - return hs -end - -if obj.status ~= nil then - -- Namespaced version of status - if obj.status.statuses ~= nil then - for i, namespace in pairs(obj.status.statuses) do - hs = getStatus(namespace) - if hs.status ~= "Progressing" then - return hs - end - end - end - - -- Older non-namespaced version of status - if obj.status.state ~= nil then - hs = getStatus(obj.status) - end -end -return hs diff --git a/resource_customizations/gloo.solo.io/Upstream/health_test.yaml b/resource_customizations/gloo.solo.io/Upstream/health_test.yaml deleted file mode 100644 index bd7846b5019f1..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/gloo-rejected.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/non-namespaced-gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/non-namespaced-gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/non-namespaced-gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/non-namespaced-gloo-rejected.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/gloo-no-status.yaml diff --git a/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-accepted.yaml b/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-accepted.yaml deleted file mode 100644 index b0094f46d8d2a..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-accepted.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Upstream -status: - statuses: - gloo-system: - reportedBy: gateway - state: Accepted - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Accepted diff --git a/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-no-status.yaml b/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-no-status.yaml deleted file mode 100644 index b68a5e16b03be..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-no-status.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Upstream diff --git a/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-pending.yaml b/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-pending.yaml deleted file mode 100644 index bef8b9eef9b88..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Upstream -status: - statuses: - gloo-system: - reportedBy: gateway - state: Pending - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Pending diff --git a/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-rejected.yaml b/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-rejected.yaml deleted file mode 100644 index 02cef224d46bd..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-rejected.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Upstream -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: Rejected - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Rejected diff --git a/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-warning.yaml b/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-warning.yaml deleted file mode 100644 index 359e27871ba15..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/testdata/gloo-warning.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Upstream -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: Warning - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Warning diff --git a/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-accepted.yaml b/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-accepted.yaml deleted file mode 100644 index 85a792fc0b37f..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-accepted.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Upstream -status: - reportedBy: gateway - state: 1 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 1 diff --git a/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-pending.yaml b/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-pending.yaml deleted file mode 100644 index ec29ff4967bd1..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Upstream -status: - statuses: - gloo-system: - reportedBy: gateway - state: 0 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 0 diff --git a/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-rejected.yaml b/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-rejected.yaml deleted file mode 100644 index 7779628abf3b9..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-rejected.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Upstream -status: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: 2 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 2 diff --git a/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-warning.yaml b/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-warning.yaml deleted file mode 100644 index 8f170e814adf9..0000000000000 --- a/resource_customizations/gloo.solo.io/Upstream/testdata/non-namespaced-gloo-warning.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: Upstream -status: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: 3 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 3 diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/health.lua b/resource_customizations/gloo.solo.io/UpstreamGroup/health.lua deleted file mode 100644 index 4e0930462f38a..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/health.lua +++ /dev/null @@ -1,54 +0,0 @@ -hs = { - status = "Progressing", - message = "Update in progress" -} - -function getStatus(status) - -- Accepted - if status.state == "Accepted" or status.state == 1 then - hs.status = "Healthy" - hs.message = "The resource has been validated" - return hs - end - - -- Warning - if status.state == "Warning" or status.state == 3 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - -- Pending - if status.state == "Pending" or status.state == 0 then - hs.status = "Suspended" - hs.message = "The resource has not yet been validated" - return hs - end - - -- Rejected - if status.state == "Rejected" or status.state == 2 then - hs.status = "Degraded" - hs.message = status.reason - return hs - end - - return hs -end - -if obj.status ~= nil then - -- Namespaced version of status - if obj.status.statuses ~= nil then - for i, namespace in pairs(obj.status.statuses) do - hs = getStatus(namespace) - if hs.status ~= "Progressing" then - return hs - end - end - end - - -- Older non-namespaced version of status - if obj.status.state ~= nil then - hs = getStatus(obj.status) - end -end -return hs diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/health_test.yaml b/resource_customizations/gloo.solo.io/UpstreamGroup/health_test.yaml deleted file mode 100644 index bd7846b5019f1..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/health_test.yaml +++ /dev/null @@ -1,37 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/gloo-rejected.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for warning" - inputPath: testdata/non-namespaced-gloo-warning.yaml -- healthStatus: - status: Suspended - message: "The resource has not yet been validated" - inputPath: testdata/non-namespaced-gloo-pending.yaml -- healthStatus: - status: Healthy - message: "The resource has been validated" - inputPath: testdata/non-namespaced-gloo-accepted.yaml -- healthStatus: - status: Degraded - message: "message that will describe all the reasons for rejection" - inputPath: testdata/non-namespaced-gloo-rejected.yaml -- healthStatus: - status: Progressing - message: "Update in progress" - inputPath: testdata/gloo-no-status.yaml diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-accepted.yaml b/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-accepted.yaml deleted file mode 100644 index 90a4023ce60cf..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-accepted.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: UpstreamGroup -status: - statuses: - gloo-system: - reportedBy: gateway - state: Accepted - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Accepted diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-no-status.yaml b/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-no-status.yaml deleted file mode 100644 index 45f71721283b5..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-no-status.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: UpstreamGroup diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-pending.yaml b/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-pending.yaml deleted file mode 100644 index 76f8a8d227567..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: UpstreamGroup -status: - statuses: - gloo-system: - reportedBy: gateway - state: Pending - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Pending diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-rejected.yaml b/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-rejected.yaml deleted file mode 100644 index e66fa5a7e67fd..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-rejected.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: UpstreamGroup -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: Rejected - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Rejected diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-warning.yaml b/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-warning.yaml deleted file mode 100644 index b7c550394341f..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/gloo-warning.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: UpstreamGroup -status: - statuses: - gloo-system: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: Warning - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: Accepted - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: Warning diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-accepted.yaml b/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-accepted.yaml deleted file mode 100644 index 63e8dd8af6bbc..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-accepted.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: UpstreamGroup -status: - reportedBy: gateway - state: 1 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 1 diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-pending.yaml b/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-pending.yaml deleted file mode 100644 index b1367f520f824..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-pending.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: UpstreamGroup -status: - statuses: - gloo-system: - reportedBy: gateway - state: 0 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 0 diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-rejected.yaml b/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-rejected.yaml deleted file mode 100644 index 7c4312539bc78..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-rejected.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: UpstreamGroup -status: - reason: "message that will describe all the reasons for rejection" - reportedBy: gateway - state: 2 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 2 diff --git a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-warning.yaml b/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-warning.yaml deleted file mode 100644 index 319e1bc6645b7..0000000000000 --- a/resource_customizations/gloo.solo.io/UpstreamGroup/testdata/non-namespaced-gloo-warning.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: gloo.solo.io/v1 -kind: UpstreamGroup -status: - reason: "message that will describe all the reasons for warning" - reportedBy: gateway - state: 3 - subresourceStatuses: - '*v1.Proxy.gateway-proxy_gloo-system': - reportedBy: gloo - state: 1 - '*v1.Proxy.internal-proxy_gloo-system': - reportedBy: gloo - state: 3 diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/action_test.yaml b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/action_test.yaml deleted file mode 100644 index 1831eb389cb24..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/action_test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_helmrelease.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_helmrelease.yaml - result: - - name: reconcile - disabled: true - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_helmrelease.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: reconcile - inputPath: testdata/initial_helmrelease.yaml - expectedOutputPath: testdata/reconciled_helmrelease.yaml -- action: suspend - inputPath: testdata/initial_helmrelease.yaml - expectedOutputPath: testdata/suspended_helmrelease.yaml -- action: resume - inputPath: testdata/suspended_helmrelease.yaml - expectedOutputPath: testdata/resumed_helmrelease.yaml diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/discovery.lua b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/discovery.lua deleted file mode 100644 index 9000998815515..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/discovery.lua +++ /dev/null @@ -1,18 +0,0 @@ -local actions = {} - -actions["reconcile"] = {["disabled"] = true} -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["reconcile"]["disabled"] = false - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/reconcile/action.lua b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/reconcile/action.lua deleted file mode 100644 index a534b36fb3ebc..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/reconcile/action.lua +++ /dev/null @@ -1,7 +0,0 @@ -local os = require("os") -if obj.metadata.annotations == nil then - obj.metadata.annotations = {} -end -obj.metadata.annotations["reconcile.fluxcd.io/requestedAt"] = "By Argo CD at: " .. os.date("!%Y-%m-%dT%X") - -return obj diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/resume/action.lua b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/suspend/action.lua b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/initial_helmrelease.yaml b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/initial_helmrelease.yaml deleted file mode 100644 index a5be419c51dc7..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/initial_helmrelease.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - timeout: 5m - chart: - spec: - chart: podinfo - version: '6.5.*' - sourceRef: - kind: HelmRepository - name: podinfo - interval: 5m - releaseName: podinfo - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - test: - enable: true - driftDetection: - mode: enabled - ignore: - - paths: ["/spec/replicas"] - target: - kind: Deployment - values: - replicaCount: 2 diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/reconciled_helmrelease.yaml b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/reconciled_helmrelease.yaml deleted file mode 100644 index 89eb46c511eb1..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/reconciled_helmrelease.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: podinfo - namespace: default - annotations: - reconcile.fluxcd.io/requestedAt: 'By Argo CD at: 0001-01-01T00:00:00' -spec: - interval: 10m - timeout: 5m - chart: - spec: - chart: podinfo - version: '6.5.*' - sourceRef: - kind: HelmRepository - name: podinfo - interval: 5m - releaseName: podinfo - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - test: - enable: true - driftDetection: - mode: enabled - ignore: - - paths: ["/spec/replicas"] - target: - kind: Deployment - values: - replicaCount: 2 diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/resumed_helmrelease.yaml b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/resumed_helmrelease.yaml deleted file mode 100644 index 84988820ada9f..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/resumed_helmrelease.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - timeout: 5m - chart: - spec: - chart: podinfo - version: '6.5.*' - sourceRef: - kind: HelmRepository - name: podinfo - interval: 5m - releaseName: podinfo - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - test: - enable: true - suspend: false - driftDetection: - mode: enabled - ignore: - - paths: ["/spec/replicas"] - target: - kind: Deployment - values: - replicaCount: 2 diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/suspended_helmrelease.yaml b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/suspended_helmrelease.yaml deleted file mode 100644 index 21e46d51751a3..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/actions/testdata/suspended_helmrelease.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - timeout: 5m - chart: - spec: - chart: podinfo - version: '6.5.*' - sourceRef: - kind: HelmRepository - name: podinfo - interval: 5m - releaseName: podinfo - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - test: - enable: true - suspend: true - driftDetection: - mode: enabled - ignore: - - paths: ["/spec/replicas"] - target: - kind: Deployment - values: - replicaCount: 2 diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/health.lua b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/health.lua deleted file mode 100644 index dd062f92e7143..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/health.lua +++ /dev/null @@ -1,45 +0,0 @@ -local hs = {} -if obj.spec.suspend ~= nil and obj.spec.suspend == true then - hs.message = obj.kind .. " is suspended" - hs.status = "Suspended" - return hs -end -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numProgressing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "Unknown" then - numProgressing = numProgressing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - numProgressing = numProgressing + 1 - elseif condition.type == "Released" or condition.type == "TestSuccess" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - end - end - end - if(numProgressing == 2) then - hs.message = message - hs.status = "Progressing" - return hs - elseif(numSucceeded == 2) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/health_test.yaml b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/health_test.yaml deleted file mode 100644 index 64fce9951d5ba..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: Progressing - inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: RollbackSucceeded - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: InstallSucceeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/degraded.yaml b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/degraded.yaml deleted file mode 100644 index c5986d6cfc507..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/degraded.yaml +++ /dev/null @@ -1,70 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - timeout: 5m - chart: - spec: - chart: podinfo - version: '6.5.*' - sourceRef: - kind: HelmRepository - name: podinfo - interval: 5m - releaseName: podinfo - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - test: - enable: true - driftDetection: - mode: enabled - ignore: - - paths: ["/spec/replicas"] - target: - kind: Deployment - values: - replicaCount: 2 -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm rollback to previous release default/podinfo.v24 with - chart podinfo@6.5.4 succeeded - observedGeneration: 5 - reason: ProgressingWithRetry - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm rollback to previous release default/podinfo.v24 with - chart podinfo@6.5.4 succeeded - observedGeneration: 5 - reason: RollbackSucceeded - status: "False" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: "Helm upgrade failed for release default/podinfo with chart - podinfo@6.5.4: cannot patch \"podinfo\" with kind Deployment: admission webhook - \"validate.kyverno.svc-fail\" denied the request: \n\nresource Deployment/default/podinfo - was blocked due to the following policies \n\ndisallow-privilege-escalation:\n - \ autogen-privilege-escalation: 'validation error: Privilege escalation is disallowed.\n - \ The fields spec.containers[*].securityContext.allowPrivilegeEscalation, - spec.initContainers[*].securityContext.allowPrivilegeEscalation,\n and spec.ephemeralContainers[*].securityContext.allowPrivilegeEscalation - must\n be set to `false`. rule autogen-privilege-escalation failed at path - /spec/template/spec/containers/0/securityContext/allowPrivilegeEscalation/'" - observedGeneration: 5 - reason: UpgradeFailed - status: "False" - type: Released - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm rollback to previous release default/podinfo.v24 with - chart podinfo@6.5.4 succeeded - observedGeneration: 5 - reason: RollbackSucceeded - status: "True" - type: Remediated diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/healthy.yaml b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/healthy.yaml deleted file mode 100644 index f76ca38f23a09..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/healthy.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - timeout: 5m - chart: - spec: - chart: podinfo - version: '6.5.*' - sourceRef: - kind: HelmRepository - name: podinfo - interval: 5m - releaseName: podinfo - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - test: - enable: true - driftDetection: - mode: enabled - ignore: - - paths: ["/spec/replicas"] - target: - kind: Deployment - values: - replicaCount: 2 -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm install succeeded for release default/podinfo.v1 with - chart podinfo@6.5.4 - observedGeneration: 2 - reason: InstallSucceeded - status: "True" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm install succeeded for release default/podinfo.v1 with - chart podinfo@6.5.4 - observedGeneration: 1 - reason: InstallSucceeded - status: "True" - type: Released diff --git a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/progressing.yaml b/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/progressing.yaml deleted file mode 100644 index f6653b2139526..0000000000000 --- a/resource_customizations/helm.toolkit.fluxcd.io/HelmRelease/testdata/progressing.yaml +++ /dev/null @@ -1,54 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - timeout: 5m - chart: - spec: - chart: podinfo - version: '6.5.*' - sourceRef: - kind: HelmRepository - name: podinfo - interval: 5m - releaseName: podinfo - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - test: - enable: true - driftDetection: - mode: enabled - ignore: - - paths: ["/spec/replicas"] - target: - kind: Deployment - values: - replicaCount: 2 -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Running 'upgrade' action with timeout of 5m0s - observedGeneration: 3 - reason: Progressing - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Running 'upgrade' action with timeout of 5m0s - observedGeneration: 3 - reason: Progressing - status: Unknown - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm install succeeded for release default/podinfo.v1 with - chart podinfo@6.5.4 - observedGeneration: 1 - reason: InstallSucceeded - status: "True" - type: Released diff --git a/resource_customizations/iam.aws.crossplane.io/Policy/health.lua b/resource_customizations/iam.aws.crossplane.io/Policy/health.lua deleted file mode 100644 index 70af5cb9570c0..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/Policy/health.lua +++ /dev/null @@ -1,41 +0,0 @@ -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 Policy to be available" - else - hs.status = "Degraded" - hs.message = synced_message - end - return hs - end -end - -hs.status = "Progressing" -hs.message = "Waiting for Policy to be created" -return hs diff --git a/resource_customizations/iam.aws.crossplane.io/Policy/health_test.yaml b/resource_customizations/iam.aws.crossplane.io/Policy/health_test.yaml deleted file mode 100644 index 07244a3f5b8eb..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/Policy/health_test.yaml +++ /dev/null @@ -1,10 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: 'observe failed: cannot check if policy is up to date: invalid character - '']'' looking for beginning of value' - inputPath: testdata/ReconcileError.yaml -- healthStatus: - status: Healthy - message: Available - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/iam.aws.crossplane.io/Policy/testdata/ReconcileError.yaml b/resource_customizations/iam.aws.crossplane.io/Policy/testdata/ReconcileError.yaml deleted file mode 100644 index 4c00dffe0fdb8..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/Policy/testdata/ReconcileError.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: iam.aws.crossplane.io/v1beta1 -kind: Policy -metadata: - name: example -spec: - deletionPolicy: Delete - forProvider: - description: example - document: "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": - \"Allow\",\n \"Action\": [\n \"s3:*\",\n ], \n \"Resource\": - [\n \"arn:aws:s3:::example\"\n ]\n }\n ]\n}\n" - name: example - tags: - - key: crossplane-name - value: example - - key: crossplane-providerconfig - value: provider-aws - - key: crossplane-kind - value: policy.iam.aws.crossplane.io - providerConfigRef: - name: provider-aws -status: - atProvider: - arn: arn:aws:iam::123:policy/example - attachmentCount: 1 - defaultVersionId: v1 - isAttachable: true - policyId: ABC - conditions: - - lastTransitionTime: "2024-07-11T11:01:01Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2024-07-11T11:01:01Z" - message: 'observe failed: cannot check if policy is up to date: invalid character - '']'' looking for beginning of value' - reason: ReconcileError - status: "False" - type: Synced diff --git a/resource_customizations/iam.aws.crossplane.io/Policy/testdata/healthy.yaml b/resource_customizations/iam.aws.crossplane.io/Policy/testdata/healthy.yaml deleted file mode 100644 index 04d8dd8cf1aef..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/Policy/testdata/healthy.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: iam.aws.crossplane.io/v1beta1 -kind: Policy -metadata: - name: example -spec: - deletionPolicy: Delete - forProvider: - description: example - document: | - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": ["s3:ListBucket"], - "Resource": ["arn:aws:s3:::examples"] - } - ] - } - name: examples-s3-hello-s3 - tags: - - key: crossplane-name - value: example - - key: crossplane-providerconfig - value: provider-aws - - key: crossplane-kind - value: policy.iam.aws.crossplane.io - providerConfigRef: - name: provider-aws -status: - atProvider: - arn: arn:aws:iam::123:policy/examples-s3-hello-s3 - attachmentCount: 1 - defaultVersionId: v2 - isAttachable: true - policyId: 123 - conditions: - - lastTransitionTime: "2024-07-11T08:18:16Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2024-07-11T08:18:07Z" - reason: ReconcileSuccess - status: "True" - type: Synced diff --git a/resource_customizations/iam.aws.crossplane.io/Role/health.lua b/resource_customizations/iam.aws.crossplane.io/Role/health.lua deleted file mode 100644 index a264c2049d1b5..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/Role/health.lua +++ /dev/null @@ -1,41 +0,0 @@ -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 Role to be available" - else - hs.status = "Degraded" - hs.message = synced_message - end - return hs - end -end - -hs.status = "Progressing" -hs.message = "Waiting for Role to be created" -return hs diff --git a/resource_customizations/iam.aws.crossplane.io/Role/health_test.yaml b/resource_customizations/iam.aws.crossplane.io/Role/health_test.yaml deleted file mode 100644 index 4e4c93684fef3..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/Role/health_test.yaml +++ /dev/null @@ -1,10 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: 'connect failed: cannot get referenced Provider: ProviderConfig.aws.crossplane.io - "provider-aws1" not found' - inputPath: testdata/ReconcileError.yaml -- healthStatus: - status: Healthy - message: Available - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/iam.aws.crossplane.io/Role/testdata/ReconcileError.yaml b/resource_customizations/iam.aws.crossplane.io/Role/testdata/ReconcileError.yaml deleted file mode 100644 index 6a71e20da4628..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/Role/testdata/ReconcileError.yaml +++ /dev/null @@ -1,54 +0,0 @@ -apiVersion: iam.aws.crossplane.io/v1beta1 -kind: Role -metadata: - name: example -spec: - deletionPolicy: Delete - forProvider: - assumeRolePolicyDocument: | - { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "", - "Effect": "Allow", - "Principal": { - "Federated": "arn:aws:iam::123:oidc-provider/oidc.eks.eu-north-1.amazonaws.com/id/123ABC" - }, - "Action": "sts:AssumeRoleWithWebIdentity", - "Condition": { - "StringEquals": { - "oidc.eks.eu-north-1.amazonaws.com/id/123ABC:sub": "system:serviceaccount:ABC:example", - "oidc.eks.eu-north-1.amazonaws.com/id/123ABC:aud": "sts.amazonaws.com" - } - } - } - ] - } - description: example - maxSessionDuration: 3600 - path: / - tags: - - key: crossplane-kind - value: role.iam.aws.crossplane.io - - key: crossplane-name - value: example - - key: crossplane-providerconfig - value: provider-aws - providerConfigRef: - name: provider-aws1 -status: - atProvider: - arn: arn:aws:iam::123:role/examples-s31 - roleID: ABC123 - conditions: - - lastTransitionTime: "2024-07-11T13:51:47Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2024-07-11T13:54:11Z" - message: 'connect failed: cannot get referenced Provider: ProviderConfig.aws.crossplane.io - "provider-aws1" not found' - reason: ReconcileError - status: "False" - type: Synced diff --git a/resource_customizations/iam.aws.crossplane.io/Role/testdata/healthy.yaml b/resource_customizations/iam.aws.crossplane.io/Role/testdata/healthy.yaml deleted file mode 100644 index 6f4d92cc10af5..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/Role/testdata/healthy.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: iam.aws.crossplane.io/v1beta1 -kind: Role -metadata: - name: example -spec: - deletionPolicy: Delete - forProvider: - assumeRolePolicyDocument: | - { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "", - "Effect": "Allow", - "Principal": { - "Federated": "arn:aws:iam::123:oidc-provider/oidc.eks.eu-north-1.amazonaws.com/id/123ABC" - }, - "Action": "sts:AssumeRoleWithWebIdentity", - "Condition": { - "StringEquals": { - "oidc.eks.eu-north-1.amazonaws.com/id/123ABC:sub": "system:serviceaccount:ABC:example", - "oidc.eks.eu-north-1.amazonaws.com/id/123ABC:aud": "sts.amazonaws.com" - } - } - } - ] - } - description: example - maxSessionDuration: 3600 - path: / - tags: - - key: crossplane-kind - value: role.iam.aws.crossplane.io - - key: crossplane-name - value: example - - key: crossplane-providerconfig - value: provider-aws - providerConfigRef: - name: provider-aws -status: - atProvider: - arn: arn:aws:iam::123:role/example - roleID: ABC123 - conditions: - - lastTransitionTime: "2024-07-11T07:49:50Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2024-07-11T07:49:49Z" - reason: ReconcileSuccess - status: "True" - type: Synced diff --git a/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/health.lua b/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/health.lua deleted file mode 100644 index 516c30b36c305..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/health.lua +++ /dev/null @@ -1,41 +0,0 @@ -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 RolePolicyAttachment to be available" - else - hs.status = "Degraded" - hs.message = synced_message - end - return hs - end -end - -hs.status = "Progressing" -hs.message = "Waiting for RolePolicyAttachment to be created" -return hs diff --git a/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/health_test.yaml b/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/health_test.yaml deleted file mode 100644 index 1ba7ed049cca4..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/health_test.yaml +++ /dev/null @@ -1,10 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: 'create failed: failed to attach the policy to role: NoSuchEntity: The - role with name example cannot be found.' - inputPath: testdata/ReconcileError.yaml -- healthStatus: - status: Healthy - message: Available - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/testdata/ReconcileError.yaml b/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/testdata/ReconcileError.yaml deleted file mode 100644 index 9249805319225..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/testdata/ReconcileError.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: iam.aws.crossplane.io/v1beta1 -kind: RolePolicyAttachment -metadata: - name: example -spec: - deletionPolicy: Delete - forProvider: - policyArn: arn:aws:iam::123:policy/example - roleName: example - providerConfigRef: - name: provider-aws -status: - atProvider: - attachedPolicyArn: "" - conditions: - - lastTransitionTime: "2024-07-11T13:44:28Z" - reason: Creating - status: "False" - type: Ready - - lastTransitionTime: "2024-07-11T13:44:28Z" - message: 'create failed: failed to attach the policy to role: NoSuchEntity: The - role with name example cannot be found.' - reason: ReconcileError - status: "False" - type: Synced diff --git a/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/testdata/healthy.yaml b/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/testdata/healthy.yaml deleted file mode 100644 index 41d4d17a9c415..0000000000000 --- a/resource_customizations/iam.aws.crossplane.io/RolePolicyAttachment/testdata/healthy.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: iam.aws.crossplane.io/v1beta1 -kind: RolePolicyAttachment -metadata: - name: example -spec: - deletionPolicy: Delete - forProvider: - policyArn: arn:aws:iam::123:policy/example - roleName: example - providerConfigRef: - name: provider-aws -status: - atProvider: - attachedPolicyArn: arn:aws:iam::123:policy/example - conditions: - - lastTransitionTime: "2024-07-11T08:19:17Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2024-07-11T08:18:16Z" - reason: ReconcileSuccess - status: "True" - type: Synced diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/health.lua b/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/health.lua deleted file mode 100644 index 688d06f0886ad..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/health.lua +++ /dev/null @@ -1,38 +0,0 @@ -local hs = {} -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numProgressing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "False" then - numProgressing = numProgressing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - if condition.reason == "NewGeneration" or condition.reason == "AccessingRepository" or condition.reason == "ApplyingPolicy" then - numProgressing = numProgressing + 1 - end - end - end - if(numProgressing == 2) then - hs.message = message - hs.status = "Progressing" - return hs - elseif(numSucceeded == 1) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/health_test.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/health_test.yaml deleted file mode 100644 index 0f8d9c4a64541..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - # - healthStatus: - # status: Progressing - # message: DependencyNotReady - # inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: DependencyNotReady - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: Succeeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/degraded.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/degraded.yaml deleted file mode 100644 index 4fcbc4498c045..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/degraded.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImagePolicy -metadata: - name: podinfo - namespace: argocd -spec: - imageRepositoryRef: - name: podinfo-faulty - policy: - semver: - range: x.x.x -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'processing object: new generation 1 -> 2' - observedGeneration: 2 - reason: ProgressingWithRetry - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to get the referred ImageRepository: referenced ImageRepository - does not exist: ImageRepository.image.toolkit.fluxcd.io "podinfo-faulty" not found' - observedGeneration: 2 - reason: DependencyNotReady - status: "False" - type: Ready diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/healthy.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/healthy.yaml deleted file mode 100644 index b1fdf01bedb36..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/healthy.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImagePolicy -metadata: - name: podinfo - namespace: argocd -spec: - imageRepositoryRef: - name: podinfo - policy: - semver: - range: x.x.x -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Latest image tag for 'stefanprodan/podinfo' resolved to 5.1.4 - observedGeneration: 1 - reason: Succeeded - status: "True" - type: Ready diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/progressing.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/progressing.yaml deleted file mode 100644 index 90e71223b3837..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImagePolicy/testdata/progressing.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImagePolicy -metadata: - name: podinfo - namespace: argocd -spec: - imageRepositoryRef: - name: podinfo - policy: - semver: - range: x.x.x -status: - conditions: [] diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/action_test.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/action_test.yaml deleted file mode 100644 index 69bd3dbb46c06..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/action_test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_imagerepository.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_imagerepository.yaml - result: - - name: reconcile - disabled: true - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_imagerepository.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: reconcile - inputPath: testdata/initial_imagerepository.yaml - expectedOutputPath: testdata/reconciled_imagerepository.yaml -- action: suspend - inputPath: testdata/initial_imagerepository.yaml - expectedOutputPath: testdata/suspended_imagerepository.yaml -- action: resume - inputPath: testdata/suspended_imagerepository.yaml - expectedOutputPath: testdata/resumed_imagerepository.yaml diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/discovery.lua b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/discovery.lua deleted file mode 100644 index 9000998815515..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/discovery.lua +++ /dev/null @@ -1,18 +0,0 @@ -local actions = {} - -actions["reconcile"] = {["disabled"] = true} -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["reconcile"]["disabled"] = false - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/reconcile/action.lua b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/reconcile/action.lua deleted file mode 100644 index a534b36fb3ebc..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/reconcile/action.lua +++ /dev/null @@ -1,7 +0,0 @@ -local os = require("os") -if obj.metadata.annotations == nil then - obj.metadata.annotations = {} -end -obj.metadata.annotations["reconcile.fluxcd.io/requestedAt"] = "By Argo CD at: " .. os.date("!%Y-%m-%dT%X") - -return obj diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/resume/action.lua b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/suspend/action.lua b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/initial_imagerepository.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/initial_imagerepository.yaml deleted file mode 100644 index c3491a774ca78..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/initial_imagerepository.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageRepository -metadata: - name: podinfo - namespace: default -spec: - image: stefanprodan/podinfo - interval: 1h - provider: generic diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/reconciled_imagerepository.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/reconciled_imagerepository.yaml deleted file mode 100644 index 8d9a0625d67c6..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/reconciled_imagerepository.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageRepository -metadata: - name: podinfo - namespace: default - annotations: - reconcile.fluxcd.io/requestedAt: 'By Argo CD at: 0001-01-01T00:00:00' -spec: - image: stefanprodan/podinfo - interval: 1h - provider: generic diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/resumed_imagerepository.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/resumed_imagerepository.yaml deleted file mode 100644 index 1317a1e58007c..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/resumed_imagerepository.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageRepository -metadata: - name: podinfo - namespace: default -spec: - image: stefanprodan/podinfo - interval: 1h - provider: generic - suspend: false diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/suspended_imagerepository.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/suspended_imagerepository.yaml deleted file mode 100644 index b7286cd242438..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/actions/testdata/suspended_imagerepository.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageRepository -metadata: - name: podinfo - namespace: default -spec: - image: stefanprodan/podinfo - interval: 1h - provider: generic - suspend: true diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/health.lua b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/health.lua deleted file mode 100644 index aa65850494c99..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/health.lua +++ /dev/null @@ -1,43 +0,0 @@ -local hs = {} -if obj.spec.suspend ~= nil and obj.spec.suspend == true then - hs.message = obj.kind .. " is suspended" - hs.status = "Suspended" - return hs -end -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numProgressing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "False" then - numProgressing = numProgressing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - if condition.reason == "NewGeneration" or condition.reason == "Scanning" then - numProgressing = numProgressing + 1 - end - end - end - if(numProgressing == 2) then - hs.message = message - hs.status = "Progressing" - return hs - elseif(numSucceeded == 1) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/health_test.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/health_test.yaml deleted file mode 100644 index 22170d09007cd..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - # - healthStatus: - # status: Progressing - # message: ReadOperationFailed - # inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: ReadOperationFailed - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: Succeeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/degraded.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/degraded.yaml deleted file mode 100644 index ddc47923d3a0a..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/degraded.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageRepository -metadata: - name: podinfo - namespace: default -spec: - image: stefanprodan/podinfo-faulty - interval: 1h - provider: generic -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'scanning: new image name' - observedGeneration: 2 - reason: ProgressingWithRetry - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'scan failed: GET https://index.docker.io/v2/stefanprodan/podinfo-faulty/tags/list?n=1000: - UNAUTHORIZED: authentication required; [map[Action:pull Class: Name:stefanprodan/podinfo-faulty - Type:repository]]' - observedGeneration: 2 - reason: ReadOperationFailed - status: "False" - type: Ready diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/healthy.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/healthy.yaml deleted file mode 100644 index 11ed02af76016..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/healthy.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageRepository -metadata: - name: podinfo - namespace: default -spec: - image: stefanprodan/podinfo - interval: 1h - provider: generic -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'successful scan: found 233 tags' - observedGeneration: 1 - reason: Succeeded - status: "True" - type: Ready diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/progressing.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/progressing.yaml deleted file mode 100644 index a0ac7d4718100..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageRepository/testdata/progressing.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageRepository -metadata: - name: podinfo - namespace: default -spec: - image: stefanprodan/podinfo - interval: 1h - provider: generic -status: - conditions: [] diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/action_test.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/action_test.yaml deleted file mode 100644 index c69e51b26a2e4..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/action_test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_imageupdateautomation.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_imageupdateautomation.yaml - result: - - name: reconcile - disabled: true - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_imageupdateautomation.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: reconcile - inputPath: testdata/initial_imageupdateautomation.yaml - expectedOutputPath: testdata/reconciled_imageupdateautomation.yaml -- action: suspend - inputPath: testdata/initial_imageupdateautomation.yaml - expectedOutputPath: testdata/suspended_imageupdateautomation.yaml -- action: resume - inputPath: testdata/suspended_imageupdateautomation.yaml - expectedOutputPath: testdata/resumed_imageupdateautomation.yaml diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/discovery.lua b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/discovery.lua deleted file mode 100644 index 9000998815515..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/discovery.lua +++ /dev/null @@ -1,18 +0,0 @@ -local actions = {} - -actions["reconcile"] = {["disabled"] = true} -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["reconcile"]["disabled"] = false - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/reconcile/action.lua b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/reconcile/action.lua deleted file mode 100644 index a534b36fb3ebc..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/reconcile/action.lua +++ /dev/null @@ -1,7 +0,0 @@ -local os = require("os") -if obj.metadata.annotations == nil then - obj.metadata.annotations = {} -end -obj.metadata.annotations["reconcile.fluxcd.io/requestedAt"] = "By Argo CD at: " .. os.date("!%Y-%m-%dT%X") - -return obj diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/resume/action.lua b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/suspend/action.lua b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/initial_imageupdateautomation.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/initial_imageupdateautomation.yaml deleted file mode 100644 index 049b7be69a583..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/initial_imageupdateautomation.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageUpdateAutomation -metadata: - name: podinfo-update - namespace: default -spec: - interval: 30m - sourceRef: - kind: GitRepository - name: podinfo - git: - commit: - author: - email: fluxcdbot@users.noreply.github.com - name: fluxcdbot - push: - branch: main - update: - path: ./ diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/reconciled_imageupdateautomation.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/reconciled_imageupdateautomation.yaml deleted file mode 100644 index 9f39dfdaf7dc7..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/reconciled_imageupdateautomation.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageUpdateAutomation -metadata: - name: podinfo-update - namespace: default - annotations: - reconcile.fluxcd.io/requestedAt: 'By Argo CD at: 0001-01-01T00:00:00' -spec: - interval: 30m - sourceRef: - kind: GitRepository - name: podinfo - git: - commit: - author: - email: fluxcdbot@users.noreply.github.com - name: fluxcdbot - push: - branch: main - update: - path: ./ diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/resumed_imageupdateautomation.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/resumed_imageupdateautomation.yaml deleted file mode 100644 index 25bb66e190fe5..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/resumed_imageupdateautomation.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageUpdateAutomation -metadata: - name: podinfo-update - namespace: default -spec: - interval: 30m - sourceRef: - kind: GitRepository - name: podinfo - git: - commit: - author: - email: fluxcdbot@users.noreply.github.com - name: fluxcdbot - push: - branch: main - suspend: false - update: - path: ./ diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/suspended_imageupdateautomation.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/suspended_imageupdateautomation.yaml deleted file mode 100644 index 9968fddf35e35..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/actions/testdata/suspended_imageupdateautomation.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageUpdateAutomation -metadata: - name: podinfo-update - namespace: default -spec: - interval: 30m - sourceRef: - kind: GitRepository - name: podinfo - git: - commit: - author: - email: fluxcdbot@users.noreply.github.com - name: fluxcdbot - push: - branch: main - suspend: true - update: - path: ./ diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/health.lua b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/health.lua deleted file mode 100644 index b4f27ab073ec2..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/health.lua +++ /dev/null @@ -1,41 +0,0 @@ -local hs = {} -if obj.spec.suspend ~= nil and obj.spec.suspend == true then - hs.message = obj.kind .. " is suspended" - hs.status = "Suspended" - return hs -end -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numProgressing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "Unknown" then - numProgressing = numProgressing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - numProgressing = numProgressing + 1 - end - end - if(numProgressing == 2) then - hs.message = message - hs.status = "Progressing" - return hs - elseif(numSucceeded == 1) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/health_test.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/health_test.yaml deleted file mode 100644 index e9509ef4d8ded..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - # - healthStatus: - # status: Progressing - # message: Progressing - # inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: ReconciliationFailed - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: ReconciliationSucceeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/degraded.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/degraded.yaml deleted file mode 100644 index 8de0cfa46ae10..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/degraded.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageUpdateAutomation -metadata: - name: podinfo-update - namespace: default -spec: - interval: 30m - sourceRef: - kind: GitRepository - name: podinfo - git: - commit: - author: - email: fluxcdbot@users.noreply.github.com - name: fluxcdbot - push: - branch: main - update: - path: ./ -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'walking path for files: lstat /tmp/deploy: - no such file or directory' - reason: ReconciliationFailed - status: "False" - type: Ready diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/healthy.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/healthy.yaml deleted file mode 100644 index 970b28a77dbf2..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/healthy.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageUpdateAutomation -metadata: - name: podinfo-update - namespace: default -spec: - interval: 30m - sourceRef: - kind: GitRepository - name: podinfo - git: - commit: - author: - email: fluxcdbot@users.noreply.github.com - name: fluxcdbot - push: - branch: main - update: - path: ./ -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: no updates made; last commit a1b24e5 at 2024-07-16T12:00:00Z - reason: ReconciliationSucceeded - status: "True" - type: Ready diff --git a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/progressing.yaml b/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/progressing.yaml deleted file mode 100644 index 72e7e19fd2335..0000000000000 --- a/resource_customizations/image.toolkit.fluxcd.io/ImageUpdateAutomation/testdata/progressing.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageUpdateAutomation -metadata: - name: podinfo-update - namespace: default -spec: - interval: 30m - sourceRef: - kind: GitRepository - name: podinfo - git: - commit: - author: - email: fluxcdbot@users.noreply.github.com - name: fluxcdbot - push: - branch: main - update: - path: ./ -status: - conditions: [] diff --git a/resource_customizations/k8s.mariadb.com/Backup/health.lua b/resource_customizations/k8s.mariadb.com/Backup/health.lua deleted file mode 100644 index ac78b482648e0..0000000000000 --- a/resource_customizations/k8s.mariadb.com/Backup/health.lua +++ /dev/null @@ -1,25 +0,0 @@ -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.reason .. " " .. condition.message - - if condition.status == "False" then - if condition.reason == "CronJobScheduled" and condition.message == "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/k8s.mariadb.com/Backup/health_test.yaml b/resource_customizations/k8s.mariadb.com/Backup/health_test.yaml deleted file mode 100644 index 2acfdebc9052b..0000000000000 --- a/resource_customizations/k8s.mariadb.com/Backup/health_test.yaml +++ /dev/null @@ -1,9 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "CronJobSucess Success" - inputPath: testdata/ok.yaml -- healthStatus: - status: Degraded - message: "CronJobScheduled Failed" - inputPath: testdata/failed.yaml diff --git a/resource_customizations/k8s.mariadb.com/Backup/testdata/failed.yaml b/resource_customizations/k8s.mariadb.com/Backup/testdata/failed.yaml deleted file mode 100644 index 731288ba52e60..0000000000000 --- a/resource_customizations/k8s.mariadb.com/Backup/testdata/failed.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: k8s.mariadb.com/v1alpha1 -kind: Backup -metadata: - name: backup-local -spec: - backoffLimit: 5 - logLevel: info - mariaDbRef: - name: mariadb - waitForIt: true - maxRetention: 168h - restartPolicy: OnFailure - schedule: - cron: 0 */2 * * * - suspend: false - serviceAccountName: backup-local - storage: - persistentVolumeClaim: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 80Gi -status: - conditions: - - lastTransitionTime: "2024-04-22T20:00:00Z" - message: Failed - reason: CronJobScheduled - status: "False" - type: Complete diff --git a/resource_customizations/k8s.mariadb.com/Backup/testdata/ok.yaml b/resource_customizations/k8s.mariadb.com/Backup/testdata/ok.yaml deleted file mode 100644 index daf8b55fc6c0f..0000000000000 --- a/resource_customizations/k8s.mariadb.com/Backup/testdata/ok.yaml +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: k8s.mariadb.com/v1alpha1 -kind: Backup -metadata: - annotations: - argocd.argoproj.io/tracking-id: apps-bridge-demo-de1:k8s.mariadb.com/Backup:bridge/backup-local-bridge - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"k8s.mariadb.com/v1alpha1","kind":"Backup","metadata":{"annotations":{"argocd.argoproj.io/tracking-id":"apps-bridge-demo-de1:k8s.mariadb.com/Backup:bridge/backup-local-bridge"},"name":"backup-local-bridge","namespace":"bridge"},"spec":{"args":["--databases bridge"],"mariaDbRef":{"name":"mariadb"},"maxRetention":"168h","schedule":{"cron":"0 1-23/2 * * *","suspend":false},"storage":{"persistentVolumeClaim":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"80Gi"}}}}}} - creationTimestamp: "2024-04-12T12:35:41Z" - generation: 2 - name: backup-local-bridge - namespace: bridge - resourceVersion: "506591405" - uid: 67364d0a-6da9-4369-97fd-45ea468dbbea -spec: - args: - - --databases bridge - backoffLimit: 5 - logLevel: info - mariaDbRef: - name: mariadb - waitForIt: true - maxRetention: 168h - restartPolicy: OnFailure - schedule: - cron: 0 1-23/2 * * * - suspend: false - serviceAccountName: backup-local-bridge - storage: - persistentVolumeClaim: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 80Gi -status: - conditions: - - lastTransitionTime: "2024-04-24T15:00:23Z" - message: Success - reason: CronJobSucess - status: "True" - type: Complete diff --git a/resource_customizations/k8s.mariadb.com/Database/health.lua b/resource_customizations/k8s.mariadb.com/Database/health.lua deleted file mode 100644 index 17372dbbbf503..0000000000000 --- a/resource_customizations/k8s.mariadb.com/Database/health.lua +++ /dev/null @@ -1,23 +0,0 @@ -local health_status = {} -health_status.status = "Progressing" -health_status.message = "No status info available" - -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.type == "Ready" then - if condition.status == "True" then - health_status.status = "Healthy" - else - health_status.status = "Degraded" - end - return health_status - end - end -end - - -return health_status diff --git a/resource_customizations/k8s.mariadb.com/Database/health_test.yaml b/resource_customizations/k8s.mariadb.com/Database/health_test.yaml deleted file mode 100644 index fa21c37fe26d0..0000000000000 --- a/resource_customizations/k8s.mariadb.com/Database/health_test.yaml +++ /dev/null @@ -1,5 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "Created" - inputPath: testdata/database-ready.yaml diff --git a/resource_customizations/k8s.mariadb.com/Database/testdata/database-ready.yaml b/resource_customizations/k8s.mariadb.com/Database/testdata/database-ready.yaml deleted file mode 100644 index f1ccff4f2699b..0000000000000 --- a/resource_customizations/k8s.mariadb.com/Database/testdata/database-ready.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: k8s.mariadb.com/v1alpha1 -kind: Database -metadata: - name: dbname -spec: - characterSet: utf8 - collate: utf8_general_ci - mariaDbRef: - name: mariadb - waitForIt: true - requeueInterval: 30s - retryInterval: 5s -status: - conditions: - - lastTransitionTime: "2024-04-12T13:43:57Z" - message: Created - reason: Created - status: "True" - type: Ready diff --git a/resource_customizations/k8s.mariadb.com/Grant/health.lua b/resource_customizations/k8s.mariadb.com/Grant/health.lua deleted file mode 100644 index 8e426f15e2b3c..0000000000000 --- a/resource_customizations/k8s.mariadb.com/Grant/health.lua +++ /dev/null @@ -1,22 +0,0 @@ -local health_status = {} -health_status.status = "Progressing" -health_status.message = "No status info available" -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.type == "Ready" then - if condition.status == "True" then - health_status.status = "Healthy" - else - health_status.status = "Degraded" - end - return health_status - end - end -end - - -return health_status diff --git a/resource_customizations/k8s.mariadb.com/Grant/health_test.yaml b/resource_customizations/k8s.mariadb.com/Grant/health_test.yaml deleted file mode 100644 index 1f420e183cbb5..0000000000000 --- a/resource_customizations/k8s.mariadb.com/Grant/health_test.yaml +++ /dev/null @@ -1,6 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "Created" - inputPath: testdata/grant-ready.yaml - diff --git a/resource_customizations/k8s.mariadb.com/Grant/testdata/grant-ready.yaml b/resource_customizations/k8s.mariadb.com/Grant/testdata/grant-ready.yaml deleted file mode 100644 index 2081010f9dae3..0000000000000 --- a/resource_customizations/k8s.mariadb.com/Grant/testdata/grant-ready.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: k8s.mariadb.com/v1alpha1 -kind: Grant -metadata: - name: mariadb-metrics -spec: - database: '*' - grantOption: false - mariaDbRef: - name: mariadb - namespace: bridge - waitForIt: false - privileges: - - SELECT - - PROCESS - - REPLICATION CLIENT - - REPLICA MONITOR - - SLAVE MONITOR - table: '*' - username: mariadb-metrics -status: - conditions: - - lastTransitionTime: "2024-04-20T20:45:02Z" - message: Created - reason: Created - status: "True" - type: Ready diff --git a/resource_customizations/k8s.mariadb.com/MariaDB/health.lua b/resource_customizations/k8s.mariadb.com/MariaDB/health.lua deleted file mode 100644 index b0278bb22650e..0000000000000 --- a/resource_customizations/k8s.mariadb.com/MariaDB/health.lua +++ /dev/null @@ -1,25 +0,0 @@ -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/k8s.mariadb.com/MariaDB/health_test.yaml b/resource_customizations/k8s.mariadb.com/MariaDB/health_test.yaml deleted file mode 100644 index f3dba1ac80c58..0000000000000 --- a/resource_customizations/k8s.mariadb.com/MariaDB/health_test.yaml +++ /dev/null @@ -1,25 +0,0 @@ -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/k8s.mariadb.com/MariaDB/testdata/mariadb_error.yaml b/resource_customizations/k8s.mariadb.com/MariaDB/testdata/mariadb_error.yaml deleted file mode 100644 index 9566f9fa3c262..0000000000000 --- a/resource_customizations/k8s.mariadb.com/MariaDB/testdata/mariadb_error.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: k8s.mariadb.com/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/k8s.mariadb.com/MariaDB/testdata/no_status.yaml b/resource_customizations/k8s.mariadb.com/MariaDB/testdata/no_status.yaml deleted file mode 100644 index dcf61713069c1..0000000000000 --- a/resource_customizations/k8s.mariadb.com/MariaDB/testdata/no_status.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: k8s.mariadb.com/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/k8s.mariadb.com/MariaDB/testdata/restore_complete.yaml b/resource_customizations/k8s.mariadb.com/MariaDB/testdata/restore_complete.yaml deleted file mode 100644 index e861bc1119683..0000000000000 --- a/resource_customizations/k8s.mariadb.com/MariaDB/testdata/restore_complete.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: k8s.mariadb.com/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/k8s.mariadb.com/MariaDB/testdata/restore_not_complete.yaml b/resource_customizations/k8s.mariadb.com/MariaDB/testdata/restore_not_complete.yaml deleted file mode 100644 index df7882ec56147..0000000000000 --- a/resource_customizations/k8s.mariadb.com/MariaDB/testdata/restore_not_complete.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: k8s.mariadb.com/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/k8s.mariadb.com/MariaDB/testdata/statefulset_not_ready.yaml b/resource_customizations/k8s.mariadb.com/MariaDB/testdata/statefulset_not_ready.yaml deleted file mode 100644 index faaf12dab205e..0000000000000 --- a/resource_customizations/k8s.mariadb.com/MariaDB/testdata/statefulset_not_ready.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: k8s.mariadb.com/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/k8s.mariadb.com/MariaDB/testdata/statefulset_ready.yaml b/resource_customizations/k8s.mariadb.com/MariaDB/testdata/statefulset_ready.yaml deleted file mode 100644 index 90d82fc08d583..0000000000000 --- a/resource_customizations/k8s.mariadb.com/MariaDB/testdata/statefulset_ready.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: k8s.mariadb.com/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/k8s.mariadb.com/SqlJob/health.lua b/resource_customizations/k8s.mariadb.com/SqlJob/health.lua deleted file mode 100644 index 0a666f2c28fe3..0000000000000 --- a/resource_customizations/k8s.mariadb.com/SqlJob/health.lua +++ /dev/null @@ -1,21 +0,0 @@ -local health_status = {} -health_status.status = "Progressing" -health_status.message = "No status info available" - -if obj.status ~= nil and obj.status.conditions ~= nil then - - for i, condition in ipairs(obj.status.conditions) do - - health_status.message = condition.reason .. " " .. condition.message - if condition.reason == "JobComplete" and condition.status == "True" then - health_status.status = "Healthy" - return health_status - end - - if condition.reason == "JobFailed" and condition.status == "True" then - health_status.status = "Degraded" - return health_status - end - end -end -return health_status diff --git a/resource_customizations/k8s.mariadb.com/SqlJob/health_test.yaml b/resource_customizations/k8s.mariadb.com/SqlJob/health_test.yaml deleted file mode 100644 index 4ecd2cb9446c3..0000000000000 --- a/resource_customizations/k8s.mariadb.com/SqlJob/health_test.yaml +++ /dev/null @@ -1,9 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "JobComplete Success" - inputPath: testdata/sqljobs-ok.yaml -- healthStatus: - status: Degraded - message: "JobFailed Failed" - inputPath: testdata/sqljobs-failed.yaml diff --git a/resource_customizations/k8s.mariadb.com/SqlJob/testdata/sqljobs-failed.yaml b/resource_customizations/k8s.mariadb.com/SqlJob/testdata/sqljobs-failed.yaml deleted file mode 100644 index f676a151f4057..0000000000000 --- a/resource_customizations/k8s.mariadb.com/SqlJob/testdata/sqljobs-failed.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: k8s.mariadb.com/v1alpha1 -kind: SqlJob -metadata: - name: jobname -spec: - backoffLimit: 5 - database: dbname - mariaDbRef: - name: mariadb - waitForIt: true - passwordSecretKeyRef: - key: password - name: mariadb-root - restartPolicy: OnFailure - serviceAccountName: jobname - sql: "Some SQL" - username: root -status: - conditions: - - lastTransitionTime: "2024-03-19T11:39:00Z" - message: Failed - reason: JobFailed - status: "True" - type: Complete diff --git a/resource_customizations/k8s.mariadb.com/SqlJob/testdata/sqljobs-ok.yaml b/resource_customizations/k8s.mariadb.com/SqlJob/testdata/sqljobs-ok.yaml deleted file mode 100644 index 84d80cfa6abb1..0000000000000 --- a/resource_customizations/k8s.mariadb.com/SqlJob/testdata/sqljobs-ok.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: k8s.mariadb.com/v1alpha1 -kind: SqlJob -metadata: - name: jobname -spec: - backoffLimit: 5 - database: dbname - mariaDbRef: - name: mariadb - waitForIt: true - passwordSecretKeyRef: - key: password - name: mariadb-root - restartPolicy: Never - serviceAccountName: jobname - sql: "some SQL;" -status: - conditions: - - lastTransitionTime: "2024-04-22T14:08:49Z" - message: Success - reason: JobComplete - status: "True" - type: Complete diff --git a/resource_customizations/k8s.mariadb.com/User/health.lua b/resource_customizations/k8s.mariadb.com/User/health.lua deleted file mode 100644 index 8f0fcb704ab08..0000000000000 --- a/resource_customizations/k8s.mariadb.com/User/health.lua +++ /dev/null @@ -1,23 +0,0 @@ -local health_status = {} - -health_status.status = "Progressing" -health_status.message = "No status info available" - -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.type == "Ready" then - if condition.status == "True" then - health_status.status = "Healthy" - else - health_status.status = "Degraded" - end - return health_status - end - end -end - -return health_status diff --git a/resource_customizations/k8s.mariadb.com/User/health_test.yaml b/resource_customizations/k8s.mariadb.com/User/health_test.yaml deleted file mode 100644 index b6e41d0e578c3..0000000000000 --- a/resource_customizations/k8s.mariadb.com/User/health_test.yaml +++ /dev/null @@ -1,5 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: "Created" - inputPath: testdata/user-created.yaml diff --git a/resource_customizations/k8s.mariadb.com/User/testdata/user-created.yaml b/resource_customizations/k8s.mariadb.com/User/testdata/user-created.yaml deleted file mode 100644 index 935f621efe464..0000000000000 --- a/resource_customizations/k8s.mariadb.com/User/testdata/user-created.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: k8s.mariadb.com/v1alpha1 -kind: User -metadata: - creationTimestamp: "2024-04-12T13:43:56Z" - finalizers: - - user.k8s.mariadb.com/finalizer - generation: 1 - labels: - db.bridge.a3p.com: mariadb - name: mariadb-metrics - namespace: bridge - ownerReferences: - - apiVersion: k8s.mariadb.com/v1alpha1 - blockOwnerDeletion: true - controller: true - kind: MariaDB - name: mariadb - uid: a29fc76f-66a5-4612-9b15-16c405f7edd9 - resourceVersion: "345121483" - uid: ecce1099-7b71-418b-b386-893db5fd7e59 -spec: - mariaDbRef: - name: mariadb - namespace: bridge - waitForIt: false - maxUserConnections: 3 - name: mariadb-metrics - passwordSecretKeyRef: - key: password - name: mariadb-metrics-password -status: - conditions: - - lastTransitionTime: "2024-04-12T13:43:57Z" - message: Created - reason: Created - status: "True" - type: Ready diff --git a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health.lua b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health.lua index 071e288989502..7422fd4104727 100644 --- a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health.lua +++ b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health.lua @@ -1,10 +1,5 @@ local health_status = {} if obj.status ~= nil then - if obj.status.state == "ClusterRollingUpgrading" then - health_status.message = "Kafka Cluster is Rolling Upgrading." - health_status.status = "Progressing" - return health_status - end if obj.status.brokersState ~= nil then local numberBrokers = 0 local healthyBrokers = 0 @@ -28,6 +23,11 @@ if obj.status ~= nil then health_status.status = "Progressing" return health_status end + if obj.status.state == "ClusterRollingUpgrading" then + health_status.message = "Kafka Cluster is Rolling Upgrading." + health_status.status = "Progressing" + return health_status + end end else health_status.message = "Broker Config is out of Sync or CruiseControlState is not Ready" @@ -38,4 +38,4 @@ if obj.status ~= nil then end health_status.status = "Progressing" health_status.message = "Waiting for KafkaCluster" -return health_status +return health_status \ No newline at end of file diff --git a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health_test.yaml b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health_test.yaml index 33e921c2ab236..776cc02739326 100644 --- a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health_test.yaml +++ b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health_test.yaml @@ -7,10 +7,6 @@ tests: status: Progressing message: "Waiting for KafkaCluster" inputPath: testdata/updating.yaml -- healthStatus: - status: Progressing - message: "Kafka Cluster is Rolling Upgrading." - inputPath: testdata/rollingUpgrade.yaml - healthStatus: status: Degraded message: "Broker Config is out of Sync or CruiseControlState is not Ready" @@ -18,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/rollingUpgrade.yaml b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/testdata/rollingUpgrade.yaml deleted file mode 100644 index c0bbfb335e243..0000000000000 --- a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/testdata/rollingUpgrade.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: kafka.banzaicloud.io/v1beta1 -kind: KafkaCluster -metadata: - finalizers: - - finalizer.kafkaclusters.kafka.banzaicloud.io - - topics.kafkaclusters.kafka.banzaicloud.io - - users.kafkaclusters.kafka.banzaicloud.io - generation: 4 - labels: - argocd.argoproj.io/instance: kafka-cluster - controller-tools.k8s.io: "1.0" - name: kafkacluster - namespace: kafka - name: kafkacluster - namespace: kafka - resourceVersion: "31935335" - selfLink: /apis/kafka.banzaicloud.io/v1beta1/namespaces/2269-kafka/kafkaclusters/kafkacluster - uid: c6affef0-651d-44c7-8bff-638961517c8d -spec: {} -status: - alertCount: 0 - brokersState: - "0": - configurationState: ConfigInSync - gracefulActionState: - cruiseControlState: GracefulUpscaleSucceeded - errorMessage: CruiseControlTopicReady - rackAwarenessState: | - broker.rack=us-east-1,us-east-1c - "1": - configurationState: ConfigInSync - gracefulActionState: - cruiseControlState: GracefulUpscaleSucceeded - errorMessage: CruiseControlTopicReady - rackAwarenessState: | - broker.rack=us-east-1,us-east-1b - "2": - configurationState: ConfigOutOfSync - gracefulActionState: - cruiseControlState: GracefulUpscaleSucceeded - errorMessage: CruiseControlTopicReady - rackAwarenessState: | - broker.rack=us-east-1,us-east-1a - cruiseControlTopicStatus: CruiseControlTopicReady - rollingUpgradeStatus: - errorCount: 0 - lastSuccess: "" - state: ClusterRollingUpgrading diff --git a/resource_customizations/kafka.strimzi.io/KafkaBridge/health.lua b/resource_customizations/kafka.strimzi.io/KafkaBridge/health.lua deleted file mode 100644 index 5cc1908db0ac2..0000000000000 --- a/resource_customizations/kafka.strimzi.io/KafkaBridge/health.lua +++ /dev/null @@ -1,21 +0,0 @@ -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 == "NotReady" and condition.status == "True" 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 = "" - return hs - end - end - end -end - -hs.status = "Progressing" -hs.message = "Waiting for KafkaBridge" -return hs diff --git a/resource_customizations/kafka.strimzi.io/KafkaBridge/health_test.yaml b/resource_customizations/kafka.strimzi.io/KafkaBridge/health_test.yaml deleted file mode 100644 index 3598282b2b4bd..0000000000000 --- a/resource_customizations/kafka.strimzi.io/KafkaBridge/health_test.yaml +++ /dev/null @@ -1,12 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: "Waiting for KafkaBridge" - inputPath: testdata/progressing_noStatus.yaml -- healthStatus: - status: Degraded - message: "Error" - inputPath: testdata/degraded.yaml -- healthStatus: - status: Healthy - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/degraded.yaml b/resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/degraded.yaml deleted file mode 100644 index 46a692282e1be..0000000000000 --- a/resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/degraded.yaml +++ /dev/null @@ -1,54 +0,0 @@ -apiVersion: kafka.strimzi.io/v1beta1 -kind: KafkaBridge -metadata: - creationTimestamp: "2020-02-13T14:03:15Z" - deletionGracePeriodSeconds: 0 - deletionTimestamp: "2020-05-28T10:29:44Z" - finalizers: - - foregroundDeletion - generation: 25 - labels: - app.kubernetes.io/instance: kafka-bridge - name: kafka-bridge - namespace: strimzi - resourceVersion: "43088521" - selfLink: /apis/kafka.strimzi.io/v1beta1/namespaces/strimzi/kafkabridge/kafka - uid: 941ae21d-4e69-11ea-a53d-06e66a171f98 -spec: - authentication: - passwordSecret: - password: password - secretName: kafka-bridge - type: scram-sha-512 - username: kafka-bridge - bootstrapServers: 'kafka-bootstrap:9095' - enableMetrics: true - http: - port: 8080 - logging: - loggers: - kafka.root.logger.level: DEBUG - logger.send.level: DEBUG - logger.send.name: http.openapi.operation.send - type: inline - producer: - config: - ssl.cipher.suites: TLS_AES_256_GCM_SHA384 - ssl.enabled.protocols: TLSv1.3 - ssl.protocol: TLSv1.3 - replicas: 1 - tls: - trustedCertificates: - - certificate: ca.crt - secretName: kafka-cluster-cluster-ca-cert -status: - conditions: - - lastTransitionTime: '2024-05-15T09:34:44.930056634Z' - status: "True" - type: NotReady - message: "Error" - labelSelector: >- - strimzi.io/cluster=kafka-bridge,strimzi.io/name=kafka-bridge-bridge,strimzi.io/kind=KafkaBridge - observedGeneration: 14 - replicas: 1 - url: 'http://kafka-bridge-bridge-service.strimzi.svc:8080' \ No newline at end of file diff --git a/resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/healthy.yaml b/resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/healthy.yaml deleted file mode 100644 index 0246a7a964429..0000000000000 --- a/resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/healthy.yaml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: kafka.strimzi.io/v1beta1 -kind: KafkaBridge -metadata: - creationTimestamp: "2020-02-13T14:03:15Z" - deletionGracePeriodSeconds: 0 - deletionTimestamp: "2020-05-28T10:29:44Z" - finalizers: - - foregroundDeletion - generation: 25 - labels: - app.kubernetes.io/instance: kafka-bridge - name: kafka-bridge - namespace: strimzi - resourceVersion: "43088521" - selfLink: /apis/kafka.strimzi.io/v1beta1/namespaces/strimzi/kafkabridge/kafka - uid: 941ae21d-4e69-11ea-a53d-06e66a171f98 -spec: - authentication: - passwordSecret: - password: password - secretName: kafka-bridge - type: scram-sha-512 - username: kafka-bridge - bootstrapServers: 'kafka-bootstrap:9095' - enableMetrics: true - http: - port: 8080 - logging: - loggers: - kafka.root.logger.level: DEBUG - logger.send.level: DEBUG - logger.send.name: http.openapi.operation.send - type: inline - producer: - config: - ssl.cipher.suites: TLS_AES_256_GCM_SHA384 - ssl.enabled.protocols: TLSv1.3 - ssl.protocol: TLSv1.3 - replicas: 1 - tls: - trustedCertificates: - - certificate: ca.crt - secretName: kafka-cluster-cluster-ca-cert -status: - conditions: - - lastTransitionTime: '2024-05-15T09:34:44.930056634Z' - status: 'True' - type: Ready - labelSelector: >- - strimzi.io/cluster=kafka-bridge,strimzi.io/name=kafka-bridge-bridge,strimzi.io/kind=KafkaBridge - observedGeneration: 14 - replicas: 1 - url: 'http://kafka-bridge-bridge-service.strimzi.svc:8080' \ No newline at end of file diff --git a/resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/progressing_noStatus.yaml b/resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/progressing_noStatus.yaml deleted file mode 100644 index 5b1d35293d16d..0000000000000 --- a/resource_customizations/kafka.strimzi.io/KafkaBridge/testdata/progressing_noStatus.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: kafka.strimzi.io/v1beta1 -kind: KafkaBridge -metadata: - creationTimestamp: "2020-02-13T14:03:15Z" - deletionGracePeriodSeconds: 0 - deletionTimestamp: "2020-05-28T10:29:44Z" - finalizers: - - foregroundDeletion - generation: 25 - labels: - app.kubernetes.io/instance: kafka-bridge - name: kafka-bridge - namespace: strimzi - resourceVersion: "43088521" - selfLink: /apis/kafka.strimzi.io/v1beta1/namespaces/strimzi/kafkabridge/kafka - uid: 941ae21d-4e69-11ea-a53d-06e66a171f98 -spec: - authentication: - passwordSecret: - password: password - secretName: kafka-bridge - type: scram-sha-512 - username: kafka-bridge - bootstrapServers: 'kafka-bootstrap:9095' - enableMetrics: true - http: - port: 8080 - logging: - loggers: - kafka.root.logger.level: DEBUG - logger.send.level: DEBUG - logger.send.name: http.openapi.operation.send - type: inline - producer: - config: - ssl.cipher.suites: TLS_AES_256_GCM_SHA384 - ssl.enabled.protocols: TLSv1.3 - ssl.protocol: TLSv1.3 - replicas: 1 - tls: - trustedCertificates: - - certificate: ca.crt - secretName: kafka-cluster-cluster-ca-cert \ No newline at end of file diff --git a/resource_customizations/kafka.strimzi.io/KafkaConnector/health.lua b/resource_customizations/kafka.strimzi.io/KafkaConnector/health.lua deleted file mode 100644 index 3f35894629bf0..0000000000000 --- a/resource_customizations/kafka.strimzi.io/KafkaConnector/health.lua +++ /dev/null @@ -1,21 +0,0 @@ -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 == "NotReady" and condition.status == "True" 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 = "" - return hs - end - end - end -end - -hs.status = "Progressing" -hs.message = "Waiting for KafkaConnector" -return hs diff --git a/resource_customizations/kafka.strimzi.io/KafkaConnector/health_test.yaml b/resource_customizations/kafka.strimzi.io/KafkaConnector/health_test.yaml deleted file mode 100644 index ec96bb4f029d5..0000000000000 --- a/resource_customizations/kafka.strimzi.io/KafkaConnector/health_test.yaml +++ /dev/null @@ -1,12 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: "Waiting for KafkaConnector" - inputPath: testdata/progressing_noStatus.yaml -- healthStatus: - status: Degraded - message: "The following tasks have failed: 0, see connectorStatus for more details." - inputPath: testdata/degraded.yaml -- healthStatus: - status: Healthy - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/degraded.yaml b/resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/degraded.yaml deleted file mode 100644 index 806da605e36d3..0000000000000 --- a/resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/degraded.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: kafka.strimzi.io/v1beta1 -kind: KafkaConnector -metadata: - creationTimestamp: "2020-02-13T14:03:15Z" - deletionGracePeriodSeconds: 0 - deletionTimestamp: "2020-05-28T10:29:44Z" - finalizers: - - foregroundDeletion - generation: 25 - labels: - app.kubernetes.io/instance: kafka-connect - strimzi.io/cluster: strimzi-connect-cluster - name: my-connector - namespace: strimzi - resourceVersion: "43088521" - selfLink: /apis/kafka.strimzi.io/v1beta1/namespaces/strimzi/kafkaconnector/kafka - uid: 941ae21d-4e69-11ea-a53d-06e66a171f98 -spec: - class: org.apache.kafka.connect.file.FileStreamSourceConnector - tasksMax: 2 - config: - file: "/opt/kafka/LICENSE" - topic: my-topic -status: - autoRestart: - count: 1 - lastRestartTimestamp: '2024-05-17T15:55:21.611546835Z' - conditions: - - lastTransitionTime: '2024-05-17T15:57:09.059039185Z' - message: >- - The following tasks have failed: 0, see connectorStatus for more - details. - reason: Throwable - status: 'True' - type: NotReady - connectorStatus: - connector: - state: RUNNING - worker_id: >- - kafka-connect-cluster-connect-0.kafka-connect-cluster-connect.strimzi.svc:8083 - name: my-connector - tasks: - - id: 0 - state: FAILED - trace: "org.apache.kafka.connect.errors.ConnectException: Tolerance exceeded in error handler..." - worker_id: >- - kafka-connect-cluster-connect-0.kafka-connect-cluster-connect.strimzi.svc:8083 - type: source - observedGeneration: 1 - tasksMax: 1 - topics: [] \ No newline at end of file diff --git a/resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/healthy.yaml b/resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/healthy.yaml deleted file mode 100644 index 18f88ed67a140..0000000000000 --- a/resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/healthy.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: kafka.strimzi.io/v1beta1 -kind: KafkaConnector -metadata: - creationTimestamp: "2020-02-13T14:03:15Z" - deletionGracePeriodSeconds: 0 - deletionTimestamp: "2020-05-28T10:29:44Z" - finalizers: - - foregroundDeletion - generation: 25 - labels: - app.kubernetes.io/instance: kafka-connect - strimzi.io/cluster: strimzi-connect-cluster - name: my-connector - namespace: strimzi - resourceVersion: "43088521" - selfLink: /apis/kafka.strimzi.io/v1beta1/namespaces/strimzi/kafkaconnector/kafka - uid: 941ae21d-4e69-11ea-a53d-06e66a171f98 -spec: - class: org.apache.kafka.connect.file.FileStreamSourceConnector - tasksMax: 2 - config: - file: "/opt/kafka/LICENSE" - topic: my-topic -status: - conditions: - - lastTransitionTime: '2024-05-17T15:55:22.356665885Z' - status: 'True' - type: Ready - connectorStatus: - connector: - state: RUNNING - worker_id: >- - kafka-connect-cluster-connect-0.kafka-connect-cluster-connect.strimzi.svc:8083 - name: my-connector - tasks: - - id: 0 - state: RUNNING - worker_id: >- - kafka-connect-cluster-connect-0.kafka-connect-cluster-connect.strimzi.svc:8083 - type: source - observedGeneration: 1 - tasksMax: 1 - topics: [] \ No newline at end of file diff --git a/resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/progressing_noStatus.yaml b/resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/progressing_noStatus.yaml deleted file mode 100644 index 7dd14e5fc1241..0000000000000 --- a/resource_customizations/kafka.strimzi.io/KafkaConnector/testdata/progressing_noStatus.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: kafka.strimzi.io/v1beta1 -kind: KafkaConnector -metadata: - creationTimestamp: "2020-02-13T14:03:15Z" - deletionGracePeriodSeconds: 0 - deletionTimestamp: "2020-05-28T10:29:44Z" - finalizers: - - foregroundDeletion - generation: 25 - labels: - app.kubernetes.io/instance: kafka-connect - strimzi.io/cluster: strimzi-connect-cluster - name: my-connector - namespace: strimzi - resourceVersion: "43088521" - selfLink: /apis/kafka.strimzi.io/v1beta1/namespaces/strimzi/kafkaconnector/kafka - uid: 941ae21d-4e69-11ea-a53d-06e66a171f98 -spec: - class: org.apache.kafka.connect.file.FileStreamSourceConnector - tasksMax: 2 - config: - file: "/opt/kafka/LICENSE" - topic: my-topic \ No newline at end of file diff --git a/resource_customizations/keda.sh/ScaledObject/health.lua b/resource_customizations/keda.sh/ScaledObject/health.lua deleted file mode 100644 index 84cc5ad17a433..0000000000000 --- a/resource_customizations/keda.sh/ScaledObject/health.lua +++ /dev/null @@ -1,35 +0,0 @@ -local hs = {} -local healthy = false -local degraded = false -local suspended = false -if obj.status ~= nil then - if obj.status.conditions ~= nil then - for i, condition in ipairs(obj.status.conditions) do - if condition.status == "False" and condition.type == "Ready" then - hs.message = condition.message - degraded = true - end - if condition.status == "True" and condition.type == "Ready" then - hs.message = condition.message - healthy = true - end - if condition.status == "True" and condition.type == "Paused" then - hs.message = condition.message - suspended = true - end - end - end -end -if degraded == true then - hs.status = "Degraded" - return hs -elseif healthy == true and suspended == false then - hs.status = "Healthy" - return hs -elseif healthy == true and suspended == true then - hs.status = "Suspended" - return hs -end -hs.status = "Progressing" -hs.message = "Creating HorizontalPodAutoscaler Object" -return hs \ No newline at end of file diff --git a/resource_customizations/keda.sh/ScaledObject/health_test.yaml b/resource_customizations/keda.sh/ScaledObject/health_test.yaml deleted file mode 100644 index 969334650ba14..0000000000000 --- a/resource_customizations/keda.sh/ScaledObject/health_test.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: "Creating HorizontalPodAutoscaler Object" - inputPath: testdata/keda-progressing.yaml -- healthStatus: - status: Degraded - message: "ScaledObject doesn't have correct Idle/Min/Max Replica Counts specification" - inputPath: testdata/keda-degraded-1.yaml -- healthStatus: - status: Degraded - message: "ScaledObject doesn't have correct triggers specification" - inputPath: testdata/keda-degraded.yaml -- healthStatus: - status: Healthy - message: "ScaledObject is defined correctly and is ready for scaling" - inputPath: testdata/keda-healthy.yaml -- healthStatus: - status: Suspended - message: "ScaledObject is paused" - inputPath: testdata/keda-suspended.yaml diff --git a/resource_customizations/keda.sh/ScaledObject/testdata/keda-degraded-1.yaml b/resource_customizations/keda.sh/ScaledObject/testdata/keda-degraded-1.yaml deleted file mode 100644 index 3c3aba78a16a4..0000000000000 --- a/resource_customizations/keda.sh/ScaledObject/testdata/keda-degraded-1.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: keda.sh/v1alpha1 -kind: ScaledObject -metadata: - annotations: - finalizers: - - finalizer.keda.sh - labels: - argocd.argoproj.io/instance: keda-default - name: keda - namespace: keda - resourceVersion: '160591442' - uid: 73ee438a-f383-43f3-9346-b901d9773f4b -spec: - maxReplicaCount: 3 - minReplicaCount: 0 - scaleTargetRef: - name: keda - triggers: - - metadata: - desiredReplicas: '1' - end: 00 17 * * 1-5 - start: 00 08 * * 1-5 - timezone: Europe/Stockholm - type: cron -status: - conditions: - - message: >- - ScaledObject doesn't have correct Idle/Min/Max Replica Counts specification - reason: ScaledObjectCheckFailed - status: 'False' - type: Ready - - message: ScaledObject check failed - reason: UnknownState - status: Unknown - type: Active - - message: No fallbacks are active on this scaled object - reason: NoFallbackFound - status: 'False' - type: Fallback - - status: Unknown - type: Paused - externalMetricNames: - - s0-cron-Europe-Stockholm-0008xx1-5-0019xx1-5 - hpaName: keda-hpa - lastActiveTime: '2023-12-18T17:59:55Z' - originalReplicaCount: 1 - scaleTargetGVKR: - group: apps - kind: Deployment - resource: deployments - version: v1 - scaleTargetKind: apps/v1.Deployment \ No newline at end of file diff --git a/resource_customizations/keda.sh/ScaledObject/testdata/keda-degraded.yaml b/resource_customizations/keda.sh/ScaledObject/testdata/keda-degraded.yaml deleted file mode 100644 index 4996905fed7f4..0000000000000 --- a/resource_customizations/keda.sh/ScaledObject/testdata/keda-degraded.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: keda.sh/v1alpha1 -kind: ScaledObject -metadata: - annotations: - finalizers: - - finalizer.keda.sh - labels: - argocd.argoproj.io/instance: keda-default - name: keda - namespace: keda - resourceVersion: '160591442' - uid: 73ee438a-f383-43f3-9346-b901d9773f4b -spec: - maxReplicaCount: 3 - minReplicaCount: 0 - scaleTargetRef: - name: keda - triggers: - - metadata: - desiredReplicas: '1' - end: 00 17 * * 1-5 - start: 00 08 * * 1-5 - timezone: Europe/Stockholm - type: cron -status: - conditions: - - message: ScaledObject doesn't have correct triggers specification - reason: ScaledObjectCheckFailed - status: 'False' - type: Ready - - message: Scaling is not performed because triggers are not active - reason: ScalerNotActive - status: 'False' - type: Active - - message: No fallbacks are active on this scaled object - reason: NoFallbackFound - status: 'False' - type: Fallback - - status: Unknown - type: Paused - externalMetricNames: - - s0-cron-Europe-Stockholm-0008xx1-5-0019xx1-5 - hpaName: keda-hpa - lastActiveTime: '2023-12-18T17:59:55Z' - originalReplicaCount: 1 - scaleTargetGVKR: - group: apps - kind: Deployment - resource: deployments - version: v1 - scaleTargetKind: apps/v1.Deployment \ No newline at end of file diff --git a/resource_customizations/keda.sh/ScaledObject/testdata/keda-healthy.yaml b/resource_customizations/keda.sh/ScaledObject/testdata/keda-healthy.yaml deleted file mode 100644 index 38bd24dc1953f..0000000000000 --- a/resource_customizations/keda.sh/ScaledObject/testdata/keda-healthy.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: keda.sh/v1alpha1 -kind: ScaledObject -metadata: - annotations: - finalizers: - - finalizer.keda.sh - labels: - argocd.argoproj.io/instance: keda-default - name: keda - namespace: keda - resourceVersion: '160591442' - uid: 73ee438a-f383-43f3-9346-b901d9773f4b -spec: - maxReplicaCount: 3 - minReplicaCount: 0 - scaleTargetRef: - name: backstage - triggers: - - metadata: - desiredReplicas: '1' - end: 00 17 * * 1-5 - start: 00 08 * * 1-5 - timezone: Europe/Stockholm - type: cron -status: - conditions: - - message: ScaledObject is defined correctly and is ready for scaling - reason: ScaledObjectReady - status: 'True' - type: Ready - - message: Scaling is not performed because triggers are not active - reason: ScalerNotActive - status: 'False' - type: Active - - message: No fallbacks are active on this scaled object - reason: NoFallbackFound - status: 'False' - type: Fallback - - status: Unknown - type: Paused - externalMetricNames: - - s0-cron-Europe-Stockholm-0008xx1-5-0019xx1-5 - hpaName: keda-hpa-backstage-kambi-standard-chart - lastActiveTime: '2023-12-18T17:59:55Z' - originalReplicaCount: 1 - scaleTargetGVKR: - group: apps - kind: Deployment - resource: deployments - version: v1 - scaleTargetKind: apps/v1.Deployment \ No newline at end of file diff --git a/resource_customizations/keda.sh/ScaledObject/testdata/keda-progressing.yaml b/resource_customizations/keda.sh/ScaledObject/testdata/keda-progressing.yaml deleted file mode 100644 index 2206bfbff97f1..0000000000000 --- a/resource_customizations/keda.sh/ScaledObject/testdata/keda-progressing.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: keda.sh/v1alpha1 -kind: ScaledObject -metadata: - annotations: - finalizers: - - finalizer.keda.sh - labels: - argocd.argoproj.io/instance: keda-default - name: keda - namespace: keda - resourceVersion: '160591442' - uid: 73ee438a-f383-43f3-9346-b901d9773f4b -spec: - maxReplicaCount: 3 - minReplicaCount: 0 - scaleTargetRef: - name: backstage - triggers: - - metadata: - desiredReplicas: '1' - end: 00 17 * * 1-5 - start: 00 08 * * 1-5 - timezone: Europe/Stockholm - type: cron -status: - conditions: - - message: Creating HorizontalPodAutoscaler Object - reason: Running - status: 'True' - type: Running \ No newline at end of file diff --git a/resource_customizations/keda.sh/ScaledObject/testdata/keda-suspended.yaml b/resource_customizations/keda.sh/ScaledObject/testdata/keda-suspended.yaml deleted file mode 100644 index a2d0b2b5dcf67..0000000000000 --- a/resource_customizations/keda.sh/ScaledObject/testdata/keda-suspended.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: keda.sh/v1alpha1 -kind: ScaledObject -metadata: - annotations: - finalizers: - - finalizer.keda.sh - labels: - argocd.argoproj.io/instance: keda-default - name: keda - namespace: keda - resourceVersion: '160591442' - uid: 73ee438a-f383-43f3-9346-b901d9773f4b -spec: - maxReplicaCount: 3 - minReplicaCount: 0 - scaleTargetRef: - name: backstage - triggers: - - metadata: - desiredReplicas: '1' - end: 00 17 * * 1-5 - start: 00 08 * * 1-5 - timezone: Europe/Stockholm - type: cron -status: - conditions: - - message: ScaledObject is defined correctly and is ready for scaling - reason: ScaledObjectReady - status: 'True' - type: Ready - - message: ScaledObject check failed - reason: UnknownState - status: Unknown - type: Active - - status: Unknown - type: Fallback - - message: ScaledObject is paused - reason: ScaledObjectPaused - status: 'True' - type: Paused - externalMetricNames: - - s0-cron-Europe-Stockholm-0008xx1-5-0019xx1-5 - hpaName: keda-hpa-backstage-kambi-standard-chart - lastActiveTime: '2023-12-18T17:59:55Z' - originalReplicaCount: 1 - scaleTargetGVKR: - group: apps - kind: Deployment - resource: deployments - version: v1 - scaleTargetKind: apps/v1.Deployment \ No newline at end of file diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/action_test.yaml b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/action_test.yaml deleted file mode 100644 index da2b9953274da..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/action_test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_kustomization.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_kustomization.yaml - result: - - name: reconcile - disabled: true - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_kustomization.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: reconcile - inputPath: testdata/initial_kustomization.yaml - expectedOutputPath: testdata/reconciled_kustomization.yaml -- action: suspend - inputPath: testdata/initial_kustomization.yaml - expectedOutputPath: testdata/suspended_kustomization.yaml -- action: resume - inputPath: testdata/suspended_kustomization.yaml - expectedOutputPath: testdata/resumed_kustomization.yaml diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/discovery.lua b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/discovery.lua deleted file mode 100644 index 9000998815515..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/discovery.lua +++ /dev/null @@ -1,18 +0,0 @@ -local actions = {} - -actions["reconcile"] = {["disabled"] = true} -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["reconcile"]["disabled"] = false - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/reconcile/action.lua b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/reconcile/action.lua deleted file mode 100644 index a534b36fb3ebc..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/reconcile/action.lua +++ /dev/null @@ -1,7 +0,0 @@ -local os = require("os") -if obj.metadata.annotations == nil then - obj.metadata.annotations = {} -end -obj.metadata.annotations["reconcile.fluxcd.io/requestedAt"] = "By Argo CD at: " .. os.date("!%Y-%m-%dT%X") - -return obj diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/resume/action.lua b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/suspend/action.lua b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/initial_kustomization.yaml b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/initial_kustomization.yaml deleted file mode 100644 index baa2331533a04..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/initial_kustomization.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: kustomize.toolkit.fluxcd.io/v1 -kind: Kustomization -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - targetNamespace: default - sourceRef: - kind: GitRepository - name: podinfo - path: "./kustomize" - prune: true - timeout: 1m diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/reconciled_kustomization.yaml b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/reconciled_kustomization.yaml deleted file mode 100644 index fa3019c176bb2..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/reconciled_kustomization.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: kustomize.toolkit.fluxcd.io/v1 -kind: Kustomization -metadata: - name: podinfo - namespace: default - annotations: - reconcile.fluxcd.io/requestedAt: 'By Argo CD at: 0001-01-01T00:00:00' -spec: - interval: 10m - targetNamespace: default - sourceRef: - kind: GitRepository - name: podinfo - path: "./kustomize" - prune: true - timeout: 1m diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/resumed_kustomization.yaml b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/resumed_kustomization.yaml deleted file mode 100644 index 48bc7baffefbb..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/resumed_kustomization.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: kustomize.toolkit.fluxcd.io/v1 -kind: Kustomization -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - targetNamespace: default - sourceRef: - kind: GitRepository - name: podinfo - path: "./kustomize" - prune: true - suspend: false - timeout: 1m diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/suspended_kustomization.yaml b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/suspended_kustomization.yaml deleted file mode 100644 index b4684ef2d0d56..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/actions/testdata/suspended_kustomization.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: kustomize.toolkit.fluxcd.io/v1 -kind: Kustomization -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - targetNamespace: default - sourceRef: - kind: GitRepository - name: podinfo - path: "./kustomize" - prune: true - suspend: true - timeout: 1m diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/health.lua b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/health.lua deleted file mode 100644 index b4f27ab073ec2..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/health.lua +++ /dev/null @@ -1,41 +0,0 @@ -local hs = {} -if obj.spec.suspend ~= nil and obj.spec.suspend == true then - hs.message = obj.kind .. " is suspended" - hs.status = "Suspended" - return hs -end -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numProgressing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "Unknown" then - numProgressing = numProgressing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - numProgressing = numProgressing + 1 - end - end - if(numProgressing == 2) then - hs.message = message - hs.status = "Progressing" - return hs - elseif(numSucceeded == 1) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/health_test.yaml b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/health_test.yaml deleted file mode 100644 index 62c520424189c..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: Progressing - inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: ArtifactFailed - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: InstallSucceeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/degraded.yaml b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/degraded.yaml deleted file mode 100644 index 6816b329d48e1..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/degraded.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: kustomize.toolkit.fluxcd.io/v1 -kind: Kustomization -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - targetNamespace: default - sourceRef: - kind: GitRepository - name: podinfo - path: "./kustomize" - prune: true - timeout: 1m -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: GitRepository.source.toolkit.fluxcd.io "podinfo" not found - observedGeneration: 1 - reason: ArtifactFailed - status: "False" - type: Ready - observedGeneration: -1 diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/healthy.yaml b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/healthy.yaml deleted file mode 100644 index a6fc2fb02fdc6..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/healthy.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: kustomize.toolkit.fluxcd.io/v1 -kind: Kustomization -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - targetNamespace: default - sourceRef: - kind: GitRepository - name: podinfo - path: "./kustomize" - prune: true - timeout: 1m -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm install succeeded for release default/podinfo.v1 with - chart podinfo@6.5.4 - observedGeneration: 2 - reason: InstallSucceeded - status: "True" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm install succeeded for release default/podinfo.v1 with - chart podinfo@6.5.4 - observedGeneration: 1 - reason: InstallSucceeded - status: "True" - type: Released diff --git a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/progressing.yaml b/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/progressing.yaml deleted file mode 100644 index 3bfa6e4159b09..0000000000000 --- a/resource_customizations/kustomize.toolkit.fluxcd.io/Kustomization/testdata/progressing.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: kustomize.toolkit.fluxcd.io/v1 -kind: Kustomization -metadata: - name: podinfo - namespace: default -spec: - interval: 10m - targetNamespace: default - sourceRef: - kind: GitRepository - name: podinfo - path: "./kustomize" - prune: true - timeout: 1m -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Running 'upgrade' action with timeout of 5m0s - observedGeneration: 3 - reason: Progressing - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Running 'upgrade' action with timeout of 5m0s - observedGeneration: 3 - reason: Progressing - status: Unknown - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm install succeeded for release default/podinfo.v1 with - chart podinfo@6.5.4 - observedGeneration: 1 - reason: InstallSucceeded - status: "True" - type: Released diff --git a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/health.lua b/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/health.lua deleted file mode 100644 index caedc1f309fda..0000000000000 --- a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/health.lua +++ /dev/null @@ -1,14 +0,0 @@ -local hs = {} -if obj.status.status == "Succeeded" then - hs.status = "Healthy" - hs.message = "KeptnWorkloadVersion is healthy" - return hs -end -if obj.status.status == "Failed" then - hs.status = "Degraded" - hs.message = "KeptnWorkloadVersion is degraded" - return hs -end -hs.status = "Progressing" -hs.message = "KeptnWorkloadVersion is progressing" -return hs \ No newline at end of file diff --git a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/health_test.yaml b/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/health_test.yaml deleted file mode 100644 index 3fbc2bc524968..0000000000000 --- a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "KeptnWorkloadVersion is progressing" - inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: "KeptnWorkloadVersion is degraded" - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: "KeptnWorkloadVersion is healthy" - inputPath: testdata/healthy.yaml \ No newline at end of file diff --git a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/degraded.yaml b/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/degraded.yaml deleted file mode 100644 index 0df7b8ca4fe08..0000000000000 --- a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/degraded.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: lifecycle.keptn.sh/v1alpha4 -kind: KeptnWorkloadVersion -metadata: - annotations: - traceparent: 00-5050e556a9aaf22814aa689d0518f4d3-cbcff966a6d32c39-01 - creationTimestamp: "2022-12-14T13:17:36Z" - generation: 2 - name: podtato-head-podtato-head-entry-0.2.7 - namespace: podtato-kubectl - ownerReferences: - - apiVersion: lifecycle.keptn.sh/v1alpha2 - blockOwnerDeletion: true - controller: true - kind: KeptnWorkload - name: podtato-head-podtato-head-entry - uid: dcafe814-7f9d-4d50-9a66-f61c81bfe764 - resourceVersion: "226253" - uid: 6987404b-c7b9-40f5-95e9-d5aad55a3f3b -spec: - app: podtato-head - resourceReference: - kind: ReplicaSet - name: podtato-head-entry-6fc8964846 - uid: 2b6e44bf-27e3-4305-a9fb-65d2f412936b - traceId: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-0ae50f2d844888ab-01 - version: 0.2.7 - workloadName: podtato-head-podtato-head-entry -status: - currentPhase: PreDeployTasks - deploymentStatus: Succeeded - phaseTraceIDs: - "": - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-ca249d3f6e024547-01 - WorkloadDeploy: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-3be53185e6024eb4-01 - WorkloadPostDeployEvaluations: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-0dc305a08a0ccf14-01 - WorkloadPostDeployTasks: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-4c7cf78cbbc40e14-01 - WorkloadPreDeployEvaluations: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-5eed0ec5420cfc89-01 - WorkloadPreDeployTasks: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-bef05615fc0138ac-01 - postDeploymentEvaluationStatus: Progressing - postDeploymentStatus: Progressing - preDeploymentEvaluationStatus: Failed - preDeploymentStatus: Failed - startTime: "2022-12-14T13:17:57Z" - status: Failed \ No newline at end of file diff --git a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/healthy.yaml b/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/healthy.yaml deleted file mode 100644 index b8879f0b29415..0000000000000 --- a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/healthy.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: lifecycle.keptn.sh/v1alpha4 -kind: KeptnWorkloadVersion -metadata: - annotations: - traceparent: 00-5050e556a9aaf22814aa689d0518f4d3-cbcff966a6d32c39-01 - creationTimestamp: "2022-12-14T13:17:36Z" - generation: 2 - name: podtato-head-podtato-head-entry-0.2.7 - namespace: podtato-kubectl - ownerReferences: - - apiVersion: lifecycle.keptn.sh/v1alpha2 - blockOwnerDeletion: true - controller: true - kind: KeptnWorkload - name: podtato-head-podtato-head-entry - uid: dcafe814-7f9d-4d50-9a66-f61c81bfe764 - resourceVersion: "226253" - uid: 6987404b-c7b9-40f5-95e9-d5aad55a3f3b -spec: - app: podtato-head - resourceReference: - kind: ReplicaSet - name: podtato-head-entry-6fc8964846 - uid: 2b6e44bf-27e3-4305-a9fb-65d2f412936b - traceId: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-0ae50f2d844888ab-01 - version: 0.2.7 - workloadName: podtato-head-podtato-head-entry -status: - currentPhase: Completed - deploymentStatus: Succeeded - endTime: "2022-12-14T13:18:41Z" - phaseTraceIDs: - "": - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-ca249d3f6e024547-01 - WorkloadDeploy: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-3be53185e6024eb4-01 - WorkloadPostDeployEvaluations: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-0dc305a08a0ccf14-01 - WorkloadPostDeployTasks: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-4c7cf78cbbc40e14-01 - WorkloadPreDeployEvaluations: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-5eed0ec5420cfc89-01 - WorkloadPreDeployTasks: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-bef05615fc0138ac-01 - postDeploymentEvaluationStatus: Succeeded - postDeploymentStatus: Succeeded - preDeploymentEvaluationStatus: Succeeded - preDeploymentStatus: Succeeded - startTime: "2022-12-14T13:17:57Z" - status: Succeeded \ No newline at end of file diff --git a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/progressing.yaml b/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/progressing.yaml deleted file mode 100644 index b339bb469a8e6..0000000000000 --- a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadVersion/testdata/progressing.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: lifecycle.keptn.sh/v1alpha4 -kind: KeptnWorkloadVersion -metadata: - annotations: - traceparent: 00-5050e556a9aaf22814aa689d0518f4d3-cbcff966a6d32c39-01 - creationTimestamp: "2022-12-14T13:17:36Z" - generation: 2 - name: podtato-head-podtato-head-entry-0.2.7 - namespace: podtato-kubectl - ownerReferences: - - apiVersion: lifecycle.keptn.sh/v1alpha2 - blockOwnerDeletion: true - controller: true - kind: KeptnWorkload - name: podtato-head-podtato-head-entry - uid: dcafe814-7f9d-4d50-9a66-f61c81bfe764 - resourceVersion: "226253" - uid: 6987404b-c7b9-40f5-95e9-d5aad55a3f3b -spec: - app: podtato-head - resourceReference: - kind: ReplicaSet - name: podtato-head-entry-6fc8964846 - uid: 2b6e44bf-27e3-4305-a9fb-65d2f412936b - traceId: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-0ae50f2d844888ab-01 - version: 0.2.7 - workloadName: podtato-head-podtato-head-entry -status: - currentPhase: Completed - deploymentStatus: Succeeded - phaseTraceIDs: - "": - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-ca249d3f6e024547-01 - WorkloadDeploy: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-3be53185e6024eb4-01 - WorkloadPostDeployEvaluations: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-0dc305a08a0ccf14-01 - WorkloadPostDeployTasks: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-4c7cf78cbbc40e14-01 - WorkloadPreDeployEvaluations: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-5eed0ec5420cfc89-01 - WorkloadPreDeployTasks: - traceparent: 00-ecdd1f5a7e1068ac9b0d044aa165ca4c-bef05615fc0138ac-01 - postDeploymentEvaluationStatus: Progressing - postDeploymentStatus: Progressing - preDeploymentEvaluationStatus: Succeeded - preDeploymentStatus: Succeeded - startTime: "2022-12-14T13:17:57Z" - status: Progressing \ No newline at end of file diff --git a/resource_customizations/metrics.keptn.sh/Analysis/health.lua b/resource_customizations/metrics.keptn.sh/Analysis/health.lua deleted file mode 100644 index 449ccb3505d4c..0000000000000 --- a/resource_customizations/metrics.keptn.sh/Analysis/health.lua +++ /dev/null @@ -1,19 +0,0 @@ -local hs = {} -if obj.status.pass == true then - hs.status = "Healthy" - hs.message = "Analysis is healthy" - return hs -end -if obj.status.warning == true then - hs.status = "Healthy" - hs.message = "Analysis is healthy with warnings" - return hs -end -if obj.status.pass == false then - hs.status = "Degraded" - hs.message = "Analysis is degraded" - return hs -end -hs.status = "Progressing" -hs.message = "Analysis is progressing" -return hs diff --git a/resource_customizations/metrics.keptn.sh/Analysis/health_test.yaml b/resource_customizations/metrics.keptn.sh/Analysis/health_test.yaml deleted file mode 100644 index 945d2c9058ba4..0000000000000 --- a/resource_customizations/metrics.keptn.sh/Analysis/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "Analysis is progressing" - inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: "Analysis is degraded" - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: "Analysis is healthy" - inputPath: testdata/healthy_pass.yaml - - healthStatus: - status: Healthy - message: "Analysis is healthy with warnings" - inputPath: testdata/healthy_warning.yaml diff --git a/resource_customizations/metrics.keptn.sh/Analysis/testdata/degraded.yaml b/resource_customizations/metrics.keptn.sh/Analysis/testdata/degraded.yaml deleted file mode 100644 index b79dce0184b5e..0000000000000 --- a/resource_customizations/metrics.keptn.sh/Analysis/testdata/degraded.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: metrics.keptn.sh/v1 -kind: Analysis -metadata: - labels: - app.kubernetes.io/name: analysis - app.kubernetes.io/instance: analysis-sample - app.kubernetes.io/part-of: metrics-operator - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/created-by: metrics-operator - name: analysis-sample -spec: - timeframe: - recent: 5m - args: - project: my-project - stage: dev - service: svc1 - nodename: test - analysisDefinition: - name: ad-my-proj-dev-svc1 - namespace: keptn-system -status: - pass: false - state: Completed diff --git a/resource_customizations/metrics.keptn.sh/Analysis/testdata/healthy_pass.yaml b/resource_customizations/metrics.keptn.sh/Analysis/testdata/healthy_pass.yaml deleted file mode 100644 index 17c04d9e9f265..0000000000000 --- a/resource_customizations/metrics.keptn.sh/Analysis/testdata/healthy_pass.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: metrics.keptn.sh/v1 -kind: Analysis -metadata: - labels: - app.kubernetes.io/name: analysis - app.kubernetes.io/instance: analysis-sample - app.kubernetes.io/part-of: metrics-operator - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/created-by: metrics-operator - name: analysis-sample -spec: - timeframe: - recent: 5m - args: - project: my-project - stage: dev - service: svc1 - nodename: test - analysisDefinition: - name: ad-my-proj-dev-svc1 - namespace: keptn-system -status: - pass: true - state: Completed diff --git a/resource_customizations/metrics.keptn.sh/Analysis/testdata/healthy_warning.yaml b/resource_customizations/metrics.keptn.sh/Analysis/testdata/healthy_warning.yaml deleted file mode 100644 index 81eed8af49949..0000000000000 --- a/resource_customizations/metrics.keptn.sh/Analysis/testdata/healthy_warning.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: metrics.keptn.sh/v1 -kind: Analysis -metadata: - labels: - app.kubernetes.io/name: analysis - app.kubernetes.io/instance: analysis-sample - app.kubernetes.io/part-of: metrics-operator - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/created-by: metrics-operator - name: analysis-sample -spec: - timeframe: - recent: 5m - args: - project: my-project - stage: dev - service: svc1 - nodename: test - analysisDefinition: - name: ad-my-proj-dev-svc1 - namespace: keptn-system -status: - warning: true - state: Completed diff --git a/resource_customizations/metrics.keptn.sh/Analysis/testdata/progressing.yaml b/resource_customizations/metrics.keptn.sh/Analysis/testdata/progressing.yaml deleted file mode 100644 index cd42e73b64471..0000000000000 --- a/resource_customizations/metrics.keptn.sh/Analysis/testdata/progressing.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: metrics.keptn.sh/v1 -kind: Analysis -metadata: - labels: - app.kubernetes.io/name: analysis - app.kubernetes.io/instance: analysis-sample - app.kubernetes.io/part-of: metrics-operator - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/created-by: metrics-operator - name: analysis-sample -spec: - timeframe: - recent: 5m - args: - project: my-project - stage: dev - service: svc1 - nodename: test - analysisDefinition: - name: ad-my-proj-dev-svc1 - namespace: keptn-system -status: - state: Progressing diff --git a/resource_customizations/metrics.keptn.sh/KeptnMetric/health.lua b/resource_customizations/metrics.keptn.sh/KeptnMetric/health.lua deleted file mode 100644 index 0275f503c4ce2..0000000000000 --- a/resource_customizations/metrics.keptn.sh/KeptnMetric/health.lua +++ /dev/null @@ -1,14 +0,0 @@ -local hs = {} -if (obj.status.errMsg == nil or obj.status.errMsg == "") and obj.status.value ~= nil then - hs.status = "Healthy" - hs.message = "KeptnMetric is healthy" - return hs -end -if obj.status.errMsg ~= nil and obj.status.errMsg ~= "" then - hs.status = "Degraded" - hs.message = "KeptnMetric is degraded" - return hs -end -hs.status = "Progressing" -hs.message = "KeptnMetric is progressing" -return hs diff --git a/resource_customizations/metrics.keptn.sh/KeptnMetric/health_test.yaml b/resource_customizations/metrics.keptn.sh/KeptnMetric/health_test.yaml deleted file mode 100644 index 0f170f6f5f846..0000000000000 --- a/resource_customizations/metrics.keptn.sh/KeptnMetric/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: "KeptnMetric is progressing" - inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: "KeptnMetric is degraded" - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: "KeptnMetric is healthy" - inputPath: testdata/healthy.yaml - - healthStatus: - status: Healthy - message: "KeptnMetric is healthy" - inputPath: testdata/healthy_empty_error.yaml diff --git a/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/degraded.yaml b/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/degraded.yaml deleted file mode 100644 index cdd429bb8224b..0000000000000 --- a/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/degraded.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: metrics.keptn.sh/v1 -kind: KeptnMetric -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"metrics.keptn.sh/v1","kind":"KeptnMetric","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"podtato-head"},"name":"available-cpus","namespace":"podtato-kubectl"},"spec":{"fetchIntervalSeconds":10,"provider":{"name":"my-provider"},"query":"sum(kube_node_status_capacity{resource='cpu'})"}} - creationTimestamp: '2024-07-16T07:34:42Z' - generation: 1 - labels: - app.kubernetes.io/instance: podtato-head - name: available-cpus - namespace: podtato-kubectl - resourceVersion: '405403' - uid: c448a014-b6b6-45a4-91ff-89949b9d0fce -spec: - fetchIntervalSeconds: 10 - provider: - name: my-provider - query: sum(kube_node_status_capacity{resource='cpu'}) -status: - errMsg: >- - Post "http://prometheus-k8s.monitoring.svc.cluster.local:9090/api/v1/query": - dial tcp: lookup prometheus-k8s.monitoring.svc.cluster.local on - lastUpdated: '2024-07-23T12:49:44Z' diff --git a/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/healthy.yaml b/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/healthy.yaml deleted file mode 100644 index 2c5ecad045350..0000000000000 --- a/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/healthy.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: metrics.keptn.sh/v1 -kind: KeptnMetric -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"metrics.keptn.sh/v1","kind":"KeptnMetric","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"podtato-head"},"name":"available-cpus","namespace":"podtato-kubectl"},"spec":{"fetchIntervalSeconds":10,"provider":{"name":"my-provider"},"query":"sum(kube_node_status_capacity{resource='cpu'})"}} - creationTimestamp: '2024-07-16T07:34:42Z' - generation: 1 - labels: - app.kubernetes.io/instance: podtato-head - name: available-cpus - namespace: podtato-kubectl - resourceVersion: '405403' - uid: c448a014-b6b6-45a4-91ff-89949b9d0fce -spec: - fetchIntervalSeconds: 10 - provider: - name: my-provider - query: sum(kube_node_status_capacity{resource='cpu'}) -status: - value: '100' - lastUpdated: '2024-07-23T12:49:44Z' diff --git a/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/healthy_empty_error.yaml b/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/healthy_empty_error.yaml deleted file mode 100644 index 758ccf9170a2a..0000000000000 --- a/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/healthy_empty_error.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: metrics.keptn.sh/v1 -kind: KeptnMetric -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"metrics.keptn.sh/v1","kind":"KeptnMetric","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"podtato-head"},"name":"available-cpus","namespace":"podtato-kubectl"},"spec":{"fetchIntervalSeconds":10,"provider":{"name":"my-provider"},"query":"sum(kube_node_status_capacity{resource='cpu'})"}} - creationTimestamp: '2024-07-16T07:34:42Z' - generation: 1 - labels: - app.kubernetes.io/instance: podtato-head - name: available-cpus - namespace: podtato-kubectl - resourceVersion: '405403' - uid: c448a014-b6b6-45a4-91ff-89949b9d0fce -spec: - fetchIntervalSeconds: 10 - provider: - name: my-provider - query: sum(kube_node_status_capacity{resource='cpu'}) -status: - errMsg: "" - value: 100 - lastUpdated: '2024-07-23T12:49:44Z' diff --git a/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/progressing.yaml b/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/progressing.yaml deleted file mode 100644 index 71f219fbae7b4..0000000000000 --- a/resource_customizations/metrics.keptn.sh/KeptnMetric/testdata/progressing.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: metrics.keptn.sh/v1 -kind: KeptnMetric -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"metrics.keptn.sh/v1","kind":"KeptnMetric","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"podtato-head"},"name":"available-cpus","namespace":"podtato-kubectl"},"spec":{"fetchIntervalSeconds":10,"provider":{"name":"my-provider"},"query":"sum(kube_node_status_capacity{resource='cpu'})"}} - creationTimestamp: '2024-07-16T07:34:42Z' - generation: 1 - labels: - app.kubernetes.io/instance: podtato-head - name: available-cpus - namespace: podtato-kubectl - resourceVersion: '405403' - uid: c448a014-b6b6-45a4-91ff-89949b9d0fce -spec: - fetchIntervalSeconds: 10 - provider: - name: my-provider - query: sum(kube_node_status_capacity{resource='cpu'}) -status: - lastUpdated: '2024-07-23T12:49:44Z' diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/action_test.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/action_test.yaml deleted file mode 100644 index acb19fbfce785..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/action_test.yaml +++ /dev/null @@ -1,26 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_alert.yaml - result: - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_alert.yaml - result: - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_alert.yaml - result: - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: suspend - inputPath: testdata/initial_alert.yaml - expectedOutputPath: testdata/suspended_alert.yaml -- action: resume - inputPath: testdata/suspended_alert.yaml - expectedOutputPath: testdata/resumed_alert.yaml diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/discovery.lua b/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/discovery.lua deleted file mode 100644 index f4c659d3d0f9c..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/discovery.lua +++ /dev/null @@ -1,16 +0,0 @@ -local actions = {} - -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/resume/action.lua b/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/suspend/action.lua b/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/initial_alert.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/initial_alert.yaml deleted file mode 100644 index 37ddc069c82f4..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/initial_alert.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1beta3 -kind: Alert -metadata: - name: slack - namespace: flux-system -spec: - summary: "Cluster addons impacted in us-east-2" - providerRef: - name: slack-bot - eventSeverity: error - eventSources: - - kind: GitRepository - name: '*' - - kind: Kustomization - name: '*' diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/resumed_alert.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/resumed_alert.yaml deleted file mode 100644 index 3a0a57ff5b258..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/resumed_alert.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1beta3 -kind: Alert -metadata: - name: slack - namespace: flux-system -spec: - summary: "Cluster addons impacted in us-east-2" - providerRef: - name: slack-bot - eventSeverity: error - eventSources: - - kind: GitRepository - name: '*' - - kind: Kustomization - name: '*' - suspend: false diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/suspended_alert.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/suspended_alert.yaml deleted file mode 100644 index 8f416896bc1ec..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Alert/actions/testdata/suspended_alert.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1beta3 -kind: Alert -metadata: - name: slack - namespace: flux-system -spec: - summary: "Cluster addons impacted in us-east-2" - providerRef: - name: slack-bot - eventSeverity: error - eventSources: - - kind: GitRepository - name: '*' - - kind: Kustomization - name: '*' - suspend: true diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/action_test.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/action_test.yaml deleted file mode 100644 index 4438d3ed13020..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/action_test.yaml +++ /dev/null @@ -1,26 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_provider.yaml - result: - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_provider.yaml - result: - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_provider.yaml - result: - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: suspend - inputPath: testdata/initial_provider.yaml - expectedOutputPath: testdata/suspended_provider.yaml -- action: resume - inputPath: testdata/suspended_provider.yaml - expectedOutputPath: testdata/resumed_provider.yaml diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/discovery.lua b/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/discovery.lua deleted file mode 100644 index f4c659d3d0f9c..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/discovery.lua +++ /dev/null @@ -1,16 +0,0 @@ -local actions = {} - -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/resume/action.lua b/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/suspend/action.lua b/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/initial_provider.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/initial_provider.yaml deleted file mode 100644 index d53ecd3697b08..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/initial_provider.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1beta3 -kind: Provider -metadata: - name: slack-bot - namespace: flagger-system -spec: - type: slack - channel: general - address: https://slack.com/api/chat.postMessage - secretRef: - name: slack-bot-token diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/resumed_provider.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/resumed_provider.yaml deleted file mode 100644 index 684589b3bc45b..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/resumed_provider.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1beta3 -kind: Provider -metadata: - name: slack-bot - namespace: flagger-system -spec: - type: slack - channel: general - address: https://slack.com/api/chat.postMessage - secretRef: - name: slack-bot-token - suspend: false diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/suspended_provider.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/suspended_provider.yaml deleted file mode 100644 index 330e3a6116755..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Provider/actions/testdata/suspended_provider.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1beta3 -kind: Provider -metadata: - name: slack-bot - namespace: flagger-system -spec: - type: slack - channel: general - address: https://slack.com/api/chat.postMessage - secretRef: - name: slack-bot-token - suspend: true diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/action_test.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/action_test.yaml deleted file mode 100644 index eff2eff163846..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/action_test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_receiver.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_receiver.yaml - result: - - name: reconcile - disabled: true - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_receiver.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: reconcile - inputPath: testdata/initial_receiver.yaml - expectedOutputPath: testdata/reconciled_receiver.yaml -- action: suspend - inputPath: testdata/initial_receiver.yaml - expectedOutputPath: testdata/suspended_receiver.yaml -- action: resume - inputPath: testdata/suspended_receiver.yaml - expectedOutputPath: testdata/resumed_receiver.yaml diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/discovery.lua b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/discovery.lua deleted file mode 100644 index 9000998815515..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/discovery.lua +++ /dev/null @@ -1,18 +0,0 @@ -local actions = {} - -actions["reconcile"] = {["disabled"] = true} -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["reconcile"]["disabled"] = false - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/reconcile/action.lua b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/reconcile/action.lua deleted file mode 100644 index a534b36fb3ebc..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/reconcile/action.lua +++ /dev/null @@ -1,7 +0,0 @@ -local os = require("os") -if obj.metadata.annotations == nil then - obj.metadata.annotations = {} -end -obj.metadata.annotations["reconcile.fluxcd.io/requestedAt"] = "By Argo CD at: " .. os.date("!%Y-%m-%dT%X") - -return obj diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/resume/action.lua b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/suspend/action.lua b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/initial_receiver.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/initial_receiver.yaml deleted file mode 100644 index fa00d3e65de3e..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/initial_receiver.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1 -kind: Receiver -metadata: - name: github-receiver - namespace: flux-system -spec: - type: github - events: - - "ping" - - "push" - secretRef: - name: receiver-token - resources: - - apiVersion: source.toolkit.fluxcd.io/v1 - kind: GitRepository - name: flux-system diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/reconciled_receiver.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/reconciled_receiver.yaml deleted file mode 100644 index 90594de5b3331..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/reconciled_receiver.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1 -kind: Receiver -metadata: - name: github-receiver - namespace: flux-system - annotations: - reconcile.fluxcd.io/requestedAt: 'By Argo CD at: 0001-01-01T00:00:00' -spec: - type: github - events: - - "ping" - - "push" - secretRef: - name: receiver-token - resources: - - apiVersion: source.toolkit.fluxcd.io/v1 - kind: GitRepository - name: flux-system diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/resumed_receiver.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/resumed_receiver.yaml deleted file mode 100644 index 660d1cb0aed68..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/resumed_receiver.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1 -kind: Receiver -metadata: - name: github-receiver - namespace: flux-system -spec: - type: github - events: - - "ping" - - "push" - secretRef: - name: receiver-token - suspend: false - resources: - - apiVersion: source.toolkit.fluxcd.io/v1 - kind: GitRepository - name: flux-system diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/suspended_receiver.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/suspended_receiver.yaml deleted file mode 100644 index b24fe8dda9aab..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/actions/testdata/suspended_receiver.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1 -kind: Receiver -metadata: - name: github-receiver - namespace: flux-system -spec: - type: github - events: - - "ping" - - "push" - secretRef: - name: receiver-token - suspend: true - resources: - - apiVersion: source.toolkit.fluxcd.io/v1 - kind: GitRepository - name: flux-system diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/health.lua b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/health.lua deleted file mode 100644 index 1586e9d50c8b5..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/health.lua +++ /dev/null @@ -1,41 +0,0 @@ -local hs = {} -if obj.spec.suspend ~= nil and obj.spec.suspend == true then - hs.message = obj.kind .. " is suspended" - hs.status = "Suspended" - return hs -end -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numFailing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "False" then - numFailing = numFailing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - numFailing = numFailing + 1 - end - end - if(numFailing == 2) then - hs.message = message - hs.status = "Degraded" - return hs - elseif(numSucceeded == 1) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/health_test.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/health_test.yaml deleted file mode 100644 index 7e4d4ea018273..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - # - healthStatus: - # status: Progressing - # message: Progressing - # inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: TokenNotFound - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: InstallSucceeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/degraded.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/degraded.yaml deleted file mode 100644 index ba42fb102c85c..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/degraded.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1 -kind: Receiver -metadata: - name: github-receiver - namespace: flux-system -spec: - type: github - events: - - "ping" - - "push" - secretRef: - name: receiver-token - resources: - - apiVersion: source.toolkit.fluxcd.io/v1 - kind: GitRepository - name: flux-system -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Reconciliation in progress - observedGeneration: 1 - reason: ProgressingWithRetry - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'unable to read token from secret ''flux-system/receiver-token'' error: - secrets "receiver-token" not found' - observedGeneration: 1 - reason: TokenNotFound - status: "False" - type: Ready diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/healthy.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/healthy.yaml deleted file mode 100644 index 7b99499e98419..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/healthy.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1 -kind: Receiver -metadata: - name: github-receiver - namespace: flux-system -spec: - type: github - events: - - "ping" - - "push" - secretRef: - name: receiver-token - resources: - - apiVersion: source.toolkit.fluxcd.io/v1 - kind: GitRepository - name: flux-system -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm install succeeded for release flux-system/github-receiver.v1 with - chart podinfo@6.5.4 - observedGeneration: 2 - reason: InstallSucceeded - status: "True" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: Helm install succeeded for release flux-system/github-receiver.v1 with - chart podinfo@6.5.4 - observedGeneration: 1 - reason: InstallSucceeded - status: "True" - type: Released diff --git a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/progressing.yaml b/resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/progressing.yaml deleted file mode 100644 index 78dccc33b7536..0000000000000 --- a/resource_customizations/notification.toolkit.fluxcd.io/Receiver/testdata/progressing.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: notification.toolkit.fluxcd.io/v1 -kind: Receiver -metadata: - name: github-receiver - namespace: flux-system -spec: - type: github - events: - - "ping" - - "push" - secretRef: - name: receiver-token - resources: - - apiVersion: source.toolkit.fluxcd.io/v1 - kind: GitRepository - name: flux-system -status: - conditions: [] diff --git a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/health.lua b/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/health.lua deleted file mode 100644 index 1bcd2892b4160..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/health.lua +++ /dev/null @@ -1,32 +0,0 @@ -local hs = {} -local healthyCondition = {} - -if obj.status ~= nil then - if obj.status.conditions ~= nil then - for i, condition in ipairs(obj.status.conditions) do - if condition.type == "ChildResourcesHealthy" then - healthyCondition = condition - end - end - end - - if obj.metadata.generation == obj.status.observedGeneration then - if (healthyCondition ~= {} and healthyCondition.status == "False" and (obj.metadata.generation == healthyCondition.observedGeneration) and healthyCondition.reason == "ISBSvcFailed") or obj.status.phase == "Failed" then - hs.status = "Degraded" - if obj.status.phase == "Failed" then - hs.message = obj.status.message - else - hs.message = healthyCondition.message - end - return hs - elseif healthyCondition ~= {} and healthyCondition.status == "True" and (obj.metadata.generation == healthyCondition.observedGeneration) and obj.status.phase == "Deployed" then - hs.status = "Healthy" - hs.message = healthyCondition.message - return hs - end - end -end - -hs.status = "Progressing" -hs.message = "Waiting for ISBService status" -return hs \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/health_test.yaml b/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/health_test.yaml deleted file mode 100644 index b0b683266c6eb..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/health_test.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: "Waiting for ISBService status" - inputPath: testdata/progressing.yaml -- healthStatus: - status: Healthy - message: "Successful" - inputPath: testdata/healthy.yaml -- healthStatus: - status: Degraded - message: "ISBService Failed" - inputPath: testdata/degraded.yaml -- healthStatus: - status: Progressing - message: "Waiting for ISBService status" - inputPath: testdata/progressing-nostatus.yaml -- healthStatus: - status: Progressing - message: "Waiting for ISBService status" - inputPath: testdata/progressing-reason.yaml \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/degraded.yaml b/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/degraded.yaml deleted file mode 100644 index 56429f44a13c6..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/degraded.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: ISBServiceRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '2' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"ISBServiceRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"2"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-isbsvc","namespace":"demo-app"},"spec":{"interStepBufferService":{"jetstream":{"persistence":{"volumeSize":"1Gi"},"version":"degraded"}}}} - creationTimestamp: '2024-07-12T20:56:22Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 6 - labels: - argocd.argoproj.io/instance: demo-app - name: my-isbsvc - namespace: demo-app - resourceVersion: '5515640' - uid: 0a364143-ddfb-4bb8-9a61-b17b7954de4b -spec: - interStepBufferService: - jetstream: - persistence: - volumeSize: 1Gi - version: degraded -status: - conditions: - - lastTransitionTime: '2024-07-15T22:38:02Z' - message: Successful - observedGeneration: 6 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-15T22:38:02Z' - message: ISBService Failed - observedGeneration: 6 - reason: ISBSvcFailed - status: 'False' - type: ChildResourcesHealthy - message: Deployed - observedGeneration: 6 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/healthy.yaml b/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/healthy.yaml deleted file mode 100644 index 1ae59573cfffa..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/healthy.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: ISBServiceRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '2' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"ISBServiceRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"2"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-isbsvc","namespace":"demo-app"},"spec":{"interStepBufferService":{"jetstream":{"persistence":{"volumeSize":"1Gi"},"version":"latest"}}}} - creationTimestamp: '2024-07-12T20:56:22Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: my-isbsvc - namespace: demo-app - resourceVersion: '5455982' - uid: 0a364143-ddfb-4bb8-9a61-b17b7954de4b -spec: - interStepBufferService: - jetstream: - persistence: - volumeSize: 1Gi - version: latest -status: - conditions: - - lastTransitionTime: '2024-07-12T20:56:23Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T20:56:23Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourcesHealthy - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing-nostatus.yaml b/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing-nostatus.yaml deleted file mode 100644 index 3a886da092714..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing-nostatus.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: ISBServiceRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '2' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"ISBServiceRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"2"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-isbsvc","namespace":"demo-app"},"spec":{"interStepBufferService":{"jetstream":{"persistence":{"volumeSize":"1Gi"},"version":"latest"}}}} - creationTimestamp: '2024-07-12T20:56:22Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 2 - labels: - argocd.argoproj.io/instance: demo-app - name: my-isbsvc - namespace: demo-app - resourceVersion: '5455982' - uid: 0a364143-ddfb-4bb8-9a61-b17b7954de4b -spec: - interStepBufferService: - jetstream: - persistence: - volumeSize: 1Gi - version: latest \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing-reason.yaml b/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing-reason.yaml deleted file mode 100644 index 07dd50dc20d21..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing-reason.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: ISBServiceRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '2' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"ISBServiceRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"2"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-isbsvc","namespace":"demo-app"},"spec":{"interStepBufferService":{"jetstream":{"persistence":{"volumeSize":"1Gi"},"version":"latest"}}}} - creationTimestamp: '2024-07-12T20:56:22Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: my-isbsvc - namespace: demo-app - resourceVersion: '5455982' - uid: 0a364143-ddfb-4bb8-9a61-b17b7954de4b -spec: - interStepBufferService: - jetstream: - persistence: - volumeSize: 1Gi - version: latest -status: - conditions: - - lastTransitionTime: '2024-07-12T20:56:23Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T20:56:23Z' - message: Progressing - observedGeneration: 1 - reason: Progressing - status: 'False' - type: ChildResourcesHealthy - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing.yaml b/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing.yaml deleted file mode 100644 index af9d3d0062433..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/ISBServiceRollout/testdata/progressing.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: ISBServiceRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '2' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"ISBServiceRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"2"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-isbsvc","namespace":"demo-app"},"spec":{"interStepBufferService":{"jetstream":{"persistence":{"volumeSize":"1Gi"},"version":"latest"}}}} - creationTimestamp: '2024-07-12T20:56:22Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 2 - labels: - argocd.argoproj.io/instance: demo-app - name: my-isbsvc - namespace: demo-app - resourceVersion: '5455982' - uid: 0a364143-ddfb-4bb8-9a61-b17b7954de4b -spec: - interStepBufferService: - jetstream: - persistence: - volumeSize: 1Gi - version: latest -status: - conditions: - - lastTransitionTime: '2024-07-12T20:56:23Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T20:56:23Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourcesHealthy - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/health.lua b/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/health.lua deleted file mode 100644 index 2e221a7323649..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/health.lua +++ /dev/null @@ -1,32 +0,0 @@ -local hs = {} -local healthyCondition = {} - -if obj.status ~= nil then - if obj.status.conditions ~= nil then - for i, condition in ipairs(obj.status.conditions) do - if condition.type == "ChildResourcesHealthy" then - healthyCondition = condition - end - end - end - - if obj.metadata.generation == obj.status.observedGeneration then - if (healthyCondition ~= {} and healthyCondition.status == "False" and (obj.metadata.generation == healthyCondition.observedGeneration) and healthyCondition.reason == "MonoVertexFailed") or obj.status.phase == "Failed" then - hs.status = "Degraded" - if obj.status.phase == "Failed" then - hs.message = obj.status.message - else - hs.message = healthyCondition.message - end - return hs - elseif healthyCondition ~= {} and healthyCondition.status == "True" and (obj.metadata.generation == healthyCondition.observedGeneration) and obj.status.phase == "Deployed" then - hs.status = "Healthy" - hs.message = healthyCondition.message - return hs - end - end -end - -hs.status = "Progressing" -hs.message = "Waiting for MonoVertex status" -return hs \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/health_test.yaml b/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/health_test.yaml deleted file mode 100644 index aee12b9ceb9c3..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: "Waiting for MonoVertex status" - inputPath: testdata/progressing.yaml -- healthStatus: - status: Healthy - message: "Successful" - inputPath: testdata/healthy.yaml -- healthStatus: - status: Degraded - message: "MonoVertex Failed" - inputPath: testdata/degraded.yaml -- healthStatus: - status: Progressing - message: "Waiting for MonoVertex status" - inputPath: testdata/progressing-reason.yaml \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/degraded.yaml b/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/degraded.yaml deleted file mode 100644 index a6eedc9419f41..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/degraded.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: MonoVertexRollout -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"MonoVertexRollout","metadata":{"annotations":{},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-monovertex","namespace":"example-namespace"},"spec":{"monoVertex":{"spec":{"sink":{"udsink":{"container":{"image":"quay.io/numaio/numaflow-java/simple-sink:stable"}}},"source":{"transformer":{"container":{"image":"quay.io/numaio/numaflow-rs/source-transformer-now:stable"}},"udsource":{"container":{"image":"quay.io/numaio/numaflow-java/source-simple-source:stable"}}}}}}} - creationTimestamp: '2024-08-21T20:44:18Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: my-monovertex - namespace: example-namespace - resourceVersion: '947414' - uid: a63f377e-1500-437e-9267-579f4a790518 -spec: - monoVertex: - spec: - sink: - udsink: - container: - image: 'bad-image' - source: - transformer: - container: - image: 'quay.io/numaio/numaflow-rs/source-transformer-now:stable' - udsource: - container: - image: 'quay.io/numaio/numaflow-java/source-simple-source:stable' -status: - conditions: - - lastTransitionTime: '2024-08-21T20:44:18Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-08-22T21:10:23Z' - message: MonoVertex Failed - observedGeneration: 1 - reason: MonoVertexFailed - status: 'False' - type: ChildResourcesHealthy - message: Deployed - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/healthy.yaml b/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/healthy.yaml deleted file mode 100644 index ee9d76c826dc4..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/healthy.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: MonoVertexRollout -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"MonoVertexRollout","metadata":{"annotations":{},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-monovertex","namespace":"example-namespace"},"spec":{"monoVertex":{"spec":{"sink":{"udsink":{"container":{"image":"quay.io/numaio/numaflow-java/simple-sink:stable"}}},"source":{"transformer":{"container":{"image":"quay.io/numaio/numaflow-rs/source-transformer-now:stable"}},"udsource":{"container":{"image":"quay.io/numaio/numaflow-java/source-simple-source:stable"}}}}}}} - creationTimestamp: '2024-08-21T20:44:18Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: my-monovertex - namespace: example-namespace - resourceVersion: '947414' - uid: a63f377e-1500-437e-9267-579f4a790518 -spec: - monoVertex: - spec: - sink: - udsink: - container: - image: 'quay.io/numaio/numaflow-java/simple-sink:stable' - source: - transformer: - container: - image: 'quay.io/numaio/numaflow-rs/source-transformer-now:stable' - udsource: - container: - image: 'quay.io/numaio/numaflow-java/source-simple-source:stable' -status: - conditions: - - lastTransitionTime: '2024-08-21T20:44:18Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-08-22T21:10:23Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourcesHealthy - message: Deployed - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/progressing-reason.yaml b/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/progressing-reason.yaml deleted file mode 100644 index 3b147417e04d3..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/progressing-reason.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: MonoVertexRollout -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"MonoVertexRollout","metadata":{"annotations":{},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-monovertex","namespace":"example-namespace"},"spec":{"monoVertex":{"spec":{"sink":{"udsink":{"container":{"image":"quay.io/numaio/numaflow-java/simple-sink:stable"}}},"source":{"transformer":{"container":{"image":"quay.io/numaio/numaflow-rs/source-transformer-now:stable"}},"udsource":{"container":{"image":"quay.io/numaio/numaflow-java/source-simple-source:stable"}}}}}}} - creationTimestamp: '2024-08-21T20:44:18Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: my-monovertex - namespace: example-namespace - resourceVersion: '947414' - uid: a63f377e-1500-437e-9267-579f4a790518 -spec: - monoVertex: - spec: - sink: - udsink: - container: - image: 'quay.io/numaio/numaflow-java/simple-sink:stable' - source: - transformer: - container: - image: 'quay.io/numaio/numaflow-rs/source-transformer-now:stable' - udsource: - container: - image: 'quay.io/numaio/numaflow-java/source-simple-source:stable' -status: - conditions: - - lastTransitionTime: '2024-08-21T20:44:18Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-08-22T21:10:23Z' - message: Progressing - observedGeneration: 1 - reason: Progressing - status: 'False' - type: ChildResourcesHealthy - message: Deployed - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/progressing.yaml b/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/progressing.yaml deleted file mode 100644 index 14ebed98a3a85..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/MonoVertexRollout/testdata/progressing.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: MonoVertexRollout -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"MonoVertexRollout","metadata":{"annotations":{},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-monovertex","namespace":"example-namespace"},"spec":{"monoVertex":{"spec":{"sink":{"udsink":{"container":{"image":"quay.io/numaio/numaflow-java/simple-sink:stable"}}},"source":{"transformer":{"container":{"image":"quay.io/numaio/numaflow-rs/source-transformer-now:stable"}},"udsource":{"container":{"image":"quay.io/numaio/numaflow-java/source-simple-source:stable"}}}}}}} - creationTimestamp: '2024-08-21T20:44:18Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 2 - labels: - argocd.argoproj.io/instance: demo-app - name: my-monovertex - namespace: example-namespace - resourceVersion: '947414' - uid: a63f377e-1500-437e-9267-579f4a790518 -spec: - monoVertex: - spec: - sink: - udsink: - container: - image: 'quay.io/numaio/numaflow-java/simple-sink:stable' - source: - transformer: - container: - image: 'quay.io/numaio/numaflow-rs/source-transformer-now:stable' - udsource: - container: - image: 'quay.io/numaio/numaflow-java/source-simple-source:stable' -status: - conditions: - - lastTransitionTime: '2024-08-21T20:44:18Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-08-22T21:10:23Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourcesHealthy - message: Deployed - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/health.lua b/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/health.lua deleted file mode 100644 index 9ff005740f6e8..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/health.lua +++ /dev/null @@ -1,32 +0,0 @@ -local hs = {} -local healthyCondition = {} - -if obj.status ~= nil then - if obj.status.conditions ~= nil then - for i, condition in ipairs(obj.status.conditions) do - if condition.type == "ChildResourcesHealthy" then - healthyCondition = condition - end - end - end - - if obj.metadata.generation == obj.status.observedGeneration then - if (healthyCondition ~= {} and healthyCondition.status == "False" and (obj.metadata.generation == healthyCondition.observedGeneration) and healthyCondition.reason == "Degraded") or obj.status.phase == "Failed" then - hs.status = "Degraded" - if obj.status.phase == "Failed" then - hs.message = obj.status.message - else - hs.message = healthyCondition.message - end - return hs - elseif healthyCondition ~= {} and healthyCondition.status == "True" and (obj.metadata.generation == healthyCondition.observedGeneration) and obj.status.phase == "Deployed" then - hs.status = "Healthy" - hs.message = healthyCondition.message - return hs - end - end -end - -hs.status = "Progressing" -hs.message = "Waiting for NumaflowController status" -return hs \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/health_test.yaml b/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/health_test.yaml deleted file mode 100644 index 30bb880f2d38a..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: "Waiting for NumaflowController status" - inputPath: testdata/progressing.yaml -- healthStatus: - status: Healthy - message: "Successful" - inputPath: testdata/healthy.yaml -- healthStatus: - status: Degraded - message: "no controller definition found for version degraded" - inputPath: testdata/degraded.yaml -- healthStatus: - status: Progressing - message: "Waiting for NumaflowController status" - inputPath: testdata/progressing-reason.yaml \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/degraded.yaml b/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/degraded.yaml deleted file mode 100644 index 4fa21c3195893..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/degraded.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: NumaflowControllerRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '1' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"NumaflowControllerRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"1"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"numaflow-controller","namespace":"demo-app"},"spec":{"controller":{"version":"xxx"}}} - creationTimestamp: '2024-07-12T20:56:20Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 4 - labels: - argocd.argoproj.io/instance: demo-app - name: numaflow-controller - namespace: demo-app - resourceVersion: '5514384' - uid: 904ab9bb-953e-4979-a124-5c92e8e25147 -spec: - controller: - version: degraded -status: - conditions: - - lastTransitionTime: '2024-07-15T22:29:52Z' - message: '' - reason: Unknown - status: Unknown - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-15T22:29:52Z' - message: '' - reason: Unknown - status: Unknown - type: ChildResourcesHealthy - message: no controller definition found for version degraded - observedGeneration: 4 - phase: Failed diff --git a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/healthy.yaml b/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/healthy.yaml deleted file mode 100644 index 1efc00714e37b..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/healthy.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: NumaflowControllerRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '1' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"NumaflowControllerRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"1"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"numaflow-controller","namespace":"demo-app"},"spec":{"controller":{"version":"1.2.1"}}} - creationTimestamp: '2024-07-12T20:56:20Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: numaflow-controller - namespace: demo-app - resourceVersion: '5456204' - uid: 904ab9bb-953e-4979-a124-5c92e8e25147 -spec: - controller: - version: 1.2.1 -status: - conditions: - - lastTransitionTime: '2024-07-12T20:56:26Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T20:56:26Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourcesHealthy - observedGeneration: 1 - phase: Deployed diff --git a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/progressing-reason.yaml b/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/progressing-reason.yaml deleted file mode 100644 index e3c55cefc2c66..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/progressing-reason.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: NumaflowControllerRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '1' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"NumaflowControllerRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"1"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"numaflow-controller","namespace":"demo-app"},"spec":{"controller":{"version":"1.2.1"}}} - creationTimestamp: '2024-07-12T20:56:20Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: numaflow-controller - namespace: demo-app - resourceVersion: '5456204' - uid: 904ab9bb-953e-4979-a124-5c92e8e25147 -spec: - controller: - version: 1.2.1 -status: - conditions: - - lastTransitionTime: '2024-07-12T20:56:26Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T20:56:26Z' - message: Progressing - observedGeneration: 1 - reason: Progressing - status: 'False' - type: ChildResourcesHealthy - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/progressing.yaml b/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/progressing.yaml deleted file mode 100644 index e6c000df9d48a..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/NumaflowControllerRollout/testdata/progressing.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: NumaflowControllerRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '1' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"NumaflowControllerRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"1"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"numaflow-controller","namespace":"demo-app"},"spec":{"controller":{"version":"1.2.1"}}} - creationTimestamp: '2024-07-12T20:56:20Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 2 - labels: - argocd.argoproj.io/instance: demo-app - name: numaflow-controller - namespace: demo-app - resourceVersion: '5456204' - uid: 904ab9bb-953e-4979-a124-5c92e8e25147 -spec: - controller: - version: 1.2.1 -status: - conditions: - - lastTransitionTime: '2024-07-12T20:56:26Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T20:56:26Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourcesHealthy - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/PipelineRollout/health.lua b/resource_customizations/numaplane.numaproj.io/PipelineRollout/health.lua deleted file mode 100644 index 649cbb643d7f9..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/PipelineRollout/health.lua +++ /dev/null @@ -1,40 +0,0 @@ -local hs = {} -local healthyCondition = {} -local pipelinePaused = {} - -if obj.status ~= nil then - if obj.status.conditions ~= nil then - for i, condition in ipairs(obj.status.conditions) do - if condition.type == "ChildResourcesHealthy" then - healthyCondition = condition - end - if condition.type == "PipelinePausingOrPaused" then - pipelinePaused = condition - end - end - end - - if obj.metadata.generation == obj.status.observedGeneration then - if (healthyCondition ~= {} and healthyCondition.status == "False" and (obj.metadata.generation == healthyCondition.observedGeneration) and healthyCondition.reason == "PipelineFailed") or obj.status.phase == "Failed" then - hs.status = "Degraded" - if obj.status.phase == "Failed" then - hs.message = obj.status.message - else - hs.message = healthyCondition.message - end - return hs - elseif (pipelinePaused ~= {} and pipelinePaused.status == "True") and (obj.metadata.generation == pipelinePaused.observedGeneration) then - hs.status = "Suspended" - hs.message = pipelinePaused.message - return hs - elseif (healthyCondition ~= {} and healthyCondition.status == "True") and (obj.metadata.generation == healthyCondition.observedGeneration) and obj.status.phase == "Deployed" then - hs.status = "Healthy" - hs.message = healthyCondition.message - return hs - end - end -end - -hs.status = "Progressing" -hs.message = "Waiting for Pipeline status" -return hs \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/PipelineRollout/health_test.yaml b/resource_customizations/numaplane.numaproj.io/PipelineRollout/health_test.yaml deleted file mode 100644 index 99274d213992b..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/PipelineRollout/health_test.yaml +++ /dev/null @@ -1,21 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: "Waiting for Pipeline status" - inputPath: testdata/progressing.yaml -- healthStatus: - status: Healthy - message: "Successful" - inputPath: testdata/healthy.yaml -- healthStatus: - status: Suspended - message: "Pipeline paused" - inputPath: testdata/paused.yaml -- healthStatus: - status: Degraded - message: "Pipeline Failed" - inputPath: testdata/degraded.yaml -- healthStatus: - status: Progressing - message: "Waiting for Pipeline status" - inputPath: testdata/progressing-reason.yaml \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/degraded.yaml b/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/degraded.yaml deleted file mode 100644 index 81da0cb4ad8d8..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/degraded.yaml +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: PipelineRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '3' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"PipelineRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"3"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-other-pipeline","namespace":"demo-app"},"spec":{"pipeline":{"edges":[{"from":"in","to":"cat"},{"from":"cat","to":"out"},{"from":"cat","to":"out2"}],"interStepBufferServiceName":"my-isbsvc","vertices":[{"name":"in","source":{"generator":{"duration":"15s","rpu":5}}},{"name":"cat","udf":{"builtin":{"name":"cat"}}},{"name":"out","sink":{"log":{}}},{"name":"out2","sink":{"log":{}}}]}}} - creationTimestamp: '2024-07-12T20:56:24Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: my-other-pipeline - namespace: demo-app - resourceVersion: '5456110' - uid: 472d6284-b2d9-45ee-a159-fd4c3ad08d8c -spec: - pipeline: - edges: - - from: in - to: cat - - from: cat - to: out - - from: cat - to: out2 - interStepBufferServiceName: my-isbsvc - vertices: - - name: in - source: - generator: - duration: 15s - rpu: 5 - # - name: cat - # udf: - # builtin: - # name: cat - - name: out - sink: - log: {} - - name: out2 - sink: - log: {} -status: - conditions: - - lastTransitionTime: '2024-07-12T20:56:24Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T20:56:24Z' - message: Pipeline Failed - observedGeneration: 1 - reason: PipelineFailed - status: 'False' - type: ChildResourcesHealthy - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/healthy.yaml b/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/healthy.yaml deleted file mode 100644 index 842ef30e57889..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/healthy.yaml +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: PipelineRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '3' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"PipelineRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"3"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-other-pipeline","namespace":"demo-app"},"spec":{"pipeline":{"edges":[{"from":"in","to":"cat"},{"from":"cat","to":"out"},{"from":"cat","to":"out2"}],"interStepBufferServiceName":"my-isbsvc","vertices":[{"name":"in","source":{"generator":{"duration":"15s","rpu":5}}},{"name":"cat","udf":{"builtin":{"name":"cat"}}},{"name":"out","sink":{"log":{}}},{"name":"out2","sink":{"log":{}}}]}}} - creationTimestamp: '2024-07-12T20:56:24Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: my-other-pipeline - namespace: demo-app - resourceVersion: '5456110' - uid: 472d6284-b2d9-45ee-a159-fd4c3ad08d8c -spec: - pipeline: - edges: - - from: in - to: cat - - from: cat - to: out - - from: cat - to: out2 - interStepBufferServiceName: my-isbsvc - vertices: - - name: in - source: - generator: - duration: 15s - rpu: 5 - - name: cat - udf: - builtin: - name: cat - - name: out - sink: - log: {} - - name: out2 - sink: - log: {} -status: - conditions: - - lastTransitionTime: '2024-07-12T20:56:24Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T20:56:24Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourcesHealthy - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/paused.yaml b/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/paused.yaml deleted file mode 100644 index 8bd209bbfb2ea..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/paused.yaml +++ /dev/null @@ -1,65 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: PipelineRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '3' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"PipelineRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"3"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-other-pipeline","namespace":"demo-app"},"spec":{"pipeline":{"edges":[{"from":"in","to":"cat"},{"from":"cat","to":"out"},{"from":"cat","to":"out2"}],"interStepBufferServiceName":"my-isbsvc","vertices":[{"name":"in","source":{"generator":{"duration":"15s","rpu":5}}},{"name":"cat","udf":{"builtin":{"name":"cat"}}},{"name":"out","sink":{"log":{}}},{"name":"out2","sink":{"log":{}}}]}}} - creationTimestamp: '2024-07-12T20:56:24Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: my-other-pipeline - namespace: demo-app - resourceVersion: '5458594' - uid: 472d6284-b2d9-45ee-a159-fd4c3ad08d8c -spec: - pipeline: - edges: - - from: in - to: cat - - from: cat - to: out - - from: cat - to: out2 - interStepBufferServiceName: my-isbsvc - vertices: - - name: in - source: - generator: - duration: 15s - rpu: 5 - - name: cat - udf: - builtin: - name: cat - - name: out - sink: - log: {} - - name: out2 - sink: - log: {} -status: - conditions: - - lastTransitionTime: '2024-07-12T21:14:18Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T21:14:18Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourcesHealthy - - lastTransitionTime: '2024-07-12T21:14:17Z' - message: Pipeline paused - observedGeneration: 1 - reason: Paused - status: 'True' - type: PipelinePausingOrPaused - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/progressing-reason.yaml b/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/progressing-reason.yaml deleted file mode 100644 index 5ec81c30145b3..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/progressing-reason.yaml +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: PipelineRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '3' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"PipelineRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"3"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-other-pipeline","namespace":"demo-app"},"spec":{"pipeline":{"edges":[{"from":"in","to":"cat"},{"from":"cat","to":"out"},{"from":"cat","to":"out2"}],"interStepBufferServiceName":"my-isbsvc","vertices":[{"name":"in","source":{"generator":{"duration":"15s","rpu":5}}},{"name":"cat","udf":{"builtin":{"name":"cat"}}},{"name":"out","sink":{"log":{}}},{"name":"out2","sink":{"log":{}}}]}}} - creationTimestamp: '2024-07-12T20:56:24Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 1 - labels: - argocd.argoproj.io/instance: demo-app - name: my-other-pipeline - namespace: demo-app - resourceVersion: '5456110' - uid: 472d6284-b2d9-45ee-a159-fd4c3ad08d8c -spec: - pipeline: - edges: - - from: in - to: cat - - from: cat - to: out - - from: cat - to: out2 - interStepBufferServiceName: my-isbsvc - vertices: - - name: in - source: - generator: - duration: 15s - rpu: 5 - - name: cat - udf: - builtin: - name: cat - - name: out - sink: - log: {} - - name: out2 - sink: - log: {} -status: - conditions: - - lastTransitionTime: '2024-07-12T20:56:24Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T20:56:24Z' - message: Progressing - observedGeneration: 1 - reason: Progressing - status: 'False' - type: ChildResourcesHealthy - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/progressing.yaml b/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/progressing.yaml deleted file mode 100644 index a161254fb0b8e..0000000000000 --- a/resource_customizations/numaplane.numaproj.io/PipelineRollout/testdata/progressing.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: numaplane.numaproj.io/v1alpha1 -kind: PipelineRollout -metadata: - annotations: - argocd.argoproj.io/sync-wave: '3' - kubectl.kubernetes.io/last-applied-configuration: > - {"apiVersion":"numaplane.numaproj.io/v1alpha1","kind":"PipelineRollout","metadata":{"annotations":{"argocd.argoproj.io/sync-wave":"3"},"labels":{"argocd.argoproj.io/instance":"demo-app"},"name":"my-other-pipeline","namespace":"demo-app"},"spec":{"pipeline":{"edges":[{"from":"in","to":"cat"},{"from":"cat","to":"out"},{"from":"cat","to":"out2"}],"interStepBufferServiceName":"my-isbsvc","vertices":[{"name":"in","source":{"generator":{"duration":"15s","rpu":5}}},{"name":"out","sink":{"log":{}}},{"name":"out2","sink":{"log":{}}}]}}} - creationTimestamp: '2024-07-12T20:56:24Z' - finalizers: - - numaplane.numaproj.io/numaplane-controller - generation: 2 - labels: - argocd.argoproj.io/instance: demo-app - name: my-other-pipeline - namespace: demo-app - resourceVersion: '5461141' - uid: 472d6284-b2d9-45ee-a159-fd4c3ad08d8c -spec: - pipeline: - edges: - - from: in - to: cat - - from: cat - to: out - - from: cat - to: out2 - interStepBufferServiceName: my-isbsvc - vertices: - - name: in - source: - generator: - duration: 15s - rpu: 5 - - name: out - sink: - log: {} - - name: out2 - sink: - log: {} -status: - conditions: - - lastTransitionTime: '2024-07-12T21:31:03Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourceDeployed - - lastTransitionTime: '2024-07-12T21:31:03Z' - message: Successful - observedGeneration: 1 - reason: Successful - status: 'True' - type: ChildResourcesHealthy - - lastTransitionTime: '2024-07-12T21:14:17Z' - message: '' - observedGeneration: 1 - reason: Paused - status: 'True' - type: PipelinePausingOrPaused - observedGeneration: 1 - phase: Deployed \ No newline at end of file diff --git a/resource_customizations/openfaas.com/Function/health.lua b/resource_customizations/openfaas.com/Function/health.lua deleted file mode 100644 index df72e228b04fa..0000000000000 --- a/resource_customizations/openfaas.com/Function/health.lua +++ /dev/null @@ -1,31 +0,0 @@ -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 == "Stalled" and condition.status == "True" then - hs.status = "Degraded" - hs.message = condition.message - return hs - end - if condition.type == "Ready" and condition.status == "True" then - if obj.status.replicas ~= nil and obj.status.replicas > 0 then - hs.status = "Healthy" - hs.message = condition.message - else - hs.status = "Suspended" - hs.message = "No replicas available" - end - return hs - end - end - end -end - -hs.status = "Progressing" -hs.message = "Waiting for Function" -return hs diff --git a/resource_customizations/openfaas.com/Function/health_test.yaml b/resource_customizations/openfaas.com/Function/health_test.yaml deleted file mode 100644 index 750089fac48ea..0000000000000 --- a/resource_customizations/openfaas.com/Function/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: "Waiting for Function" - inputPath: testdata/progressing.yaml -- healthStatus: - status: Degraded - message: "Secret missing: secrets \"missing-secret\" not found" - inputPath: testdata/degraded_no_secret.yaml -- healthStatus: - status: Healthy - message: "Deployment and service reconciled" - inputPath: testdata/healthy.yaml -- healthStatus: - status: Suspended - message: "No replicas available" - inputPath: testdata/suspended_zero_replicas.yaml diff --git a/resource_customizations/openfaas.com/Function/testdata/degraded_no_secret.yaml b/resource_customizations/openfaas.com/Function/testdata/degraded_no_secret.yaml deleted file mode 100644 index a1c0c981f1176..0000000000000 --- a/resource_customizations/openfaas.com/Function/testdata/degraded_no_secret.yaml +++ /dev/null @@ -1,48 +0,0 @@ -{ - "apiVersion": "openfaas.com/v1", - "kind": "Function", - "metadata": { - "annotations": { - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"openfaas.com/v1\",\"kind\":\"Function\",\"metadata\":{\"annotations\":{},\"name\":\"env\",\"namespace\":\"openfaas-fn\"},\"spec\":{\"annotations\":{},\"environment\":{\"fprocess\":\"env\",\"test\":\"yes\"},\"image\":\"ghcr.io/openfaas/alpine:latest\",\"labels\":{},\"name\":\"env\",\"secrets\":[\"missing-secret\"]}}\n" - }, - "creationTimestamp": "2024-04-29T13:42:46Z", - "generation": 1, - "name": "env", - "namespace": "openfaas-fn", - "resourceVersion": "580675", - "uid": "7a00bc7b-eb01-4f6a-b5f7-7893422ace7d" - }, - "spec": { - "annotations": {}, - "environment": { - "fprocess": "env", - "test": "yes" - }, - "image": "ghcr.io/openfaas/alpine:latest", - "labels": {}, - "name": "env", - "secrets": [ - "missing-secret" - ] - }, - "status": { - "conditions": [ - { - "lastTransitionTime": "2024-04-29T13:42:46Z", - "message": "Function queued for creation", - "observedGeneration": 1, - "reason": "Reconciling", - "status": "True", - "type": "Reconciling" - }, - { - "lastTransitionTime": "2024-04-29T13:42:46Z", - "message": "Secret missing: secrets \"missing-secret\" not found", - "observedGeneration": 1, - "reason": "SecretMissing", - "status": "True", - "type": "Stalled" - } - ] - } -} diff --git a/resource_customizations/openfaas.com/Function/testdata/healthy.yaml b/resource_customizations/openfaas.com/Function/testdata/healthy.yaml deleted file mode 100644 index 7d09972561710..0000000000000 --- a/resource_customizations/openfaas.com/Function/testdata/healthy.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: openfaas.com/v1 -kind: Function -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"openfaas.com/v1","kind":"Function","metadata":{"annotations":{},"name":"env","namespace":"openfaas-fn"},"spec":{"annotations":{},"environment":{"fprocess":"env","test":"yes"},"image":"ghcr.io/openfaas/alpine:latest","labels":{},"name":"env"}} - creationTimestamp: "2024-04-29T13:38:50Z" - generation: 1 - name: env - namespace: openfaas-fn - resourceVersion: "580323" - uid: 865f74b9-cbc5-455a-abd7-4a1cdeae22d1 -spec: - annotations: {} - environment: - fprocess: env - test: "yes" - image: ghcr.io/openfaas/alpine:latest - labels: {} - name: env -status: - availableReplicas: 1 - conditions: - - lastTransitionTime: "2024-04-29T13:38:50Z" - message: Deployment and service reconciled - observedGeneration: 1 - reason: Ready - status: "True" - type: Ready - - lastTransitionTime: "2024-04-29T13:38:53Z" - message: At least one replica available - observedGeneration: 1 - reason: ReplicaAvailable - status: "True" - type: Healthy - replicas: 1 diff --git a/resource_customizations/openfaas.com/Function/testdata/progressing.yaml b/resource_customizations/openfaas.com/Function/testdata/progressing.yaml deleted file mode 100644 index 4b70ad08c19aa..0000000000000 --- a/resource_customizations/openfaas.com/Function/testdata/progressing.yaml +++ /dev/null @@ -1,30 +0,0 @@ ---- -apiVersion: openfaas.com/v1 -kind: Function -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"openfaas.com/v1","kind":"Function","metadata":{"annotations":{},"name":"env","namespace":"openfaas-fn"},"spec":{"annotations":{},"environment":{"fprocess":"env","test":"yes"},"image":"ghcr.io/openfaas/alpine:latest","labels":{},"name":"env"}} - creationTimestamp: "2024-04-29T13:38:50Z" - generation: 1 - name: env - namespace: openfaas-fn - resourceVersion: "580277" - uid: 865f74b9-cbc5-455a-abd7-4a1cdeae22d1 -spec: - annotations: {} - environment: - fprocess: env - test: "yes" - image: ghcr.io/openfaas/alpine:latest - labels: {} - name: env -status: - conditions: - - lastTransitionTime: "2024-04-29T13:38:50Z" - message: Function queued for creation - observedGeneration: 1 - reason: Reconciling - status: "True" - type: Reconciling ---- diff --git a/resource_customizations/openfaas.com/Function/testdata/suspended_zero_replicas.yaml b/resource_customizations/openfaas.com/Function/testdata/suspended_zero_replicas.yaml deleted file mode 100644 index 3307dfd8e4fe4..0000000000000 --- a/resource_customizations/openfaas.com/Function/testdata/suspended_zero_replicas.yaml +++ /dev/null @@ -1,35 +0,0 @@ ---- -apiVersion: openfaas.com/v1 -kind: Function -metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"openfaas.com/v1","kind":"Function","metadata":{"annotations":{},"name":"env","namespace":"openfaas-fn"},"spec":{"annotations":{},"environment":{"fprocess":"env","test":"yes"},"image":"ghcr.io/openfaas/alpine:latest","labels":{},"name":"env"}} - creationTimestamp: "2024-04-29T13:38:50Z" - generation: 1 - name: env - namespace: openfaas-fn - resourceVersion: "580543" - uid: 865f74b9-cbc5-455a-abd7-4a1cdeae22d1 -spec: - annotations: {} - environment: - fprocess: env - test: "yes" - image: ghcr.io/openfaas/alpine:latest - labels: {} - name: env -status: - conditions: - - lastTransitionTime: "2024-04-29T13:38:50Z" - message: Deployment and service reconciled - observedGeneration: 1 - reason: Ready - status: "True" - type: Ready - - lastTransitionTime: "2024-04-29T13:41:27Z" - message: At least one replica available - observedGeneration: 1 - reason: ReplicaAvailable - status: "False" - type: Healthy diff --git a/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/health.lua b/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/health.lua deleted file mode 100644 index 03cb9181447b7..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/health.lua +++ /dev/null @@ -1,15 +0,0 @@ -hs = {} -if obj.status == nil or obj.status.compliant == nil then - hs.status = "Progressing" - hs.message = "Waiting for the status to be reported" - return hs -end -if obj.status.compliant == "Compliant" then - hs.status = "Healthy" - hs.message = "All certificates found comply with the policy" - return hs -else - hs.status = "Degraded" - hs.message = "At least once certificate does not comply with the policy" - return hs -end diff --git a/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/health_test.yaml b/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/health_test.yaml deleted file mode 100644 index 017ce9ba50e60..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: Waiting for the status to be reported - inputPath: testdata/progressing_no_status.yaml - - healthStatus: - status: Degraded - message: At least once certificate does not comply with the policy - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: All certificates found comply with the policy - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/degraded.yaml b/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/degraded.yaml deleted file mode 100644 index 4d44b3ad88d6e..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/degraded.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: CertificatePolicy -metadata: - name: policy-certificate - namespace: local-cluster -spec: - minimumDuration: 3000h - namespaceSelector: - exclude: - - kube-* - include: - - default - - cert-manager-operator - remediationAction: inform - severity: low -status: - compliancyDetails: - cert-manager-operator: - message: | - Found 1 non compliant certificates in the namespace cert-manager-operator. - List of non compliant certificates: - ca-root-secret expires in 2159h53m40.509362797s - nonCompliantCertificates: 1 - nonCompliantCertificatesList: - ca-root-secret: - ca: true - duration: 7776000000000000 - expiration: 2159h53m40.509362797s - expiry: 7775620509362797 - secretName: ca-root-secret - default: - message: | - Found 0 non compliant certificates in the namespace default. - compliant: NonCompliant diff --git a/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/healthy.yaml b/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/healthy.yaml deleted file mode 100644 index 8e999cf937ffd..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/healthy.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: CertificatePolicy -metadata: - name: policy-certificate - namespace: local-cluster -spec: - minimumDuration: 300h - namespaceSelector: - exclude: - - kube-* - include: - - default - - cert-manager-operator - remediationAction: inform - severity: low -status: - compliancyDetails: - cert-manager-operator: - message: | - Found 0 non compliant certificates in the namespace cert-manager-operator. - default: - message: | - Found 0 non compliant certificates in the namespace default. - compliant: Compliant diff --git a/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/progressing_no_status.yaml b/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/progressing_no_status.yaml deleted file mode 100644 index 5cb54c6075bb3..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/CertificatePolicy/testdata/progressing_no_status.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: CertificatePolicy -metadata: - name: policy-certificate - namespace: local-cluster -spec: - minimumDuration: 300h - namespaceSelector: - exclude: - - kube-* - include: - - default - - cert-manager-operator - remediationAction: inform - severity: low diff --git a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/health.lua b/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/health.lua deleted file mode 100644 index 5a4f936faa7c5..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/health.lua +++ /dev/null @@ -1,33 +0,0 @@ -hs = {} -if obj.status == nil or obj.status.compliant == nil then - hs.status = "Progressing" - hs.message = "Waiting for the status to be reported" - return hs -end -if obj.status.lastEvaluatedGeneration ~= obj.metadata.generation then - hs.status = "Progressing" - hs.message = "Waiting for the status to be updated" - return hs -end -if obj.status.compliant == "Compliant" then - hs.status = "Healthy" -else - hs.status = "Degraded" -end -if obj.status.compliancyDetails ~= nil then - messages = {} - for i, compliancy in ipairs(obj.status.compliancyDetails) do - if compliancy.conditions ~= nil then - for i, condition in ipairs(compliancy.conditions) do - if condition.message ~= nil and condition.type ~= nil then - table.insert(messages, condition.type .. " - " .. condition.message) - end - end - end - end - hs.message = table.concat(messages, "; ") - return hs -end -hs.status = "Progressing" -hs.message = "Waiting for compliance" -return hs diff --git a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/health_test.yaml b/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/health_test.yaml deleted file mode 100644 index 7eb34bbea2889..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/health_test.yaml +++ /dev/null @@ -1,27 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: Waiting for the status to be reported - inputPath: testdata/progressing_no_status.yaml - - healthStatus: - status: Degraded - message: >- - violation - namespaces [argo-example] not found; violation - namespaces - [argo-example-2] not found - inputPath: testdata/degraded.yaml - - healthStatus: - status: Progressing - message: Waiting for the status to be updated - inputPath: testdata/progressing.yaml - - healthStatus: - status: Healthy - message: >- - notification - namespaces [argo-example] was created successfully; - notification - namespaces [argo-example-2] was created successfully - inputPath: testdata/healthy_created.yaml - - healthStatus: - status: Healthy - message: >- - notification - namespaces [argo-example] found as specified; - notification - namespaces [argo-example-2] found as specified - inputPath: testdata/healthy_found.yaml diff --git a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/degraded.yaml b/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/degraded.yaml deleted file mode 100644 index 407c0e54620d2..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/degraded.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: ConfigurationPolicy -metadata: - name: policy-namespace - generation: 2 - namespace: local-cluster -spec: - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example - recreateOption: None - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example-2 - recreateOption: None - pruneObjectBehavior: None - remediationAction: inform - severity: low -status: - compliancyDetails: - - Compliant: NonCompliant - Validity: {} - conditions: - - lastTransitionTime: '2024-07-29T16:34:29Z' - message: 'namespaces [argo-example] not found' - reason: K8s does not have a `must have` object - status: 'True' - type: violation - - Compliant: NonCompliant - Validity: {} - conditions: - - lastTransitionTime: '2024-07-29T16:39:00Z' - message: 'namespaces [argo-example-2] not found' - reason: K8s does not have a `must have` object - status: 'True' - type: violation - compliant: NonCompliant - lastEvaluated: '2024-07-29T16:39:18Z' - lastEvaluatedGeneration: 2 - relatedObjects: - - compliant: NonCompliant - object: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example - reason: Resource not found but should exist - - compliant: NonCompliant - object: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example-2 - reason: Resource not found but should exist diff --git a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/healthy_created.yaml b/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/healthy_created.yaml deleted file mode 100644 index 36d5034053374..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/healthy_created.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: ConfigurationPolicy -metadata: - name: policy-namespace - generation: 3 - namespace: local-cluster -spec: - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example - recreateOption: None - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example-2 - recreateOption: None - pruneObjectBehavior: None - remediationAction: enforce - severity: low -status: - compliancyDetails: - - Compliant: Compliant - Validity: {} - conditions: - - lastTransitionTime: '2024-07-29T16:58:50Z' - message: 'namespaces [argo-example] was created successfully' - reason: K8s creation success - status: 'True' - type: notification - - Compliant: Compliant - Validity: {} - conditions: - - lastTransitionTime: '2024-07-29T16:58:50Z' - message: 'namespaces [argo-example-2] was created successfully' - reason: K8s creation success - status: 'True' - type: notification - compliant: Compliant - lastEvaluated: '2024-07-29T16:58:50Z' - lastEvaluatedGeneration: 3 - relatedObjects: - - compliant: Compliant - object: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example - properties: - createdByPolicy: true - uid: 782f50ee-4fa9-41d6-900e-66d9eaf8b111 - reason: K8s creation success - - compliant: Compliant - object: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example-2 - properties: - createdByPolicy: true - uid: ce34051f-a0dc-4db2-9f8f-64cc9223d4d7 - reason: K8s creation success diff --git a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/healthy_found.yaml b/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/healthy_found.yaml deleted file mode 100644 index 8975989c09529..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/healthy_found.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: ConfigurationPolicy -metadata: - name: policy-namespace - generation: 3 - namespace: local-cluster -spec: - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example - recreateOption: None - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example-2 - recreateOption: None - pruneObjectBehavior: None - remediationAction: enforce - severity: low -status: - compliancyDetails: - - Compliant: Compliant - Validity: {} - conditions: - - lastTransitionTime: '2024-07-29T16:58:59Z' - message: 'namespaces [argo-example] found as specified' - reason: K8s `must have` object already exists - status: 'True' - type: notification - - Compliant: Compliant - Validity: {} - conditions: - - lastTransitionTime: '2024-07-29T16:58:59Z' - message: 'namespaces [argo-example-2] found as specified' - reason: K8s `must have` object already exists - status: 'True' - type: notification - compliant: Compliant - lastEvaluated: '2024-07-29T16:59:26Z' - lastEvaluatedGeneration: 3 - relatedObjects: - - compliant: Compliant - object: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example - properties: - createdByPolicy: true - uid: 782f50ee-4fa9-41d6-900e-66d9eaf8b111 - reason: Resource found as expected - - compliant: Compliant - object: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example-2 - properties: - createdByPolicy: true - uid: ce34051f-a0dc-4db2-9f8f-64cc9223d4d7 - reason: Resource found as expected diff --git a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/progressing.yaml b/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/progressing.yaml deleted file mode 100644 index 1b2cd4860ea08..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/progressing.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: ConfigurationPolicy -metadata: - name: policy-namespace - generation: 3 - namespace: local-cluster -spec: - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example - recreateOption: None - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example-2 - recreateOption: None - pruneObjectBehavior: None - remediationAction: enforce - severity: low -status: - compliancyDetails: - - Compliant: NonCompliant - Validity: {} - conditions: - - lastTransitionTime: '2024-07-29T16:34:29Z' - message: 'namespaces [argo-example] not found' - reason: K8s does not have a `must have` object - status: 'True' - type: violation - - Compliant: NonCompliant - Validity: {} - conditions: - - lastTransitionTime: '2024-07-29T16:39:00Z' - message: 'namespaces [argo-example-2] not found' - reason: K8s does not have a `must have` object - status: 'True' - type: violation - compliant: NonCompliant - lastEvaluated: '2024-07-29T16:39:18Z' - lastEvaluatedGeneration: 2 - relatedObjects: - - compliant: NonCompliant - object: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example - reason: Resource not found but should exist - - compliant: NonCompliant - object: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example-2 - reason: Resource not found but should exist diff --git a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/progressing_no_status.yaml b/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/progressing_no_status.yaml deleted file mode 100644 index 1e43ce59ef121..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/ConfigurationPolicy/testdata/progressing_no_status.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: ConfigurationPolicy -metadata: - name: policy-namespace - generation: 2 - namespace: local-cluster -spec: - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example - recreateOption: None - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: argo-example-2 - recreateOption: None - pruneObjectBehavior: None - remediationAction: inform - severity: low diff --git a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/health.lua b/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/health.lua deleted file mode 100644 index de8ed51192143..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/health.lua +++ /dev/null @@ -1,26 +0,0 @@ -hs = {} -if obj.status == nil or obj.status.conditions == nil then - hs.status = "Progressing" - hs.message = "Waiting for the status to be reported" - return hs -end -if obj.status.observedGeneration ~= nil and obj.status.observedGeneration ~= obj.metadata.generation then - hs.status = "Progressing" - hs.message = "Waiting for the status to be updated" - return hs -end -for i, condition in ipairs(obj.status.conditions) do - if condition.type == "Compliant" then - hs.message = condition.message - if condition.status == "True" then - hs.status = "Healthy" - return hs - else - hs.status = "Degraded" - return hs - end - end -end -hs.status = "Progressing" -hs.message = "Waiting for the compliance condition" -return hs diff --git a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/health_test.yaml b/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/health_test.yaml deleted file mode 100644 index 4c28366631eae..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/health_test.yaml +++ /dev/null @@ -1,48 +0,0 @@ -tests: - - healthStatus: - status: Progressing - message: Waiting for the status to be reported - inputPath: testdata/progressing_no_status.yaml - - healthStatus: - status: Degraded - message: >- - NonCompliant; the policy spec is valid, the policy does not specify an - OperatorGroup but one already exists in the namespace - assuming that - OperatorGroup is correct, the Subscription required by the policy was - not found, there are no relevant InstallPlans in the namespace, the - ClusterServiceVersion required by the policy was not found, no CRDs were - found for the operator, there are no relevant deployments because the - ClusterServiceVersion is missing, CatalogSource was found - inputPath: testdata/degraded.yaml - - healthStatus: - status: Progressing - message: Waiting for the status to be updated - inputPath: testdata/progressing_old_generation.yaml - - healthStatus: - status: Progressing - message: Waiting for the compliance condition - inputPath: testdata/progressing_no_compliance.yaml - - healthStatus: - status: Healthy - message: >- - Compliant; the policy spec is valid, the policy does not specify an - OperatorGroup but one already exists in the namespace - assuming that - OperatorGroup is correct, the Subscription matches what is required by - the policy, no InstallPlans requiring approval were found, - ClusterServiceVersion (argocd-operator.v0.11.0) - install strategy - completed with no errors, there are CRDs present for the operator, all - operator Deployments have their minimum availability, CatalogSource was - found - inputPath: testdata/healthy_no_generation.yaml - - healthStatus: - status: Healthy - message: >- - Compliant; the policy spec is valid, the policy does not specify an - OperatorGroup but one already exists in the namespace - assuming that - OperatorGroup is correct, the Subscription matches what is required by - the policy, no InstallPlans requiring approval were found, - ClusterServiceVersion (argocd-operator.v0.11.0) - install strategy - completed with no errors, there are CRDs present for the operator, all - operator Deployments have their minimum availability, CatalogSource was - found - inputPath: testdata/healthy_with_generation.yaml diff --git a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/degraded.yaml b/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/degraded.yaml deleted file mode 100644 index 1256bc25586bc..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/degraded.yaml +++ /dev/null @@ -1,69 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1beta1 -kind: OperatorPolicy -metadata: - name: install-argocd - generation: 1 - namespace: local-cluster -spec: - complianceConfig: - catalogSourceUnhealthy: Compliant - deploymentsUnavailable: NonCompliant - upgradesAvailable: Compliant - complianceType: musthave - remediationAction: inform - removalBehavior: - clusterServiceVersions: Delete - customResourceDefinitions: Keep - operatorGroups: DeleteIfUnused - subscriptions: Delete - severity: high - subscription: - channel: alpha - name: argocd-operator - source: community-operators - sourceNamespace: openshift-marketplace - upgradeApproval: None - versions: [] -status: - compliant: NonCompliant - conditions: - - lastTransitionTime: '2024-07-29T15:20:48Z' - message: CatalogSource was found - reason: CatalogSourcesFound - status: 'False' - type: CatalogSourcesUnhealthy - - lastTransitionTime: '2024-07-29T15:20:48Z' - message: >- - NonCompliant; the policy spec is valid, the policy does not specify an - OperatorGroup but one already exists in the namespace - assuming that - OperatorGroup is correct, the Subscription required by the policy was - not found, there are no relevant InstallPlans in the namespace, the - ClusterServiceVersion required by the policy was not found, no CRDs were - found for the operator, there are no relevant deployments because the - ClusterServiceVersion is missing, CatalogSource was found - reason: NonCompliant - status: 'False' - type: Compliant - - lastTransitionTime: '2024-07-29T15:20:48Z' - message: the Subscription required by the policy was not found - reason: SubscriptionMissing - status: 'False' - type: SubscriptionCompliant - relatedObjects: - - compliant: Compliant - object: - apiVersion: operators.coreos.com/v1alpha1 - kind: CatalogSource - metadata: - name: community-operators - namespace: openshift-marketplace - reason: Resource found as expected - - compliant: NonCompliant - object: - apiVersion: operators.coreos.com/v1alpha1 - kind: Subscription - metadata: - name: argocd-operator - namespace: openshift-operators - reason: Resource not found but should exist - resolvedSubscriptionLabel: argocd-operator.openshift-operators diff --git a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/healthy_no_generation.yaml b/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/healthy_no_generation.yaml deleted file mode 100644 index 39feedba5149b..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/healthy_no_generation.yaml +++ /dev/null @@ -1,73 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1beta1 -kind: OperatorPolicy -metadata: - name: install-argocd - generation: 2 - namespace: local-cluster -spec: - complianceConfig: - catalogSourceUnhealthy: Compliant - deploymentsUnavailable: NonCompliant - upgradesAvailable: Compliant - complianceType: musthave - remediationAction: enforce - removalBehavior: - clusterServiceVersions: Delete - customResourceDefinitions: Keep - operatorGroups: DeleteIfUnused - subscriptions: Delete - severity: high - subscription: - channel: alpha - name: argocd-operator - source: community-operators - sourceNamespace: openshift-marketplace - upgradeApproval: Automatic - versions: [] -status: - compliant: Compliant - conditions: - - lastTransitionTime: '2024-07-29T15:20:48Z' - message: CatalogSource was found - reason: CatalogSourcesFound - status: 'False' - type: CatalogSourcesUnhealthy - - lastTransitionTime: '2024-07-29T15:48:20Z' - message: >- - Compliant; the policy spec is valid, the policy does not specify an - OperatorGroup but one already exists in the namespace - assuming that - OperatorGroup is correct, the Subscription matches what is required by - the policy, no InstallPlans requiring approval were found, - ClusterServiceVersion (argocd-operator.v0.11.0) - install strategy - completed with no errors, there are CRDs present for the operator, all - operator Deployments have their minimum availability, CatalogSource was - found - reason: Compliant - status: 'True' - type: Compliant - - lastTransitionTime: '2024-07-29T15:47:45Z' - message: the Subscription matches what is required by the policy - reason: SubscriptionMatches - status: 'True' - type: SubscriptionCompliant - relatedObjects: - - compliant: Compliant - object: - apiVersion: operators.coreos.com/v1alpha1 - kind: CatalogSource - metadata: - name: community-operators - namespace: openshift-marketplace - reason: Resource found as expected - - compliant: Compliant - object: - apiVersion: operators.coreos.com/v1alpha1 - kind: Subscription - metadata: - name: argocd-operator - namespace: openshift-operators - properties: - createdByPolicy: true - uid: f3e6d8a7-eb73-4b29-b804-bf4609d2f7fb - reason: Resource found as expected - resolvedSubscriptionLabel: argocd-operator.openshift-operators diff --git a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/healthy_with_generation.yaml b/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/healthy_with_generation.yaml deleted file mode 100644 index 07d45c229b979..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/healthy_with_generation.yaml +++ /dev/null @@ -1,74 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1beta1 -kind: OperatorPolicy -metadata: - name: install-argocd - generation: 2 - namespace: local-cluster -spec: - complianceConfig: - catalogSourceUnhealthy: Compliant - deploymentsUnavailable: NonCompliant - upgradesAvailable: Compliant - complianceType: musthave - remediationAction: enforce - removalBehavior: - clusterServiceVersions: Delete - customResourceDefinitions: Keep - operatorGroups: DeleteIfUnused - subscriptions: Delete - severity: high - subscription: - channel: alpha - name: argocd-operator - source: community-operators - sourceNamespace: openshift-marketplace - upgradeApproval: Automatic - versions: [] -status: - compliant: Compliant - conditions: - - lastTransitionTime: '2024-07-29T15:20:48Z' - message: CatalogSource was found - reason: CatalogSourcesFound - status: 'False' - type: CatalogSourcesUnhealthy - - lastTransitionTime: '2024-07-29T15:48:20Z' - message: >- - Compliant; the policy spec is valid, the policy does not specify an - OperatorGroup but one already exists in the namespace - assuming that - OperatorGroup is correct, the Subscription matches what is required by - the policy, no InstallPlans requiring approval were found, - ClusterServiceVersion (argocd-operator.v0.11.0) - install strategy - completed with no errors, there are CRDs present for the operator, all - operator Deployments have their minimum availability, CatalogSource was - found - reason: Compliant - status: 'True' - type: Compliant - - lastTransitionTime: '2024-07-29T15:47:45Z' - message: the Subscription matches what is required by the policy - reason: SubscriptionMatches - status: 'True' - type: SubscriptionCompliant - observedGeneration: 2 - relatedObjects: - - compliant: Compliant - object: - apiVersion: operators.coreos.com/v1alpha1 - kind: CatalogSource - metadata: - name: community-operators - namespace: openshift-marketplace - reason: Resource found as expected - - compliant: Compliant - object: - apiVersion: operators.coreos.com/v1alpha1 - kind: Subscription - metadata: - name: argocd-operator - namespace: openshift-operators - properties: - createdByPolicy: true - uid: f3e6d8a7-eb73-4b29-b804-bf4609d2f7fb - reason: Resource found as expected - resolvedSubscriptionLabel: argocd-operator.openshift-operators diff --git a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_no_compliance.yaml b/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_no_compliance.yaml deleted file mode 100644 index fdd7596aeb3c9..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_no_compliance.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1beta1 -kind: OperatorPolicy -metadata: - name: install-argocd - generation: 2 - namespace: local-cluster -spec: - complianceConfig: - catalogSourceUnhealthy: Compliant - deploymentsUnavailable: NonCompliant - upgradesAvailable: Compliant - complianceType: musthave - remediationAction: enforce - removalBehavior: - clusterServiceVersions: Delete - customResourceDefinitions: Keep - operatorGroups: DeleteIfUnused - subscriptions: Delete - severity: high - subscription: - channel: alpha - name: argocd-operator - source: community-operators - sourceNamespace: openshift-marketplace - upgradeApproval: Automatic - versions: [] -status: - compliant: Compliant - conditions: - - lastTransitionTime: '2024-07-29T15:20:48Z' - message: CatalogSource was found - reason: CatalogSourcesFound - status: 'False' - type: CatalogSourcesUnhealthy - - lastTransitionTime: '2024-07-29T15:47:45Z' - message: the Subscription matches what is required by the policy - reason: SubscriptionMatches - status: 'True' - type: SubscriptionCompliant - observedGeneration: 2 - relatedObjects: - - compliant: Compliant - object: - apiVersion: operators.coreos.com/v1alpha1 - kind: CatalogSource - metadata: - name: community-operators - namespace: openshift-marketplace - reason: Resource found as expected - - compliant: Compliant - object: - apiVersion: operators.coreos.com/v1alpha1 - kind: Subscription - metadata: - name: argocd-operator - namespace: openshift-operators - properties: - createdByPolicy: true - uid: f3e6d8a7-eb73-4b29-b804-bf4609d2f7fb - reason: Resource found as expected - resolvedSubscriptionLabel: argocd-operator.openshift-operators diff --git a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_no_status.yaml b/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_no_status.yaml deleted file mode 100644 index e40a779400243..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_no_status.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1beta1 -kind: OperatorPolicy -metadata: - name: install-argocd - generation: 1 - namespace: local-cluster -spec: - complianceConfig: - catalogSourceUnhealthy: Compliant - deploymentsUnavailable: NonCompliant - upgradesAvailable: Compliant - complianceType: musthave - remediationAction: inform - removalBehavior: - clusterServiceVersions: Delete - customResourceDefinitions: Keep - operatorGroups: DeleteIfUnused - subscriptions: Delete - severity: high - subscription: - channel: alpha - name: argocd-operator - source: community-operators - sourceNamespace: openshift-marketplace - upgradeApproval: None - versions: [] diff --git a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_old_generation.yaml b/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_old_generation.yaml deleted file mode 100644 index 4cdbaad9f5a2e..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/OperatorPolicy/testdata/progressing_old_generation.yaml +++ /dev/null @@ -1,63 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1beta1 -kind: OperatorPolicy -metadata: - name: install-argocd - generation: 2 - namespace: local-cluster -spec: - complianceConfig: - catalogSourceUnhealthy: Compliant - deploymentsUnavailable: NonCompliant - upgradesAvailable: Compliant - complianceType: musthave - remediationAction: enforce - removalBehavior: - clusterServiceVersions: Delete - customResourceDefinitions: Keep - operatorGroups: DeleteIfUnused - subscriptions: Delete - severity: high - subscription: - channel: alpha - name: argocd-operator - source: community-operators - sourceNamespace: openshift-marketplace - upgradeApproval: Automatic - versions: [] -status: - compliant: NonCompliant - conditions: - - lastTransitionTime: '2024-07-29T15:20:48Z' - message: CatalogSource was found - reason: CatalogSourcesFound - status: 'False' - type: CatalogSourcesUnhealthy - - lastTransitionTime: '2024-07-29T15:20:48Z' - message: 'NonCompliant; the policy spec is valid, the policy does not specify an OperatorGroup but one already exists in the namespace - assuming that OperatorGroup is correct, the Subscription required by the policy was not found, there are no relevant InstallPlans in the namespace, the ClusterServiceVersion required by the policy was not found, no CRDs were found for the operator, there are no relevant deployments because the ClusterServiceVersion is missing, CatalogSource was found' - reason: NonCompliant - status: 'False' - type: Compliant - - lastTransitionTime: '2024-07-29T15:20:48Z' - message: the Subscription required by the policy was not found - reason: SubscriptionMissing - status: 'False' - type: SubscriptionCompliant - observedGeneration: 1 - relatedObjects: - - compliant: Compliant - object: - apiVersion: operators.coreos.com/v1alpha1 - kind: CatalogSource - metadata: - name: community-operators - namespace: openshift-marketplace - reason: Resource found as expected - - compliant: NonCompliant - object: - apiVersion: operators.coreos.com/v1alpha1 - kind: Subscription - metadata: - name: argocd-operator - namespace: openshift-operators - reason: Resource not found but should exist - resolvedSubscriptionLabel: argocd-operator.openshift-operators diff --git a/resource_customizations/policy.open-cluster-management.io/Policy/health.lua b/resource_customizations/policy.open-cluster-management.io/Policy/health.lua deleted file mode 100644 index 9b43c04c4b5e7..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/Policy/health.lua +++ /dev/null @@ -1,38 +0,0 @@ -hs = {} -if obj.status == nil or obj.status.compliant == nil then - hs.status = "Progressing" - hs.message = "Waiting for the status to be reported" - return hs -end -if obj.status.compliant == "Compliant" then - hs.status = "Healthy" -else - hs.status = "Degraded" -end -noncompliants = {} -if obj.status.status ~= nil then - -- "root" policy - for i, entry in ipairs(obj.status.status) do - if entry.compliant ~= "Compliant" then - noncompliants[i] = entry.clustername - end - end - if table.getn(noncompliants) == 0 then - hs.message = "All clusters are compliant" - else - hs.message = "NonCompliant clusters: " .. table.concat(noncompliants, ", ") - end -elseif obj.status.details ~= nil then - -- "replicated" policy - for i, entry in ipairs(obj.status.details) do - if entry.compliant ~= "Compliant" then - noncompliants[i] = entry.templateMeta.name - end - end - if table.getn(noncompliants) == 0 then - hs.message = "All templates are compliant" - else - hs.message = "NonCompliant templates: " .. table.concat(noncompliants, ", ") - end -end -return hs diff --git a/resource_customizations/policy.open-cluster-management.io/Policy/health_test.yaml b/resource_customizations/policy.open-cluster-management.io/Policy/health_test.yaml deleted file mode 100644 index ede9cc5c8a2c0..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/Policy/health_test.yaml +++ /dev/null @@ -1,17 +0,0 @@ -tests: - - healthStatus: - status: Degraded - message: 'NonCompliant clusters: local-cluster, managed' - inputPath: testdata/degraded_root.yaml - - healthStatus: - status: Degraded - message: 'NonCompliant templates: example-namespace' - inputPath: testdata/degraded_replicated.yaml - - healthStatus: - status: Healthy - message: All clusters are compliant - inputPath: testdata/healthy_root.yaml - - healthStatus: - status: Healthy - message: All templates are compliant - inputPath: testdata/healthy_replicated.yaml diff --git a/resource_customizations/policy.open-cluster-management.io/Policy/testdata/degraded_replicated.yaml b/resource_customizations/policy.open-cluster-management.io/Policy/testdata/degraded_replicated.yaml deleted file mode 100644 index 5a0c3305fc4d6..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/Policy/testdata/degraded_replicated.yaml +++ /dev/null @@ -1,80 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: Policy -metadata: - name: open-cluster-management-global-set.argo-example - namespace: local-cluster - labels: - policy.open-cluster-management.io/cluster-name: local-cluster - policy.open-cluster-management.io/cluster-namespace: local-cluster - policy.open-cluster-management.io/root-policy: open-cluster-management-global-set.argo-example -spec: - disabled: false - policy-templates: - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: example-namespace - spec: - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: example - remediationAction: inform - severity: low - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: example-pod - spec: - namespaceSelector: - exclude: - - kube-* - include: - - default - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Pod - metadata: - name: foobar - spec: - containers: - - image: 'registry.redhat.io/rhel9/httpd-24:latest' - name: httpd - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - runAsNonRoot: true - remediationAction: enforce - severity: low -status: - compliant: NonCompliant - details: - - compliant: NonCompliant - history: - - eventName: open-cluster-management-global-set.argo-example.17e701cc5101e3a4 - lastTimestamp: '2024-07-30T13:49:19Z' - message: 'NonCompliant; violation - namespaces [example] not found' - templateMeta: - creationTimestamp: null - name: example-namespace - - compliant: Compliant - history: - - eventName: open-cluster-management-global-set.argo-example.17e7034c879045a3 - lastTimestamp: '2024-07-30T14:16:49Z' - message: 'Compliant; notification - pods [foobar] was created successfully in namespace default' - - eventName: open-cluster-management-global-set.argo-example.17e7020b47782ddc - lastTimestamp: '2024-07-30T13:53:49Z' - message: 'NonCompliant; violation - pods [foobar] not found in namespace default' - templateMeta: - creationTimestamp: null - name: example-pod diff --git a/resource_customizations/policy.open-cluster-management.io/Policy/testdata/degraded_root.yaml b/resource_customizations/policy.open-cluster-management.io/Policy/testdata/degraded_root.yaml deleted file mode 100644 index 62c54297c4240..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/Policy/testdata/degraded_root.yaml +++ /dev/null @@ -1,68 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: Policy -metadata: - generation: 2 - name: argo-example - namespace: open-cluster-management-global-set -spec: - disabled: false - policy-templates: - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: example-namespace - spec: - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: example - remediationAction: inform - severity: low - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: example-pod - spec: - namespaceSelector: - exclude: - - kube-* - include: - - default - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Pod - metadata: - name: foobar - spec: - containers: - - image: 'registry.redhat.io/rhel9/httpd-24:latest' - name: httpd - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - runAsNonRoot: true - remediationAction: inform - severity: low - remediationAction: inform -status: - compliant: NonCompliant - placement: - - placement: argo-example-placement - placementBinding: argo-example-placement - status: - - clustername: local-cluster - clusternamespace: local-cluster - compliant: NonCompliant - - clustername: managed - clusternamespace: managed - compliant: NonCompliant diff --git a/resource_customizations/policy.open-cluster-management.io/Policy/testdata/healthy_replicated.yaml b/resource_customizations/policy.open-cluster-management.io/Policy/testdata/healthy_replicated.yaml deleted file mode 100644 index 132311cf2bdf3..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/Policy/testdata/healthy_replicated.yaml +++ /dev/null @@ -1,91 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: Policy -metadata: - name: open-cluster-management-global-set.argo-example - generation: 4 - namespace: local-cluster - labels: - policy.open-cluster-management.io/cluster-name: local-cluster - policy.open-cluster-management.io/cluster-namespace: local-cluster - policy.open-cluster-management.io/root-policy: open-cluster-management-global-set.argo-example -spec: - disabled: false - policy-templates: - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: example-namespace - spec: - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: example - remediationAction: inform - severity: low - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: example-pod - spec: - namespaceSelector: - exclude: - - kube-* - include: - - default - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Pod - metadata: - name: foobar - spec: - containers: - - image: 'registry.redhat.io/rhel9/httpd-24:latest' - name: httpd - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - runAsNonRoot: true - remediationAction: inform - severity: low - remediationAction: inform -status: - compliant: Compliant - details: - - compliant: Compliant - history: - - eventName: open-cluster-management-global-set.argo-example.17e703831ab809b3 - lastTimestamp: '2024-07-30T14:20:44Z' - message: 'Compliant; notification - namespaces [example] found as specified' - - eventName: open-cluster-management-global-set.argo-example.17e703810146765a - lastTimestamp: '2024-07-30T14:20:35Z' - message: 'Compliant; notification - namespaces [example] was created successfully' - - eventName: open-cluster-management-global-set.argo-example.17e701cc5101e3a4 - lastTimestamp: '2024-07-30T13:49:19Z' - message: 'NonCompliant; violation - namespaces [example] not found' - templateMeta: - creationTimestamp: null - name: example-namespace - - compliant: Compliant - history: - - eventName: open-cluster-management-global-set.argo-example.17e7034ea145078e - lastTimestamp: '2024-07-30T14:16:58Z' - message: 'Compliant; notification - pods [foobar] found as specified in namespace default' - - eventName: open-cluster-management-global-set.argo-example.17e7034c879045a3 - lastTimestamp: '2024-07-30T14:16:49Z' - message: 'Compliant; notification - pods [foobar] was created successfully in namespace default' - - eventName: open-cluster-management-global-set.argo-example.17e7020b47782ddc - lastTimestamp: '2024-07-30T13:53:49Z' - message: 'NonCompliant; violation - pods [foobar] not found in namespace default' - templateMeta: - creationTimestamp: null - name: example-pod diff --git a/resource_customizations/policy.open-cluster-management.io/Policy/testdata/healthy_root.yaml b/resource_customizations/policy.open-cluster-management.io/Policy/testdata/healthy_root.yaml deleted file mode 100644 index e46b8a7db147c..0000000000000 --- a/resource_customizations/policy.open-cluster-management.io/Policy/testdata/healthy_root.yaml +++ /dev/null @@ -1,68 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: Policy -metadata: - generation: 4 - name: argo-example - namespace: open-cluster-management-global-set -spec: - disabled: false - policy-templates: - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: example-namespace - spec: - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Namespace - metadata: - name: example - remediationAction: inform - severity: low - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: example-pod - spec: - namespaceSelector: - exclude: - - kube-* - include: - - default - object-templates: - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Pod - metadata: - name: foobar - spec: - containers: - - image: 'registry.redhat.io/rhel9/httpd-24:latest' - name: httpd - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - runAsNonRoot: true - remediationAction: inform - severity: low - remediationAction: inform -status: - compliant: Compliant - placement: - - placement: argo-example-placement - placementBinding: argo-example-placement - status: - - clustername: local-cluster - clusternamespace: local-cluster - compliant: Compliant - - clustername: managed - clusternamespace: managed - compliant: Compliant diff --git a/resource_customizations/policy/PodDisruptionBudget/health.lua b/resource_customizations/policy/PodDisruptionBudget/health.lua deleted file mode 100644 index afaa61752cb67..0000000000000 --- a/resource_customizations/policy/PodDisruptionBudget/health.lua +++ /dev/null @@ -1,23 +0,0 @@ --- Reference CRD can be found here: --- https://kubernetes.io/docs/reference/kubernetes-api/policy-resources/pod-disruption-budget-v1/ -hs = {} -hs.status = "Progressing" -hs.message = "Waiting for status" - -if obj.status ~= nil then - if obj.status.conditions ~= nil then - for i, condition in ipairs(obj.status.conditions) do - if condition.status == "False" then - hs.status = "Degraded" - hs.message = "PodDisruptionBudget has " .. condition.reason - return hs - end - if condition.status == "True" then - hs.status = "Healthy" - hs.message = "PodDisruptionBudget has " .. condition.reason - end - end - end -end - -return hs diff --git a/resource_customizations/policy/PodDisruptionBudget/health_test.yaml b/resource_customizations/policy/PodDisruptionBudget/health_test.yaml deleted file mode 100644 index 3ad31b60186ee..0000000000000 --- a/resource_customizations/policy/PodDisruptionBudget/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: -- healthStatus: - status: Healthy - message: 'PodDisruptionBudget has SufficientPods' - inputPath: testdata/healthy.yaml -- healthStatus: - status: Progressing - message: 'Waiting for status' - inputPath: testdata/progressing.yaml -- healthStatus: - status: Degraded - message: 'PodDisruptionBudget has InsufficientPods' - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/policy/PodDisruptionBudget/testdata/degraded.yaml b/resource_customizations/policy/PodDisruptionBudget/testdata/degraded.yaml deleted file mode 100644 index 2c2a854e8bc52..0000000000000 --- a/resource_customizations/policy/PodDisruptionBudget/testdata/degraded.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: foo - namespace: bar -spec: - minAvailable: 3 - selector: - matchLabels: - app.kubernetes.io/name: foo -status: - conditions: - - lastTransitionTime: "2024-09-06T18:29:05Z" - message: "" - observedGeneration: 2 - reason: InsufficientPods - status: "False" - type: DisruptionAllowed - currentHealthy: 2 - desiredHealthy: 3 - disruptionsAllowed: 0 - expectedPods: 2 - observedGeneration: 2 diff --git a/resource_customizations/policy/PodDisruptionBudget/testdata/healthy.yaml b/resource_customizations/policy/PodDisruptionBudget/testdata/healthy.yaml deleted file mode 100644 index 9a971588a6820..0000000000000 --- a/resource_customizations/policy/PodDisruptionBudget/testdata/healthy.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: foo - namespace: bar -spec: - minAvailable: 1 - selector: - matchLabels: - app.kubernetes.io/name: foo -status: - conditions: - - lastTransitionTime: "2024-09-06T18:29:05Z" - message: "" - observedGeneration: 1 - reason: SufficientPods - status: "True" - type: DisruptionAllowed - currentHealthy: 2 - desiredHealthy: 1 - disruptionsAllowed: 1 - expectedPods: 2 - observedGeneration: 1 diff --git a/resource_customizations/policy/PodDisruptionBudget/testdata/progressing.yaml b/resource_customizations/policy/PodDisruptionBudget/testdata/progressing.yaml deleted file mode 100644 index 3edcc7fd4cfa2..0000000000000 --- a/resource_customizations/policy/PodDisruptionBudget/testdata/progressing.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: foo - namespace: default -spec: - minAvailable: 1 - selector: - matchLabels: - app.kubernetes.io/name: foo diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/health.lua b/resource_customizations/rabbitmq.com/RabbitmqCluster/health.lua deleted file mode 100644 index 22916ad6f84cd..0000000000000 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/health.lua +++ /dev/null @@ -1,53 +0,0 @@ -hs = {} -clusterAvailable = {} -allReplicasReady = {} - -if obj.status ~= nil then - if obj.status.conditions ~= nil then - - for i, condition in ipairs(obj.status.conditions) do - if condition.type == "ReconcileSuccess" and condition.status == "False" then - hs.status = "Degraded" - hs.message = condition.message - return hs - end - if condition.type == "ClusterAvailable" then - clusterAvailable.status = condition.status - clusterAvailable.message = condition.message - end - if condition.type == "AllReplicasReady" then - allReplicasReady.status = condition.status - allReplicasReady.message = condition.message - end - end - - if clusterAvailable.status == "Unknown" or allReplicasReady.status == "Unknown" then - hs.status = "Degraded" - hs.message = "No statefulset or endpoints found" - return hs - end - - if clusterAvailable.status == "False" then - hs.status = "Progressing" - hs.message = "Waiting for RabbitMQ cluster formation" - return hs - end - - if allReplicasReady.status == "False" then - hs.status = "Progressing" - hs.message = "Waiting for RabbitMQ instances ready" - return hs - end - - if clusterAvailable.status == "True" and allReplicasReady.status == "True" then - hs.status = "Healthy" - hs.message = "RabbitMQ cluster ready" - return hs - end - - end -end - -hs.status = "Progressing" -hs.message = "Waiting for RabbitMQ Operator" -return hs \ No newline at end of file diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/health_test.yaml b/resource_customizations/rabbitmq.com/RabbitmqCluster/health_test.yaml deleted file mode 100644 index 7e7c44e4b57ce..0000000000000 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/health_test.yaml +++ /dev/null @@ -1,29 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: Unknown 'foo' parameter - inputPath: testdata/degraded_badconfig.yaml -- healthStatus: - status: Degraded - message: No statefulset or endpoints found - inputPath: testdata/degraded_cluster_unknown.yaml -- healthStatus: - status: Degraded - message: No statefulset or endpoints found - inputPath: testdata/degraded_replicas_unknown.yaml -- healthStatus: - status: Progressing - message: Waiting for RabbitMQ Operator - inputPath: testdata/progressing_no_status.yaml -- healthStatus: - status: Progressing - message: Waiting for RabbitMQ cluster formation - inputPath: testdata/progressing_cluster_unavailable.yaml -- healthStatus: - status: Progressing - message: Waiting for RabbitMQ instances ready - inputPath: testdata/progressing_pods_not_ready.yaml -- healthStatus: - status: Healthy - message: RabbitMQ cluster ready - inputPath: testdata/healthy.yaml \ No newline at end of file diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_badconfig.yaml b/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_badconfig.yaml deleted file mode 100644 index 29b03cb406ccb..0000000000000 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_badconfig.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: rabbitmq.com/v1beta1 -kind: RabbitmqCluster -metadata: - labels: - app: example-rabbitmq - name: example-rabbitmq - namespace: example -spec: - image: docker.io/bitnami/rabbitmq:3.10.7-debian-11-r8 - persistence: - storage: 32Gi - storageClassName: default - rabbitmq: - replicas: 3 - resources: - limits: - cpu: 250m - memory: 1792Mi - requests: - cpu: 250m - memory: 1792Mi - service: - type: ClusterIP - foo: bar -status: - conditions: - - lastTransitionTime: "2023-08-30T07:44:39Z" - message: Unknown 'foo' parameter - reason: Initializing - status: "False" - type: ReconcileSuccess \ No newline at end of file diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_cluster_unknown.yaml b/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_cluster_unknown.yaml deleted file mode 100644 index 86b7be4bd468b..0000000000000 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_cluster_unknown.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: rabbitmq.com/v1beta1 -kind: RabbitmqCluster -metadata: - labels: - app: example-rabbitmq - name: example-rabbitmq - namespace: example -spec: - image: docker.io/bitnami/rabbitmq:3.10.7-debian-11-r8 - persistence: - storage: 32Gi - storageClassName: default - rabbitmq: - replicas: 3 - resources: - limits: - cpu: 250m - memory: 1792Mi - requests: - cpu: 250m - memory: 1792Mi - service: - type: ClusterIP -status: - conditions: - - lastTransitionTime: "2023-08-30T07:44:34Z" - reason: NotAllPodsReady - message: 0/3 Pods ready - status: "False" - type: AllReplicasReady - - lastTransitionTime: "2023-08-30T07:37:06Z" - reason: CouldNotRetrieveEndpoints - message: Could not verify available service endpoints - status: "Unknown" - type: ClusterAvailable - - lastTransitionTime: "2023-08-30T07:33:06Z" - reason: NoWarnings - status: "True" - type: NoWarnings - - lastTransitionTime: "2023-08-30T07:44:39Z" - message: Finish reconciling - reason: Success - status: "True" - type: ReconcileSuccess \ No newline at end of file diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_replicas_unknown.yaml b/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_replicas_unknown.yaml deleted file mode 100644 index f92f5afd66d17..0000000000000 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/degraded_replicas_unknown.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: rabbitmq.com/v1beta1 -kind: RabbitmqCluster -metadata: - labels: - app: example-rabbitmq - name: example-rabbitmq - namespace: example -spec: - image: docker.io/bitnami/rabbitmq:3.10.7-debian-11-r8 - persistence: - storage: 32Gi - storageClassName: default - rabbitmq: - replicas: 3 - resources: - limits: - cpu: 250m - memory: 1792Mi - requests: - cpu: 250m - memory: 1792Mi - service: - type: ClusterIP -status: - conditions: - - lastTransitionTime: "2023-08-30T07:44:34Z" - reason: MissingStatefulSet - message: Could not find StatefulSet - status: "Unknown" - type: AllReplicasReady - - lastTransitionTime: "2023-08-30T07:37:06Z" - reason: NoEndpointsAvailable - message: The service has no endpoints available - status: "False" - type: ClusterAvailable - - lastTransitionTime: "2023-08-30T07:33:06Z" - reason: NoWarnings - status: "True" - type: NoWarnings - - lastTransitionTime: "2023-08-30T07:44:39Z" - message: Finish reconciling - reason: Success - status: "True" - type: ReconcileSuccess \ No newline at end of file diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/healthy.yaml b/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/healthy.yaml deleted file mode 100644 index b436fba2bce26..0000000000000 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/healthy.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: rabbitmq.com/v1beta1 -kind: RabbitmqCluster -metadata: - labels: - app: example-rabbitmq - name: example-rabbitmq - namespace: example -spec: - image: docker.io/bitnami/rabbitmq:3.10.7-debian-11-r8 - persistence: - storage: 32Gi - storageClassName: default - rabbitmq: - replicas: 3 - resources: - limits: - cpu: 250m - memory: 1792Mi - requests: - cpu: 250m - memory: 1792Mi - service: - type: ClusterIP -status: - conditions: - - lastTransitionTime: "2023-08-30T07:44:34Z" - reason: AllPodsAreReady - status: "True" - type: AllReplicasReady - - lastTransitionTime: "2023-08-30T07:37:06Z" - reason: AtLeastOneEndpointAvailable - status: "True" - type: ClusterAvailable - - lastTransitionTime: "2023-08-30T07:33:06Z" - reason: NoWarnings - status: "True" - type: NoWarnings - - lastTransitionTime: "2023-08-30T07:44:39Z" - message: Finish reconciling - reason: Success - status: "True" - type: ReconcileSuccess \ No newline at end of file diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_cluster_unavailable.yaml b/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_cluster_unavailable.yaml deleted file mode 100644 index b01d9f52e2874..0000000000000 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_cluster_unavailable.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: rabbitmq.com/v1beta1 -kind: RabbitmqCluster -metadata: - labels: - app: example-rabbitmq - name: example-rabbitmq - namespace: example -spec: - image: docker.io/bitnami/rabbitmq:3.10.7-debian-11-r8 - persistence: - storage: 32Gi - storageClassName: default - rabbitmq: - replicas: 3 - resources: - limits: - cpu: 250m - memory: 1792Mi - requests: - cpu: 250m - memory: 1792Mi - service: - type: ClusterIP -status: - conditions: - - lastTransitionTime: "2023-08-30T07:44:34Z" - reason: NotAllPodsReady - message: 0/3 Pods ready - status: "False" - type: AllReplicasReady - - lastTransitionTime: "2023-08-30T07:37:06Z" - reason: NoEndpointsAvailable - message: The service has no endpoints available - status: "False" - type: ClusterAvailable - - lastTransitionTime: "2023-08-30T07:33:06Z" - reason: NoWarnings - status: "True" - type: NoWarnings - - lastTransitionTime: "2023-08-30T07:44:39Z" - message: Finish reconciling - reason: Success - status: "True" - type: ReconcileSuccess \ No newline at end of file diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_no_status.yaml b/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_no_status.yaml deleted file mode 100644 index b1ef1afe03562..0000000000000 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_no_status.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: rabbitmq.com/v1beta1 -kind: RabbitmqCluster -metadata: - labels: - app: example-rabbitmq - name: example-rabbitmq - namespace: example -spec: - image: docker.io/bitnami/rabbitmq:3.10.7-debian-11-r8 - persistence: - storage: 32Gi - storageClassName: default - rabbitmq: - replicas: 3 - resources: - limits: - cpu: 250m - memory: 1792Mi - requests: - cpu: 250m - memory: 1792Mi - service: - type: ClusterIP \ No newline at end of file diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_pods_not_ready.yaml b/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_pods_not_ready.yaml deleted file mode 100644 index 0cbbb2d050250..0000000000000 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/testdata/progressing_pods_not_ready.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: rabbitmq.com/v1beta1 -kind: RabbitmqCluster -metadata: - labels: - app: example-rabbitmq - name: example-rabbitmq - namespace: example -spec: - image: docker.io/bitnami/rabbitmq:3.10.7-debian-11-r8 - persistence: - storage: 32Gi - storageClassName: default - rabbitmq: - replicas: 3 - resources: - limits: - cpu: 250m - memory: 1792Mi - requests: - cpu: 250m - memory: 1792Mi - service: - type: ClusterIP -status: - conditions: - - lastTransitionTime: "2023-08-30T07:44:34Z" - reason: NotAllPodsReady - message: 1/3 Pods ready - status: "False" - type: AllReplicasReady - - lastTransitionTime: "2023-08-30T07:37:06Z" - reason: AtLeastOneEndpointAvailable - status: "True" - type: ClusterAvailable - - lastTransitionTime: "2023-08-30T07:33:06Z" - reason: NoWarnings - status: "True" - type: NoWarnings - - lastTransitionTime: "2023-08-30T07:44:39Z" - message: Finish reconciling - reason: Success - status: "True" - type: ReconcileSuccess \ No newline at end of file diff --git a/resource_customizations/rds.aws.crossplane.io/DBCluster/health.lua b/resource_customizations/rds.aws.crossplane.io/DBCluster/health.lua deleted file mode 100644 index dafa85d47a859..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBCluster/health.lua +++ /dev/null @@ -1,41 +0,0 @@ -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 DBCluster to be available" - else - hs.status = "Degraded" - hs.message = synced_message - end - return hs - end -end - -hs.status = "Progressing" -hs.message = "Waiting for DBCluster to be created" -return hs diff --git a/resource_customizations/rds.aws.crossplane.io/DBCluster/health_test.yaml b/resource_customizations/rds.aws.crossplane.io/DBCluster/health_test.yaml deleted file mode 100644 index 280532d2bc195..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBCluster/health_test.yaml +++ /dev/null @@ -1,18 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: Waiting for DBCluster to be available - inputPath: testdata/creating.yaml -- healthStatus: - status: Healthy - message: Available - inputPath: testdata/healthy.yaml -- healthStatus: - status: Degraded - message: "create failed: cannot create DBCluster in AWS: InvalidParameterValue: - Invalid DB engine\n\tstatus code: 400, request id: " - inputPath: testdata/degraded.yaml -- healthStatus: - status: Suspended - message: ReconcilePaused - inputPath: testdata/suspended.yaml diff --git a/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/creating.yaml b/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/creating.yaml deleted file mode 100644 index e5addf24b688b..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/creating.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: rds.aws.crossplane.io/v1alpha1 -kind: DBCluster -metadata: - name: test-rds1 -spec: - deletionPolicy: Delete - forProvider: - allowMajorVersionUpgrade: true - applyImmediately: true - autogeneratePassword: true - databaseName: app - dbSubnetGroupName: test-rds - engine: aurora-postgresql - engineVersion: '16.2' - masterUsername: root - skipFinalSnapshot: true - managementPolicies: - - '*' - providerConfigRef: - name: provider-aws - publishConnectionDetailsTo: - configRef: - name: store-config - name: test-rds1-rds -status: - atProvider: - activityStreamStatus: stopped - clusterCreateTime: '2024-07-15T14:23:42Z' - crossAccountClone: false - dbClusterARN: 'arn:aws:rds:abc123:cluster:test-rds1' - dbClusterIdentifier: test-rds1 - dbClusterParameterGroup: default.aurora-postgresql16 - dbClusterResourceID: cluster-abc123 - dbSubnetGroup: test-rds - endpoint: test-rds1.cluster-abc.rds.amazonaws.com - status: creating - vpcSecurityGroups: - - status: active - vpcSecurityGroupID: sg-abc123 - conditions: - - lastTransitionTime: '2024-07-15T14:23:42Z' - reason: Creating - status: 'False' - type: Ready - - lastTransitionTime: '2024-07-15T14:23:42Z' - reason: ReconcileSuccess - status: 'True' - type: Synced diff --git a/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/degraded.yaml b/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/degraded.yaml deleted file mode 100644 index 1203a9421accd..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/degraded.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: rds.aws.crossplane.io/v1alpha1 -kind: DBCluster -metadata: - name: test-rds1 -spec: - deletionPolicy: Delete - forProvider: - allowMajorVersionUpgrade: true - applyImmediately: true - autogeneratePassword: true - databaseName: app - dbSubnetGroupName: test-rds - engine: foobar - engineVersion: '16.2' - masterUsername: root - skipFinalSnapshot: true - managementPolicies: - - '*' - providerConfigRef: - name: provider-aws - publishConnectionDetailsTo: - configRef: - name: store-config - name: test-rds1-rds -status: - atProvider: {} - conditions: - - lastTransitionTime: "2024-07-17T18:03:12Z" - reason: Creating - status: "False" - type: Ready - - lastTransitionTime: "2024-07-17T18:03:12Z" - message: "create failed: cannot create DBCluster in AWS: InvalidParameterValue: - Invalid DB engine\n\tstatus code: 400, request id: " - reason: ReconcileError - status: "False" - type: Synced diff --git a/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/healthy.yaml b/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/healthy.yaml deleted file mode 100644 index 3e824228a25ad..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/healthy.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: rds.aws.crossplane.io/v1alpha1 -kind: DBCluster -metadata: - name: test-rds1 -spec: - deletionPolicy: Delete - forProvider: - allowMajorVersionUpgrade: true - applyImmediately: true - autogeneratePassword: true - databaseName: app - dbSubnetGroupName: test-rds - engine: aurora-postgresql - engineVersion: '16.2' - masterUsername: root - skipFinalSnapshot: true - managementPolicies: - - '*' - providerConfigRef: - name: provider-aws - publishConnectionDetailsTo: - configRef: - name: store-config - name: test-rds1-rds -status: - atProvider: - activityStreamStatus: stopped - clusterCreateTime: "2024-07-15T14:23:42Z" - crossAccountClone: false - dbClusterARN: arn:aws:rds:abc123:cluster:test-rds1 - dbClusterIdentifier: test-rds1 - dbClusterMembers: - - dbClusterParameterGroupStatus: in-sync - dbInstanceIdentifier: test-rds1-0 - isClusterWriter: true - promotionTier: 1 - dbClusterParameterGroup: default.aurora-postgresql16 - dbClusterResourceID: cluster-abc123 - dbSubnetGroup: sandbox5-valhalla-rds - earliestRestorableTime: "2024-07-15T14:24:40Z" - endpoint: test-rds1.cluster-abc123.rds.amazonaws.com - engineVersion: "16.2" - hostedZoneID: abc123 - httpEndpointEnabled: false - iamDatabaseAuthenticationEnabled: false - latestRestorableTime: "2024-07-15T14:46:08Z" - multiAZ: false - readerEndpoint: test-rds1.abc123.rds.amazonaws.com - status: available - vpcSecurityGroups: - - status: active - vpcSecurityGroupID: sg-abc123 - conditions: - - lastTransitionTime: "2024-07-15T14:48:40Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2024-07-15T14:23:42Z" - reason: ReconcileSuccess - status: "True" - type: Synced diff --git a/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/suspended.yaml b/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/suspended.yaml deleted file mode 100644 index 960f4d86733ee..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBCluster/testdata/suspended.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: rds.aws.crossplane.io/v1alpha1 -kind: DBCluster -metadata: - name: test-rds1 - annotations: - crossplane.io/paused: "true" -spec: - deletionPolicy: Delete - forProvider: - allowMajorVersionUpgrade: true - applyImmediately: true - autogeneratePassword: true - databaseName: app - dbSubnetGroupName: test-rds - engine: aurora-postgresql - engineVersion: '16.2' - masterUsername: root - skipFinalSnapshot: true - managementPolicies: - - '*' - providerConfigRef: - name: provider-aws - publishConnectionDetailsTo: - configRef: - name: store-config - name: test-rds1-rds -status: - atProvider: {} - conditions: - - lastTransitionTime: "2024-07-17T18:03:12Z" - reason: Creating - status: "False" - type: Ready - - lastTransitionTime: "2024-07-17T18:04:55Z" - reason: ReconcilePaused - status: "False" - type: Synced diff --git a/resource_customizations/rds.aws.crossplane.io/DBInstance/health.lua b/resource_customizations/rds.aws.crossplane.io/DBInstance/health.lua deleted file mode 100644 index 91e22df91a6be..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBInstance/health.lua +++ /dev/null @@ -1,41 +0,0 @@ -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 DBInstance to be available" - else - hs.status = "Degraded" - hs.message = synced_message - end - return hs - end -end - -hs.status = "Progressing" -hs.message = "Waiting for DBInstance to be created" -return hs diff --git a/resource_customizations/rds.aws.crossplane.io/DBInstance/health_test.yaml b/resource_customizations/rds.aws.crossplane.io/DBInstance/health_test.yaml deleted file mode 100644 index d8db77007ca88..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBInstance/health_test.yaml +++ /dev/null @@ -1,14 +0,0 @@ -tests: -- healthStatus: - status: Progressing - message: Waiting for DBInstance to be available - inputPath: testdata/creating.yaml -- healthStatus: - status: Healthy - message: Available - inputPath: testdata/healthy.yaml -- healthStatus: - status: Degraded - message: "update failed: cannot update DBInstance in AWS: InvalidParameterValue: - Invalid DB Instance class: db.t4g.foobar\n\tstatus code: 400, request id: " - inputPath: testdata/degraded.yaml diff --git a/resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/creating.yaml b/resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/creating.yaml deleted file mode 100644 index 883d55e0041b4..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/creating.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: rds.aws.crossplane.io/v1alpha1 -kind: DBInstance -metadata: - name: test-rds1-0 -spec: - deletionPolicy: Delete - forProvider: - autoMinorVersionUpgrade: true - caCertificateIdentifier: rds-ca-rsa2048-g1 - dbClusterIdentifier: test-rds1 - dbInstanceClass: db.t4g.medium - dbName: app - dbSubnetGroupName: test-rds - enablePerformanceInsights: false - engine: aurora-postgresql - licenseModel: postgresql-license - masterUsername: root - multiAZ: false - preferredMaintenanceWindow: 'tue:00:36-tue:01:06' - promotionTier: 1 - publiclyAccessible: false - region: eu-north-1 - storageThroughput: 0 - managementPolicies: - - '*' - providerConfigRef: - name: provider-aws -status: - atProvider: - certificateDetails: - cAIdentifier: rds-ca-rsa2048-g1 - customerOwnedIPEnabled: false - dbInstanceARN: 'arn:aws:rds:abc:db:test-rds1-0' - dbInstanceIdentifier: test-rds1-0 - dbInstancePort: 0 - dbInstanceStatus: creating - conditions: - - lastTransitionTime: '2024-07-15T14:25:07Z' - reason: Creating - status: 'False' - type: Ready - - lastTransitionTime: '2024-07-15T14:25:07Z' - reason: ReconcileSuccess - status: 'True' - type: Synced diff --git a/resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/degraded.yaml b/resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/degraded.yaml deleted file mode 100644 index 60fd417bada80..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/degraded.yaml +++ /dev/null @@ -1,64 +0,0 @@ -apiVersion: rds.aws.crossplane.io/v1alpha1 -kind: DBInstance -metadata: - name: test-rds1-0 -spec: - deletionPolicy: Delete - forProvider: - autoMinorVersionUpgrade: true - caCertificateIdentifier: rds-ca-rsa2048-g1 - dbClusterIdentifier: test-rds1 - dbInstanceClass: db.t4g.foobar - dbName: app - dbSubnetGroupName: test-rds - enablePerformanceInsights: false - engine: aurora-postgresql - licenseModel: postgresql-license - masterUsername: root - multiAZ: false - preferredMaintenanceWindow: 'tue:00:36-tue:01:06' - promotionTier: 1 - publiclyAccessible: false - region: eu-north-1 - storageThroughput: 0 - managementPolicies: - - '*' - providerConfigRef: - name: provider-aws -status: - atProvider: - certificateDetails: - cAIdentifier: rds-ca-rsa2048-g1 - validTill: "2025-07-15T16:06:53Z" - customerOwnedIPEnabled: false - dbInstanceARN: arn:aws:rds:123:db:test-rds1-app-0 - dbInstanceIdentifier: test-rds1-app-0 - dbInstancePort: 0 - dbInstanceStatus: available - dbiResourceID: db-123 - endpoint: - address: test-rds1-app-0.123.abc.rds.amazonaws.com - hostedZoneID: ABC213 - port: 5432 - engineVersion: "16.2" - iamDatabaseAuthenticationEnabled: false - instanceCreateTime: "2024-07-15T16:08:27Z" - optionGroupMemberships: - - optionGroupName: default:aurora-postgresql-16 - status: in-sync - pendingModifiedValues: {} - performanceInsightsEnabled: false - vpcSecurityGroups: - - status: active - vpcSecurityGroupID: sg-abc123 - conditions: - - lastTransitionTime: "2024-07-15T17:04:24Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2024-07-17T17:57:38Z" - message: "update failed: cannot update DBInstance in AWS: InvalidParameterValue: - Invalid DB Instance class: db.t4g.foobar\n\tstatus code: 400, request id: " - reason: ReconcileError - status: "False" - type: Synced diff --git a/resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/healthy.yaml b/resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/healthy.yaml deleted file mode 100644 index c993c91b7bd6d..0000000000000 --- a/resource_customizations/rds.aws.crossplane.io/DBInstance/testdata/healthy.yaml +++ /dev/null @@ -1,65 +0,0 @@ -apiVersion: rds.aws.crossplane.io/v1alpha1 -kind: DBInstance -metadata: - name: test-rds1-0 -spec: - deletionPolicy: Delete - forProvider: - autoMinorVersionUpgrade: true - caCertificateIdentifier: rds-ca-rsa2048-g1 - dbClusterIdentifier: test-rds1 - dbInstanceClass: db.t4g.medium - dbName: app - dbSubnetGroupName: test-rds - enablePerformanceInsights: false - engine: aurora-postgresql - licenseModel: postgresql-license - masterUsername: root - multiAZ: false - preferredMaintenanceWindow: 'tue:00:36-tue:01:06' - promotionTier: 1 - publiclyAccessible: false - region: eu-north-1 - storageThroughput: 0 - managementPolicies: - - '*' - providerConfigRef: - name: provider-aws -status: - atProvider: - certificateDetails: - cAIdentifier: rds-ca-rsa2048-g1 - validTill: "2025-07-15T14:27:27Z" - customerOwnedIPEnabled: false - dbInstanceARN: arn:aws:rds:abc123:db:test-rds1-0 - dbInstanceIdentifier: test-rds1-0 - dbInstancePort: 0 - dbInstanceStatus: available - dbParameterGroups: - - dbParameterGroupName: default.aurora-postgresql16 - parameterApplyStatus: in-sync - dbiResourceID: db-abc123 - endpoint: - address: test-rds1-0.abc123.rds.amazonaws.com - hostedZoneID: abc123 - port: 5432 - engineVersion: "16.2" - iamDatabaseAuthenticationEnabled: false - instanceCreateTime: "2024-07-15T14:29:00Z" - optionGroupMemberships: - - optionGroupName: default:aurora-postgresql-16 - status: in-sync - pendingModifiedValues: {} - performanceInsightsEnabled: false - vpcSecurityGroups: - - status: active - vpcSecurityGroupID: sg-abc123 - conditions: - - lastTransitionTime: "2024-07-15T14:48:40Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2024-07-15T14:25:07Z" - reason: ReconcileSuccess - status: "True" - type: Synced diff --git a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health.lua b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health.lua deleted file mode 100644 index 0cf5253e910ff..0000000000000 --- a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health.lua +++ /dev/null @@ -1,41 +0,0 @@ -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/health_test.yaml b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health_test.yaml deleted file mode 100644 index aa83951d5a2db..0000000000000 --- a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health_test.yaml +++ /dev/null @@ -1,25 +0,0 @@ -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/testdata/degraded_reconcileError.yaml b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/degraded_reconcileError.yaml deleted file mode 100644 index 31bc5123c7bfd..0000000000000 --- a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/degraded_reconcileError.yaml +++ /dev/null @@ -1,35 +0,0 @@ -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 deleted file mode 100644 index f808e46cc8c92..0000000000000 --- a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/healthy.yaml +++ /dev/null @@ -1,29 +0,0 @@ -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 deleted file mode 100644 index abf59775fb8e0..0000000000000 --- a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_creating.yaml +++ /dev/null @@ -1,29 +0,0 @@ -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 deleted file mode 100644 index 28d778d055050..0000000000000 --- a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_noStatus.yaml +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index 522c0e878dcf8..0000000000000 --- a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/suspended_reconcilePaused.yaml +++ /dev/null @@ -1,27 +0,0 @@ -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/s3.aws.crossplane.io/Bucket/health.lua b/resource_customizations/s3.aws.crossplane.io/Bucket/health.lua deleted file mode 100644 index ae9d8ff97ff9f..0000000000000 --- a/resource_customizations/s3.aws.crossplane.io/Bucket/health.lua +++ /dev/null @@ -1,41 +0,0 @@ -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 Bucket to be available" - else - hs.status = "Degraded" - hs.message = synced_message - end - return hs - end -end - -hs.status = "Progressing" -hs.message = "Waiting for Bucket to be created" -return hs diff --git a/resource_customizations/s3.aws.crossplane.io/Bucket/health_test.yaml b/resource_customizations/s3.aws.crossplane.io/Bucket/health_test.yaml deleted file mode 100644 index 7fd4388805e9b..0000000000000 --- a/resource_customizations/s3.aws.crossplane.io/Bucket/health_test.yaml +++ /dev/null @@ -1,14 +0,0 @@ -tests: -- healthStatus: - status: Degraded - message: >- - delete failed: operation error S3: DeleteBucket, https response error - StatusCode: 409, RequestID: ABC123, HostID: - ABC/123/ABC=, - api error BucketNotEmpty: The bucket you tried to delete is not empty. - You must delete all versions in the bucket. - inputPath: testdata/ReconcileError.yaml -- healthStatus: - status: Healthy - message: Available - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/s3.aws.crossplane.io/Bucket/testdata/ReconcileError.yaml b/resource_customizations/s3.aws.crossplane.io/Bucket/testdata/ReconcileError.yaml deleted file mode 100644 index 601a08e623d0c..0000000000000 --- a/resource_customizations/s3.aws.crossplane.io/Bucket/testdata/ReconcileError.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: s3.aws.crossplane.io/v1beta1 -kind: Bucket -metadata: - name: example -spec: - deletionPolicy: Delete - forProvider: - locationConstraint: eu-north-1 - objectOwnership: BucketOwnerEnforced - paymentConfiguration: - payer: BucketOwner - publicAccessBlockConfiguration: - blockPublicAcls: true - blockPublicPolicy: true - ignorePublicAcls: true - restrictPublicBuckets: true - serverSideEncryptionConfiguration: - rules: - - applyServerSideEncryptionByDefault: - sseAlgorithm: AES256 - versioningConfiguration: - status: Suspended - providerConfigRef: - name: provider-aws -status: - atProvider: - arn: 'arn:aws:s3:::example' - conditions: - - lastTransitionTime: '2024-07-12T09:51:07Z' - reason: Deleting - status: 'False' - type: Ready - - lastTransitionTime: '2024-07-12T09:51:07Z' - message: >- - delete failed: operation error S3: DeleteBucket, https response error - StatusCode: 409, RequestID: ABC123, HostID: - ABC/123/ABC=, - api error BucketNotEmpty: The bucket you tried to delete is not empty. - You must delete all versions in the bucket. - reason: ReconcileError - status: 'False' - type: Synced diff --git a/resource_customizations/s3.aws.crossplane.io/Bucket/testdata/healthy.yaml b/resource_customizations/s3.aws.crossplane.io/Bucket/testdata/healthy.yaml deleted file mode 100644 index fc29b984719f0..0000000000000 --- a/resource_customizations/s3.aws.crossplane.io/Bucket/testdata/healthy.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: s3.aws.crossplane.io/v1beta1 -kind: Bucket -metadata: - name: example -spec: - deletionPolicy: Delete - forProvider: - locationConstraint: eu-north-1 - objectOwnership: BucketOwnerEnforced - paymentConfiguration: - payer: BucketOwner - publicAccessBlockConfiguration: - blockPublicAcls: true - blockPublicPolicy: true - ignorePublicAcls: true - restrictPublicBuckets: true - serverSideEncryptionConfiguration: - rules: - - applyServerSideEncryptionByDefault: - sseAlgorithm: AES256 - versioningConfiguration: - status: Suspended - providerConfigRef: - name: provider-aws -status: - atProvider: - arn: arn:aws:s3:::example - conditions: - - lastTransitionTime: "2024-07-12T12:50:46Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2024-07-12T12:50:44Z" - reason: ReconcileSuccess - status: "True" - type: Synced diff --git a/resource_customizations/serving.kserve.io/InferenceService/health.lua b/resource_customizations/serving.kserve.io/InferenceService/health.lua index 85da1161f315f..fbcfbf77820f9 100644 --- a/resource_customizations/serving.kserve.io/InferenceService/health.lua +++ b/resource_customizations/serving.kserve.io/InferenceService/health.lua @@ -1,13 +1,3 @@ --- isInferenceServiceInRawDeploymentMode determines if the inference service deployed in RawDeployment mode --- KServe v12 and above supports Rawdeployment for Inference graphs. For Inference services, KServe has supported RawDeployment model since [v0.7.0](https://github.com/kserve/kserve/releases/tag/v0.7.0). -function isInferenceServiceInRawDeploymentMode(obj) - if obj.metadata.annotations == nil then - return false - end - local deploymentMode = obj.metadata.annotations["serving.kserve.io/deploymentMode"] - return deploymentMode ~= nil and deploymentMode == "RawDeployment" -end - local health_status = {} health_status.status = "Progressing" health_status.message = "Waiting for status update." @@ -35,7 +25,7 @@ if obj.status ~= nil and obj.status.conditions ~= nil then end end end - if ((isInferenceServiceInRawDeploymentMode(obj) and status_true == 3) or status_true == 5) and status_false == 0 and status_unknown == 0 then + 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 diff --git a/resource_customizations/serving.kserve.io/InferenceService/health_test.yaml b/resource_customizations/serving.kserve.io/InferenceService/health_test.yaml index 1dc5576f93f3a..e8f32bd51f798 100644 --- a/resource_customizations/serving.kserve.io/InferenceService/health_test.yaml +++ b/resource_customizations/serving.kserve.io/InferenceService/health_test.yaml @@ -11,7 +11,3 @@ tests: status: Healthy message: Inference Service is healthy. inputPath: testdata/healthy.yaml -- healthStatus: - status: Healthy - message: Inference Service is healthy. - inputPath: testdata/healthy_raw.yaml diff --git a/resource_customizations/serving.kserve.io/InferenceService/testdata/healthy_raw.yaml b/resource_customizations/serving.kserve.io/InferenceService/testdata/healthy_raw.yaml deleted file mode 100644 index 5f9d805625d9c..0000000000000 --- a/resource_customizations/serving.kserve.io/InferenceService/testdata/healthy_raw.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: serving.kserve.io/v1beta1 -kind: InferenceService -metadata: - name: helloworld - namespace: default - annotations: - serving.kserve.io/deploymentMode: RawDeployment -spec: {} -status: - conditions: - - lastTransitionTime: '2024-05-14T03:49:11Z' - status: 'True' - type: IngressReady - - lastTransitionTime: '2024-05-16T18:48:56Z' - status: 'True' - type: PredictorReady - - lastTransitionTime: '2024-05-16T18:48:56Z' - status: 'True' - type: Ready diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/action_test.yaml b/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/action_test.yaml deleted file mode 100644 index 170d73e9a8e02..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/action_test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_bucket.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_bucket.yaml - result: - - name: reconcile - disabled: true - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_bucket.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: reconcile - inputPath: testdata/initial_bucket.yaml - expectedOutputPath: testdata/reconciled_bucket.yaml -- action: suspend - inputPath: testdata/initial_bucket.yaml - expectedOutputPath: testdata/suspended_bucket.yaml -- action: resume - inputPath: testdata/suspended_bucket.yaml - expectedOutputPath: testdata/resumed_bucket.yaml diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/discovery.lua b/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/discovery.lua deleted file mode 100644 index 9000998815515..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/discovery.lua +++ /dev/null @@ -1,18 +0,0 @@ -local actions = {} - -actions["reconcile"] = {["disabled"] = true} -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["reconcile"]["disabled"] = false - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/reconcile/action.lua b/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/reconcile/action.lua deleted file mode 100644 index a534b36fb3ebc..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/reconcile/action.lua +++ /dev/null @@ -1,7 +0,0 @@ -local os = require("os") -if obj.metadata.annotations == nil then - obj.metadata.annotations = {} -end -obj.metadata.annotations["reconcile.fluxcd.io/requestedAt"] = "By Argo CD at: " .. os.date("!%Y-%m-%dT%X") - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/resume/action.lua b/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/suspend/action.lua b/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/initial_bucket.yaml b/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/initial_bucket.yaml deleted file mode 100644 index 2de992d401e6c..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/initial_bucket.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: Bucket -metadata: - name: minio-bucket - namespace: default -spec: - interval: 5m0s - endpoint: minio.example.com - insecure: true - secretRef: - name: minio-bucket-secret - bucketName: example diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/reconciled_bucket.yaml b/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/reconciled_bucket.yaml deleted file mode 100644 index 80074067a75bf..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/reconciled_bucket.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: Bucket -metadata: - name: minio-bucket - namespace: default - annotations: - reconcile.fluxcd.io/requestedAt: 'By Argo CD at: 0001-01-01T00:00:00' -spec: - interval: 5m0s - endpoint: minio.example.com - insecure: true - secretRef: - name: minio-bucket-secret - bucketName: example diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/resumed_bucket.yaml b/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/resumed_bucket.yaml deleted file mode 100644 index 40cfe6c9fc43d..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/resumed_bucket.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: Bucket -metadata: - name: minio-bucket - namespace: default -spec: - interval: 5m0s - endpoint: minio.example.com - insecure: true - secretRef: - name: minio-bucket-secret - bucketName: example - suspend: false diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/suspended_bucket.yaml b/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/suspended_bucket.yaml deleted file mode 100644 index 0f10c70214c62..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/actions/testdata/suspended_bucket.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: Bucket -metadata: - name: minio-bucket - namespace: default -spec: - interval: 5m0s - endpoint: minio.example.com - insecure: true - secretRef: - name: minio-bucket-secret - bucketName: example - suspend: true diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/health.lua b/resource_customizations/source.toolkit.fluxcd.io/Bucket/health.lua deleted file mode 100644 index 9ad39cb708294..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/health.lua +++ /dev/null @@ -1,45 +0,0 @@ -local hs = {} -if obj.spec.suspend ~= nil and obj.spec.suspend == true then - hs.message = obj.kind .. " is suspended" - hs.status = "Suspended" - return hs -end -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numProgressing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "Unknown" then - numProgressing = numProgressing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - numProgressing = numProgressing + 1 - elseif condition.type == "ArtifactOutdated" and condition.status == "True" then - message = message .. " " .. condition.reason - elseif condition.type == "ArtifactInStorage" and condition.status == "True" then - numSucceeded = numSucceeded + 1 - end - end - if(numProgressing == 2) then - hs.message = message - hs.status = "Progressing" - return hs - elseif(numSucceeded == 2) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/health_test.yaml b/resource_customizations/source.toolkit.fluxcd.io/Bucket/health_test.yaml deleted file mode 100644 index 1cbb664013978..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - # - healthStatus: - # status: Progressing - # message: Progressing - # inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: BucketOperationFailed - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: Succeeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/degraded.yaml b/resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/degraded.yaml deleted file mode 100644 index d2d469e36563c..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/degraded.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: Bucket -metadata: - name: minio-bucket - namespace: default -spec: - interval: 5m0s - endpoint: minio.example.com - insecure: true - secretRef: - name: minio-bucket-secret - bucketName: example -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: building artifact - observedGeneration: 1 - reason: ProgressingWithRetry - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to confirm existence of ''example'' bucket: XML syntax error - on line 5: element closed by ' - observedGeneration: 1 - reason: BucketOperationFailed - status: "False" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to confirm existence of ''example'' bucket: XML syntax error - on line 5: element closed by ' - observedGeneration: 1 - reason: BucketOperationFailed - status: "True" - type: FetchFailed diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/healthy.yaml b/resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/healthy.yaml deleted file mode 100644 index b0e39bd81c5f3..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/healthy.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: Bucket -metadata: - name: minio-bucket - namespace: default -spec: - interval: 5m0s - endpoint: minio.example.com - insecure: true - secretRef: - name: minio-bucket-secret - bucketName: example -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'stored artifact: revision ''sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855''' - observedGeneration: 3 - reason: Succeeded - status: "True" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'stored artifact: revision ''sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855''' - observedGeneration: 3 - reason: Succeeded - status: "True" - type: ArtifactInStorage diff --git a/resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/progressing.yaml b/resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/progressing.yaml deleted file mode 100644 index be2e1b364bbab..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/Bucket/testdata/progressing.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: Bucket -metadata: - name: minio-bucket - namespace: default -spec: - interval: 5m0s - endpoint: minio.example.com - insecure: true - secretRef: - name: minio-bucket-secret - bucketName: example -status: - conditions: [] diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/action_test.yaml b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/action_test.yaml deleted file mode 100644 index 203f40629d209..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/action_test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_gitrepository.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_gitrepository.yaml - result: - - name: reconcile - disabled: true - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_gitrepository.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: reconcile - inputPath: testdata/initial_gitrepository.yaml - expectedOutputPath: testdata/reconciled_gitrepository.yaml -- action: suspend - inputPath: testdata/initial_gitrepository.yaml - expectedOutputPath: testdata/suspended_gitrepository.yaml -- action: resume - inputPath: testdata/suspended_gitrepository.yaml - expectedOutputPath: testdata/resumed_gitrepository.yaml diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/discovery.lua b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/discovery.lua deleted file mode 100644 index 9000998815515..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/discovery.lua +++ /dev/null @@ -1,18 +0,0 @@ -local actions = {} - -actions["reconcile"] = {["disabled"] = true} -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["reconcile"]["disabled"] = false - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/reconcile/action.lua b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/reconcile/action.lua deleted file mode 100644 index a534b36fb3ebc..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/reconcile/action.lua +++ /dev/null @@ -1,7 +0,0 @@ -local os = require("os") -if obj.metadata.annotations == nil then - obj.metadata.annotations = {} -end -obj.metadata.annotations["reconcile.fluxcd.io/requestedAt"] = "By Argo CD at: " .. os.date("!%Y-%m-%dT%X") - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/resume/action.lua b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/suspend/action.lua b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/initial_gitrepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/initial_gitrepository.yaml deleted file mode 100644 index 3cd5664e591f0..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/initial_gitrepository.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: GitRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m - url: https://github.com/stefanprodan/podinfo - ref: - branch: master diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/reconciled_gitrepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/reconciled_gitrepository.yaml deleted file mode 100644 index 4cfe3861aca1f..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/reconciled_gitrepository.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: GitRepository -metadata: - name: podinfo - namespace: default - annotations: - reconcile.fluxcd.io/requestedAt: 'By Argo CD at: 0001-01-01T00:00:00' -spec: - interval: 5m - url: https://github.com/stefanprodan/podinfo - ref: - branch: master diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/resumed_gitrepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/resumed_gitrepository.yaml deleted file mode 100644 index 0a204953f5fe7..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/resumed_gitrepository.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: GitRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m - url: https://github.com/stefanprodan/podinfo - ref: - branch: master - suspend: false diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/suspended_gitrepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/suspended_gitrepository.yaml deleted file mode 100644 index 22c7be6772be2..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/actions/testdata/suspended_gitrepository.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: GitRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m - url: https://github.com/stefanprodan/podinfo - ref: - branch: master - suspend: true diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/health.lua b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/health.lua deleted file mode 100644 index 9ad39cb708294..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/health.lua +++ /dev/null @@ -1,45 +0,0 @@ -local hs = {} -if obj.spec.suspend ~= nil and obj.spec.suspend == true then - hs.message = obj.kind .. " is suspended" - hs.status = "Suspended" - return hs -end -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numProgressing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "Unknown" then - numProgressing = numProgressing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - numProgressing = numProgressing + 1 - elseif condition.type == "ArtifactOutdated" and condition.status == "True" then - message = message .. " " .. condition.reason - elseif condition.type == "ArtifactInStorage" and condition.status == "True" then - numSucceeded = numSucceeded + 1 - end - end - if(numProgressing == 2) then - hs.message = message - hs.status = "Progressing" - return hs - elseif(numSucceeded == 2) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/health_test.yaml b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/health_test.yaml deleted file mode 100644 index c743c0b477d1a..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - # - healthStatus: - # status: Progressing - # message: Progressing - # inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: GitOperationFailed - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: Succeeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/degraded.yaml b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/degraded.yaml deleted file mode 100644 index 653e71945dbf5..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/degraded.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: GitRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m - url: https://github.com/stefanprodan/podinfo-faulty - ref: - branch: master -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'processing object: new generation 1 -> 2' - observedGeneration: 2 - reason: ProgressingWithRetry - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to checkout and determine revision: unable to list remote for - ''https://github.com/stefanprodan/podinfo-faulty'': authentication required' - observedGeneration: 2 - reason: GitOperationFailed - status: "False" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to checkout and determine revision: unable to list remote for - ''https://github.com/stefanprodan/podinfo-faulty'': authentication required' - observedGeneration: 2 - reason: GitOperationFailed - status: "True" - type: FetchFailed - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: stored artifact for revision 'master@sha1:08238eada746de8114efa36d36e2aa93bd76cfab' - observedGeneration: 1 - reason: Succeeded - status: "True" - type: ArtifactInStorage diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/healthy.yaml b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/healthy.yaml deleted file mode 100644 index 0bf0210615591..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/healthy.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: GitRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m - url: https://github.com/stefanprodan/podinfo - ref: - branch: master -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: stored artifact for revision 'master@sha1:08238eada746de8114efa36d36e2aa93bd76cfab' - observedGeneration: 1 - reason: Succeeded - status: "True" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: stored artifact for revision 'master@sha1:08238eada746de8114efa36d36e2aa93bd76cfab' - observedGeneration: 1 - reason: Succeeded - status: "True" - type: ArtifactInStorage diff --git a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/progressing.yaml b/resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/progressing.yaml deleted file mode 100644 index c1c99bf084246..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/GitRepository/testdata/progressing.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: GitRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m - url: https://github.com/stefanprodan/podinfo - ref: - branch: master -status: - conditions: [] diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/action_test.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/action_test.yaml deleted file mode 100644 index e5d34eb71f1bf..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/action_test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_helmchart.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_helmchart.yaml - result: - - name: reconcile - disabled: true - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_helmchart.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: reconcile - inputPath: testdata/initial_helmchart.yaml - expectedOutputPath: testdata/reconciled_helmchart.yaml -- action: suspend - inputPath: testdata/initial_helmchart.yaml - expectedOutputPath: testdata/suspended_helmchart.yaml -- action: resume - inputPath: testdata/suspended_helmchart.yaml - expectedOutputPath: testdata/resumed_helmchart.yaml diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/discovery.lua b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/discovery.lua deleted file mode 100644 index 9000998815515..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/discovery.lua +++ /dev/null @@ -1,18 +0,0 @@ -local actions = {} - -actions["reconcile"] = {["disabled"] = true} -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["reconcile"]["disabled"] = false - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/reconcile/action.lua b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/reconcile/action.lua deleted file mode 100644 index a534b36fb3ebc..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/reconcile/action.lua +++ /dev/null @@ -1,7 +0,0 @@ -local os = require("os") -if obj.metadata.annotations == nil then - obj.metadata.annotations = {} -end -obj.metadata.annotations["reconcile.fluxcd.io/requestedAt"] = "By Argo CD at: " .. os.date("!%Y-%m-%dT%X") - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/resume/action.lua b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/suspend/action.lua b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/initial_helmchart.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/initial_helmchart.yaml deleted file mode 100644 index da341e25a4d73..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/initial_helmchart.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmChart -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - chart: podinfo - reconcileStrategy: ChartVersion - sourceRef: - kind: HelmRepository - name: podinfo - version: '5.*' diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/reconciled_helmchart.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/reconciled_helmchart.yaml deleted file mode 100644 index e3d3e5fedeb34..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/reconciled_helmchart.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmChart -metadata: - name: podinfo - namespace: default - annotations: - reconcile.fluxcd.io/requestedAt: 'By Argo CD at: 0001-01-01T00:00:00' -spec: - interval: 5m0s - chart: podinfo - reconcileStrategy: ChartVersion - sourceRef: - kind: HelmRepository - name: podinfo - version: '5.*' diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/resumed_helmchart.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/resumed_helmchart.yaml deleted file mode 100644 index 9711ecdeee097..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/resumed_helmchart.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmChart -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - chart: podinfo - reconcileStrategy: ChartVersion - sourceRef: - kind: HelmRepository - name: podinfo - suspend: false - version: '5.*' diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/suspended_helmchart.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/suspended_helmchart.yaml deleted file mode 100644 index 76e63f010c577..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/actions/testdata/suspended_helmchart.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmChart -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - chart: podinfo - reconcileStrategy: ChartVersion - sourceRef: - kind: HelmRepository - name: podinfo - suspend: true - version: '5.*' diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/health.lua b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/health.lua deleted file mode 100644 index 9ad39cb708294..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/health.lua +++ /dev/null @@ -1,45 +0,0 @@ -local hs = {} -if obj.spec.suspend ~= nil and obj.spec.suspend == true then - hs.message = obj.kind .. " is suspended" - hs.status = "Suspended" - return hs -end -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numProgressing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "Unknown" then - numProgressing = numProgressing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - numProgressing = numProgressing + 1 - elseif condition.type == "ArtifactOutdated" and condition.status == "True" then - message = message .. " " .. condition.reason - elseif condition.type == "ArtifactInStorage" and condition.status == "True" then - numSucceeded = numSucceeded + 1 - end - end - if(numProgressing == 2) then - hs.message = message - hs.status = "Progressing" - return hs - elseif(numSucceeded == 2) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/health_test.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/health_test.yaml deleted file mode 100644 index 275b858d3c3c2..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - # - healthStatus: - # status: Progressing - # message: Progressing - # inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: SourceUnavailable - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: ChartPullSucceeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/degraded.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/degraded.yaml deleted file mode 100644 index 1875514c4a623..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/degraded.yaml +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmChart -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - chart: podinfo - reconcileStrategy: ChartVersion - sourceRef: - kind: HelmRepository - name: podinfo - version: '5.*' -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'processing object: new generation 1 -> 2' - observedGeneration: 2 - reason: ProgressingWithRetry - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to get source: HelmRepository.source.toolkit.fluxcd.io "podinfo-faulty" - not found' - observedGeneration: 2 - reason: SourceUnavailable - status: "False" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to get source: HelmRepository.source.toolkit.fluxcd.io "podinfo-faulty" - not found' - observedGeneration: 2 - reason: SourceUnavailable - status: "True" - type: FetchFailed - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: pulled 'podinfo' chart with version '5.2.1' - observedGeneration: 1 - reason: ChartPullSucceeded - status: "True" - type: ArtifactInStorage diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/healthy.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/healthy.yaml deleted file mode 100644 index 7b39f91436460..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/healthy.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmChart -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - chart: podinfo - reconcileStrategy: ChartVersion - sourceRef: - kind: HelmRepository - name: podinfo - version: '5.*' -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: pulled 'podinfo' chart with version '5.2.1' - observedGeneration: 1 - reason: ChartPullSucceeded - status: "True" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: pulled 'podinfo' chart with version '5.2.1' - observedGeneration: 1 - reason: ChartPullSucceeded - status: "True" - type: ArtifactInStorage diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/progressing.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/progressing.yaml deleted file mode 100644 index 038511b26308a..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmChart/testdata/progressing.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmChart -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - chart: podinfo - reconcileStrategy: ChartVersion - sourceRef: - kind: HelmRepository - name: podinfo - version: '5.*' -status: - conditions: [] - diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/action_test.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/action_test.yaml deleted file mode 100644 index c4d8d22e4ffce..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/action_test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_helmrepository.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_helmrepository.yaml - result: - - name: reconcile - disabled: true - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_helmrepository.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: reconcile - inputPath: testdata/initial_helmrepository.yaml - expectedOutputPath: testdata/reconciled_helmrepository.yaml -- action: suspend - inputPath: testdata/initial_helmrepository.yaml - expectedOutputPath: testdata/suspended_helmrepository.yaml -- action: resume - inputPath: testdata/suspended_helmrepository.yaml - expectedOutputPath: testdata/resumed_helmrepository.yaml diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/discovery.lua b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/discovery.lua deleted file mode 100644 index 9000998815515..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/discovery.lua +++ /dev/null @@ -1,18 +0,0 @@ -local actions = {} - -actions["reconcile"] = {["disabled"] = true} -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["reconcile"]["disabled"] = false - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/reconcile/action.lua b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/reconcile/action.lua deleted file mode 100644 index a534b36fb3ebc..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/reconcile/action.lua +++ /dev/null @@ -1,7 +0,0 @@ -local os = require("os") -if obj.metadata.annotations == nil then - obj.metadata.annotations = {} -end -obj.metadata.annotations["reconcile.fluxcd.io/requestedAt"] = "By Argo CD at: " .. os.date("!%Y-%m-%dT%X") - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/resume/action.lua b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/suspend/action.lua b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/initial_helmrepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/initial_helmrepository.yaml deleted file mode 100644 index e055d5aa054b3..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/initial_helmrepository.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - url: https://stefanprodan.github.io/podinfo diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/reconciled_helmrepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/reconciled_helmrepository.yaml deleted file mode 100644 index d879fada430fc..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/reconciled_helmrepository.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: podinfo - namespace: default - annotations: - reconcile.fluxcd.io/requestedAt: 'By Argo CD at: 0001-01-01T00:00:00' -spec: - interval: 5m0s - url: https://stefanprodan.github.io/podinfo diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/resumed_helmrepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/resumed_helmrepository.yaml deleted file mode 100644 index a68df7d1f41bd..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/resumed_helmrepository.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - suspend: false - url: https://stefanprodan.github.io/podinfo diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/suspended_helmrepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/suspended_helmrepository.yaml deleted file mode 100644 index fe69f4142ab6f..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/actions/testdata/suspended_helmrepository.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - suspend: true - url: https://stefanprodan.github.io/podinfo diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/health.lua b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/health.lua deleted file mode 100644 index 9ad39cb708294..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/health.lua +++ /dev/null @@ -1,45 +0,0 @@ -local hs = {} -if obj.spec.suspend ~= nil and obj.spec.suspend == true then - hs.message = obj.kind .. " is suspended" - hs.status = "Suspended" - return hs -end -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numProgressing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "Unknown" then - numProgressing = numProgressing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - numProgressing = numProgressing + 1 - elseif condition.type == "ArtifactOutdated" and condition.status == "True" then - message = message .. " " .. condition.reason - elseif condition.type == "ArtifactInStorage" and condition.status == "True" then - numSucceeded = numSucceeded + 1 - end - end - if(numProgressing == 2) then - hs.message = message - hs.status = "Progressing" - return hs - elseif(numSucceeded == 2) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/health_test.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/health_test.yaml deleted file mode 100644 index 2093ed4de070f..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - # - healthStatus: - # status: Progressing - # message: Progressing - # inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: Failed - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: Succeeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/degraded.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/degraded.yaml deleted file mode 100644 index ebeddb5e05bc7..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/degraded.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - url: https://stefanprodan.github.io/podinfo-faulty -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'processing object: new generation 1 -> 2' - observedGeneration: 2 - reason: ProgressingWithRetry - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to fetch Helm repository index: failed to cache index to temporary - file: failed to fetch https://stefanprodan.github.io/podinfo-faulty/index.yaml - : 404 Not Found' - observedGeneration: 2 - reason: Failed - status: "False" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to fetch Helm repository index: failed to cache index to temporary - file: failed to fetch https://stefanprodan.github.io/podinfo-faulty/index.yaml - : 404 Not Found' - observedGeneration: 2 - reason: Failed - status: "True" - type: FetchFailed - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'stored artifact: revision ''sha256:3dfe15d87f81dedc8ddaf116c7302892e54a0d8f269e35f65aaff9ac4d1b179c''' - observedGeneration: 1 - reason: Succeeded - status: "True" - type: ArtifactInStorage diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/healthy.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/healthy.yaml deleted file mode 100644 index b483b480478cc..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/healthy.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - url: https://stefanprodan.github.io/podinfo -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'stored artifact: revision ''sha256:3dfe15d87f81dedc8ddaf116c7302892e54a0d8f269e35f65aaff9ac4d1b179c''' - observedGeneration: 1 - reason: Succeeded - status: "True" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'stored artifact: revision ''sha256:3dfe15d87f81dedc8ddaf116c7302892e54a0d8f269e35f65aaff9ac4d1b179c''' - observedGeneration: 1 - reason: Succeeded - status: "True" - type: ArtifactInStorage diff --git a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/progressing.yaml b/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/progressing.yaml deleted file mode 100644 index a13de50e72dcb..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/HelmRepository/testdata/progressing.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - url: https://stefanprodan.github.io/podinfo -status: - conditions: [] - diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/action_test.yaml b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/action_test.yaml deleted file mode 100644 index 925f5dfa1877c..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/action_test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -discoveryTests: -- inputPath: testdata/initial_ocirepository.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -- inputPath: testdata/suspended_ocirepository.yaml - result: - - name: reconcile - disabled: true - - name: suspend - disabled: true - - name: resume - disabled: false -- inputPath: testdata/resumed_ocirepository.yaml - result: - - name: reconcile - disabled: false - - name: suspend - disabled: false - - name: resume - disabled: true -actionTests: -- action: reconcile - inputPath: testdata/initial_ocirepository.yaml - expectedOutputPath: testdata/reconciled_ocirepository.yaml -- action: suspend - inputPath: testdata/initial_ocirepository.yaml - expectedOutputPath: testdata/suspended_ocirepository.yaml -- action: resume - inputPath: testdata/suspended_ocirepository.yaml - expectedOutputPath: testdata/resumed_ocirepository.yaml diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/discovery.lua b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/discovery.lua deleted file mode 100644 index 9000998815515..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/discovery.lua +++ /dev/null @@ -1,18 +0,0 @@ -local actions = {} - -actions["reconcile"] = {["disabled"] = true} -actions["suspend"] = {["disabled"] = true} -actions["resume"] = {["disabled"] = true} - -local suspend = false -if obj.spec.suspend ~= nil then - suspend = obj.spec.suspend -end -if suspend then - actions["resume"]["disabled"] = false -else - actions["reconcile"]["disabled"] = false - actions["suspend"]["disabled"] = false -end - -return actions diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/reconcile/action.lua b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/reconcile/action.lua deleted file mode 100644 index a534b36fb3ebc..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/reconcile/action.lua +++ /dev/null @@ -1,7 +0,0 @@ -local os = require("os") -if obj.metadata.annotations == nil then - obj.metadata.annotations = {} -end -obj.metadata.annotations["reconcile.fluxcd.io/requestedAt"] = "By Argo CD at: " .. os.date("!%Y-%m-%dT%X") - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/resume/action.lua b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/resume/action.lua deleted file mode 100644 index cb41993777699..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/resume/action.lua +++ /dev/null @@ -1,5 +0,0 @@ -if obj.spec.suspend ~= nil and obj.spec.suspend then - obj.spec.suspend = false -end - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/suspend/action.lua b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/suspend/action.lua deleted file mode 100644 index 1a338b2ad1361..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/suspend/action.lua +++ /dev/null @@ -1,3 +0,0 @@ -obj.spec.suspend = true - -return obj diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/initial_ocirepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/initial_ocirepository.yaml deleted file mode 100644 index cd33f5200ee82..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/initial_ocirepository.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: OCIRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - url: oci://ghcr.io/stefanprodan/manifests/podinfo - ref: - tag: latest diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/reconciled_ocirepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/reconciled_ocirepository.yaml deleted file mode 100644 index b2df7fab875f3..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/reconciled_ocirepository.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: OCIRepository -metadata: - name: podinfo - namespace: default - annotations: - reconcile.fluxcd.io/requestedAt: 'By Argo CD at: 0001-01-01T00:00:00' -spec: - interval: 5m0s - url: oci://ghcr.io/stefanprodan/manifests/podinfo - ref: - tag: latest diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/resumed_ocirepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/resumed_ocirepository.yaml deleted file mode 100644 index 5a0ffad096e3c..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/resumed_ocirepository.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: OCIRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - url: oci://ghcr.io/stefanprodan/manifests/podinfo - ref: - tag: latest - suspend: false diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/suspended_ocirepository.yaml b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/suspended_ocirepository.yaml deleted file mode 100644 index 3fa77a4980317..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/actions/testdata/suspended_ocirepository.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: OCIRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - url: oci://ghcr.io/stefanprodan/manifests/podinfo - ref: - tag: latest - suspend: true diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/health.lua b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/health.lua deleted file mode 100644 index 9ad39cb708294..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/health.lua +++ /dev/null @@ -1,45 +0,0 @@ -local hs = {} -if obj.spec.suspend ~= nil and obj.spec.suspend == true then - hs.message = obj.kind .. " is suspended" - hs.status = "Suspended" - return hs -end -if obj.status ~= nil then - if obj.status.conditions ~= nil then - local numProgressing = 0 - local numSucceeded = 0 - local message = "" - for _, condition in ipairs(obj.status.conditions) do - if condition.type == "Ready" then - if condition.status == "True" then - numSucceeded = numSucceeded + 1 - elseif condition.status == "Unknown" then - numProgressing = numProgressing + 1 - end - message = condition.reason - elseif condition.type == "Reconciling" and condition.status == "True" then - numProgressing = numProgressing + 1 - elseif condition.type == "ArtifactOutdated" and condition.status == "True" then - message = message .. " " .. condition.reason - elseif condition.type == "ArtifactInStorage" and condition.status == "True" then - numSucceeded = numSucceeded + 1 - end - end - if(numProgressing == 2) then - hs.message = message - hs.status = "Progressing" - return hs - elseif(numSucceeded == 2) then - hs.message = message - hs.status = "Healthy" - return hs - else - hs.message = message - hs.status = "Degraded" - return hs - end - end -end -hs.message = "Status unknown" -hs.status = "Progressing" -return hs diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/health_test.yaml b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/health_test.yaml deleted file mode 100644 index 04adef01baa8b..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/health_test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -tests: - # - healthStatus: - # status: Progressing - # message: Progressing - # inputPath: testdata/progressing.yaml - - healthStatus: - status: Degraded - message: OCIArtifactPullFailed - inputPath: testdata/degraded.yaml - - healthStatus: - status: Healthy - message: Succeeded - inputPath: testdata/healthy.yaml diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/degraded.yaml b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/degraded.yaml deleted file mode 100644 index 9c91459ee2a01..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/degraded.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: OCIRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - url: oci://ghcr.io/stefanprodan/manifests/podinfo - ref: - tag: latest -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'processing object: new generation 1 -> 2' - observedGeneration: 2 - reason: Progressing - status: "True" - type: Reconciling - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to determine artifact digest: GET https://ghcr.io/token?scope=repository%!!(MISSING)A(MISSING)stefanprodan%!!(MISSING)F(MISSING)manifests%!!(MISSING)F(MISSING)podinfo-faulty%!!(MISSING)A(MISSING)pull&service=ghcr.io: - DENIED: requested access to the resource is denied' - observedGeneration: 2 - reason: OCIArtifactPullFailed - status: "False" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: 'failed to determine artifact digest: GET https://ghcr.io/token?scope=repository%!A(MISSING)stefanprodan%!F(MISSING)manifests%!F(MISSING)podinfo-faulty%!A(MISSING)pull&service=ghcr.io: - DENIED: requested access to the resource is denied' - observedGeneration: 2 - reason: OCIArtifactPullFailed - status: "True" - type: FetchFailed - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: stored artifact for digest 'latest@sha256:f74fa29c9ebfc7f55b0d829166812ce03c9e3951ab16954863cef1d12837c7a5' - observedGeneration: 1 - reason: Succeeded - status: "True" - type: ArtifactInStorage diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/healthy.yaml b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/healthy.yaml deleted file mode 100644 index e20aa0f1963fc..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/healthy.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: OCIRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - url: oci://ghcr.io/stefanprodan/manifests/podinfo - ref: - tag: latest -status: - conditions: - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: stored artifact for digest 'latest@sha256:f74fa29c9ebfc7f55b0d829166812ce03c9e3951ab16954863cef1d12837c7a5' - observedGeneration: 1 - reason: Succeeded - status: "True" - type: Ready - - lastTransitionTime: "2024-07-16T12:00:00Z" - message: stored artifact for digest 'latest@sha256:f74fa29c9ebfc7f55b0d829166812ce03c9e3951ab16954863cef1d12837c7a5' - observedGeneration: 1 - reason: Succeeded - status: "True" - type: ArtifactInStorage diff --git a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/progressing.yaml b/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/progressing.yaml deleted file mode 100644 index b6635496469d6..0000000000000 --- a/resource_customizations/source.toolkit.fluxcd.io/OCIRepository/testdata/progressing.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: OCIRepository -metadata: - name: podinfo - namespace: default -spec: - interval: 5m0s - url: oci://ghcr.io/stefanprodan/manifests/podinfo - ref: - tag: latest -status: - conditions: [] diff --git a/server/account/account.go b/server/account/account.go index 541401a731022..502cd8693e11c 100644 --- a/server/account/account.go +++ b/server/account/account.go @@ -48,7 +48,7 @@ func (s *Server) UpdatePassword(ctx context.Context, q *account.UpdatePasswordRe // assuming user is trying to update someone else if username is different or issuer is not Argo CD if updatedUsername != username || issuer != session.SessionManagerClaimsIssuer { if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceAccounts, rbacpolicy.ActionUpdate, q.Name); err != nil { - return nil, fmt.Errorf("permission denied: %w", err) + return nil, err } } @@ -70,22 +70,22 @@ func (s *Server) UpdatePassword(ctx context.Context, q *account.UpdatePasswordRe iat, err := session.Iat(ctx) if err != nil { - return nil, fmt.Errorf("failed to get issue time: %w", err) + return nil, err } if time.Since(iat) > common.ChangePasswordSSOTokenMaxAge { return nil, errors.New("SSO token is too old. Please use 'argocd relogin' to get a new token.") } } - // Need to validate password complexity with regular expression + //Need to validate password complexity with regular expression passwordPattern, err := s.settingsMgr.GetPasswordPattern() if err != nil { - return nil, fmt.Errorf("failed to get password pattern: %w", err) + return nil, err } validPasswordRegexp, err := regexp.Compile(passwordPattern) if err != nil { - return nil, fmt.Errorf("failed to compile password regex: %w", err) + return nil, err } if !validPasswordRegexp.Match([]byte(q.NewPassword)) { @@ -95,7 +95,7 @@ func (s *Server) UpdatePassword(ctx context.Context, q *account.UpdatePasswordRe hashedPassword, err := password.HashPassword(q.NewPassword) if err != nil { - return nil, fmt.Errorf("failed to hash password: %w", err) + return nil, err } err = s.settingsMgr.UpdateAccount(updatedUsername, func(acc *settings.Account) error { @@ -104,8 +104,9 @@ func (s *Server) UpdatePassword(ctx context.Context, q *account.UpdatePasswordRe acc.PasswordMtime = &now return nil }) + if err != nil { - return nil, fmt.Errorf("failed to update account password: %w", err) + return nil, err } if updatedUsername == username { @@ -114,6 +115,7 @@ func (s *Server) UpdatePassword(ctx context.Context, q *account.UpdatePasswordRe log.Infof("user '%s' updated password of user '%s'", username, updatedUsername) } return &account.UpdatePasswordResponse{}, nil + } // CanI checks if the current account has permission to perform an action @@ -132,7 +134,7 @@ func (s *Server) CanI(ctx context.Context, r *account.CanIRequest) (*account.Can if r.Resource == "logs" { serverRBACLogEnforceEnable, err := s.settingsMgr.GetServerRBACLogEnforceEnable() if err != nil { - return nil, fmt.Errorf("failed to get server RBAC log enforcement setting: %w", err) + return nil, err } if !serverRBACLogEnforceEnable { @@ -174,7 +176,7 @@ func (s *Server) ensureHasAccountPermission(ctx context.Context, action string, return nil } if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceAccounts, action, account); err != nil { - return fmt.Errorf("permission denied for account %s with action %s: %w", account, action, err) + return err } return nil } @@ -184,7 +186,7 @@ func (s *Server) ListAccounts(ctx context.Context, r *account.ListAccountRequest resp := account.AccountsList{} accounts, err := s.settingsMgr.GetAccounts() if err != nil { - return nil, fmt.Errorf("failed to get accounts: %w", err) + return nil, err } for name, a := range accounts { if err := s.ensureHasAccountPermission(ctx, rbacpolicy.ActionGet, name); err == nil { @@ -200,11 +202,11 @@ func (s *Server) ListAccounts(ctx context.Context, r *account.ListAccountRequest // GetAccount returns an account func (s *Server) GetAccount(ctx context.Context, r *account.GetAccountRequest) (*account.Account, error) { if err := s.ensureHasAccountPermission(ctx, rbacpolicy.ActionGet, r.Name); err != nil { - return nil, fmt.Errorf("permission denied to get account %s: %w", r.Name, err) + return nil, err } a, err := s.settingsMgr.GetAccount(r.Name) if err != nil { - return nil, fmt.Errorf("failed to get account %s: %w", r.Name, err) + return nil, err } return toApiAccount(r.Name, *a), nil } @@ -212,14 +214,14 @@ func (s *Server) GetAccount(ctx context.Context, r *account.GetAccountRequest) ( // CreateToken creates a token func (s *Server) CreateToken(ctx context.Context, r *account.CreateTokenRequest) (*account.CreateTokenResponse, error) { if err := s.ensureHasAccountPermission(ctx, rbacpolicy.ActionUpdate, r.Name); err != nil { - return nil, fmt.Errorf("permission denied to create token for account %s: %w", r.Name, err) + return nil, err } id := r.Id if id == "" { uniqueId, err := uuid.NewRandom() if err != nil { - return nil, fmt.Errorf("failed to generate unique ID: %w", err) + return nil, err } id = uniqueId.String() } @@ -252,7 +254,7 @@ func (s *Server) CreateToken(ctx context.Context, r *account.CreateTokenRequest) return nil }) if err != nil { - return nil, fmt.Errorf("failed to update account with new token: %w", err) + return nil, err } return &account.CreateTokenResponse{Token: tokenString}, nil } @@ -260,7 +262,7 @@ func (s *Server) CreateToken(ctx context.Context, r *account.CreateTokenRequest) // DeleteToken deletes a token func (s *Server) DeleteToken(ctx context.Context, r *account.DeleteTokenRequest) (*account.EmptyResponse, error) { if err := s.ensureHasAccountPermission(ctx, rbacpolicy.ActionUpdate, r.Name); err != nil { - return nil, fmt.Errorf("permission denied to delete account %s: %w", r.Name, err) + return nil, err } err := s.settingsMgr.UpdateAccount(r.Name, func(account *settings.Account) error { @@ -271,7 +273,7 @@ func (s *Server) DeleteToken(ctx context.Context, r *account.DeleteTokenRequest) return status.Errorf(codes.NotFound, "token with id '%s' does not exist", r.Id) }) if err != nil { - return nil, fmt.Errorf("failed to delete account %s: %w", r.Name, err) + return nil, err } return &account.EmptyResponse{}, nil } diff --git a/server/account/account_test.go b/server/account/account_test.go index 03290ad3692c4..d65c2e925b63d 100644 --- a/server/account/account_test.go +++ b/server/account/account_test.go @@ -7,7 +7,6 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" v1 "k8s.io/api/core/v1" @@ -83,7 +82,7 @@ func getAdminAccount(mgr *settings.SettingsManager) (*settings.Account, error) { func adminContext(ctx context.Context) context.Context { // nolint:staticcheck - return context.WithValue(ctx, "claims", &jwt.RegisteredClaims{Subject: "admin", Issuer: sessionutil.SessionManagerClaimsIssuer}) + return context.WithValue(ctx, "claims", &jwt.StandardClaims{Subject: "admin", Issuer: sessionutil.SessionManagerClaimsIssuer}) } func ssoAdminContext(ctx context.Context, iat time.Time) context.Context { @@ -110,33 +109,33 @@ func TestUpdatePassword(t *testing.T) { // ensure password is not allowed to be updated if given bad password _, err = accountServer.UpdatePassword(ctx, &account.UpdatePasswordRequest{CurrentPassword: "badpassword", NewPassword: "newpassword"}) - require.Error(t, err) - require.NoError(t, accountServer.sessionMgr.VerifyUsernamePassword("admin", "oldpassword")) - require.Error(t, accountServer.sessionMgr.VerifyUsernamePassword("admin", "newpassword")) + assert.Error(t, err) + assert.NoError(t, accountServer.sessionMgr.VerifyUsernamePassword("admin", "oldpassword")) + assert.Error(t, accountServer.sessionMgr.VerifyUsernamePassword("admin", "newpassword")) // verify old password works _, err = sessionServer.Create(ctx, &sessionpkg.SessionCreateRequest{Username: "admin", Password: "oldpassword"}) - require.NoError(t, err) + assert.NoError(t, err) // verify new password doesn't _, err = sessionServer.Create(ctx, &sessionpkg.SessionCreateRequest{Username: "admin", Password: "newpassword"}) - require.Error(t, err) + assert.Error(t, err) // ensure password can be updated with valid password and immediately be used adminAccount, err := getAdminAccount(accountServer.settingsMgr) - require.NoError(t, err) + assert.NoError(t, err) prevHash := adminAccount.PasswordHash _, err = accountServer.UpdatePassword(ctx, &account.UpdatePasswordRequest{CurrentPassword: "oldpassword", NewPassword: "newpassword"}) - require.NoError(t, err) + assert.NoError(t, err) adminAccount, err = getAdminAccount(accountServer.settingsMgr) - require.NoError(t, err) + assert.NoError(t, err) assert.NotEqual(t, prevHash, adminAccount.PasswordHash) - require.NoError(t, accountServer.sessionMgr.VerifyUsernamePassword("admin", "newpassword")) - require.Error(t, accountServer.sessionMgr.VerifyUsernamePassword("admin", "oldpassword")) + assert.NoError(t, accountServer.sessionMgr.VerifyUsernamePassword("admin", "newpassword")) + assert.Error(t, accountServer.sessionMgr.VerifyUsernamePassword("admin", "oldpassword")) // verify old password is invalid _, err = sessionServer.Create(ctx, &sessionpkg.SessionCreateRequest{Username: "admin", Password: "oldpassword"}) - require.Error(t, err) + assert.Error(t, err) // verify new password works _, err = sessionServer.Create(ctx, &sessionpkg.SessionCreateRequest{Username: "admin", Password: "newpassword"}) - require.NoError(t, err) + assert.NoError(t, err) } func TestUpdatePassword_AdminUpdatesAnotherUser(t *testing.T) { @@ -146,10 +145,10 @@ func TestUpdatePassword_AdminUpdatesAnotherUser(t *testing.T) { ctx := adminContext(context.Background()) _, err := accountServer.UpdatePassword(ctx, &account.UpdatePasswordRequest{CurrentPassword: "oldpassword", NewPassword: "newpassword", Name: "anotherUser"}) - require.NoError(t, err) + assert.NoError(t, err) _, err = sessionServer.Create(ctx, &sessionpkg.SessionCreateRequest{Username: "anotherUser", Password: "newpassword"}) - require.NoError(t, err) + assert.NoError(t, err) } func TestUpdatePassword_DoesNotHavePermissions(t *testing.T) { @@ -163,7 +162,7 @@ func TestUpdatePassword_DoesNotHavePermissions(t *testing.T) { }) ctx := adminContext(context.Background()) _, err := accountServer.UpdatePassword(ctx, &account.UpdatePasswordRequest{CurrentPassword: "oldpassword", NewPassword: "newpassword", Name: "anotherUser"}) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "permission denied") }) @@ -171,7 +170,7 @@ func TestUpdatePassword_DoesNotHavePermissions(t *testing.T) { accountServer, _ := newTestAccountServerExt(context.Background(), enforcer) ctx := ssoAdminContext(context.Background(), time.Now()) _, err := accountServer.UpdatePassword(ctx, &account.UpdatePasswordRequest{CurrentPassword: "oldpassword", NewPassword: "newpassword", Name: "admin"}) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "permission denied") }) } @@ -182,7 +181,7 @@ func TestUpdatePassword_ProjectToken(t *testing.T) { }) ctx := projTokenContext(context.Background()) _, err := accountServer.UpdatePassword(ctx, &account.UpdatePasswordRequest{CurrentPassword: "oldpassword", NewPassword: "newpassword"}) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "password can only be changed for local users") } @@ -193,7 +192,7 @@ func TestUpdatePassword_OldSSOToken(t *testing.T) { ctx := ssoAdminContext(context.Background(), time.Now().Add(-2*common.ChangePasswordSSOTokenMaxAge)) _, err := accountServer.UpdatePassword(ctx, &account.UpdatePasswordRequest{CurrentPassword: "oldpassword", NewPassword: "newpassword", Name: "anotherUser"}) - require.Error(t, err) + assert.Error(t, err) } func TestUpdatePassword_SSOUserUpdatesAnotherUser(t *testing.T) { @@ -203,10 +202,10 @@ func TestUpdatePassword_SSOUserUpdatesAnotherUser(t *testing.T) { ctx := ssoAdminContext(context.Background(), time.Now()) _, err := accountServer.UpdatePassword(ctx, &account.UpdatePasswordRequest{CurrentPassword: "oldpassword", NewPassword: "newpassword", Name: "anotherUser"}) - require.NoError(t, err) + assert.NoError(t, err) _, err = sessionServer.Create(ctx, &sessionpkg.SessionCreateRequest{Username: "anotherUser", Password: "newpassword"}) - require.NoError(t, err) + assert.NoError(t, err) } func TestListAccounts_NoAccountsConfigured(t *testing.T) { @@ -214,7 +213,7 @@ func TestListAccounts_NoAccountsConfigured(t *testing.T) { accountServer, _ := newTestAccountServer(ctx) resp, err := accountServer.ListAccounts(ctx, &account.ListAccountRequest{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, resp.Items, 1) } @@ -227,7 +226,7 @@ func TestListAccounts_AccountsAreConfigured(t *testing.T) { }) resp, err := accountServer.ListAccounts(ctx, &account.ListAccountRequest{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, resp.Items, 3) assert.ElementsMatch(t, []*account.Account{ {Name: "admin", Capabilities: []string{"login"}, Enabled: true}, @@ -244,15 +243,15 @@ func TestGetAccount(t *testing.T) { t.Run("ExistingAccount", func(t *testing.T) { acc, err := accountServer.GetAccount(ctx, &account.GetAccountRequest{Name: "account1"}) - require.NoError(t, err) + assert.NoError(t, err) - assert.Equal(t, "account1", acc.Name) + assert.Equal(t, acc.Name, "account1") }) t.Run("NonExistingAccount", func(t *testing.T) { _, err := accountServer.GetAccount(ctx, &account.GetAccountRequest{Name: "bad-name"}) - require.Error(t, err) - assert.Equal(t, codes.NotFound, status.Code(err)) + assert.Error(t, err) + assert.Equal(t, status.Code(err), codes.NotFound) }) } @@ -263,10 +262,10 @@ func TestCreateToken_SuccessfullyCreated(t *testing.T) { }) _, err := accountServer.CreateToken(ctx, &account.CreateTokenRequest{Name: "account1"}) - require.NoError(t, err) + assert.NoError(t, err) acc, err := accountServer.GetAccount(ctx, &account.GetAccountRequest{Name: "account1"}) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, acc.Tokens, 1) } @@ -278,7 +277,7 @@ func TestCreateToken_DoesNotHaveCapability(t *testing.T) { }) _, err := accountServer.CreateToken(ctx, &account.CreateTokenRequest{Name: "account1"}) - require.Error(t, err) + assert.Error(t, err) } func TestCreateToken_UserSpecifiedID(t *testing.T) { @@ -288,12 +287,13 @@ func TestCreateToken_UserSpecifiedID(t *testing.T) { }) _, err := accountServer.CreateToken(ctx, &account.CreateTokenRequest{Name: "account1", Id: "test"}) - require.NoError(t, err) + assert.NoError(t, err) _, err = accountServer.CreateToken(ctx, &account.CreateTokenRequest{Name: "account1", Id: "test"}) - require.Error(t, err) - assert.Contains(t, err.Error(), "failed to update account with new token:") - assert.Contains(t, err.Error(), "account already has token with id 'test'") + if !assert.Error(t, err) { + return + } + assert.Contains(t, "account already has token with id 'test'", err.Error()) } func TestDeleteToken_SuccessfullyRemoved(t *testing.T) { @@ -304,21 +304,22 @@ func TestDeleteToken_SuccessfullyRemoved(t *testing.T) { }) _, err := accountServer.DeleteToken(ctx, &account.DeleteTokenRequest{Name: "account1", Id: "123"}) - require.NoError(t, err) + assert.NoError(t, err) acc, err := accountServer.GetAccount(ctx, &account.GetAccountRequest{Name: "account1"}) - require.NoError(t, err) + assert.NoError(t, err) - assert.Empty(t, acc.Tokens) + assert.Len(t, acc.Tokens, 0) } func TestCanI_GetLogsAllowNoSwitch(t *testing.T) { + accountServer, _ := newTestAccountServer(context.Background(), func(cm *v1.ConfigMap, secret *v1.Secret) { }) ctx := projTokenContext(context.Background()) resp, err := accountServer.CanI(ctx, &account.CanIRequest{Resource: "logs", Action: "get", Subresource: ""}) - require.NoError(t, err) + assert.NoError(t, err) assert.EqualValues(t, "yes", resp.Value) } @@ -333,28 +334,30 @@ func TestCanI_GetLogsDenySwitchOn(t *testing.T) { ctx := projTokenContext(context.Background()) resp, err := accountServer.CanI(ctx, &account.CanIRequest{Resource: "logs", Action: "get", Subresource: "*/*"}) - require.NoError(t, err) + assert.NoError(t, err) assert.EqualValues(t, "no", resp.Value) } func TestCanI_GetLogsAllowSwitchOn(t *testing.T) { + accountServer, _ := newTestAccountServer(context.Background(), func(cm *v1.ConfigMap, secret *v1.Secret) { cm.Data["server.rbac.log.enforce.enable"] = "true" }) ctx := projTokenContext(context.Background()) resp, err := accountServer.CanI(ctx, &account.CanIRequest{Resource: "logs", Action: "get", Subresource: ""}) - require.NoError(t, err) + assert.NoError(t, err) assert.EqualValues(t, "yes", resp.Value) } func TestCanI_GetLogsAllowSwitchOff(t *testing.T) { + accountServer, _ := newTestAccountServer(context.Background(), func(cm *v1.ConfigMap, secret *v1.Secret) { cm.Data["server.rbac.log.enforce.enable"] = "false" }) ctx := projTokenContext(context.Background()) resp, err := accountServer.CanI(ctx, &account.CanIRequest{Resource: "logs", Action: "get", Subresource: ""}) - require.NoError(t, err) + assert.NoError(t, err) assert.EqualValues(t, "yes", resp.Value) } diff --git a/server/application/application.go b/server/application/application.go index 08a8ab19d008b..5bd47adc359ed 100644 --- a/server/application/application.go +++ b/server/application/application.go @@ -34,11 +34,10 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" argocommon "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" - "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" applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" @@ -66,6 +65,7 @@ import ( type AppResourceTreeFn func(ctx context.Context, app *appv1.Application) (*appv1.ApplicationTree, error) const ( + maxPodLogsToRender = 10 backgroundPropagationPolicy string = "background" foregroundPropagationPolicy string = "foreground" ) @@ -112,7 +112,6 @@ func NewServer( settingsMgr *settings.SettingsManager, projInformer cache.SharedIndexInformer, enabledNamespaces []string, - enableK8sEvent []string, ) (application.ApplicationServiceServer, AppResourceTreeFn) { if appBroadcaster == nil { appBroadcaster = &broadcasterHandler{} @@ -134,7 +133,7 @@ func NewServer( kubectl: kubectl, enf: enf, projectLock: projectLock, - auditLogger: argo.NewAuditLogger(namespace, kubeclientset, "argocd-server", enableK8sEvent), + auditLogger: argo.NewAuditLogger(namespace, kubeclientset, "argocd-server"), settingsMgr: settingsMgr, projInformer: projInformer, enabledNamespaces: enabledNamespaces, @@ -182,7 +181,7 @@ func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespa 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, nil, status.Error(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error()) + return nil, 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. @@ -204,7 +203,7 @@ func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespa // 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, nil, status.Error(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error()) + return nil, 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. @@ -221,7 +220,7 @@ func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespa }).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, nil, status.Error(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error()) + return nil, nil, status.Errorf(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error()) } // Get the app's associated project, and make sure all project restrictions are enforced. proj, err := s.getAppProject(ctx, a, logCtx) @@ -390,19 +389,33 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq return updated, nil } -func (s *Server) queryRepoServer(ctx context.Context, proj *appv1.AppProject, action func( +func (s *Server) queryRepoServer(ctx context.Context, a *appv1.Application, proj *appv1.AppProject, action func( client apiclient.RepoServerServiceClient, + repo *appv1.Repository, helmRepos []*appv1.Repository, helmCreds []*appv1.RepoCreds, helmOptions *appv1.HelmOptions, + kustomizeOptions *appv1.KustomizeOptions, enabledSourceTypes map[string]bool, -) error, -) error { +) error) error { + closer, client, err := s.repoClientset.NewRepoServerClient() if err != nil { return fmt.Errorf("error creating repo server client: %w", err) } defer ioutil.Close(closer) + repo, err := s.db.GetRepository(ctx, a.Spec.GetSource().RepoURL) + if err != nil { + return fmt.Errorf("error getting repository: %w", err) + } + kustomizeSettings, err := s.settingsMgr.GetKustomizeSettings() + if err != nil { + return fmt.Errorf("error getting kustomize settings: %w", err) + } + kustomizeOptions, err := kustomizeSettings.GetOptions(a.Spec.GetSource()) + if err != nil { + return fmt.Errorf("error getting kustomize settings options: %w", err) + } helmRepos, err := s.db.ListHelmRepositories(ctx) if err != nil { @@ -429,7 +442,7 @@ func (s *Server) queryRepoServer(ctx context.Context, proj *appv1.AppProject, ac if err != nil { return fmt.Errorf("error getting settings enabled source types: %w", err) } - return action(client, permittedHelmRepos, permittedHelmCredentials, helmOptions, enabledSourceTypes) + return action(client, repo, permittedHelmRepos, permittedHelmCredentials, helmOptions, kustomizeOptions, enabledSourceTypes) } // GetManifests returns application manifests @@ -442,14 +455,19 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan return nil, err } + source := a.Spec.GetSource() + if !s.isNamespaceEnabled(a.Namespace) { return nil, security.NamespaceNotPermittedError(a.Namespace) } - manifestInfos := make([]*apiclient.ManifestResponse, 0) - err = s.queryRepoServer(ctx, proj, func( - client apiclient.RepoServerServiceClient, helmRepos []*appv1.Repository, helmCreds []*appv1.RepoCreds, helmOptions *appv1.HelmOptions, enableGenerateManifests map[string]bool, - ) error { + var manifestInfo *apiclient.ManifestResponse + err = s.queryRepoServer(ctx, a, proj, func( + client apiclient.RepoServerServiceClient, repo *appv1.Repository, helmRepos []*appv1.Repository, helmCreds []*appv1.RepoCreds, helmOptions *appv1.HelmOptions, kustomizeOptions *appv1.KustomizeOptions, enableGenerateManifests map[string]bool) error { + revision := source.TargetRevision + if q.GetRevision() != "" { + revision = q.GetRevision() + } appInstanceLabelKey, err := s.settingsMgr.GetAppInstanceLabelKey() if err != nil { return fmt.Errorf("error getting app instance label key from settings: %w", err) @@ -470,108 +488,60 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan return fmt.Errorf("error getting API resources: %w", err) } - sources := make([]appv1.ApplicationSource, 0) - appSpec := a.Spec.DeepCopy() - if a.Spec.HasMultipleSources() { - numOfSources := int64(len(a.Spec.GetSources())) - for i, pos := range q.SourcePositions { - if pos <= 0 || pos > numOfSources { - return fmt.Errorf("source position is out of range") - } - appSpec.Sources[pos-1].TargetRevision = q.Revisions[i] - } - sources = appSpec.GetSources() - } else { - source := a.Spec.GetSource() - if q.GetRevision() != "" { - source.TargetRevision = q.GetRevision() - } - sources = append(sources, source) - } - - // Store the map of all sources having ref field into a map for applications with sources field - refSources, err := argo.GetRefSources(context.Background(), sources, appSpec.Project, s.db.GetRepository, []string{}, false) + manifestInfo, err = client.GenerateManifest(ctx, &apiclient.ManifestRequest{ + Repo: repo, + Revision: revision, + AppLabelKey: appInstanceLabelKey, + AppName: a.InstanceName(s.ns), + Namespace: a.Spec.Destination.Namespace, + ApplicationSource: &source, + Repos: helmRepos, + KustomizeOptions: kustomizeOptions, + KubeVersion: serverVersion, + ApiVersions: argo.APIResourcesToStrings(apiResources, true), + HelmRepoCreds: helmCreds, + HelmOptions: helmOptions, + TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)), + EnabledSourceTypes: enableGenerateManifests, + ProjectName: proj.Name, + ProjectSourceRepos: proj.Spec.SourceRepos, + }) if err != nil { - return fmt.Errorf("failed to get ref sources: %w", err) - } - - for _, source := range sources { - repo, err := s.db.GetRepository(ctx, source.RepoURL, proj.Name) - if err != nil { - return fmt.Errorf("error getting repository: %w", err) - } - - kustomizeSettings, err := s.settingsMgr.GetKustomizeSettings() - if err != nil { - return fmt.Errorf("error getting kustomize settings: %w", err) - } - - kustomizeOptions, err := kustomizeSettings.GetOptions(source) - if err != nil { - return fmt.Errorf("error getting kustomize settings options: %w", err) - } - - manifestInfo, err := client.GenerateManifest(ctx, &apiclient.ManifestRequest{ - Repo: repo, - Revision: source.TargetRevision, - AppLabelKey: appInstanceLabelKey, - AppName: a.InstanceName(s.ns), - Namespace: a.Spec.Destination.Namespace, - ApplicationSource: &source, - Repos: helmRepos, - KustomizeOptions: kustomizeOptions, - KubeVersion: serverVersion, - ApiVersions: argo.APIResourcesToStrings(apiResources, true), - HelmRepoCreds: helmCreds, - HelmOptions: helmOptions, - TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)), - EnabledSourceTypes: enableGenerateManifests, - ProjectName: proj.Name, - ProjectSourceRepos: proj.Spec.SourceRepos, - HasMultipleSources: a.Spec.HasMultipleSources(), - RefSources: refSources, - AnnotationManifestGeneratePaths: a.GetAnnotation(v1alpha1.AnnotationKeyManifestGeneratePaths), - }) - if err != nil { - return fmt.Errorf("error generating manifests: %w", err) - } - manifestInfos = append(manifestInfos, manifestInfo) + return fmt.Errorf("error generating manifests: %w", err) } return nil }) + if err != nil { return nil, err } - manifests := &apiclient.ManifestResponse{} - for _, manifestInfo := range manifestInfos { - for i, manifest := range manifestInfo.Manifests { - obj := &unstructured.Unstructured{} - err = json.Unmarshal([]byte(manifest), obj) + for i, manifest := range manifestInfo.Manifests { + obj := &unstructured.Unstructured{} + err = json.Unmarshal([]byte(manifest), obj) + if err != nil { + return nil, fmt.Errorf("error unmarshaling manifest into unstructured: %w", err) + } + if obj.GetKind() == kube.SecretKind && obj.GroupVersionKind().Group == "" { + obj, _, err = diff.HideSecretData(obj, nil) if err != nil { - return nil, fmt.Errorf("error unmarshaling manifest into unstructured: %w", err) + return nil, fmt.Errorf("error hiding secret data: %w", err) } - if obj.GetKind() == kube.SecretKind && obj.GroupVersionKind().Group == "" { - obj, _, err = diff.HideSecretData(obj, nil) - if err != nil { - return nil, fmt.Errorf("error hiding secret data: %w", err) - } - data, err := json.Marshal(obj) - if err != nil { - return nil, fmt.Errorf("error marshaling manifest: %w", err) - } - manifestInfo.Manifests[i] = string(data) + data, err := json.Marshal(obj) + if err != nil { + return nil, fmt.Errorf("error marshaling manifest: %w", err) } + manifestInfo.Manifests[i] = string(data) } - manifests.Manifests = append(manifests.Manifests, manifestInfo.Manifests...) } - return manifests, nil + return manifestInfo, nil } func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_GetManifestsWithFilesServer) error { ctx := stream.Context() query, err := manifeststream.ReceiveApplicationManifestQueryWithFiles(stream) + if err != nil { return fmt.Errorf("error getting query: %w", err) } @@ -586,9 +556,9 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get } var manifestInfo *apiclient.ManifestResponse - err = s.queryRepoServer(ctx, proj, func( - client apiclient.RepoServerServiceClient, helmRepos []*appv1.Repository, helmCreds []*appv1.RepoCreds, helmOptions *appv1.HelmOptions, enableGenerateManifests map[string]bool, - ) error { + err = s.queryRepoServer(ctx, a, proj, func( + client apiclient.RepoServerServiceClient, repo *appv1.Repository, helmRepos []*appv1.Repository, helmCreds []*appv1.RepoCreds, helmOptions *appv1.HelmOptions, kustomizeOptions *appv1.KustomizeOptions, enableGenerateManifests map[string]bool) error { + appInstanceLabelKey, err := s.settingsMgr.GetAppInstanceLabelKey() if err != nil { return fmt.Errorf("error getting app instance label key from settings: %w", err) @@ -616,38 +586,23 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get return fmt.Errorf("error getting app project: %w", err) } - repo, err := s.db.GetRepository(ctx, a.Spec.GetSource().RepoURL, proj.Name) - if err != nil { - return fmt.Errorf("error getting repository: %w", err) - } - - kustomizeSettings, err := s.settingsMgr.GetKustomizeSettings() - if err != nil { - return fmt.Errorf("error getting kustomize settings: %w", err) - } - kustomizeOptions, err := kustomizeSettings.GetOptions(a.Spec.GetSource()) - if err != nil { - return fmt.Errorf("error getting kustomize settings options: %w", err) - } - req := &apiclient.ManifestRequest{ - Repo: repo, - Revision: source.TargetRevision, - AppLabelKey: appInstanceLabelKey, - AppName: a.Name, - Namespace: a.Spec.Destination.Namespace, - ApplicationSource: &source, - Repos: helmRepos, - KustomizeOptions: kustomizeOptions, - KubeVersion: serverVersion, - ApiVersions: argo.APIResourcesToStrings(apiResources, true), - HelmRepoCreds: helmCreds, - HelmOptions: helmOptions, - TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)), - EnabledSourceTypes: enableGenerateManifests, - ProjectName: proj.Name, - ProjectSourceRepos: proj.Spec.SourceRepos, - AnnotationManifestGeneratePaths: a.GetAnnotation(v1alpha1.AnnotationKeyManifestGeneratePaths), + Repo: repo, + Revision: source.TargetRevision, + AppLabelKey: appInstanceLabelKey, + AppName: a.Name, + Namespace: a.Spec.Destination.Namespace, + ApplicationSource: &source, + Repos: helmRepos, + KustomizeOptions: kustomizeOptions, + KubeVersion: serverVersion, + ApiVersions: argo.APIResourcesToStrings(apiResources, true), + HelmRepoCreds: helmCreds, + HelmOptions: helmOptions, + TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)), + EnabledSourceTypes: enableGenerateManifests, + ProjectName: proj.Name, + ProjectSourceRepos: proj.Spec.SourceRepos, } repoStreamClient, err := client.GenerateManifestWithFiles(stream.Context()) @@ -668,6 +623,7 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get manifestInfo = resp return nil }) + if err != nil { return err } @@ -742,27 +698,17 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app if refreshType == appv1.RefreshTypeHard { // force refresh cached application details - if err := s.queryRepoServer(ctx, proj, func( + if err := s.queryRepoServer(ctx, a, proj, func( client apiclient.RepoServerServiceClient, + repo *appv1.Repository, helmRepos []*appv1.Repository, _ []*appv1.RepoCreds, helmOptions *appv1.HelmOptions, + kustomizeOptions *appv1.KustomizeOptions, enabledSourceTypes map[string]bool, ) error { source := app.Spec.GetSource() - repo, err := s.db.GetRepository(ctx, a.Spec.GetSource().RepoURL, proj.Name) - if err != nil { - return fmt.Errorf("error getting repository: %w", err) - } - kustomizeSettings, err := s.settingsMgr.GetKustomizeSettings() - if err != nil { - return fmt.Errorf("error getting kustomize settings: %w", err) - } - kustomizeOptions, err := kustomizeSettings.GetOptions(a.Spec.GetSource()) - if err != nil { - return fmt.Errorf("error getting kustomize settings options: %w", err) - } - _, err = client.GetAppDetails(ctx, &apiclient.RepoServerAppDetailsQuery{ + _, err := client.GetAppDetails(ctx, &apiclient.RepoServerAppDetailsQuery{ Repo: repo, Source: &source, AppName: appName, @@ -1051,8 +997,7 @@ func (s *Server) getAppProject(ctx context.Context, a *appv1.Application, logCtx return nil, vagueError } - var applicationNotAllowedToUseProjectErr *appv1.ErrApplicationNotAllowedToUseProject - if errors.As(err, &applicationNotAllowedToUseProjectErr) { + if _, ok := err.(*appv1.ErrApplicationNotAllowedToUseProject); ok { logCtx.WithFields(map[string]interface{}{ "project": a.Spec.Project, argocommon.SecurityField: argocommon.SecurityMedium, @@ -1094,9 +1039,11 @@ func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteReq a.SetCascadedDeletion(policyFinalizer) patchFinalizer = true } - } else if a.CascadedDeletion() { - a.UnSetCascadedDeletion() - patchFinalizer = true + } else { + if a.CascadedDeletion() { + a.UnSetCascadedDeletion() + patchFinalizer = true + } } if patchFinalizer { @@ -1291,7 +1238,7 @@ func (s *Server) getApplicationClusterConfig(ctx context.Context, a *appv1.Appli // getCachedAppState loads the cached state and trigger app refresh if cache is missing func (s *Server) getCachedAppState(ctx context.Context, a *appv1.Application, getFromCache func() error) error { err := getFromCache() - if err != nil && errors.Is(err, servercache.ErrCacheMiss) { + if err != nil && err == servercache.ErrCacheMiss { conditions := a.Status.GetConditions(map[appv1.ApplicationConditionType]bool{ appv1.ApplicationConditionComparisonError: true, appv1.ApplicationConditionInvalidSpecError: true, @@ -1300,9 +1247,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: ptr.To(a.GetName()), - AppNamespace: ptr.To(a.GetNamespace()), - Refresh: ptr.To(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) @@ -1325,15 +1272,9 @@ func (s *Server) getAppResources(ctx context.Context, a *appv1.Application) (*ap func (s *Server) getAppLiveResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) { a, _, err := s.getApplicationEnforceRBACInformer(ctx, action, q.GetProject(), q.GetAppNamespace(), q.GetName()) - if err != nil && errors.Is(err, permissionDeniedErr) && (action == rbacpolicy.ActionDelete || action == rbacpolicy.ActionUpdate) { - // If users dont have permission on the whole applications, maybe they have fine-grained access to the specific resources - action = fmt.Sprintf("%s/%s/%s/%s/%s", action, q.GetGroup(), q.GetKind(), q.GetNamespace(), q.GetResourceName()) - a, _, err = s.getApplicationEnforceRBACInformer(ctx, action, q.GetProject(), q.GetAppNamespace(), q.GetName()) - } if 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) @@ -1498,12 +1439,8 @@ func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMe return nil, err } - source, err := getAppSourceBySourceIndexAndVersionId(a, q.SourceIndex, q.VersionId) - if err != nil { - return nil, fmt.Errorf("error getting app source by source index and version ID: %w", err) - } - - repo, err := s.db.GetRepository(ctx, source.RepoURL, proj.Name) + source := a.Spec.GetSource() + repo, err := s.db.GetRepository(ctx, source.RepoURL) if err != nil { return nil, fmt.Errorf("error getting repository by URL: %w", err) } @@ -1525,16 +1462,10 @@ func (s *Server) RevisionChartDetails(ctx context.Context, q *application.Revisi if err != nil { return nil, err } - - source, err := getAppSourceBySourceIndexAndVersionId(a, q.SourceIndex, q.VersionId) - if err != nil { - return nil, fmt.Errorf("error getting app source by source index and version ID: %w", err) + if a.Spec.Source.Chart == "" { + return nil, fmt.Errorf("no chart found for application: %v", a.QualifiedName()) } - - if source.Chart == "" { - return nil, fmt.Errorf("no chart found for application: %v", q.GetName()) - } - repo, err := s.db.GetRepository(ctx, source.RepoURL, a.Spec.Project) + repo, err := s.db.GetRepository(ctx, a.Spec.Source.RepoURL) if err != nil { return nil, fmt.Errorf("error getting repository by URL: %w", err) } @@ -1545,81 +1476,11 @@ func (s *Server) RevisionChartDetails(ctx context.Context, q *application.Revisi defer ioutil.Close(conn) return repoClient.GetRevisionChartDetails(ctx, &apiclient.RepoServerRevisionChartDetailsRequest{ Repo: repo, - Name: source.Chart, + Name: a.Spec.Source.Chart, Revision: q.GetRevision(), }) } -// getAppSourceBySourceIndexAndVersionId returns the source for a specific source index and version ID. Source index and -// version ID are optional. If the source index is not specified, it defaults to 0. If the version ID is not specified, -// we use the source(s) currently configured for the app. If the version ID is specified, we find the source for that -// version ID. If the version ID is not found, we return an error. If the source index is out of bounds for whichever -// source we choose (configured sources or sources for a specific version), we return an error. -func getAppSourceBySourceIndexAndVersionId(a *appv1.Application, sourceIndexMaybe *int32, versionIdMaybe *int32) (appv1.ApplicationSource, error) { - // Start with all the app's configured sources. - sources := a.Spec.GetSources() - - // If the user specified a version, get the sources for that version. If the version is not found, return an error. - if versionIdMaybe != nil { - versionId := int64(*versionIdMaybe) - var err error - sources, err = getSourcesByVersionId(a, versionId) - if err != nil { - return appv1.ApplicationSource{}, fmt.Errorf("error getting source by version ID: %w", err) - } - } - - // Start by assuming we want the first source. - sourceIndex := 0 - - // If the user specified a source index, use that instead. - if sourceIndexMaybe != nil { - sourceIndex = int(*sourceIndexMaybe) - if sourceIndex >= len(sources) { - if len(sources) == 1 { - return appv1.ApplicationSource{}, fmt.Errorf("source index %d not found because there is only 1 source", sourceIndex) - } - return appv1.ApplicationSource{}, fmt.Errorf("source index %d not found because there are only %d sources", sourceIndex, len(sources)) - } - } - - source := sources[sourceIndex] - - return source, nil -} - -// getRevisionHistoryByVersionId returns the revision history for a specific version ID. -// If the version ID is not found, it returns an empty revision history and false. -func getRevisionHistoryByVersionId(histories v1alpha1.RevisionHistories, versionId int64) (appv1.RevisionHistory, bool) { - for _, h := range histories { - if h.ID == versionId { - return h, true - } - } - return appv1.RevisionHistory{}, false -} - -// getSourcesByVersionId returns the sources for a specific version ID. If there is no history, it returns an error. -// If the version ID is not found, it returns an error. If the version ID is found, and there are multiple sources, -// it returns the sources for that version ID. If the version ID is found, and there is only one source, it returns -// a slice with just the single source. -func getSourcesByVersionId(a *appv1.Application, versionId int64) ([]appv1.ApplicationSource, error) { - if len(a.Status.History) == 0 { - return nil, fmt.Errorf("version ID %d not found because the app has no history", versionId) - } - - h, ok := getRevisionHistoryByVersionId(a.Status.History, versionId) - if !ok { - return nil, fmt.Errorf("revision history not found for version ID %d", versionId) - } - - if len(h.Sources) > 0 { - return h.Sources, nil - } - - return []v1alpha1.ApplicationSource{h.Source}, nil -} - func isMatchingResource(q *application.ResourcesQuery, key kube.ResourceKey) bool { return (q.GetName() == "" || q.GetName() == key.Name) && (q.GetNamespace() == "" || q.GetNamespace() == key.Namespace) && @@ -1660,15 +1521,15 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application. var sinceSeconds, tailLines *int64 if q.GetSinceSeconds() > 0 { - sinceSeconds = ptr.To(q.GetSinceSeconds()) + sinceSeconds = pointer.Int64(q.GetSinceSeconds()) } if q.GetTailLines() > 0 { - tailLines = ptr.To(q.GetTailLines()) + tailLines = pointer.Int64(q.GetTailLines()) } var untilTime *metav1.Time if q.GetUntilTime() != "" { if val, err := time.Parse(time.RFC3339Nano, q.GetUntilTime()); err != nil { - return fmt.Errorf("invalid untilTime parameter value: %w", err) + return fmt.Errorf("invalid untilTime parameter value: %v", err) } else { untilTimeVal := metav1.NewTime(val) untilTime = &untilTimeVal @@ -1726,13 +1587,8 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application. return nil } - maxPodLogsToRender, err := s.settingsMgr.GetMaxPodLogsToRender() - if err != nil { - return fmt.Errorf("error getting MaxPodLogsToRender config: %w", err) - } - - if int64(len(pods)) > maxPodLogsToRender { - return status.Error(codes.InvalidArgument, "max pods to view logs are reached. Please provide more granular query") + if len(pods) > maxPodLogsToRender { + return errors.New("Max pods to view logs are reached. Please provide more granular query.") } var streams []chan logEntry @@ -1784,10 +1640,10 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application. ts := metav1.NewTime(entry.timeStamp) if untilTime != nil && entry.timeStamp.After(untilTime.Time) { done <- ws.Send(&application.LogEntry{ - Last: ptr.To(true), + Last: pointer.Bool(true), PodName: &entry.podName, Content: &entry.line, - TimeStampStr: ptr.To(entry.timeStamp.Format(time.RFC3339Nano)), + TimeStampStr: pointer.String(entry.timeStamp.Format(time.RFC3339Nano)), TimeStamp: &ts, }) return @@ -1796,9 +1652,9 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application. if err := ws.Send(&application.LogEntry{ PodName: &entry.podName, Content: &entry.line, - TimeStampStr: ptr.To(entry.timeStamp.Format(time.RFC3339Nano)), + TimeStampStr: pointer.String(entry.timeStamp.Format(time.RFC3339Nano)), TimeStamp: &ts, - Last: ptr.To(false), + Last: pointer.Bool(false), }); err != nil { done <- err break @@ -1809,10 +1665,10 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application. now := time.Now() nowTS := metav1.NewTime(now) done <- ws.Send(&application.LogEntry{ - Last: ptr.To(true), - PodName: ptr.To(""), - Content: ptr.To(""), - TimeStampStr: ptr.To(now.Format(time.RFC3339Nano)), + Last: pointer.Bool(true), + PodName: pointer.String(""), + Content: pointer.String(""), + TimeStampStr: pointer.String(now.Format(time.RFC3339Nano)), TimeStamp: &nowTS, }) }() @@ -1897,6 +1753,8 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR return nil, err } + source := a.Spec.GetSource() + if syncReq.Manifests != nil { if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionOverride, a.RBACName(s.ns)); err != nil { return nil, err @@ -1908,10 +1766,14 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR if a.DeletionTimestamp != nil { return nil, status.Errorf(codes.FailedPrecondition, "application is deleting") } - - revision, displayRevision, sourceRevisions, displayRevisions, err := s.resolveSourceRevisions(ctx, a, syncReq) + 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) + } + } + revision, displayRevision, err := s.resolveRevision(ctx, a, syncReq) if err != nil { - return nil, err + return nil, status.Errorf(codes.FailedPrecondition, err.Error()) } var retry *appv1.RetryStrategy @@ -1949,8 +1811,6 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR SyncStrategy: syncReq.Strategy, Resources: resources, Manifests: syncReq.Manifests, - Sources: a.Spec.Sources, - Revisions: sourceRevisions, }, InitiatedBy: appv1.OperationInitiator{Username: session.Username(ctx)}, Info: syncReq.Infos, @@ -1970,12 +1830,7 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR if len(syncReq.Resources) > 0 { partial = "partial " } - var reason string - if a.Spec.HasMultipleSources() { - reason = fmt.Sprintf("initiated %ssync to %s", partial, strings.Join(displayRevisions, ",")) - } else { - reason = fmt.Sprintf("initiated %ssync to %s", partial, displayRevision) - } + reason := fmt.Sprintf("initiated %ssync to %s", partial, displayRevision) if syncReq.Manifests != nil { reason = fmt.Sprintf("initiated %ssync locally", partial) } @@ -1983,48 +1838,6 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR return a, nil } -func (s *Server) resolveSourceRevisions(ctx context.Context, a *appv1.Application, syncReq *application.ApplicationSyncRequest) (string, string, []string, []string, error) { - if a.Spec.HasMultipleSources() { - numOfSources := int64(len(a.Spec.GetSources())) - sourceRevisions := make([]string, numOfSources) - displayRevisions := make([]string, numOfSources) - - sources := a.Spec.GetSources() - for i, pos := range syncReq.SourcePositions { - if pos <= 0 || pos > numOfSources { - return "", "", nil, nil, fmt.Errorf("source position is out of range") - } - sources[pos-1].TargetRevision = syncReq.Revisions[i] - } - for index, source := range sources { - if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.Automated != nil && !syncReq.GetDryRun() { - if text.FirstNonEmpty(a.Spec.GetSources()[index].TargetRevision, "HEAD") != text.FirstNonEmpty(source.TargetRevision, "HEAD") { - return "", "", nil, nil, status.Errorf(codes.FailedPrecondition, "Cannot sync source %s to %s: auto-sync currently set to %s", source.RepoURL, source.TargetRevision, a.Spec.Sources[index].TargetRevision) - } - } - revision, displayRevision, err := s.resolveRevision(ctx, a, syncReq, index) - if err != nil { - return "", "", nil, nil, status.Error(codes.FailedPrecondition, err.Error()) - } - sourceRevisions[index] = revision - displayRevisions[index] = displayRevision - } - return "", "", sourceRevisions, displayRevisions, nil - } else { - source := a.Spec.GetSource() - if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.Automated != nil && !syncReq.GetDryRun() { - if syncReq.GetRevision() != "" && syncReq.GetRevision() != text.FirstNonEmpty(source.TargetRevision, "HEAD") { - return "", "", nil, nil, status.Errorf(codes.FailedPrecondition, "Cannot sync to %s: auto-sync currently set to %s", syncReq.GetRevision(), source.TargetRevision) - } - } - revision, displayRevision, err := s.resolveRevision(ctx, a, syncReq, -1) - if err != nil { - return "", "", nil, nil, status.Error(codes.FailedPrecondition, err.Error()) - } - return revision, displayRevision, nil, nil, nil - } -} - func (s *Server) Rollback(ctx context.Context, rollbackReq *application.ApplicationRollbackRequest) (*appv1.Application, error) { a, _, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, rollbackReq.GetProject(), rollbackReq.GetAppNamespace(), rollbackReq.GetName(), "") if err != nil { @@ -2050,10 +1863,9 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat if deploymentInfo == nil { return nil, status.Errorf(codes.InvalidArgument, "application %s does not have deployment with id %v", a.QualifiedName(), rollbackReq.GetId()) } - if deploymentInfo.Source.IsZero() && deploymentInfo.Sources.IsZero() { + if deploymentInfo.Source.IsZero() { // Since source type was introduced to history starting with v0.12, and is now required for // rollback, we cannot support rollback to revisions deployed using Argo CD v0.11 or below - // As multi source doesn't use app.Source, we need to check to the Sources length return nil, status.Errorf(codes.FailedPrecondition, "cannot rollback to revision deployed with Argo CD v0.11 or lower. sync to revision instead.") } @@ -2066,13 +1878,11 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat op := appv1.Operation{ Sync: &appv1.SyncOperation{ Revision: deploymentInfo.Revision, - Revisions: deploymentInfo.Revisions, DryRun: rollbackReq.GetDryRun(), Prune: rollbackReq.GetPrune(), SyncOptions: syncOptions, SyncStrategy: &appv1.SyncStrategy{Apply: &appv1.SyncStrategyApply{}}, Source: &deploymentInfo.Source, - Sources: deploymentInfo.Sources, }, InitiatedBy: appv1.OperationInitiator{Username: session.Username(ctx)}, } @@ -2200,41 +2010,17 @@ func (s *Server) ListResourceLinks(ctx context.Context, req *application.Applica return finalList, nil } -func getAmbiguousRevision(app *appv1.Application, syncReq *application.ApplicationSyncRequest, sourceIndex int) string { - ambiguousRevision := "" - if app.Spec.HasMultipleSources() { - for i, pos := range syncReq.SourcePositions { - if pos == int64(sourceIndex) { - ambiguousRevision = syncReq.Revisions[i] - } - } - if ambiguousRevision == "" { - ambiguousRevision = app.Spec.Sources[sourceIndex].TargetRevision - } - } else { - ambiguousRevision = syncReq.GetRevision() - if ambiguousRevision == "" { - ambiguousRevision = app.Spec.GetSource().TargetRevision - } - } - return ambiguousRevision -} - // resolveRevision resolves the revision specified either in the sync request, or the // application source, into a concrete revision that will be used for a sync operation. -func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, syncReq *application.ApplicationSyncRequest, sourceIndex int) (string, string, error) { +func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, syncReq *application.ApplicationSyncRequest) (string, string, error) { if syncReq.Manifests != nil { return "", "", nil } - - ambiguousRevision := getAmbiguousRevision(app, syncReq, sourceIndex) - - repoUrl := app.Spec.GetSource().RepoURL - if app.Spec.HasMultipleSources() { - repoUrl = app.Spec.Sources[sourceIndex].RepoURL + ambiguousRevision := syncReq.GetRevision() + if ambiguousRevision == "" { + ambiguousRevision = app.Spec.GetSource().TargetRevision } - - repo, err := s.db.GetRepository(ctx, repoUrl, app.Spec.Project) + repo, err := s.db.GetRepository(ctx, app.Spec.GetSource().RepoURL) if err != nil { return "", "", fmt.Errorf("error getting repository by URL: %w", err) } @@ -2244,7 +2030,7 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy } defer ioutil.Close(conn) - source := app.Spec.GetSourcePtrByIndex(sourceIndex) + source := app.Spec.GetSource() if !source.IsHelm() { if git.IsCommitSHA(ambiguousRevision) { // If it's already a commit SHA, then no need to look it up @@ -2256,7 +2042,6 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy Repo: repo, App: app, AmbiguousRevision: ambiguousRevision, - SourceIndex: int64(sourceIndex), }) if err != nil { return "", "", fmt.Errorf("error resolving repo revision: %w", err) @@ -2303,8 +2088,7 @@ func (s *Server) logAppEvent(a *appv1.Application, ctx context.Context, reason s user = "Unknown user" } message := fmt.Sprintf("%s %s", user, action) - eventLabels := argo.GetAppEventLabels(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) - s.auditLogger.LogAppEvent(a, eventInfo, message, user, eventLabels) + s.auditLogger.LogAppEvent(a, eventInfo, message, user) } func (s *Server) logResourceEvent(res *appv1.ResourceNode, ctx context.Context, reason string, action string) { @@ -2359,6 +2143,7 @@ func (s *Server) getUnstructuredLiveResourceOrApp(ctx context.Context, rbacReque 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) @@ -2371,18 +2156,19 @@ func (s *Server) getAvailableActions(resourceOverrides map[string]appv1.Resource ResourceOverrides: resourceOverrides, } - discoveryScripts, err := luaVM.GetResourceActionDiscovery(obj) + discoveryScript, err := luaVM.GetResourceActionDiscovery(obj) if err != nil { return nil, fmt.Errorf("error getting Lua discovery script: %w", err) } - if len(discoveryScripts) == 0 { + if discoveryScript == "" { return []appv1.ResourceAction{}, nil } - availableActions, err := luaVM.ExecuteResourceActionDiscovery(obj, discoveryScripts) + availableActions, err := luaVM.ExecuteResourceActionDiscovery(obj, discoveryScript) if err != nil { return nil, fmt.Errorf("error executing Lua discovery script: %w", err) } return availableActions, nil + } func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceActionRunRequest) (*application.ApplicationResponse, error) { @@ -2447,7 +2233,7 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA // 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(app, proj, newObj) + err := s.verifyResourcePermitted(ctx, app, proj, newObj) if err != nil { return nil, err } @@ -2469,6 +2255,7 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA 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) } @@ -2540,7 +2327,7 @@ func (s *Server) patchResource(ctx context.Context, config *rest.Config, liveObj return &application.ApplicationResponse{}, nil } -func (s *Server) verifyResourcePermitted(app *appv1.Application, proj *appv1.AppProject, obj *unstructured.Unstructured) error { +func (s *Server) verifyResourcePermitted(ctx context.Context, app *appv1.Application, proj *appv1.AppProject, obj *unstructured.Unstructured) error { 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 { @@ -2677,7 +2464,7 @@ func (s *Server) isNamespaceEnabled(namespace string) bool { return security.IsNamespaceEnabled(namespace, s.ns, s.enabledNamespaces) } -// getProjectsFromApplicationQuery gets the project names from a query. If the legacy "project" field was specified, use +// 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 { diff --git a/server/application/application.proto b/server/application/application.proto index 945c0c417c65c..4736219cb4594 100644 --- a/server/application/application.proto +++ b/server/application/application.proto @@ -51,10 +51,6 @@ message RevisionMetadataQuery{ // the application's namespace optional string appNamespace = 3; optional string project = 4; - // source index (for multi source apps) - optional int32 sourceIndex = 5; - // versionId from historical data (for multi source apps) - optional int32 versionId = 6; } // ApplicationEventsQuery is a query for application resource events @@ -73,8 +69,6 @@ message ApplicationManifestQuery { optional string revision = 2; optional string appNamespace = 3; optional string project = 4; - repeated int64 sourcePositions = 5; - repeated string revisions = 6; } message FileChunk { @@ -135,8 +129,6 @@ message ApplicationSyncRequest { optional SyncOptions syncOptions = 11; optional string appNamespace = 12; optional string project = 13; - repeated int64 sourcePositions = 14; - repeated string revisions = 15; } // ApplicationUpdateSpecRequest is a request to update application spec diff --git a/server/application/application_test.go b/server/application/application_test.go index c182829fa19bd..abbf41ee42e72 100644 --- a/server/application/application_test.go +++ b/server/application/application_test.go @@ -37,13 +37,12 @@ import ( "k8s.io/client-go/rest" kubetesting "k8s.io/client-go/testing" k8scache "k8s.io/client-go/tools/cache" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - appv1 "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/reposerver/apiclient" @@ -69,8 +68,6 @@ const ( fakeRepoURL = "https://git.com/repo.git" ) -var testEnableEventList []string = argo.DefaultEnableEventList() - func fakeRepo() *appsv1.Repository { return &appsv1.Repository{ Repo: fakeRepoURL, @@ -135,10 +132,10 @@ func newTestAppServer(t *testing.T, objects ...runtime.Object) *Server { _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) enf.SetDefaultRole("role:admin") } - return newTestAppServerWithEnforcerConfigure(f, t, map[string]string{}, objects...) + return newTestAppServerWithEnforcerConfigure(f, t, objects...) } -func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), t *testing.T, additionalConfig map[string]string, 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, @@ -147,7 +144,6 @@ func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), t *testing.T, "app.kubernetes.io/part-of": "argocd", }, }, - Data: additionalConfig, }, &v1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "argocd-secret", @@ -246,7 +242,7 @@ func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), t *testing.T, } }() }) - broadcaster.On("OnAdd", mock.Anything, mock.Anything).Return() + broadcaster.On("OnAdd", mock.Anything).Return() broadcaster.On("OnUpdate", mock.Anything, mock.Anything).Return() broadcaster.On("OnDelete", mock.Anything).Return() @@ -308,7 +304,6 @@ func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), t *testing.T, settingsMgr, projInformer, []string{}, - testEnableEventList, ) return server.(*Server) } @@ -427,7 +422,7 @@ func newTestAppServerWithEnforcerConfigureWithBenchmark(f func(*rbac.Enforcer), } }() }) - broadcaster.On("OnAdd", mock.Anything, mock.Anything).Return() + broadcaster.On("OnAdd", mock.Anything).Return() broadcaster.On("OnUpdate", mock.Anything, mock.Anything).Return() broadcaster.On("OnDelete", mock.Anything).Return() @@ -489,7 +484,6 @@ func newTestAppServerWithEnforcerConfigureWithBenchmark(f func(*rbac.Enforcer), settingsMgr, projInformer, []string{}, - testEnableEventList, ) return server.(*Server) } @@ -612,9 +606,9 @@ func (t *TestServerStream) Recv() (*application.ApplicationManifestQueryWithFile t.headerSent = true return &application.ApplicationManifestQueryWithFilesWrapper{Part: &application.ApplicationManifestQueryWithFilesWrapper_Query{ Query: &application.ApplicationManifestQueryWithFiles{ - Name: ptr.To(t.appName), - Project: ptr.To(t.project), - Checksum: ptr.To(""), + Name: pointer.String(t.appName), + Project: pointer.String(t.project), + Checksum: pointer.String(""), }, }}, nil } @@ -757,42 +751,8 @@ func TestNoAppEnumeration(t *testing.T) { }, } }) - testAppMulti := newTestApp(func(app *appsv1.Application) { - app.Name = "test-multi" - app.Spec.Sources = appsv1.ApplicationSources{ - appsv1.ApplicationSource{ - TargetRevision: "something-old", - }, - appsv1.ApplicationSource{ - TargetRevision: "something-old", - }, - } - 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: 1, - Sources: appsv1.ApplicationSources{ - appsv1.ApplicationSource{ - TargetRevision: "something-old", - }, - appsv1.ApplicationSource{ - TargetRevision: "something-old", - }, - }, - }, - } - }) testDeployment := kube.MustToUnstructured(&deployment) - appServer := newTestAppServerWithEnforcerConfigure(f, t, map[string]string{}, testApp, testHelmApp, testAppMulti, testDeployment) + appServer := newTestAppServerWithEnforcerConfigure(f, t, testApp, testHelmApp, testDeployment) noRoleCtx := context.Background() // nolint:staticcheck @@ -800,58 +760,58 @@ func TestNoAppEnumeration(t *testing.T) { t.Run("Get", func(t *testing.T) { // nolint:staticcheck - _, err := appServer.Get(adminCtx, &application.ApplicationQuery{Name: ptr.To("test")}) - require.NoError(t, err) + _, err := appServer.Get(adminCtx, &application.ApplicationQuery{Name: pointer.String("test")}) + assert.NoError(t, err) // nolint:staticcheck - _, err = appServer.Get(noRoleCtx, &application.ApplicationQuery{Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: []string{"test"}}) + _, 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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.GetManifests(noRoleCtx, &application.ApplicationManifestQuery{Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.ListResourceEvents(noRoleCtx, &application.ApplicationResourceEventsQuery{Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test"), Spec: &appsv1.ApplicationSpec{ + _, 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: "."}, }}) - require.NoError(t, err) - _, err = appServer.UpdateSpec(noRoleCtx, &application.ApplicationUpdateSpecRequest{Name: ptr.To("test"), Spec: &appsv1.ApplicationSpec{ + 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: ptr.To("doest-not-exist"), Spec: &appsv1.ApplicationSpec{ + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test"), Spec: &appsv1.ApplicationSpec{ + _, 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: "."}, }}) @@ -859,105 +819,103 @@ func TestNoAppEnumeration(t *testing.T) { }) t.Run("Patch", func(t *testing.T) { - _, err := appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`)}) - require.NoError(t, err) - _, err = appServer.Patch(noRoleCtx, &application.ApplicationPatchRequest{Name: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`)}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.GetResource(noRoleCtx, &application.ApplicationResourceRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) + _, 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: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) + _, 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: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) + _, 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: ptr.To("doest-not-exist"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) + _, 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: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.DeleteResource(noRoleCtx, &application.ApplicationResourceDeleteRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) + _, 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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.ResourceTree(noRoleCtx, &application.ResourcesQuery{ApplicationName: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: ptr.To("test-multi"), SourceIndex: ptr.To(int32(0)), VersionId: ptr.To(int32(1))}) - require.NoError(t, err) - _, err = appServer.RevisionMetadata(noRoleCtx, &application.RevisionMetadataQuery{Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test-helm")}) - require.NoError(t, err) - _, err = appServer.RevisionChartDetails(noRoleCtx, &application.RevisionMetadataQuery{Name: ptr.To("test-helm")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.ManagedResources(noRoleCtx, &application.ResourcesQuery{ApplicationName: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.Sync(noRoleCtx, &application.ApplicationSyncRequest{Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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") }) @@ -965,70 +923,68 @@ func TestNoAppEnumeration(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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.TerminateOperation(noRoleCtx, &application.OperationTerminateRequest{Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: ptr.To("test-multi"), Id: ptr.To(int64(1))}) - require.NoError(t, err) - _, err = appServer.Rollback(noRoleCtx, &application.ApplicationRollbackRequest{Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.ListResourceActions(noRoleCtx, &application.ApplicationResourceRequest{Name: ptr.To("test")}) + _, 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: ptr.To("argoproj.io"), Kind: ptr.To("Application"), Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Action: ptr.To("restart")}) - require.NoError(t, err) - _, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Name: ptr.To("test")}) + _, 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: ptr.To("argoproj.io"), Kind: ptr.To("Application"), Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.GetApplicationSyncWindows(noRoleCtx, &application.ApplicationSyncWindowsQuery{Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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"}) - require.NoError(t, err) + 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"}) @@ -1038,58 +994,58 @@ func TestNoAppEnumeration(t *testing.T) { }) t.Run("WatchResourceTree", func(t *testing.T) { - err := appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: ptr.To("test")}, &TestResourceTreeServer{ctx: adminCtx}) - require.NoError(t, err) - err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: ptr.To("test")}, &TestResourceTreeServer{ctx: noRoleCtx}) + 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: ptr.To("does-not-exist")}, &TestResourceTreeServer{ctx: adminCtx}) + 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: ptr.To("does-not-exist"), Project: ptr.To("test")}, &TestResourceTreeServer{ctx: adminCtx}) + 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: ptr.To("test")}, &TestPodLogsServer{ctx: adminCtx}) - require.NoError(t, err) - err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: ptr.To("test")}, &TestPodLogsServer{ctx: noRoleCtx}) + 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: ptr.To("does-not-exist")}, &TestPodLogsServer{ctx: adminCtx}) + 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: ptr.To("does-not-exist"), Project: ptr.To("test")}, &TestPodLogsServer{ctx: adminCtx}) + 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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.ListLinks(noRoleCtx, &application.ListAppLinksRequest{Name: ptr.To("test")}) + _, 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: ptr.To("does-not-exist")}) + _, 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: ptr.To("does-not-exist"), Project: ptr.To("test")}) + _, 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: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.ListResourceLinks(noRoleCtx, &application.ApplicationResourceRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) + _, 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: ptr.To("does-not-exist"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) + _, 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: ptr.To("does-not-exist"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Project: ptr.To("test")}) + _, 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: ptr.To("test")}) - require.NoError(t, err) - _, err = appServer.Delete(noRoleCtx, &application.ApplicationDeleteRequest{Name: ptr.To("test")}) + _, 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: ptr.To("doest-not-exist")}) + _, 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: ptr.To("doest-not-exist"), Project: ptr.To("test")}) + _, 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") }) } @@ -1158,48 +1114,34 @@ func testListAppsWithLabels(t *testing.T, appQuery application.ApplicationQuery, label string expectedResult []string }{ - { - testName: "Equality based filtering using '=' operator", + {testName: "Equality based filtering using '=' operator", label: "key1=value1", - expectedResult: []string{"App1"}, - }, - { - testName: "Equality based filtering using '==' operator", + expectedResult: []string{"App1"}}, + {testName: "Equality based filtering using '==' operator", label: "key1==value1", - expectedResult: []string{"App1"}, - }, - { - testName: "Equality based filtering using '!=' operator", + expectedResult: []string{"App1"}}, + {testName: "Equality based filtering using '!=' operator", label: "key1!=value1", - expectedResult: []string{"App2", "App3"}, - }, - { - testName: "Set based filtering using 'in' operator", + expectedResult: []string{"App2", "App3"}}, + {testName: "Set based filtering using 'in' operator", label: "key1 in (value1, value3)", - expectedResult: []string{"App1", "App3"}, - }, - { - testName: "Set based filtering using 'notin' operator", + expectedResult: []string{"App1", "App3"}}, + {testName: "Set based filtering using 'notin' operator", label: "key1 notin (value1, value3)", - expectedResult: []string{"App2"}, - }, - { - testName: "Set based filtering using 'exists' operator", + expectedResult: []string{"App2"}}, + {testName: "Set based filtering using 'exists' operator", label: "key1", - expectedResult: []string{"App1", "App2", "App3"}, - }, - { - testName: "Set based filtering using 'not exists' operator", + expectedResult: []string{"App1", "App2", "App3"}}, + {testName: "Set based filtering using 'not exists' operator", label: "!key2", - expectedResult: []string{"App2", "App3"}, - }, + expectedResult: []string{"App2", "App3"}}, } // test valid scenarios for _, validTest := range validTests { t.Run(validTest.testName, func(t *testing.T) { appQuery.Selector = &validTest.label res, err := appServer.List(context.Background(), &appQuery) - require.NoError(t, err) + assert.NoError(t, err) apps := []string{} for i := range res.Items { apps = append(apps, res.Items[i].Name) @@ -1213,16 +1155,12 @@ func testListAppsWithLabels(t *testing.T, appQuery application.ApplicationQuery, label string errorMesage string }{ - { - testName: "Set based filtering using '>' operator", + {testName: "Set based filtering using '>' operator", label: "key1>value1", - errorMesage: "error parsing the selector", - }, - { - testName: "Set based filtering using '<' operator", + errorMesage: "error parsing the selector"}, + {testName: "Set based filtering using '<' operator", label: "key1 0 { - f := func(enf *rbac.Enforcer) { - _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) - enf.SetDefaultRole("role:admin") - } - formatInt := strconv.FormatInt(maxPodLogsToRender[0], 10) - appServer := newTestAppServerWithEnforcerConfigure(f, t, map[string]string{"server.maxPodLogsToRender": formatInt}, runtimeObjects...) - return appServer, adminCtx - } else { - appServer := newTestAppServer(t, runtimeObjects...) - return appServer, adminCtx - } } // refreshAnnotationRemover runs an infinite loop until it detects and removes refresh annotation or given context is done @@ -2312,16 +2022,17 @@ func TestGetAppRefresh_NormalRefresh(t *testing.T) { _, err := appServer.Get(context.Background(), &application.ApplicationQuery{ Name: &testApp.Name, - Refresh: ptr.To(string(appsv1.RefreshTypeNormal)), + Refresh: pointer.String(string(appsv1.RefreshTypeNormal)), }) - require.NoError(t, err) + assert.NoError(t, err) select { case <-ch: - assert.Equal(t, int32(1), atomic.LoadInt32(&patched)) + assert.Equal(t, atomic.LoadInt32(&patched), int32(1)) case <-time.After(10 * time.Second): assert.Fail(t, "Out of time ( 10 seconds )") } + } func TestGetAppRefresh_HardRefresh(t *testing.T) { @@ -2347,17 +2058,17 @@ func TestGetAppRefresh_HardRefresh(t *testing.T) { _, err := appServer.Get(context.Background(), &application.ApplicationQuery{ Name: &testApp.Name, - Refresh: ptr.To(string(appsv1.RefreshTypeHard)), + Refresh: pointer.String(string(appsv1.RefreshTypeHard)), }) - require.NoError(t, err) + assert.NoError(t, err) require.NotNil(t, getAppDetailsQuery) assert.True(t, getAppDetailsQuery.NoCache) assert.Equal(t, testApp.Spec.Source, getAppDetailsQuery.Source) - require.NoError(t, err) + assert.NoError(t, err) select { case <-ch: - assert.Equal(t, int32(1), atomic.LoadInt32(&patched)) + assert.Equal(t, atomic.LoadInt32(&patched), int32(1)) case <-time.After(10 * time.Second): assert.Fail(t, "Out of time ( 10 seconds )") } @@ -2656,10 +2367,7 @@ func TestIsApplicationPermitted(t *testing.T) { } func TestAppNamespaceRestrictions(t *testing.T) { - t.Parallel() - t.Run("List applications in controller namespace", func(t *testing.T) { - t.Parallel() testApp := newTestApp() appServer := newTestAppServer(t, testApp) apps, err := appServer.List(context.TODO(), &application.ApplicationQuery{}) @@ -2668,28 +2376,25 @@ func TestAppNamespaceRestrictions(t *testing.T) { }) t.Run("List applications with non-allowed apps existing", func(t *testing.T) { - t.Parallel() testApp1 := newTestApp() testApp1.Namespace = "argocd-1" appServer := newTestAppServer(t, testApp1) apps, err := appServer.List(context.TODO(), &application.ApplicationQuery{}) require.NoError(t, err) - require.Empty(t, apps.Items) + require.Len(t, apps.Items, 0) }) t.Run("List applications with non-allowed apps existing and explicit ns request", func(t *testing.T) { - t.Parallel() testApp1 := newTestApp() testApp2 := newTestApp() testApp2.Namespace = "argocd-1" appServer := newTestAppServer(t, testApp1, testApp2) - apps, err := appServer.List(context.TODO(), &application.ApplicationQuery{AppNamespace: ptr.To("argocd-1")}) + apps, err := appServer.List(context.TODO(), &application.ApplicationQuery{AppNamespace: pointer.String("argocd-1")}) require.NoError(t, err) - require.Empty(t, apps.Items) + require.Len(t, apps.Items, 0) }) t.Run("List applications with allowed apps in other namespaces", func(t *testing.T) { - t.Parallel() testApp1 := newTestApp() testApp1.Namespace = "argocd-1" appServer := newTestAppServer(t, testApp1) @@ -2700,30 +2405,27 @@ func TestAppNamespaceRestrictions(t *testing.T) { }) t.Run("Get application in control plane namespace", func(t *testing.T) { - t.Parallel() testApp := newTestApp() appServer := newTestAppServer(t, testApp) app, err := appServer.Get(context.TODO(), &application.ApplicationQuery{ - Name: ptr.To("test-app"), + Name: pointer.String("test-app"), }) require.NoError(t, err) assert.Equal(t, "test-app", app.GetName()) }) t.Run("Get application in other namespace when forbidden", func(t *testing.T) { - t.Parallel() testApp := newTestApp() testApp.Namespace = "argocd-1" appServer := newTestAppServer(t, testApp) app, err := appServer.Get(context.TODO(), &application.ApplicationQuery{ - Name: ptr.To("test-app"), - AppNamespace: ptr.To("argocd-1"), + Name: pointer.String("test-app"), + AppNamespace: pointer.String("argocd-1"), }) require.Error(t, err) require.ErrorContains(t, err, "permission denied") require.Nil(t, app) }) t.Run("Get application in other namespace when allowed", func(t *testing.T) { - t.Parallel() testApp := newTestApp() testApp.Namespace = "argocd-1" testApp.Spec.Project = "other-ns" @@ -2738,8 +2440,8 @@ func TestAppNamespaceRestrictions(t *testing.T) { appServer := newTestAppServer(t, testApp, otherNsProj) appServer.enabledNamespaces = []string{"argocd-1"} app, err := appServer.Get(context.TODO(), &application.ApplicationQuery{ - Name: ptr.To("test-app"), - AppNamespace: ptr.To("argocd-1"), + Name: pointer.String("test-app"), + AppNamespace: pointer.String("argocd-1"), }) require.NoError(t, err) require.NotNil(t, app) @@ -2747,7 +2449,6 @@ func TestAppNamespaceRestrictions(t *testing.T) { require.Equal(t, "test-app", app.Name) }) t.Run("Get application in other namespace when project is not allowed", func(t *testing.T) { - t.Parallel() testApp := newTestApp() testApp.Namespace = "argocd-1" testApp.Spec.Project = "other-ns" @@ -2762,15 +2463,14 @@ func TestAppNamespaceRestrictions(t *testing.T) { appServer := newTestAppServer(t, testApp, otherNsProj) appServer.enabledNamespaces = []string{"argocd-1"} app, err := appServer.Get(context.TODO(), &application.ApplicationQuery{ - Name: ptr.To("test-app"), - AppNamespace: ptr.To("argocd-1"), + Name: pointer.String("test-app"), + AppNamespace: pointer.String("argocd-1"), }) require.Error(t, err) require.Nil(t, app) require.ErrorContains(t, err, "app is not allowed in project") }) t.Run("Create application in other namespace when allowed", func(t *testing.T) { - t.Parallel() testApp := newTestApp() testApp.Namespace = "argocd-1" testApp.Spec.Project = "other-ns" @@ -2794,7 +2494,6 @@ func TestAppNamespaceRestrictions(t *testing.T) { }) t.Run("Create application in other namespace when not allowed by project", func(t *testing.T) { - t.Parallel() testApp := newTestApp() testApp.Namespace = "argocd-1" testApp.Spec.Project = "other-ns" @@ -2817,7 +2516,6 @@ func TestAppNamespaceRestrictions(t *testing.T) { }) t.Run("Create application in other namespace when not allowed by configuration", func(t *testing.T) { - t.Parallel() testApp := newTestApp() testApp.Namespace = "argocd-1" testApp.Spec.Project = "other-ns" @@ -2839,7 +2537,6 @@ func TestAppNamespaceRestrictions(t *testing.T) { require.ErrorContains(t, err, "namespace 'argocd-1' is not permitted") }) t.Run("Get application sync window in other namespace when project is allowed", func(t *testing.T) { - t.Parallel() testApp := newTestApp() testApp.Namespace = "argocd-1" testApp.Spec.Project = "other-ns" @@ -2854,11 +2551,10 @@ func TestAppNamespaceRestrictions(t *testing.T) { appServer := newTestAppServer(t, testApp, otherNsProj) appServer.enabledNamespaces = []string{"argocd-1"} active, err := appServer.GetApplicationSyncWindows(context.TODO(), &application.ApplicationSyncWindowsQuery{Name: &testApp.Name, AppNamespace: &testApp.Namespace}) - require.NoError(t, err) - assert.Empty(t, active.ActiveWindows) + assert.NoError(t, err) + assert.Equal(t, 0, len(active.ActiveWindows)) }) t.Run("Get application sync window in other namespace when project is not allowed", func(t *testing.T) { - t.Parallel() testApp := newTestApp() testApp.Namespace = "argocd-1" testApp.Spec.Project = "other-ns" @@ -2878,7 +2574,6 @@ func TestAppNamespaceRestrictions(t *testing.T) { require.ErrorContains(t, err, "app is not allowed in project") }) t.Run("Get list of links in other namespace when project is not allowed", func(t *testing.T) { - t.Parallel() testApp := newTestApp() testApp.Namespace = "argocd-1" testApp.Spec.Project = "other-ns" @@ -2893,15 +2588,14 @@ func TestAppNamespaceRestrictions(t *testing.T) { appServer := newTestAppServer(t, testApp, otherNsProj) appServer.enabledNamespaces = []string{"argocd-1"} links, err := appServer.ListLinks(context.TODO(), &application.ListAppLinksRequest{ - Name: ptr.To("test-app"), - Namespace: ptr.To("argocd-1"), + Name: pointer.String("test-app"), + Namespace: pointer.String("argocd-1"), }) require.Error(t, err) require.Nil(t, links) require.ErrorContains(t, err, "app is not allowed in project") }) t.Run("Get list of links in other namespace when project is allowed", func(t *testing.T) { - t.Parallel() testApp := newTestApp() testApp.Namespace = "argocd-1" testApp.Spec.Project = "other-ns" @@ -2916,399 +2610,10 @@ func TestAppNamespaceRestrictions(t *testing.T) { appServer := newTestAppServer(t, testApp, otherNsProj) appServer.enabledNamespaces = []string{"argocd-1"} links, err := appServer.ListLinks(context.TODO(), &application.ListAppLinksRequest{ - Name: ptr.To("test-app"), - Namespace: ptr.To("argocd-1"), + Name: pointer.String("test-app"), + Namespace: pointer.String("argocd-1"), }) require.NoError(t, err) - assert.Empty(t, links.Items) + assert.Equal(t, 0, len(links.Items)) }) } - -func TestGetAmbiguousRevision_MultiSource(t *testing.T) { - app := &appv1.Application{ - Spec: appv1.ApplicationSpec{ - Sources: []appv1.ApplicationSource{ - { - TargetRevision: "revision1", - }, - { - TargetRevision: "revision2", - }, - }, - }, - } - syncReq := &application.ApplicationSyncRequest{ - SourcePositions: []int64{0, 1}, - Revisions: []string{"rev1", "rev2"}, - } - - sourceIndex := 0 - expected := "rev1" - result := getAmbiguousRevision(app, syncReq, sourceIndex) - if result != expected { - t.Errorf("Expected ambiguous revision to be %s, but got %s", expected, result) - } - - sourceIndex = 1 - expected = "rev2" - result = getAmbiguousRevision(app, syncReq, sourceIndex) - if result != expected { - t.Errorf("Expected ambiguous revision to be %s, but got %s", expected, result) - } - - // Test when app.Spec.HasMultipleSources() is false - app.Spec = appv1.ApplicationSpec{ - Source: &appv1.ApplicationSource{ - TargetRevision: "revision3", - }, - Sources: nil, - } - syncReq = &application.ApplicationSyncRequest{ - Revision: strToPtr("revision3"), - } - expected = "revision3" - result = getAmbiguousRevision(app, syncReq, sourceIndex) - if result != expected { - t.Errorf("Expected ambiguous revision to be %s, but got %s", expected, result) - } -} - -func TestGetAmbiguousRevision_SingleSource(t *testing.T) { - app := &appv1.Application{ - Spec: appv1.ApplicationSpec{ - Source: &appv1.ApplicationSource{ - TargetRevision: "revision1", - }, - }, - } - syncReq := &application.ApplicationSyncRequest{ - Revision: strToPtr("rev1"), - } - - // Test when app.Spec.HasMultipleSources() is true - sourceIndex := 1 - expected := "rev1" - result := getAmbiguousRevision(app, syncReq, sourceIndex) - if result != expected { - t.Errorf("Expected ambiguous revision to be %s, but got %s", expected, result) - } -} - -func TestServer_ResolveSourceRevisions_MultiSource(t *testing.T) { - s := newTestAppServer(t) - - ctx := context.Background() - a := &appv1.Application{ - Spec: appv1.ApplicationSpec{ - Sources: []appv1.ApplicationSource{ - { - RepoURL: "https://github.com/example/repo.git", - }, - }, - }, - } - - syncReq := &application.ApplicationSyncRequest{ - SourcePositions: []int64{1}, - Revisions: []string{"HEAD"}, - } - - revision, displayRevision, sourceRevisions, displayRevisions, err := s.resolveSourceRevisions(ctx, a, syncReq) - - require.NoError(t, err) - assert.Equal(t, "", revision) - assert.Equal(t, "", displayRevision) - assert.Equal(t, []string{fakeResolveRevisionResponse().Revision}, sourceRevisions) - assert.Equal(t, []string{fakeResolveRevisionResponse().AmbiguousRevision}, displayRevisions) -} - -func TestServer_ResolveSourceRevisions_SingleSource(t *testing.T) { - s := newTestAppServer(t) - - ctx := context.Background() - a := &appv1.Application{ - Spec: appv1.ApplicationSpec{ - Source: &appv1.ApplicationSource{ - RepoURL: "https://github.com/example/repo.git", - }, - }, - } - - syncReq := &application.ApplicationSyncRequest{ - Revision: strToPtr("HEAD"), - } - - revision, displayRevision, sourceRevisions, displayRevisions, err := s.resolveSourceRevisions(ctx, a, syncReq) - - require.NoError(t, err) - assert.Equal(t, fakeResolveRevisionResponse().Revision, revision) - assert.Equal(t, fakeResolveRevisionResponse().AmbiguousRevision, displayRevision) - assert.Equal(t, ([]string)(nil), sourceRevisions) - assert.Equal(t, ([]string)(nil), displayRevisions) -} - -func Test_RevisionMetadata(t *testing.T) { - t.Parallel() - - singleSourceApp := newTestApp() - singleSourceApp.Name = "single-source-app" - singleSourceApp.Spec = appv1.ApplicationSpec{ - Source: &appv1.ApplicationSource{ - RepoURL: "https://github.com/argoproj/argocd-example-apps.git", - Path: "helm-guestbook", - TargetRevision: "HEAD", - }, - } - - multiSourceApp := newTestApp() - multiSourceApp.Name = "multi-source-app" - multiSourceApp.Spec = appv1.ApplicationSpec{ - Sources: []appv1.ApplicationSource{ - { - RepoURL: "https://github.com/argoproj/argocd-example-apps.git", - Path: "helm-guestbook", - TargetRevision: "HEAD", - }, - { - RepoURL: "https://github.com/argoproj/argocd-example-apps.git", - Path: "kustomize-guestbook", - TargetRevision: "HEAD", - }, - }, - } - - singleSourceHistory := []appv1.RevisionHistory{ - { - ID: 1, - Source: singleSourceApp.Spec.GetSource(), - Revision: "a", - }, - } - multiSourceHistory := []appv1.RevisionHistory{ - { - ID: 1, - Sources: multiSourceApp.Spec.GetSources(), - Revisions: []string{"a", "b"}, - }, - } - - testCases := []struct { - name string - multiSource bool - history *struct { - matchesSourceType bool - } - sourceIndex *int32 - versionId *int32 - expectErrorContains *string - }{ - { - name: "single-source app without history, no source index, no version ID", - multiSource: false, - }, - { - name: "single-source app without history, no source index, missing version ID", - multiSource: false, - versionId: ptr.To(int32(999)), - expectErrorContains: ptr.To("the app has no history"), - }, - { - name: "single source app without history, present source index, no version ID", - multiSource: false, - sourceIndex: ptr.To(int32(0)), - }, - { - name: "single source app without history, invalid source index, no version ID", - multiSource: false, - sourceIndex: ptr.To(int32(999)), - expectErrorContains: ptr.To("source index 999 not found"), - }, - { - name: "single source app with matching history, no source index, no version ID", - multiSource: false, - history: &struct{ matchesSourceType bool }{true}, - }, - { - name: "single source app with matching history, no source index, missing version ID", - multiSource: false, - history: &struct{ matchesSourceType bool }{true}, - versionId: ptr.To(int32(999)), - expectErrorContains: ptr.To("history not found for version ID 999"), - }, - { - name: "single source app with matching history, no source index, present version ID", - multiSource: false, - history: &struct{ matchesSourceType bool }{true}, - versionId: ptr.To(int32(1)), - }, - { - name: "single source app with multi-source history, no source index, no version ID", - multiSource: false, - history: &struct{ matchesSourceType bool }{false}, - }, - { - name: "single source app with multi-source history, no source index, missing version ID", - multiSource: false, - history: &struct{ matchesSourceType bool }{false}, - versionId: ptr.To(int32(999)), - expectErrorContains: ptr.To("history not found for version ID 999"), - }, - { - name: "single source app with multi-source history, no source index, present version ID", - multiSource: false, - history: &struct{ matchesSourceType bool }{false}, - versionId: ptr.To(int32(1)), - }, - { - name: "single-source app with multi-source history, source index 1, no version ID", - multiSource: false, - sourceIndex: ptr.To(int32(1)), - history: &struct{ matchesSourceType bool }{false}, - // Since the user requested source index 1, but no version ID, we'll get an error when looking at the live - // source, because the live source is single-source. - expectErrorContains: ptr.To("there is only 1 source"), - }, - { - name: "single-source app with multi-source history, invalid source index, no version ID", - multiSource: false, - sourceIndex: ptr.To(int32(999)), - history: &struct{ matchesSourceType bool }{false}, - expectErrorContains: ptr.To("source index 999 not found"), - }, - { - name: "single-source app with multi-source history, valid source index, present version ID", - multiSource: false, - sourceIndex: ptr.To(int32(1)), - history: &struct{ matchesSourceType bool }{false}, - versionId: ptr.To(int32(1)), - }, - { - name: "multi-source app without history, no source index, no version ID", - multiSource: true, - }, - { - name: "multi-source app without history, no source index, missing version ID", - multiSource: true, - versionId: ptr.To(int32(999)), - expectErrorContains: ptr.To("the app has no history"), - }, - { - name: "multi-source app without history, present source index, no version ID", - multiSource: true, - sourceIndex: ptr.To(int32(1)), - }, - { - name: "multi-source app without history, invalid source index, no version ID", - multiSource: true, - sourceIndex: ptr.To(int32(999)), - expectErrorContains: ptr.To("source index 999 not found"), - }, - { - name: "multi-source app with matching history, no source index, no version ID", - multiSource: true, - history: &struct{ matchesSourceType bool }{true}, - }, - { - name: "multi-source app with matching history, no source index, missing version ID", - multiSource: true, - history: &struct{ matchesSourceType bool }{true}, - versionId: ptr.To(int32(999)), - expectErrorContains: ptr.To("history not found for version ID 999"), - }, - { - name: "multi-source app with matching history, no source index, present version ID", - multiSource: true, - history: &struct{ matchesSourceType bool }{true}, - versionId: ptr.To(int32(1)), - }, - { - name: "multi-source app with single-source history, no source index, no version ID", - multiSource: true, - history: &struct{ matchesSourceType bool }{false}, - }, - { - name: "multi-source app with single-source history, no source index, missing version ID", - multiSource: true, - history: &struct{ matchesSourceType bool }{false}, - versionId: ptr.To(int32(999)), - expectErrorContains: ptr.To("history not found for version ID 999"), - }, - { - name: "multi-source app with single-source history, no source index, present version ID", - multiSource: true, - history: &struct{ matchesSourceType bool }{false}, - versionId: ptr.To(int32(1)), - }, - { - name: "multi-source app with single-source history, source index 1, no version ID", - multiSource: true, - sourceIndex: ptr.To(int32(1)), - history: &struct{ matchesSourceType bool }{false}, - }, - { - name: "multi-source app with single-source history, invalid source index, no version ID", - multiSource: true, - sourceIndex: ptr.To(int32(999)), - history: &struct{ matchesSourceType bool }{false}, - expectErrorContains: ptr.To("source index 999 not found"), - }, - { - name: "multi-source app with single-source history, valid source index, present version ID", - multiSource: true, - sourceIndex: ptr.To(int32(0)), - history: &struct{ matchesSourceType bool }{false}, - versionId: ptr.To(int32(1)), - }, - { - name: "multi-source app with single-source history, source index 1, present version ID", - multiSource: true, - sourceIndex: ptr.To(int32(1)), - history: &struct{ matchesSourceType bool }{false}, - versionId: ptr.To(int32(1)), - expectErrorContains: ptr.To("source index 1 not found"), - }, - } - - for _, tc := range testCases { - tcc := tc - t.Run(tcc.name, func(t *testing.T) { - t.Parallel() - - app := singleSourceApp.DeepCopy() - if tcc.multiSource { - app = multiSourceApp.DeepCopy() - } - if tcc.history != nil { - if tcc.history.matchesSourceType { - if tcc.multiSource { - app.Status.History = multiSourceHistory - } else { - app.Status.History = singleSourceHistory - } - } else { - if tcc.multiSource { - app.Status.History = singleSourceHistory - } else { - app.Status.History = multiSourceHistory - } - } - } - - s := newTestAppServer(t, app) - - request := &application.RevisionMetadataQuery{ - Name: ptr.To(app.Name), - Revision: ptr.To("HEAD"), - SourceIndex: tcc.sourceIndex, - VersionId: tcc.versionId, - } - - _, err := s.RevisionMetadata(context.Background(), request) - if tcc.expectErrorContains != nil { - require.ErrorContains(t, err, *tcc.expectErrorContains) - } else { - require.NoError(t, err) - } - }) - } -} diff --git a/server/application/broadcaster.go b/server/application/broadcaster.go index c8a562123ad19..e791e6e61de18 100644 --- a/server/application/broadcaster.go +++ b/server/application/broadcaster.go @@ -26,7 +26,7 @@ func (s *subscriber) matches(event *appv1.ApplicationWatchEvent) bool { // Broadcaster is an interface for broadcasting application informer watch events to multiple subscribers. type Broadcaster interface { Subscribe(ch chan *appv1.ApplicationWatchEvent, filters ...func(event *appv1.ApplicationWatchEvent) bool) func() - OnAdd(interface{}, bool) + OnAdd(interface{}) OnUpdate(interface{}, interface{}) OnDelete(interface{}) } @@ -76,7 +76,7 @@ func (b *broadcasterHandler) Subscribe(ch chan *appv1.ApplicationWatchEvent, fil } } -func (b *broadcasterHandler) OnAdd(obj interface{}, _ bool) { +func (b *broadcasterHandler) OnAdd(obj interface{}) { if app, ok := obj.(*appv1.Application); ok { b.notify(&appv1.ApplicationWatchEvent{Application: *app, Type: watch.Added}) } diff --git a/server/application/broadcaster_test.go b/server/application/broadcaster_test.go index 32af5f60cadc7..c95d65eb9ddc3 100644 --- a/server/application/broadcaster_test.go +++ b/server/application/broadcaster_test.go @@ -49,4 +49,5 @@ func TestBroadcasterHandler_ReceiveEvents(t *testing.T) { return } } + } diff --git a/server/application/logs.go b/server/application/logs.go index 778f04edec66e..1a840e54879b3 100644 --- a/server/application/logs.go +++ b/server/application/logs.go @@ -2,7 +2,6 @@ package application import ( "bufio" - "errors" "io" "strings" "sync" @@ -23,13 +22,13 @@ func parseLogsStream(podName string, stream io.ReadCloser, ch chan logEntry) { eof := false for !eof { line, err := bufReader.ReadString('\n') - if err != nil && errors.Is(err, io.EOF) { + if err == io.EOF { eof = true // stop if we reached end of stream and the next line is empty if line == "" { break } - } else if err != nil && !errors.Is(err, io.EOF) { + } else if err != nil && err != io.EOF { ch <- logEntry{err: err} break } @@ -47,6 +46,7 @@ func parseLogsStream(podName string, stream io.ReadCloser, ch chan logEntry) { for _, line := range strings.Split(lines, "\r") { ch <- logEntry{line: line, timeStamp: logTime, podName: podName} } + } } @@ -144,8 +144,8 @@ func mergeLogStreams(streams []chan logEntry, bufferingDuration time.Duration) c _ = send(true) - ticker.Stop() close(merged) + ticker.Stop() }() return merged } diff --git a/server/application/logs_test.go b/server/application/logs_test.go index 76bd5df134ae9..97ee4488b991d 100644 --- a/server/application/logs_test.go +++ b/server/application/logs_test.go @@ -53,6 +53,7 @@ func TestParseLogsStream_ParsingError(t *testing.T) { } func TestMergeLogStreams(t *testing.T) { + first := make(chan logEntry) go func() { parseLogsStream("first", io.NopCloser(strings.NewReader(`2021-02-09T00:00:01Z 1 diff --git a/server/application/mocks/Broadcaster.go b/server/application/mocks/Broadcaster.go index 2e5f13bd88c8b..88d682315a715 100644 --- a/server/application/mocks/Broadcaster.go +++ b/server/application/mocks/Broadcaster.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.13.1. DO NOT EDIT. package mocks @@ -12,9 +12,9 @@ type Broadcaster struct { mock.Mock } -// OnAdd provides a mock function with given fields: _a0, _a1 -func (_m *Broadcaster) OnAdd(_a0 interface{}, _a1 bool) { - _m.Called(_a0, _a1) +// OnAdd provides a mock function with given fields: _a0 +func (_m *Broadcaster) OnAdd(_a0 interface{}) { + _m.Called(_a0) } // OnDelete provides a mock function with given fields: _a0 @@ -38,10 +38,6 @@ func (_m *Broadcaster) Subscribe(ch chan *v1alpha1.ApplicationWatchEvent, filter _ca = append(_ca, _va...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for Subscribe") - } - var r0 func() if rf, ok := ret.Get(0).(func(chan *v1alpha1.ApplicationWatchEvent, ...func(*v1alpha1.ApplicationWatchEvent) bool) func()); ok { r0 = rf(ch, filters...) @@ -54,12 +50,13 @@ func (_m *Broadcaster) Subscribe(ch chan *v1alpha1.ApplicationWatchEvent, filter return r0 } -// NewBroadcaster creates a new instance of Broadcaster. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewBroadcaster(t interface { +type mockConstructorTestingTNewBroadcaster interface { mock.TestingT Cleanup(func()) -}) *Broadcaster { +} + +// NewBroadcaster creates a new instance of Broadcaster. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewBroadcaster(t mockConstructorTestingTNewBroadcaster) *Broadcaster { mock := &Broadcaster{} mock.Mock.Test(t) diff --git a/server/application/terminal.go b/server/application/terminal.go index e6ddc6d832df3..9886992e81b9e 100644 --- a/server/application/terminal.go +++ b/server/application/terminal.go @@ -6,6 +6,7 @@ import ( "net/http" "time" + util_session "github.com/argoproj/argo-cd/v2/util/session" "github.com/argoproj/gitops-engine/pkg/utils/kube" log "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" @@ -16,8 +17,6 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" - util_session "github.com/argoproj/argo-cd/v2/util/session" - appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" servercache "github.com/argoproj/argo-cd/v2/server/cache" @@ -33,32 +32,28 @@ import ( type terminalHandler struct { appLister applisters.ApplicationLister db db.ArgoDB + enf *rbac.Enforcer cache *servercache.Cache appResourceTreeFn func(ctx context.Context, app *appv1.Application) (*appv1.ApplicationTree, error) allowedShells []string namespace string enabledNamespaces []string sessionManager *util_session.SessionManager - terminalOptions *TerminalOptions -} - -type TerminalOptions struct { - DisableAuth bool - Enf *rbac.Enforcer } // NewHandler returns a new terminal handler. -func NewHandler(appLister applisters.ApplicationLister, namespace string, enabledNamespaces []string, db db.ArgoDB, cache *servercache.Cache, appResourceTree AppResourceTreeFn, allowedShells []string, sessionManager *sessionmgr.SessionManager, terminalOptions *TerminalOptions) *terminalHandler { +func NewHandler(appLister applisters.ApplicationLister, namespace string, enabledNamespaces []string, db db.ArgoDB, enf *rbac.Enforcer, cache *servercache.Cache, + appResourceTree AppResourceTreeFn, allowedShells []string, sessionManager *util_session.SessionManager) *terminalHandler { return &terminalHandler{ appLister: appLister, db: db, + enf: enf, cache: cache, appResourceTreeFn: appResourceTree, allowedShells: allowedShells, namespace: namespace, enabledNamespaces: enabledNamespaces, sessionManager: sessionManager, - terminalOptions: terminalOptions, } } @@ -149,20 +144,18 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() appRBACName := security.RBACName(s.namespace, project, appNamespace, app) - if err := s.terminalOptions.Enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACName); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACName); err != nil { http.Error(w, err.Error(), http.StatusUnauthorized) return } - if err := s.terminalOptions.Enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, appRBACName); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, appRBACName); err != nil { http.Error(w, err.Error(), http.StatusUnauthorized) return } - fieldLog := log.WithFields(log.Fields{ - "application": app, "userName": sessionmgr.Username(ctx), "container": container, - "podName": podName, "namespace": namespace, "project": project, "appNamespace": appNamespace, - }) + fieldLog := log.WithFields(log.Fields{"application": app, "userName": sessionmgr.Username(ctx), "container": container, + "podName": podName, "namespace": namespace, "project": project, "appNamespace": appNamespace}) a, err := s.appLister.Applications(ns).Get(app) if err != nil { @@ -232,7 +225,7 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fieldLog.Info("terminal session starting") - session, err := newTerminalSession(ctx, w, r, nil, s.sessionManager, appRBACName, s.terminalOptions) + session, err := newTerminalSession(ctx, w, r, nil, s.sessionManager, appRBACName, s.enf) if err != nil { http.Error(w, "Failed to start terminal session", http.StatusBadRequest) return diff --git a/server/application/websocket.go b/server/application/websocket.go index 86c85749d803b..ab3cce72f1239 100644 --- a/server/application/websocket.go +++ b/server/application/websocket.go @@ -11,6 +11,7 @@ import ( "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/server/rbacpolicy" httputil "github.com/argoproj/argo-cd/v2/util/http" + "github.com/argoproj/argo-cd/v2/util/rbac" util_session "github.com/argoproj/argo-cd/v2/util/session" "github.com/gorilla/websocket" @@ -44,7 +45,7 @@ type terminalSession struct { sessionManager *util_session.SessionManager token *string appRBACName string - terminalOpts *TerminalOptions + enf *rbac.Enforcer } // getToken get auth token from web socket request @@ -54,7 +55,7 @@ func getToken(r *http.Request) (string, error) { } // newTerminalSession create terminalSession -func newTerminalSession(ctx context.Context, w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager, appRBACName string, terminalOpts *TerminalOptions) (*terminalSession, error) { +func newTerminalSession(ctx context.Context, w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager, appRBACName string, enf *rbac.Enforcer) (*terminalSession, error) { token, err := getToken(r) if err != nil { return nil, err @@ -73,7 +74,7 @@ func newTerminalSession(ctx context.Context, w http.ResponseWriter, r *http.Requ sessionManager: sessionManager, token: &token, appRBACName: appRBACName, - terminalOpts: terminalOpts, + enf: enf, } return session, nil } @@ -139,7 +140,7 @@ func (t *terminalSession) validatePermissions(p []byte) (int, error) { Operation: "stdout", Data: "Permission denied", }) - if err := t.terminalOpts.Enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, t.appRBACName); err != nil { + if err := t.enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, t.appRBACName); err != nil { err = t.wsConn.WriteMessage(websocket.TextMessage, permissionDeniedMessage) if err != nil { log.Errorf("permission denied message err: %v", err) @@ -147,7 +148,7 @@ func (t *terminalSession) validatePermissions(p []byte) (int, error) { return copy(p, EndOfTransmission), permissionDeniedErr } - if err := t.terminalOpts.Enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, t.appRBACName); err != nil { + if err := t.enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, t.appRBACName); err != nil { err = t.wsConn.WriteMessage(websocket.TextMessage, permissionDeniedMessage) if err != nil { log.Errorf("permission denied message err: %v", err) @@ -157,12 +158,8 @@ func (t *terminalSession) validatePermissions(p []byte) (int, error) { return 0, nil } -func (t *terminalSession) performValidationsAndReconnect(p []byte) (int, error) { - // In disable auth mode, no point verifying the token or validating permissions - if t.terminalOpts.DisableAuth { - return 0, nil - } - +// Read called in a loop from remotecommand as long as the process is running +func (t *terminalSession) Read(p []byte) (int, error) { // check if token still valid _, newToken, err := t.sessionManager.VerifyToken(*t.token) // err in case if token is revoked, newToken in case if refresh happened @@ -170,17 +167,9 @@ func (t *terminalSession) performValidationsAndReconnect(p []byte) (int, error) // need to send reconnect code in case if token was refreshed return t.reconnect() } - code, err := t.validatePermissions(p) - if err != nil { - return code, err - } - - return 0, nil -} -// Read called in a loop from remotecommand as long as the process is running -func (t *terminalSession) Read(p []byte) (int, error) { - code, err := t.performValidationsAndReconnect(p) + // validate permissions + code, err := t.validatePermissions(p) if err != nil { return code, err } diff --git a/server/application/websocket_test.go b/server/application/websocket_test.go index 0d048a1727d1b..ee80f6481d7a1 100644 --- a/server/application/websocket_test.go +++ b/server/application/websocket_test.go @@ -23,7 +23,7 @@ import ( ) func newTestTerminalSession(w http.ResponseWriter, r *http.Request) terminalSession { - upgrader := websocket.Upgrader{} + var upgrader = websocket.Upgrader{} c, err := upgrader.Upgrade(w, r, nil) if err != nil { return terminalSession{} @@ -85,45 +85,6 @@ func TestReconnect(t *testing.T) { assert.Equal(t, ReconnectMessage, message.Data) } -func testServerConnection(t *testing.T, testFunc func(w http.ResponseWriter, r *http.Request), expectPermissionDenied bool) { - s := httptest.NewServer(http.HandlerFunc(testFunc)) - defer s.Close() - - u := "ws" + strings.TrimPrefix(s.URL, "http") - - // Connect to the server - ws, _, err := websocket.DefaultDialer.Dial(u, nil) - require.NoError(t, err) - - defer ws.Close() - if expectPermissionDenied { - _, p, _ := ws.ReadMessage() - - var message TerminalMessage - - err = json.Unmarshal(p, &message) - - require.NoError(t, err) - assert.Equal(t, "Permission denied", message.Data) - } -} - -func TestVerifyAndReconnectDisableAuthTrue(t *testing.T) { - validate := func(w http.ResponseWriter, r *http.Request) { - ts := newTestTerminalSession(w, r) - // Currently testing only the usecase of disableAuth: true since the disableAuth: false case - // requires a valid token to be passed in the request. - // Note that running with disableAuth: false will surprisingly succeed as well, because - // the underlying token nil pointer dereference is swallowed in a location I didn't find, - // or even swallowed by the test framework. - ts.terminalOpts = &TerminalOptions{DisableAuth: true} - code, err := ts.performValidationsAndReconnect([]byte{}) - assert.Equal(t, 0, code) - require.NoError(t, err) - } - testServerConnection(t, validate, false) -} - func TestValidateWithAdminPermissions(t *testing.T) { validate := func(w http.ResponseWriter, r *http.Request) { enf := newEnforcer() @@ -133,7 +94,7 @@ func TestValidateWithAdminPermissions(t *testing.T) { return true }) ts := newTestTerminalSession(w, r) - ts.terminalOpts = &TerminalOptions{Enf: enf} + ts.enf = enf ts.appRBACName = "test" // nolint:staticcheck ts.ctx = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"admin"}}) @@ -141,7 +102,16 @@ func TestValidateWithAdminPermissions(t *testing.T) { require.NoError(t, err) } - testServerConnection(t, validate, false) + s := httptest.NewServer(http.HandlerFunc(validate)) + defer s.Close() + + u := "ws" + strings.TrimPrefix(s.URL, "http") + + // Connect to the server + ws, _, err := websocket.DefaultDialer.Dial(u, nil) + require.NoError(t, err) + + defer ws.Close() } func TestValidateWithoutPermissions(t *testing.T) { @@ -153,7 +123,7 @@ func TestValidateWithoutPermissions(t *testing.T) { return false }) ts := newTestTerminalSession(w, r) - ts.terminalOpts = &TerminalOptions{Enf: enf} + ts.enf = enf ts.appRBACName = "test" // nolint:staticcheck ts.ctx = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"test"}}) @@ -162,5 +132,23 @@ func TestValidateWithoutPermissions(t *testing.T) { assert.Equal(t, permissionDeniedErr.Error(), err.Error()) } - testServerConnection(t, validate, true) + s := httptest.NewServer(http.HandlerFunc(validate)) + defer s.Close() + + u := "ws" + strings.TrimPrefix(s.URL, "http") + + // Connect to the server + ws, _, err := websocket.DefaultDialer.Dial(u, nil) + require.NoError(t, err) + + defer ws.Close() + + _, p, _ := ws.ReadMessage() + + var message TerminalMessage + + err = json.Unmarshal(p, &message) + + require.NoError(t, err) + assert.Equal(t, "Permission denied", message.Data) } diff --git a/server/applicationset/applicationset.go b/server/applicationset/applicationset.go index 259b59c911321..d67815bd9a53d 100644 --- a/server/applicationset/applicationset.go +++ b/server/applicationset/applicationset.go @@ -1,7 +1,6 @@ package applicationset import ( - "bytes" "context" "fmt" "reflect" @@ -18,26 +17,18 @@ import ( apierr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" - "sigs.k8s.io/controller-runtime/pkg/client" - appsettemplate "github.com/argoproj/argo-cd/v2/applicationset/controllers/template" - "github.com/argoproj/argo-cd/v2/applicationset/generators" - "github.com/argoproj/argo-cd/v2/applicationset/services" - appsetstatus "github.com/argoproj/argo-cd/v2/applicationset/status" appsetutils "github.com/argoproj/argo-cd/v2/applicationset/utils" "github.com/argoproj/argo-cd/v2/pkg/apiclient/applicationset" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" - repoapiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/server/rbacpolicy" "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/github_app" "github.com/argoproj/argo-cd/v2/util/rbac" "github.com/argoproj/argo-cd/v2/util/security" "github.com/argoproj/argo-cd/v2/util/session" @@ -45,36 +36,24 @@ import ( ) type Server struct { - ns string - db db.ArgoDB - enf *rbac.Enforcer - k8sClient kubernetes.Interface - dynamicClient dynamic.Interface - client client.Client - repoClientSet repoapiclient.Clientset - appclientset appclientset.Interface - appsetInformer cache.SharedIndexInformer - appsetLister applisters.ApplicationSetLister - projLister applisters.AppProjectNamespaceLister - auditLogger *argo.AuditLogger - settings *settings.SettingsManager - projectLock sync.KeyLock - enabledNamespaces []string - GitSubmoduleEnabled bool - EnableNewGitFileGlobbing bool - ScmRootCAPath string - AllowedScmProviders []string - EnableScmProviders bool + ns string + db db.ArgoDB + enf *rbac.Enforcer + appclientset appclientset.Interface + appsetInformer cache.SharedIndexInformer + appsetLister applisters.ApplicationSetLister + projLister applisters.AppProjectNamespaceLister + auditLogger *argo.AuditLogger + settings *settings.SettingsManager + projectLock sync.KeyLock + enabledNamespaces []string } // NewServer returns a new instance of the ApplicationSet service func NewServer( db db.ArgoDB, kubeclientset kubernetes.Interface, - dynamicClientset dynamic.Interface, - kubeControllerClientset client.Client, enf *rbac.Enforcer, - repoClientSet repoapiclient.Clientset, appclientset appclientset.Interface, appsetInformer cache.SharedIndexInformer, appsetLister applisters.ApplicationSetLister, @@ -83,39 +62,25 @@ func NewServer( namespace string, projectLock sync.KeyLock, enabledNamespaces []string, - gitSubmoduleEnabled bool, - enableNewGitFileGlobbing bool, - scmRootCAPath string, - allowedScmProviders []string, - enableScmProviders bool, - enableK8sEvent []string, ) applicationset.ApplicationSetServiceServer { s := &Server{ - ns: namespace, - db: db, - enf: enf, - dynamicClient: dynamicClientset, - client: kubeControllerClientset, - k8sClient: kubeclientset, - repoClientSet: repoClientSet, - appclientset: appclientset, - appsetInformer: appsetInformer, - appsetLister: appsetLister, - projLister: projLister, - settings: settings, - projectLock: projectLock, - auditLogger: argo.NewAuditLogger(namespace, kubeclientset, "argocd-server", enableK8sEvent), - enabledNamespaces: enabledNamespaces, - GitSubmoduleEnabled: gitSubmoduleEnabled, - EnableNewGitFileGlobbing: enableNewGitFileGlobbing, - ScmRootCAPath: scmRootCAPath, - AllowedScmProviders: allowedScmProviders, - EnableScmProviders: enableScmProviders, + ns: namespace, + db: db, + enf: enf, + appclientset: appclientset, + appsetInformer: appsetInformer, + appsetLister: appsetLister, + projLister: projLister, + settings: settings, + projectLock: projectLock, + auditLogger: argo.NewAuditLogger(namespace, kubeclientset, "argocd-server"), + enabledNamespaces: enabledNamespaces, } return s } func (s *Server) Get(ctx context.Context, q *applicationset.ApplicationSetGetQuery) (*v1alpha1.ApplicationSet, error) { + namespace := s.appsetNamespaceOrDefault(q.AppsetNamespace) if !s.isNamespaceEnabled(namespace) { @@ -123,6 +88,7 @@ func (s *Server) Get(ctx context.Context, q *applicationset.ApplicationSetGetQue } a, err := s.appsetLister.ApplicationSets(namespace).Get(q.Name) + if err != nil { return nil, fmt.Errorf("error getting ApplicationSet: %w", err) } @@ -153,6 +119,7 @@ func (s *Server) List(ctx context.Context, q *applicationset.ApplicationSetListQ newItems := make([]v1alpha1.ApplicationSet, 0) for _, a := range appsets { + // Skip any application that is neither in the conrol plane's namespace // nor in the list of enabled namespaces. if !security.IsNamespaceEnabled(a.Namespace, s.ns, s.enabledNamespaces) { @@ -178,6 +145,7 @@ func (s *Server) List(ctx context.Context, q *applicationset.ApplicationSetListQ Items: newItems, } return appsetList, nil + } func (s *Server) Create(ctx context.Context, q *applicationset.ApplicationSetCreateRequest) (*v1alpha1.ApplicationSet, error) { @@ -187,7 +155,7 @@ func (s *Server) Create(ctx context.Context, q *applicationset.ApplicationSetCre return nil, fmt.Errorf("error creating ApplicationSets: ApplicationSets is nil in request") } - projectName, err := s.validateAppSet(appset) + projectName, err := s.validateAppSet(ctx, appset) if err != nil { return nil, fmt.Errorf("error validating ApplicationSets: %w", err) } @@ -199,24 +167,7 @@ func (s *Server) Create(ctx context.Context, q *applicationset.ApplicationSetCre } if err := s.checkCreatePermissions(ctx, appset, projectName); err != nil { - return nil, fmt.Errorf("error checking create permissions for ApplicationSets %s : %w", appset.Name, err) - } - - if q.GetDryRun() { - apps, err := s.generateApplicationSetApps(ctx, log.WithField("applicationset", appset.Name), *appset, namespace) - if err != nil { - return nil, fmt.Errorf("unable to generate Applications of ApplicationSet: %w", err) - } - - statusMap := appsetstatus.GetResourceStatusMap(appset) - statusMap = appsetstatus.BuildResourceStatus(statusMap, apps) - - statuses := []v1alpha1.ResourceStatus{} - for _, status := range statusMap { - statuses = append(statuses, status) - } - appset.Status.Resources = statuses - return appset, nil + return nil, fmt.Errorf("error checking create permissions for ApplicationSets %s : %s", appset.Name, err) } s.projectLock.RLock(projectName) @@ -262,29 +213,8 @@ func (s *Server) Create(ctx context.Context, q *applicationset.ApplicationSetCre return updated, nil } -func (s *Server) generateApplicationSetApps(ctx context.Context, logEntry *log.Entry, appset v1alpha1.ApplicationSet, namespace string) ([]v1alpha1.Application, error) { - argoCDDB := s.db - - scmConfig := generators.NewSCMConfig(s.ScmRootCAPath, s.AllowedScmProviders, s.EnableScmProviders, github_app.NewAuthCredentials(argoCDDB.(db.RepoCredsDB))) - - getRepository := func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) { - return s.db.GetRepository(ctx, url, project) - } - argoCDService, err := services.NewArgoCDService(getRepository, s.GitSubmoduleEnabled, s.repoClientSet, s.EnableNewGitFileGlobbing) - if err != nil { - return nil, fmt.Errorf("error creating ArgoCDService: %w", err) - } - - appSetGenerators := generators.GetGenerators(ctx, s.client, s.k8sClient, namespace, argoCDService, s.dynamicClient, scmConfig) - - apps, _, err := appsettemplate.GenerateApplications(logEntry, appset, appSetGenerators, &appsetutils.Render{}, s.client) - if err != nil { - return nil, fmt.Errorf("error generating applications: %w", err) - } - return apps, nil -} - func (s *Server) updateAppSet(appset *v1alpha1.ApplicationSet, newAppset *v1alpha1.ApplicationSet, ctx context.Context, merge bool) (*v1alpha1.ApplicationSet, error) { + if appset != nil && appset.Spec.Template.Spec.Project != newAppset.Spec.Template.Spec.Project { // When changing projects, caller must have applicationset create and update privileges in new project // NOTE: the update check was already verified in the caller to this function @@ -326,6 +256,7 @@ func (s *Server) updateAppSet(appset *v1alpha1.ApplicationSet, newAppset *v1alph } func (s *Server) Delete(ctx context.Context, q *applicationset.ApplicationSetDeleteRequest) (*applicationset.ApplicationSetResponse, error) { + namespace := s.appsetNamespaceOrDefault(q.AppsetNamespace) appset, err := s.appclientset.ArgoprojV1alpha1().ApplicationSets(namespace).Get(ctx, q.Name, metav1.GetOptions{}) @@ -346,88 +277,10 @@ func (s *Server) Delete(ctx context.Context, q *applicationset.ApplicationSetDel } s.logAppSetEvent(appset, ctx, argo.EventReasonResourceDeleted, "deleted ApplicationSets") return &applicationset.ApplicationSetResponse{}, nil -} -func (s *Server) ResourceTree(ctx context.Context, q *applicationset.ApplicationSetTreeQuery) (*v1alpha1.ApplicationSetTree, error) { - namespace := s.appsetNamespaceOrDefault(q.AppsetNamespace) - - if !s.isNamespaceEnabled(namespace) { - return nil, security.NamespaceNotPermittedError(namespace) - } - - a, err := s.appclientset.ArgoprojV1alpha1().ApplicationSets(namespace).Get(ctx, q.Name, metav1.GetOptions{}) - if err != nil { - return nil, fmt.Errorf("error getting ApplicationSet: %w", err) - } - if err = s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplicationSets, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { - return nil, err - } - - return s.buildApplicationSetTree(a) } -func (s *Server) Generate(ctx context.Context, q *applicationset.ApplicationSetGenerateRequest) (*applicationset.ApplicationSetGenerateResponse, error) { - appset := q.GetApplicationSet() - - if appset == nil { - return nil, fmt.Errorf("error creating ApplicationSets: ApplicationSets is nil in request") - } - namespace := s.appsetNamespaceOrDefault(appset.Namespace) - - if !s.isNamespaceEnabled(namespace) { - return nil, security.NamespaceNotPermittedError(namespace) - } - projectName, err := s.validateAppSet(appset) - if err != nil { - return nil, fmt.Errorf("error validating ApplicationSets: %w", err) - } - if err := s.checkCreatePermissions(ctx, appset, projectName); err != nil { - return nil, fmt.Errorf("error checking create permissions for ApplicationSets %s : %w", appset.Name, err) - } - - logs := bytes.NewBuffer(nil) - logger := log.New() - logger.SetOutput(logs) - - apps, err := s.generateApplicationSetApps(ctx, logger.WithField("applicationset", appset.Name), *appset, namespace) - if err != nil { - return nil, fmt.Errorf("unable to generate Applications of ApplicationSet: %w\n%s", err, logs.String()) - } - res := &applicationset.ApplicationSetGenerateResponse{} - for i := range apps { - res.Applications = append(res.Applications, &apps[i]) - } - return res, nil -} - -func (s *Server) buildApplicationSetTree(a *v1alpha1.ApplicationSet) (*v1alpha1.ApplicationSetTree, error) { - var tree v1alpha1.ApplicationSetTree - - gvk := v1alpha1.ApplicationSetSchemaGroupVersionKind - parentRefs := []v1alpha1.ResourceRef{ - {Group: gvk.Group, Version: gvk.Version, Kind: gvk.Kind, Name: a.Name, Namespace: a.Namespace, UID: string(a.UID)}, - } - - apps := a.Status.Resources - for _, app := range apps { - tree.Nodes = append(tree.Nodes, v1alpha1.ResourceNode{ - Health: app.Health, - ResourceRef: v1alpha1.ResourceRef{ - Name: app.Name, - Group: app.Group, - Version: app.Version, - Kind: app.Kind, - Namespace: a.Namespace, - }, - ParentRefs: parentRefs, - }) - } - tree.Normalize() - - return &tree, nil -} - -func (s *Server) validateAppSet(appset *v1alpha1.ApplicationSet) (string, error) { +func (s *Server) validateAppSet(ctx context.Context, appset *v1alpha1.ApplicationSet) (string, error) { if appset == nil { return "", fmt.Errorf("ApplicationSet cannot be validated for nil value") } @@ -446,6 +299,7 @@ func (s *Server) validateAppSet(appset *v1alpha1.ApplicationSet) (string, error) } func (s *Server) checkCreatePermissions(ctx context.Context, appset *v1alpha1.ApplicationSet, projectName string) error { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplicationSets, rbacpolicy.ActionCreate, appset.RBACName(s.ns)); err != nil { return err } diff --git a/server/applicationset/applicationset.proto b/server/applicationset/applicationset.proto index e2e4663e94e84..2a857d41a00ce 100644 --- a/server/applicationset/applicationset.proto +++ b/server/applicationset/applicationset.proto @@ -37,7 +37,6 @@ message ApplicationSetResponse { message ApplicationSetCreateRequest { github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSet applicationset = 1; bool upsert = 2; - bool dryRun = 3; } @@ -47,38 +46,15 @@ message ApplicationSetDeleteRequest { string appsetNamespace = 2; } -message ApplicationSetTreeQuery { - string name = 1; - // The application set namespace. Default empty is argocd control plane namespace - string appsetNamespace = 2; -} - -// ApplicationSetGetQuery is a query for applicationset resources -message ApplicationSetGenerateRequest { - // the applicationsets - github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSet applicationSet = 1; -} - -// ApplicationSetGenerateResponse is a response for applicationset generate request -message ApplicationSetGenerateResponse { - repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Application applications = 1; -} // ApplicationSetService service ApplicationSetService { + // Get returns an applicationset by name rpc Get (ApplicationSetGetQuery) returns (github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSet) { option (google.api.http).get = "/api/v1/applicationsets/{name}"; } - // Generate generates - rpc Generate (ApplicationSetGenerateRequest) returns (ApplicationSetGenerateResponse) { - option (google.api.http) = { - post: "/api/v1/applicationsets" - body: "*" - }; - } - //List returns list of applicationset rpc List (ApplicationSetListQuery) returns (github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetList) { option (google.api.http).get = "/api/v1/applicationsets"; @@ -97,9 +73,4 @@ service ApplicationSetService { option (google.api.http).delete = "/api/v1/applicationsets/{name}"; } - // ResourceTree returns resource tree - rpc ResourceTree(ApplicationSetTreeQuery) returns (github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetTree) { - option (google.api.http).get = "/api/v1/applicationsets/{name}/resource-tree"; - } - -} +} \ No newline at end of file diff --git a/server/applicationset/applicationset_test.go b/server/applicationset/applicationset_test.go index 0b83dfa2c4c90..c49ddb35a7970 100644 --- a/server/applicationset/applicationset_test.go +++ b/server/applicationset/applicationset_test.go @@ -2,15 +2,11 @@ package applicationset import ( "context" - "sort" "testing" - "github.com/argoproj/gitops-engine/pkg/health" "github.com/argoproj/pkg/sync" "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" "k8s.io/client-go/kubernetes/fake" @@ -22,7 +18,6 @@ import ( 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/argo" "github.com/argoproj/argo-cd/v2/util/assets" "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/argo-cd/v2/util/errors" @@ -35,8 +30,6 @@ const ( fakeRepoURL = "https://git.com/repo.git" ) -var testEnableEventList []string = argo.DefaultEnableEventList() - func fakeRepo() *appsv1.Repository { return &appsv1.Repository{ Repo: fakeRepoURL, @@ -127,7 +120,7 @@ func newTestAppSetServerWithEnforcerConfigure(f func(*rbac.Enforcer), namespace // 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") @@ -148,10 +141,7 @@ func newTestAppSetServerWithEnforcerConfigure(f func(*rbac.Enforcer), namespace server := NewServer( db, kubeclientset, - nil, - nil, enforcer, - nil, fakeAppsClientset, appInformer, factory.Argoproj().V1alpha1().ApplicationSets().Lister(), @@ -160,12 +150,6 @@ func newTestAppSetServerWithEnforcerConfigure(f func(*rbac.Enforcer), namespace testNamespace, sync.NewKeyLock(), []string{testNamespace, "external-namespace"}, - true, - true, - "", - []string{}, - true, - testEnableEventList, ) return server.(*Server) } @@ -195,48 +179,34 @@ func testListAppsetsWithLabels(t *testing.T, appsetQuery applicationset.Applicat label string expectedResult []string }{ - { - testName: "Equality based filtering using '=' operator", + {testName: "Equality based filtering using '=' operator", label: "key1=value1", - expectedResult: []string{"AppSet1"}, - }, - { - testName: "Equality based filtering using '==' operator", + expectedResult: []string{"AppSet1"}}, + {testName: "Equality based filtering using '==' operator", label: "key1==value1", - expectedResult: []string{"AppSet1"}, - }, - { - testName: "Equality based filtering using '!=' operator", + expectedResult: []string{"AppSet1"}}, + {testName: "Equality based filtering using '!=' operator", label: "key1!=value1", - expectedResult: []string{"AppSet2", "AppSet3"}, - }, - { - testName: "Set based filtering using 'in' operator", + 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", + 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", + 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", + expectedResult: []string{"AppSet1", "AppSet2", "AppSet3"}}, + {testName: "Set based filtering using 'not exists' operator", label: "!key2", - expectedResult: []string{"AppSet2", "AppSet3"}, - }, + expectedResult: []string{"AppSet2", "AppSet3"}}, } - // test valid scenarios + //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) - require.NoError(t, err) + assert.NoError(t, err) apps := []string{} for i := range res.Items { apps = append(apps, res.Items[i].Name) @@ -250,18 +220,14 @@ func testListAppsetsWithLabels(t *testing.T, appsetQuery applicationset.Applicat label string errorMesage string }{ - { - testName: "Set based filtering using '>' operator", + {testName: "Set based filtering using '>' operator", label: "key1>value1", - errorMesage: "error parsing the selector", - }, - { - testName: "Set based filtering using '<' operator", + 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*")`) - revisionRectWidthPattern = regexp.MustCompile(`(id="revisionRect" .* 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*")`) - revisionTextXCoodPattern = regexp.MustCompile(`(id="revisionText" x=)("\d*")`) - svgHeightPattern = regexp.MustCompile(`^( 0 && len(errs) != 0 { @@ -166,7 +139,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } } - // Sample url: http://localhost:8080/api/badge?name=123&revision=true + //Sample url: http://localhost:8080/api/badge?name=123&revision=true if revisionParam, ok := r.URL.Query()["revision"]; ok && enabled && strings.EqualFold(revisionParam[0], "true") { revisionEnabled = true } @@ -200,70 +173,23 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { badge = replaceFirstGroupSubMatch(rightTextPattern, badge, rightText) if !notFound && revisionEnabled && revision != "" { - // Enable display of revision components + // Increase width of SVG and enable display of revision components + badge = svgWidthPattern.ReplaceAllString(badge, fmt.Sprintf(` 7 { - displayedRevision = revision[:7] - svgWidth = svgWidthWithRevision - } else { - svgWidth = svgWidthWithFullRevision - } - - badge = replaceFirstGroupSubMatch(revisionTextPattern, badge, fmt.Sprintf("(%s)", displayedRevision)) - } - - if widthParam, ok := r.URL.Query()["width"]; ok && enabled { - width, err := strconv.Atoi(widthParam[0]) - if err == nil { - svgWidth = width - adjustWidth = true + shortRevision := revision + if len(shortRevision) > 7 { + shortRevision = shortRevision[:7] } - } - - // Increase width of SVG - if adjustWidth { - badge = svgWidthPattern.ReplaceAllString(badge, fmt.Sprintf(`: error calling index: cannot index slice/array with nil", - }, - }, } for _, tc := range testTable { - tcc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - objs := CreateDeepLinksObject(tcc.resourceObj, tcc.appObj, tcc.clusterObj, tcc.projectObj) - output, err := EvaluateDeepLinksResponse(objs, tcc.appObj.GetName(), tcc.inputLinks) - assert.Equal(t, tcc.error, err, strings.Join(err, ",")) - assert.True(t, reflect.DeepEqual(output.Items, tcc.outputLinks)) - }) + objs := CreateDeepLinksObject(tc.resourceObj, tc.appObj, tc.clusterObj, tc.projectObj) + output, err := EvaluateDeepLinksResponse(objs, tc.appObj.GetName(), tc.inputLinks) + assert.Equal(t, tc.error, err, strings.Join(err, ",")) + assert.Equal(t, reflect.DeepEqual(output.Items, tc.outputLinks), true) } } diff --git a/server/extension/extension.go b/server/extension/extension.go index 706dfbbb31abd..aca924620756c 100644 --- a/server/extension/extension.go +++ b/server/extension/extension.go @@ -12,7 +12,6 @@ import ( "strings" "time" - "github.com/felixge/httpsnoop" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" @@ -22,7 +21,6 @@ import ( "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/argo-cd/v2/util/security" - "github.com/argoproj/argo-cd/v2/util/session" "github.com/argoproj/argo-cd/v2/util/settings" ) @@ -51,7 +49,7 @@ const ( // 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 overridden by the extension + // the client, its value will be overriden by the extension // handler. // // Example: @@ -62,17 +60,9 @@ const ( // 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 overridden by the extension + // the client, its value will be overriden by the extension // handler. HeaderArgoCDTargetClusterName = "Argocd-Target-Cluster-Name" - - // HeaderArgoCDUsername is the header name that defines the logged - // in user authenticated by Argo CD. - HeaderArgoCDUsername = "Argocd-Username" - - // HeaderArgoCDGroups is the header name that provides the 'groups' - // claim from the users authenticated in Argo CD. - HeaderArgoCDGroups = "Argocd-User-Groups" ) // RequestResources defines the authorization scope for @@ -101,7 +91,7 @@ func ValidateHeaders(r *http.Request) (*RequestResources, error) { } appNamespace, appName, err := getAppName(appHeader) if err != nil { - return nil, fmt.Errorf("error getting app details: %w", err) + return nil, fmt.Errorf("error getting app details: %s", err) } if !argo.IsValidNamespaceName(appNamespace) { return nil, errors.New("invalid value for namespace") @@ -274,34 +264,6 @@ func (p *DefaultProjectGetter) GetClusters(project string) ([]*v1alpha1.Cluster, return p.db.GetProjectClusters(context.TODO(), project) } -// UserGetter defines the contract to retrieve info from the logged in user. -type UserGetter interface { - GetUser(ctx context.Context) string - GetGroups(ctx context.Context) []string -} - -// DefaultUserGetter is the main UserGetter implementation. -type DefaultUserGetter struct { - policyEnf *rbacpolicy.RBACPolicyEnforcer -} - -// NewDefaultUserGetter return a new default UserGetter -func NewDefaultUserGetter(policyEnf *rbacpolicy.RBACPolicyEnforcer) *DefaultUserGetter { - return &DefaultUserGetter{ - policyEnf: policyEnf, - } -} - -// GetUser will return the current logged in user -func (u *DefaultUserGetter) GetUser(ctx context.Context) string { - return session.Username(ctx) -} - -// GetGroups will return the groups associated with the logged in user. -func (u *DefaultUserGetter) GetGroups(ctx context.Context) []string { - return session.Groups(ctx, u.policyEnf.GetScopes()) -} - // ApplicationGetter defines the contract to retrieve the application resource. type ApplicationGetter interface { Get(ns, name string) (*v1alpha1.Application, error) @@ -319,7 +281,7 @@ func NewDefaultApplicationGetter(al applisters.ApplicationLister) *DefaultApplic } } -// Get will retrieve the application resource for the given namespace and name. +// Get will retrieve the application resorce for the given namespace and name. func (a *DefaultApplicationGetter) Get(ns, name string) (*v1alpha1.Application, error) { return a.appLister.Applications(ns).Get(name) } @@ -338,31 +300,16 @@ type Manager struct { project ProjectGetter rbac RbacEnforcer registry ExtensionRegistry - metricsReg ExtensionMetricsRegistry - userGetter UserGetter -} - -// 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(log *log.Entry, sg SettingsGetter, ag ApplicationGetter, pg ProjectGetter, rbac RbacEnforcer, ug UserGetter) *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, - userGetter: ug, } } @@ -409,23 +356,23 @@ func parseAndValidateConfig(s *settings.ArgoCDSettings) (*ExtensionConfigs, erro extConfigMap := map[string]interface{}{} err := yaml.Unmarshal([]byte(s.ExtensionConfig), &extConfigMap) if err != nil { - return nil, fmt.Errorf("invalid extension config: %w", err) + 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: %w", err) + return nil, fmt.Errorf("error marshaling parsed extension config: %s", err) } configs := ExtensionConfigs{} err = yaml.Unmarshal(parsedExtConfigBytes, &configs) if err != nil { - return nil, fmt.Errorf("invalid parsed extension config: %w", err) + return nil, fmt.Errorf("invalid parsed extension config: %s", err) } err = validateConfigs(&configs) if err != nil { - return nil, fmt.Errorf("validation error: %w", err) + return nil, fmt.Errorf("validation error: %s", err) } return &configs, nil } @@ -476,12 +423,11 @@ func validateConfigs(configs *ExtensionConfigs) error { } // NewProxy will instantiate a new reverse proxy based on the provided -// targetURL and config. It will remove sensitive information from the -// incoming request such as the Authorization and Cookie headers. +// targetURL and config. 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: %w", err) + return nil, fmt.Errorf("failed to parse proxy URL: %s", err) } proxy := &httputil.ReverseProxy{ Transport: newTransport(config), @@ -536,15 +482,11 @@ func applyProxyConfigDefaults(c *ProxyConfig) { func (m *Manager) RegisterExtensions() error { settings, err := m.settings.Get() if err != nil { - return fmt.Errorf("error getting settings: %w", err) - } - if settings.ExtensionConfig == "" { - m.log.Infof("No extensions configured.") - return nil + return fmt.Errorf("error getting settings: %s", err) } err = m.UpdateExtensionRegistry(settings) if err != nil { - return fmt.Errorf("error updating extension registry: %w", err) + return fmt.Errorf("error updating extension registry: %s", err) } return nil } @@ -556,7 +498,7 @@ func (m *Manager) RegisterExtensions() error { func (m *Manager) UpdateExtensionRegistry(s *settings.ArgoCDSettings) error { extConfigs, err := parseAndValidateConfig(s) if err != nil { - return fmt.Errorf("error parsing extension config: %w", err) + return fmt.Errorf("error parsing extension config: %s", err) } extReg := make(map[string]ProxyRegistry) for _, ext := range extConfigs.Extensions { @@ -565,11 +507,11 @@ func (m *Manager) UpdateExtensionRegistry(s *settings.ArgoCDSettings) error { for _, service := range ext.Backend.Services { proxy, err := NewProxy(service.URL, service.Headers, ext.Backend.ProxyConfig) if err != nil { - return fmt.Errorf("error creating proxy: %w", err) + return fmt.Errorf("error creating proxy: %s", err) } err = appendProxy(proxyReg, ext.Name, service, proxy, singleBackend) if err != nil { - return fmt.Errorf("error appending proxy: %w", err) + return fmt.Errorf("error appending proxy: %s", err) } } extReg[ext.Name] = proxyReg @@ -585,8 +527,8 @@ func appendProxy(registry ProxyRegistry, extName string, service ServiceConfig, proxy *httputil.ReverseProxy, - singleBackend bool, -) error { + singleBackend bool) error { + if singleBackend { key := proxyKey(extName, "", "") if _, exist := registry[key]; exist { @@ -632,17 +574,17 @@ func (m *Manager) authorize(ctx context.Context, rr *RequestResources, extName s } 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: %w", err) + 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: %w", extName, err) + 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: %w", err) + return nil, fmt.Errorf("error getting application: %s", err) } if app == nil { return nil, fmt.Errorf("invalid Application provided in the %q header", HeaderArgoCDApplicationName) @@ -654,14 +596,14 @@ func (m *Manager) authorize(ctx context.Context, rr *RequestResources, extName s proj, err := m.project.Get(app.Spec.GetProject()) if err != nil { - return nil, fmt.Errorf("error getting project: %w", err) + 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: %w", err) + 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") @@ -673,6 +615,7 @@ func (m *Manager) authorize(ctx context.Context, rr *RequestResources, extName s // 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, "", "") @@ -738,35 +681,16 @@ func (m *Manager) CallExtension() func(http.ResponseWriter, *http.Request) { return } - user := m.userGetter.GetUser(r.Context()) - groups := m.userGetter.GetGroups(r.Context()) - prepareRequest(r, extName, app, user, groups) + 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) + proxy.ServeHTTP(w, r) } } -func registerMetrics(extName string, metrics httpsnoop.Metrics, extensionMetricsRegistry ExtensionMetricsRegistry) { - if extensionMetricsRegistry != nil { - extensionMetricsRegistry.IncExtensionRequestCounter(extName, metrics.Code) - extensionMetricsRegistry.ObserveExtensionRequestDuration(extName, metrics.Duration) - } -} - -// prepareRequest is responsible for cleaning the incoming request URL removing -// the Argo CD extension API section from it. It provides additional information to -// the backend service appending them in the outgoing request headers. The appended -// headers are: -// - Cluster destination name -// - Cluster destination server -// - Argo CD authenticated username -func prepareRequest(r *http.Request, extName string, app *v1alpha1.Application, username string, groups []string) { +// prepareRequest is reponsible for preparing and cleaning the given +// request, removing sensitive information before forwarding it to the +// proxy extension. +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) @@ -774,15 +698,4 @@ func prepareRequest(r *http.Request, extName string, app *v1alpha1.Application, if app.Spec.Destination.Server != "" { r.Header.Set(HeaderArgoCDTargetClusterURL, app.Spec.Destination.Server) } - if username != "" { - r.Header.Set(HeaderArgoCDUsername, username) - } - if len(groups) > 0 { - r.Header.Set(HeaderArgoCDGroups, strings.Join(groups, ",")) - } -} - -// 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 300e1e89a490d..273779d59ca29 100644 --- a/server/extension/extension_test.go +++ b/server/extension/extension_test.go @@ -8,14 +8,13 @@ import ( "net/http" "net/http/httptest" "strings" - "sync" "testing" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "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" @@ -56,7 +55,7 @@ func TestValidateHeaders(t *testing.T) { rr, err := extension.ValidateHeaders(r) // then - require.Error(t, err) + assert.Error(t, err) assert.Nil(t, rr) }) t.Run("will return error if application header is missing", func(t *testing.T) { @@ -71,7 +70,7 @@ func TestValidateHeaders(t *testing.T) { rr, err := extension.ValidateHeaders(r) // then - require.Error(t, err) + assert.Error(t, err) assert.Nil(t, rr) }) t.Run("will return error if project header is missing", func(t *testing.T) { @@ -86,7 +85,7 @@ func TestValidateHeaders(t *testing.T) { rr, err := extension.ValidateHeaders(r) // then - require.Error(t, err) + assert.Error(t, err) assert.Nil(t, rr) }) t.Run("will return error if invalid namespace", func(t *testing.T) { @@ -102,7 +101,7 @@ func TestValidateHeaders(t *testing.T) { rr, err := extension.ValidateHeaders(r) // then - require.Error(t, err) + assert.Error(t, err) assert.Nil(t, rr) }) t.Run("will return error if invalid app name", func(t *testing.T) { @@ -118,7 +117,7 @@ func TestValidateHeaders(t *testing.T) { rr, err := extension.ValidateHeaders(r) // then - require.Error(t, err) + assert.Error(t, err) assert.Nil(t, rr) }) t.Run("will return error if invalid project name", func(t *testing.T) { @@ -134,7 +133,7 @@ func TestValidateHeaders(t *testing.T) { rr, err := extension.ValidateHeaders(r) // then - require.Error(t, err) + assert.Error(t, err) assert.Nil(t, rr) }) } @@ -150,7 +149,7 @@ func TestRegisterExtensions(t *testing.T) { logger, _ := test.NewNullLogger() logEntry := logger.WithContext(context.Background()) - m := extension.NewManager(logEntry, settMock, nil, nil, nil, nil) + m := extension.NewManager(logEntry, settMock, nil, nil, nil) return &fixture{ settingsGetterMock: settMock, @@ -167,8 +166,7 @@ func TestRegisterExtensions(t *testing.T) { f.settingsGetterMock.On("Get", mock.Anything).Return(settings, nil) expectedProxyRegistries := []string{ "external-backend", - "some-backend", - } + "some-backend"} // when err := f.manager.RegisterExtensions() @@ -180,6 +178,7 @@ func TestRegisterExtensions(t *testing.T) { assert.True(t, found) assert.NotNil(t, proxyRegistry) } + }) t.Run("will return error if extension config is invalid", func(t *testing.T) { // given @@ -189,6 +188,10 @@ func TestRegisterExtensions(t *testing.T) { configYaml string } cases := []testCase{ + { + name: "no config", + configYaml: "", + }, { name: "no name", configYaml: getExtensionConfigNoName(), @@ -231,7 +234,7 @@ func TestRegisterExtensions(t *testing.T) { err := f.manager.RegisterExtensions() // then - require.Error(t, err, "expected error in test %s but got nil", tc.name) + assert.Error(t, err) }) } }) @@ -244,8 +247,6 @@ func TestCallExtension(t *testing.T) { settingsGetterMock *mocks.SettingsGetter rbacMock *mocks.RbacEnforcer projMock *mocks.ProjectGetter - metricsMock *mocks.ExtensionMetricsRegistry - userMock *mocks.UserGetter manager *extension.Manager } defaultProjectName := "project-name" @@ -255,13 +256,10 @@ func TestCallExtension(t *testing.T) { settMock := &mocks.SettingsGetter{} rbacMock := &mocks.RbacEnforcer{} projMock := &mocks.ProjectGetter{} - metricsMock := &mocks.ExtensionMetricsRegistry{} - userMock := &mocks.UserGetter{} logger, _ := test.NewNullLogger() logEntry := logger.WithContext(context.Background()) - m := extension.NewManager(logEntry, settMock, appMock, projMock, rbacMock, userMock) - m.AddMetricsRegistry(metricsMock) + m := extension.NewManager(logEntry, settMock, appMock, projMock, rbacMock) mux := http.NewServeMux() extHandler := http.HandlerFunc(m.CallExtension()) @@ -273,8 +271,6 @@ func TestCallExtension(t *testing.T) { settingsGetterMock: settMock, rbacMock: rbacMock, projMock: projMock, - metricsMock: metricsMock, - userMock: userMock, manager: m, } } @@ -332,11 +328,6 @@ func TestCallExtension(t *testing.T) { 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 @@ -350,11 +341,6 @@ func TestCallExtension(t *testing.T) { f.rbacMock.On("EnforceErr", mock.Anything, rbacpolicy.ResourceExtensions, rbacpolicy.ActionInvoke, mock.Anything).Return(extAccessError) } - withUser := func(f *fixture, username string, groups []string) { - f.userMock.On("GetUser", mock.Anything).Return(username) - f.userMock.On("GetGroups", mock.Anything).Return(groups) - } - withExtensionConfig := func(configYaml string, f *fixture) { secrets := make(map[string]string) secrets["extension.auth.header"] = "Bearer some-bearer-token" @@ -383,6 +369,7 @@ func TestCallExtension(t *testing.T) { } fmt.Fprintln(w, response) })) + } newExtensionRequest := func(t *testing.T, method, url string) *http.Request { t.Helper() @@ -411,7 +398,6 @@ func TestCallExtension(t *testing.T) { })) defer backendSrv.Close() withRbac(f, true, true) - withUser(f, "some-user", []string{"group1", "group2"}) withExtensionConfig(getExtensionConfig(backendEndpoint, backendSrv.URL), f) ts := startTestServer(t, f) defer ts.Close() @@ -420,18 +406,6 @@ func TestCallExtension(t *testing.T) { 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.DefaultClient.Do(r) @@ -446,15 +420,6 @@ func TestCallExtension(t *testing.T) { 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")) - assert.Equal(t, "some-user", resp.Header.Get(extension.HeaderArgoCDUsername)) - assert.Equal(t, "group1,group2", resp.Header.Get(extension.HeaderArgoCDGroups)) - - // 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 @@ -462,8 +427,6 @@ func TestCallExtension(t *testing.T) { f := setup() withExtensionConfig(getExtensionConfigString(), f) withRbac(f, true, true) - withMetrics(f) - withUser(f, "some-user", []string{"group1", "group2"}) cluster1Name := "cluster1" f.appGetterMock.On("Get", "namespace", "app-name").Return(getApp(cluster1Name, "", defaultProjectName), nil) withProject(getProjectWithDestinations("project-name", []string{cluster1Name}, []string{"some-url"}), f) @@ -503,8 +466,6 @@ func TestCallExtension(t *testing.T) { 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) - withUser(f, "some-user", []string{"group1", "group2"}) ts := startTestServer(t, f) defer ts.Close() @@ -550,8 +511,6 @@ func TestCallExtension(t *testing.T) { extName := "some-extension" withRbac(f, allowApp, allowExtension) withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) - withMetrics(f) - withUser(f, "some-user", []string{"group1", "group2"}) ts := startTestServer(t, f) defer ts.Close() r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, extName)) @@ -574,8 +533,6 @@ func TestCallExtension(t *testing.T) { extName := "some-extension" withRbac(f, allowApp, allowExtension) withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) - withMetrics(f) - withUser(f, "some-user", []string{"group1", "group2"}) ts := startTestServer(t, f) defer ts.Close() r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, extName)) @@ -599,8 +556,6 @@ func TestCallExtension(t *testing.T) { noCluster := []string{} withRbac(f, allowApp, allowExtension) withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) - withMetrics(f) - withUser(f, "some-user", []string{"group1", "group2"}) ts := startTestServer(t, f) defer ts.Close() r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, extName)) @@ -625,8 +580,6 @@ func TestCallExtension(t *testing.T) { extName := "some-extension" withRbac(f, allowApp, allowExtension) withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) - withMetrics(f) - withUser(f, "some-user", []string{"group1", "group2"}) ts := startTestServer(t, f) defer ts.Close() r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, extName)) @@ -651,8 +604,6 @@ func TestCallExtension(t *testing.T) { differentProject := "differentProject" withRbac(f, allowApp, allowExtension) withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) - withMetrics(f) - withUser(f, "some-user", []string{"group1", "group2"}) ts := startTestServer(t, f) defer ts.Close() r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, extName)) @@ -683,8 +634,6 @@ func TestCallExtension(t *testing.T) { withRbac(f, true, true) withExtensionConfig(getExtensionConfigWith2Backends(extName, "url1", "clusterName", "url2", "clusterURL"), f) withProject(getProjectWithDestinations("project-name", nil, []string{"srv1", destinationServer}), f) - withMetrics(f) - withUser(f, "some-user", []string{"group1", "group2"}) ts := startTestServer(t, f) defer ts.Close() @@ -717,8 +666,6 @@ func TestCallExtension(t *testing.T) { differentProject := "differentProject" withRbac(f, allowApp, allowExtension) withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) - withMetrics(f) - withUser(f, "some-user", []string{"group1", "group2"}) ts := startTestServer(t, f) defer ts.Close() r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/", ts.URL)) @@ -801,7 +748,6 @@ extensions: connectionTimeout: 2s ` } - func getExtensionConfigNoName() string { return ` extensions: @@ -810,7 +756,6 @@ extensions: - url: https://httpbin.org ` } - func getExtensionConfigInvalidName() string { return ` extensions: diff --git a/server/extension/mocks/ApplicationGetter.go b/server/extension/mocks/ApplicationGetter.go index 1b742bdf320ae..bad203f47ebfe 100644 --- a/server/extension/mocks/ApplicationGetter.go +++ b/server/extension/mocks/ApplicationGetter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.15.0. DO NOT EDIT. package mocks @@ -16,15 +16,7 @@ type ApplicationGetter struct { func (_m *ApplicationGetter) Get(ns string, name string) (*v1alpha1.Application, error) { ret := _m.Called(ns, name) - if len(ret) == 0 { - panic("no return value specified for Get") - } - var r0 *v1alpha1.Application - var r1 error - if rf, ok := ret.Get(0).(func(string, string) (*v1alpha1.Application, error)); ok { - return rf(ns, name) - } if rf, ok := ret.Get(0).(func(string, string) *v1alpha1.Application); ok { r0 = rf(ns, name) } else { @@ -33,6 +25,7 @@ func (_m *ApplicationGetter) Get(ns string, name string) (*v1alpha1.Application, } } + var r1 error if rf, ok := ret.Get(1).(func(string, string) error); ok { r1 = rf(ns, name) } else { @@ -42,12 +35,13 @@ func (_m *ApplicationGetter) Get(ns string, name string) (*v1alpha1.Application, return r0, r1 } -// NewApplicationGetter creates a new instance of ApplicationGetter. 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 NewApplicationGetter(t interface { +type mockConstructorTestingTNewApplicationGetter interface { mock.TestingT Cleanup(func()) -}) *ApplicationGetter { +} + +// NewApplicationGetter creates a new instance of ApplicationGetter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewApplicationGetter(t mockConstructorTestingTNewApplicationGetter) *ApplicationGetter { mock := &ApplicationGetter{} mock.Mock.Test(t) diff --git a/server/extension/mocks/ExtensionMetricsRegistry.go b/server/extension/mocks/ExtensionMetricsRegistry.go deleted file mode 100644 index be1d5285dd4de..0000000000000 --- a/server/extension/mocks/ExtensionMetricsRegistry.go +++ /dev/null @@ -1,38 +0,0 @@ -// Code generated by mockery v2.43.2. 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 index f3e156aa30182..d70b0c70ccfc6 100644 --- a/server/extension/mocks/ProjectGetter.go +++ b/server/extension/mocks/ProjectGetter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.15.0. DO NOT EDIT. package mocks @@ -16,15 +16,7 @@ type ProjectGetter struct { func (_m *ProjectGetter) Get(name string) (*v1alpha1.AppProject, error) { ret := _m.Called(name) - if len(ret) == 0 { - panic("no return value specified for Get") - } - var r0 *v1alpha1.AppProject - var r1 error - if rf, ok := ret.Get(0).(func(string) (*v1alpha1.AppProject, error)); ok { - return rf(name) - } if rf, ok := ret.Get(0).(func(string) *v1alpha1.AppProject); ok { r0 = rf(name) } else { @@ -33,6 +25,7 @@ func (_m *ProjectGetter) Get(name string) (*v1alpha1.AppProject, error) { } } + var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -46,15 +39,7 @@ func (_m *ProjectGetter) Get(name string) (*v1alpha1.AppProject, error) { func (_m *ProjectGetter) GetClusters(project string) ([]*v1alpha1.Cluster, error) { ret := _m.Called(project) - if len(ret) == 0 { - panic("no return value specified for GetClusters") - } - var r0 []*v1alpha1.Cluster - var r1 error - if rf, ok := ret.Get(0).(func(string) ([]*v1alpha1.Cluster, error)); ok { - return rf(project) - } if rf, ok := ret.Get(0).(func(string) []*v1alpha1.Cluster); ok { r0 = rf(project) } else { @@ -63,6 +48,7 @@ func (_m *ProjectGetter) GetClusters(project string) ([]*v1alpha1.Cluster, error } } + var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(project) } else { @@ -72,12 +58,13 @@ func (_m *ProjectGetter) GetClusters(project string) ([]*v1alpha1.Cluster, error return r0, r1 } -// 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. -// The first argument is typically a *testing.T value. -func NewProjectGetter(t interface { +type mockConstructorTestingTNewProjectGetter interface { mock.TestingT Cleanup(func()) -}) *ProjectGetter { +} + +// 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) diff --git a/server/extension/mocks/RbacEnforcer.go b/server/extension/mocks/RbacEnforcer.go index d247ccb72f649..01fb0c7421c69 100644 --- a/server/extension/mocks/RbacEnforcer.go +++ b/server/extension/mocks/RbacEnforcer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.15.0. DO NOT EDIT. package mocks @@ -15,10 +15,6 @@ func (_m *RbacEnforcer) EnforceErr(rvals ...interface{}) error { _ca = append(_ca, rvals...) ret := _m.Called(_ca...) - if len(ret) == 0 { - panic("no return value specified for EnforceErr") - } - var r0 error if rf, ok := ret.Get(0).(func(...interface{}) error); ok { r0 = rf(rvals...) @@ -29,12 +25,13 @@ func (_m *RbacEnforcer) EnforceErr(rvals ...interface{}) error { return r0 } -// 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. -// The first argument is typically a *testing.T value. -func NewRbacEnforcer(t interface { +type mockConstructorTestingTNewRbacEnforcer interface { mock.TestingT Cleanup(func()) -}) *RbacEnforcer { +} + +// 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) diff --git a/server/extension/mocks/SettingsGetter.go b/server/extension/mocks/SettingsGetter.go index 4880ac861b75d..303de9c5eeebf 100644 --- a/server/extension/mocks/SettingsGetter.go +++ b/server/extension/mocks/SettingsGetter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.15.0. DO NOT EDIT. package mocks @@ -16,15 +16,7 @@ type SettingsGetter struct { func (_m *SettingsGetter) Get() (*settings.ArgoCDSettings, error) { ret := _m.Called() - if len(ret) == 0 { - panic("no return value specified for Get") - } - var r0 *settings.ArgoCDSettings - var r1 error - if rf, ok := ret.Get(0).(func() (*settings.ArgoCDSettings, error)); ok { - return rf() - } if rf, ok := ret.Get(0).(func() *settings.ArgoCDSettings); ok { r0 = rf() } else { @@ -33,6 +25,7 @@ func (_m *SettingsGetter) Get() (*settings.ArgoCDSettings, error) { } } + var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -42,12 +35,13 @@ func (_m *SettingsGetter) Get() (*settings.ArgoCDSettings, error) { return r0, r1 } -// NewSettingsGetter creates a new instance of SettingsGetter. 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 NewSettingsGetter(t interface { +type mockConstructorTestingTNewSettingsGetter interface { mock.TestingT Cleanup(func()) -}) *SettingsGetter { +} + +// NewSettingsGetter creates a new instance of SettingsGetter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewSettingsGetter(t mockConstructorTestingTNewSettingsGetter) *SettingsGetter { mock := &SettingsGetter{} mock.Mock.Test(t) diff --git a/server/extension/mocks/UserGetter.go b/server/extension/mocks/UserGetter.go deleted file mode 100644 index efd7e9ec412be..0000000000000 --- a/server/extension/mocks/UserGetter.go +++ /dev/null @@ -1,66 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" -) - -// UserGetter is an autogenerated mock type for the UserGetter type -type UserGetter struct { - mock.Mock -} - -// GetGroups provides a mock function with given fields: ctx -func (_m *UserGetter) GetGroups(ctx context.Context) []string { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetGroups") - } - - var r0 []string - if rf, ok := ret.Get(0).(func(context.Context) []string); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - return r0 -} - -// GetUser provides a mock function with given fields: ctx -func (_m *UserGetter) GetUser(ctx context.Context) string { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetUser") - } - - var r0 string - if rf, ok := ret.Get(0).(func(context.Context) string); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// NewUserGetter creates a new instance of UserGetter. 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 NewUserGetter(t interface { - mock.TestingT - Cleanup(func()) -}) *UserGetter { - mock := &UserGetter{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/server/gpgkey/gpgkey.go b/server/gpgkey/gpgkey.go index 338f3a8acc2c9..375cb1e13a032 100644 --- a/server/gpgkey/gpgkey.go +++ b/server/gpgkey/gpgkey.go @@ -1,10 +1,11 @@ package gpgkey import ( - "context" "fmt" "strings" + "context" + gpgkeypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/gpgkey" appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" diff --git a/server/logout/logout.go b/server/logout/logout.go index 6129e2f9a85be..e49f815931596 100644 --- a/server/logout/logout.go +++ b/server/logout/logout.go @@ -65,10 +65,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - argoURL, err := argoCDSettings.ArgoURLForRequest(r) - if err != nil { - log.Warnf("unable to find ArgoCD URL from config: %v", err) - } + argoURL := argoCDSettings.URL if argoURL == "" { // golang does not provide any easy way to determine scheme of current request // so redirecting ot http which will auto-redirect too https if necessary diff --git a/server/logout/logout_test.go b/server/logout/logout_test.go index 3d2bab2d3662d..e20d35837f475 100644 --- a/server/logout/logout_test.go +++ b/server/logout/logout_test.go @@ -17,7 +17,6 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" @@ -36,7 +35,6 @@ var ( oidcToken = "eyJraWQiOiJYQi1MM3ZFdHhYWXJLcmRSQnVEV0NwdnZsSnk3SEJVb2d5N253M1U1Z1ZZIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiIwMHVqNnM1NDVyNU5peVNLcjVkNSIsIm5hbWUiOiJqZCByIiwiZW1haWwiOiJqYWlkZWVwMTdydWx6QGdtYWlsLmNvbSIsInZlciI6MSwiaXNzIjoiaHR0cHM6Ly9kZXYtNTY5NTA5OC5va3RhLmNvbSIsImF1ZCI6IjBvYWowM2FmSEtqN3laWXJwNWQ1IiwiaWF0IjoxNjA1NTcyMzU5LCJleHAiOjE2MDU1NzU5NTksImp0aSI6IklELl9ORDJxVG5iREFtc3hIZUt2U2ZHeVBqTXRicXFEQXdkdlRQTDZCTnpfR3ciLCJhbXIiOlsicHdkIl0sImlkcCI6IjAwb2lnaGZmdkpRTDYzWjhoNWQ1IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiamFpZGVlcDE3cnVsekBnbWFpbC5jb20iLCJhdXRoX3RpbWUiOjE2MDU1NzIzNTcsImF0X2hhc2giOiJqZVEwRml2ak9nNGI2TUpXRDIxOWxnIn0.GHkqwXgW-lrAhJdypW7SVjW0YdNLFQiRL8iwgT6DHJxP9Nb0OtkH2NKcBYAA5N6bTPLRQUHgYwWcgm5zSXmvqa7ciIgPF3tiQI8UmJA9VFRRDR-x9ExX15nskCbXfiQ67MriLslUrQUyzSCfUrSjXKwnDxbKGQncrtmRsh5asfCzJFb9excn311W9HKbT3KA0Ot7eOMnVS6V7SGfXxnKs6szcXIEMa_FhB4zDAVLr-dnxvSG_uuWcHrAkLTUVhHbdQQXF7hXIEfyr5lkMJN-drjdz-bn40GaYulEmUvO1bjcL9toCVQ3Ismypyr0b8phj4w3uRsLDZQxTxK7jAXlyQ" nonOidcToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2MDU1NzQyMTIsImlzcyI6ImFyZ29jZCIsIm5iZiI6MTYwNTU3NDIxMiwic3ViIjoiYWRtaW4ifQ.zDJ4piwWnwsHON-oPusHMXWINlnrRDTQykYogT7afeE" expectedNonOIDCLogoutURL = "http://localhost:4000" - expectedNonOIDCLogoutURLOnSecondHost = "http://argocd.my-corp.tld" expectedOIDCLogoutURL = "https://dev-5695098.okta.com/oauth2/v1/logout?id_token_hint=" + oidcToken + "&post_logout_redirect_uri=" + baseURL expectedOIDCLogoutURLWithRootPath = "https://dev-5695098.okta.com/oauth2/v1/logout?id_token_hint=" + oidcToken + "&post_logout_redirect_uri=" + baseURL + "/" + rootPath ) @@ -81,7 +79,7 @@ func TestConstructLogoutURL(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { constructedLogoutURL := constructLogoutURL(tt.logoutURL, tt.token, tt.logoutRedirectURL) - assert.Equal(t, tt.expectedLogoutURL, constructedLogoutURL) + assert.Equal(t, constructedLogoutURL, tt.expectedLogoutURL) }) } } @@ -182,34 +180,6 @@ func TestHandlerConstructLogoutURL(t *testing.T) { }, }, ) - kubeClientWithoutOIDCAndMultipleURLs := fake.NewSimpleClientset( - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.ArgoCDConfigMapName, - Namespace: "default", - Labels: map[string]string{ - "app.kubernetes.io/part-of": "argocd", - }, - }, - Data: map[string]string{ - "url": "http://localhost:4000", - "additionalUrls": "- http://argocd.my-corp.tld", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.ArgoCDSecretName, - Namespace: "default", - Labels: map[string]string{ - "app.kubernetes.io/part-of": "argocd", - }, - }, - Data: map[string][]byte{ - "admin.password": nil, - "server.secretkey": nil, - }, - }, - ) kubeClientWithoutOIDCConfig := fake.NewSimpleClientset( &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -241,7 +211,6 @@ func TestHandlerConstructLogoutURL(t *testing.T) { settingsManagerWithOIDCConfig := settings.NewSettingsManager(context.Background(), kubeClientWithOIDCConfig, "default") settingsManagerWithoutOIDCConfig := settings.NewSettingsManager(context.Background(), kubeClientWithoutOIDCConfig, "default") settingsManagerWithOIDCConfigButNoLogoutURL := settings.NewSettingsManager(context.Background(), kubeClientWithOIDCConfigButNoLogoutURL, "default") - settingsManagerWithoutOIDCAndMultipleURLs := settings.NewSettingsManager(context.Background(), kubeClientWithoutOIDCAndMultipleURLs, "default") settingsManagerWithOIDCConfigButNoURL := settings.NewSettingsManager(context.Background(), kubeClientWithOIDCConfigButNoURL, "default") sessionManager := session.NewSessionManager(settingsManagerWithOIDCConfig, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) @@ -267,13 +236,6 @@ func TestHandlerConstructLogoutURL(t *testing.T) { } return &jwt.RegisteredClaims{Issuer: "okta"}, "", nil } - nonoidcHandlerWithMultipleURLs := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithoutOIDCAndMultipleURLs, sessionManager, "", baseHRef, "default") - nonoidcHandlerWithMultipleURLs.verifyToken = func(tokenString string) (jwt.Claims, string, error) { - if !validJWTPattern.MatchString(tokenString) { - return nil, "", errors.New("invalid jwt") - } - return &jwt.RegisteredClaims{Issuer: "okta"}, "", nil - } oidcHandlerWithoutBaseURL := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfigButNoURL, sessionManager, "argocd", baseHRef, "default") oidcHandlerWithoutBaseURL.verifyToken = func(tokenString string) (jwt.Claims, string, error) { @@ -290,20 +252,17 @@ func TestHandlerConstructLogoutURL(t *testing.T) { invalidHeader["Cookie"] = []string{"argocd.token=" + invalidToken} oidcRequest, err := http.NewRequest(http.MethodGet, "http://localhost:4000/api/logout", nil) - require.NoError(t, err) + assert.NoError(t, err) oidcRequest.Header = oidcTokenHeader nonoidcRequest, err := http.NewRequest(http.MethodGet, "http://localhost:4000/api/logout", nil) - require.NoError(t, err) - nonoidcRequest.Header = nonOidcTokenHeader - nonoidcRequestOnSecondHost, err := http.NewRequest(http.MethodGet, "http://argocd.my-corp.tld/api/logout", nil) assert.NoError(t, err) - nonoidcRequestOnSecondHost.Header = nonOidcTokenHeader + nonoidcRequest.Header = nonOidcTokenHeader assert.NoError(t, err) requestWithInvalidToken, err := http.NewRequest(http.MethodGet, "http://localhost:4000/api/logout", nil) - require.NoError(t, err) + assert.NoError(t, err) requestWithInvalidToken.Header = invalidHeader invalidRequest, err := http.NewRequest(http.MethodGet, "http://localhost:4000/api/logout", nil) - require.NoError(t, err) + assert.NoError(t, err) tests := []struct { name string @@ -362,30 +321,14 @@ func TestHandlerConstructLogoutURL(t *testing.T) { expectedLogoutURL: expectedNonOIDCLogoutURL, wantErr: false, }, - { - name: "Case:non-OIDC Logout request on the first supported URL", - handler: nonoidcHandlerWithMultipleURLs, - request: nonoidcRequest, - responseRecorder: httptest.NewRecorder(), - expectedLogoutURL: expectedNonOIDCLogoutURL, - wantErr: false, - }, - { - name: "Case:non-OIDC Logout request on the second supported URL", - handler: nonoidcHandlerWithMultipleURLs, - request: nonoidcRequestOnSecondHost, - responseRecorder: httptest.NewRecorder(), - expectedLogoutURL: expectedNonOIDCLogoutURLOnSecondHost, - wantErr: false, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.handler.ServeHTTP(tt.responseRecorder, tt.request) if status := tt.responseRecorder.Code; status != http.StatusSeeOther { if !tt.wantErr { - t.Error(tt.responseRecorder.Body.String()) - t.Error("handler returned wrong status code: " + fmt.Sprintf("%d", tt.responseRecorder.Code)) + t.Errorf(tt.responseRecorder.Body.String()) + t.Errorf("handler returned wrong status code: " + fmt.Sprintf("%d", tt.responseRecorder.Code)) } } else { if tt.wantErr { diff --git a/server/metrics/metrics.go b/server/metrics/metrics.go index 3056a4e3e9332..40698e742b093 100644 --- a/server/metrics/metrics.go +++ b/server/metrics/metrics.go @@ -9,17 +9,13 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/util/profile" ) type MetricsServer struct { *http.Server - redisRequestCounter *prometheus.CounterVec - redisRequestHistogram *prometheus.HistogramVec - extensionRequestCounter *prometheus.CounterVec - extensionRequestDuration *prometheus.HistogramVec - argoVersion *prometheus.GaugeVec + redisRequestCounter *prometheus.CounterVec + redisRequestHistogram *prometheus.HistogramVec } var ( @@ -38,28 +34,6 @@ 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"}, - ) - argoVersion = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "argocd_info", - Help: "ArgoCD version information", - }, - []string{"version"}, - ) ) // NewMetricsServer returns a new prometheus server which collects api server metrics @@ -70,26 +44,18 @@ func NewMetricsServer(host string, port int) *MetricsServer { registry, prometheus.DefaultGatherer, }, promhttp.HandlerOpts{})) - argoVersion.WithLabelValues(common.GetVersion().Version).Set(1) - profile.RegisterProfiler(mux) registry.MustRegister(redisRequestCounter) registry.MustRegister(redisRequestHistogram) - registry.MustRegister(extensionRequestCounter) - registry.MustRegister(extensionRequestDuration) - registry.MustRegister(argoVersion) return &MetricsServer{ Server: &http.Server{ Addr: fmt.Sprintf("%s:%d", host, port), Handler: mux, }, - redisRequestCounter: redisRequestCounter, - redisRequestHistogram: redisRequestHistogram, - extensionRequestCounter: extensionRequestCounter, - extensionRequestDuration: extensionRequestDuration, - argoVersion: argoVersion, + redisRequestCounter: redisRequestCounter, + redisRequestHistogram: redisRequestHistogram, } } @@ -101,11 +67,3 @@ 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.go b/server/notification/notification.go index 7b8f6589fcfbf..b85cbc34eb23b 100644 --- a/server/notification/notification.go +++ b/server/notification/notification.go @@ -3,11 +3,10 @@ package notification import ( "context" + "github.com/argoproj/argo-cd/v2/pkg/apiclient/notification" "github.com/argoproj/notifications-engine/pkg/api" apierr "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/utils/ptr" - - "github.com/argoproj/argo-cd/v2/pkg/apiclient/notification" + "k8s.io/utils/pointer" ) // Server provides an Application service @@ -31,7 +30,7 @@ func (s *Server) ListTriggers(ctx context.Context, q *notification.TriggersListR } triggers := []*notification.Trigger{} for trigger := range api.GetConfig().Triggers { - triggers = append(triggers, ¬ification.Trigger{Name: ptr.To(trigger)}) + triggers = append(triggers, ¬ification.Trigger{Name: pointer.String(trigger)}) } return ¬ification.TriggerList{Items: triggers}, nil } @@ -47,7 +46,7 @@ func (s *Server) ListServices(ctx context.Context, q *notification.ServicesListR } services := []*notification.Service{} for svc := range api.GetConfig().Services { - services = append(services, ¬ification.Service{Name: ptr.To(svc)}) + services = append(services, ¬ification.Service{Name: pointer.String(svc)}) } return ¬ification.ServiceList{Items: services}, nil } @@ -63,7 +62,7 @@ func (s *Server) ListTemplates(ctx context.Context, q *notification.TemplatesLis } templates := []*notification.Template{} for tmpl := range api.GetConfig().Templates { - templates = append(templates, ¬ification.Template{Name: ptr.To(tmpl)}) + templates = append(templates, ¬ification.Template{Name: pointer.String(tmpl)}) } return ¬ification.TemplateList{Items: templates}, nil } diff --git a/server/notification/notification_test.go b/server/notification/notification_test.go index 41e0306c84bd0..ee913926bc010 100644 --- a/server/notification/notification_test.go +++ b/server/notification/notification_test.go @@ -5,17 +5,16 @@ import ( "os" "testing" - "github.com/argoproj/notifications-engine/pkg/api" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - "k8s.io/utils/ptr" - "github.com/argoproj/argo-cd/v2/pkg/apiclient/notification" "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks" service "github.com/argoproj/argo-cd/v2/util/notification/argocd" "github.com/argoproj/argo-cd/v2/util/notification/k8s" "github.com/argoproj/argo-cd/v2/util/notification/settings" + "github.com/argoproj/notifications-engine/pkg/api" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" @@ -26,6 +25,7 @@ import ( const testNamespace = "default" func TestNotificationServer(t *testing.T) { + // catalogPath := path.Join(paths[1], "config", "notifications-catalog") b, err := os.ReadFile("../../notifications_catalog/install.yaml") require.NoError(t, err) @@ -75,25 +75,25 @@ func TestNotificationServer(t *testing.T) { t.Run("TestListServices", func(t *testing.T) { server := NewServer(apiFactory) services, err := server.ListServices(ctx, ¬ification.ServicesListRequest{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, services.Items, 1) - assert.Equal(t, services.Items[0].Name, ptr.To("test")) + assert.Equal(t, services.Items[0].Name, pointer.String("test")) assert.NotEmpty(t, services.Items[0]) }) t.Run("TestListTriggers", func(t *testing.T) { server := NewServer(apiFactory) triggers, err := server.ListTriggers(ctx, ¬ification.TriggersListRequest{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, triggers.Items, 1) - assert.Equal(t, triggers.Items[0].Name, ptr.To("on-created")) + assert.Equal(t, triggers.Items[0].Name, pointer.String("on-created")) assert.NotEmpty(t, triggers.Items[0]) }) t.Run("TestListTemplates", func(t *testing.T) { server := NewServer(apiFactory) templates, err := server.ListTemplates(ctx, ¬ification.TemplatesListRequest{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, templates.Items, 1) - assert.Equal(t, templates.Items[0].Name, ptr.To("app-created")) + assert.Equal(t, templates.Items[0].Name, pointer.String("app-created")) assert.NotEmpty(t, templates.Items[0]) }) } diff --git a/server/project/project.go b/server/project/project.go index 02d393564ddf0..44ddee95eaaff 100644 --- a/server/project/project.go +++ b/server/project/project.go @@ -58,13 +58,10 @@ type Server struct { // NewServer returns a new instance of the Project service func NewServer(ns string, kubeclientset kubernetes.Interface, appclientset appclientset.Interface, enf *rbac.Enforcer, projectLock sync.KeyLock, sessionMgr *session.SessionManager, policyEnf *rbacpolicy.RBACPolicyEnforcer, - projInformer cache.SharedIndexInformer, settingsMgr *settings.SettingsManager, db db.ArgoDB, enableK8sEvent []string, -) *Server { - auditLogger := argo.NewAuditLogger(ns, kubeclientset, "argocd-server", enableK8sEvent) - return &Server{ - enf: enf, policyEnf: policyEnf, appclientset: appclientset, kubeclientset: kubeclientset, ns: ns, projectLock: projectLock, auditLogger: auditLogger, sessionMgr: sessionMgr, - projInformer: projInformer, settingsMgr: settingsMgr, db: db, - } + projInformer cache.SharedIndexInformer, settingsMgr *settings.SettingsManager, db db.ArgoDB) *Server { + auditLogger := argo.NewAuditLogger(ns, kubeclientset, "argocd-server") + return &Server{enf: enf, policyEnf: policyEnf, appclientset: appclientset, kubeclientset: kubeclientset, ns: ns, projectLock: projectLock, auditLogger: auditLogger, sessionMgr: sessionMgr, + projInformer: projInformer, settingsMgr: settingsMgr, db: db} } func validateProject(proj *v1alpha1.AppProject) error { @@ -114,7 +111,7 @@ func (s *Server) createToken(ctx context.Context, q *project.ProjectTokenCreateR } id := q.Id if err := prj.ValidateJWTTokenID(q.Role, q.Id); err != nil { - return nil, status.Error(codes.InvalidArgument, err.Error()) + return nil, status.Errorf(codes.InvalidArgument, err.Error()) } if id == "" { uniqueId, _ := uuid.NewRandom() @@ -140,8 +137,6 @@ func (s *Server) createToken(ctx context.Context, q *project.ProjectTokenCreateR } id = claims.ID - prj.NormalizeJWTTokens() - items := append(prj.Status.JWTTokensByRole[q.Role].Items, v1alpha1.JWTToken{IssuedAt: issuedAt, ExpiresAt: expiresAt, ID: id}) if _, found := prj.Status.JWTTokensByRole[q.Role]; found { prj.Status.JWTTokensByRole[q.Role] = v1alpha1.JWTTokens{Items: items} @@ -159,6 +154,7 @@ func (s *Server) createToken(ctx context.Context, q *project.ProjectTokenCreateR } s.logEvent(prj, ctx, argo.EventReasonResourceCreated, "created token") return &project.ProjectTokenResponse{Token: jwtToken}, nil + } func (s *Server) ListLinks(ctx context.Context, q *project.ListProjectLinksRequest) (*application.LinksResponse, error) { @@ -273,7 +269,7 @@ func (s *Server) Create(ctx context.Context, q *project.ProjectCreateRequest) (* res, err = s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Update(ctx, existing, metav1.UpdateOptions{}) } else { if !reflect.DeepEqual(existing.Spec, q.GetProject().Spec) { - return nil, status.Error(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("project", existing.Spec, q.GetProject().Spec)) + return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("project", existing.Spec, q.GetProject().Spec)) } return existing, nil } @@ -519,6 +515,7 @@ func (s *Server) GetSyncWindowsState(ctx context.Context, q *project.SyncWindows return nil, err } proj, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, q.Name, metav1.GetOptions{}) + if err != nil { return nil, err } diff --git a/server/project/project_test.go b/server/project/project_test.go index 41b8af9241e39..caf0df9f3ebac 100644 --- a/server/project/project_test.go +++ b/server/project/project_test.go @@ -6,14 +6,12 @@ import ( "strings" "testing" - "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/pkg/sync" "github.com/golang-jwt/jwt/v4" "github.com/google/uuid" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" corev1 "k8s.io/api/core/v1" @@ -38,8 +36,6 @@ import ( const testNamespace = "default" -var testEnableEventList []string = argo.DefaultEnableEventList() - func TestProjectServer(t *testing.T) { kubeclientset := fake.NewSimpleClientset(&corev1.ConfigMap{ ObjectMeta: v1.ObjectMeta{ @@ -94,21 +90,23 @@ func TestProjectServer(t *testing.T) { role1 := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} projectWithRole.Spec.Roles = append(projectWithRole.Spec.Roles, role1) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB) err := projectServer.NormalizeProjs() - require.NoError(t, err) + assert.NoError(t, err) appList, err := projectServer.appclientset.ArgoprojV1alpha1().AppProjects(projectWithRole.Namespace).List(context.Background(), v1.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, int64(1), appList.Items[0].Status.JWTTokensByRole[roleName].Items[0].IssuedAt) + assert.NoError(t, err) + assert.Equal(t, appList.Items[0].Status.JWTTokensByRole[roleName].Items[0].IssuedAt, int64(1)) assert.ElementsMatch(t, appList.Items[0].Status.JWTTokensByRole[roleName].Items, appList.Items[0].Spec.Roles[0].JWTTokens) + }) t.Run("TestClusterUpdateDenied", func(t *testing.T) { + enforcer.SetDefaultRole("role:projects") _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.Destinations = nil @@ -119,10 +117,11 @@ func TestProjectServer(t *testing.T) { }) t.Run("TestReposUpdateDenied", func(t *testing.T) { + enforcer.SetDefaultRole("role:projects") _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.SourceRepos = nil @@ -133,10 +132,11 @@ func TestProjectServer(t *testing.T) { }) t.Run("TestClusterResourceWhitelistUpdateDenied", func(t *testing.T) { + enforcer.SetDefaultRole("role:projects") _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.ClusterResourceWhitelist = []metav1.GroupKind{{}} @@ -147,10 +147,11 @@ func TestProjectServer(t *testing.T) { }) t.Run("TestNamespaceResourceBlacklistUpdateDenied", func(t *testing.T) { + enforcer.SetDefaultRole("role:projects") _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.NamespaceResourceBlacklist = []metav1.GroupKind{{}} @@ -169,14 +170,14 @@ func TestProjectServer(t *testing.T) { } argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.Destinations = updatedProj.Spec.Destinations[1:] _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) - require.NoError(t, err) + assert.Nil(t, err) }) t.Run("TestRemoveDestinationUsedByApp", func(t *testing.T) { @@ -186,14 +187,14 @@ func TestProjectServer(t *testing.T) { } argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.Destinations = updatedProj.Spec.Destinations[1:] _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) - require.Error(t, err) + assert.NotNil(t, err) statusCode, _ := status.FromError(err) assert.Equal(t, codes.InvalidArgument, statusCode.Code()) }) @@ -205,14 +206,14 @@ func TestProjectServer(t *testing.T) { } argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.SourceRepos = []string{} _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) - require.NoError(t, err) + assert.Nil(t, err) }) t.Run("TestRemoveSourceUsedByApp", func(t *testing.T) { @@ -222,14 +223,14 @@ func TestProjectServer(t *testing.T) { } argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.SourceRepos = []string{} _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) - require.Error(t, err) + assert.NotNil(t, err) statusCode, _ := status.FromError(err) assert.Equal(t, codes.InvalidArgument, statusCode.Code()) }) @@ -242,14 +243,14 @@ func TestProjectServer(t *testing.T) { Spec: v1alpha1.ApplicationSpec{Project: "test", Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argo-cd.git"}}, } argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := proj.DeepCopy() updatedProj.Spec.SourceRepos = []string{"https://github.com/argoproj/*"} res, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, res.Spec.SourceRepos, updatedProj.Spec.SourceRepos) }) @@ -269,7 +270,7 @@ func TestProjectServer(t *testing.T) { argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := proj.DeepCopy() updatedProj.Spec.Destinations = []v1alpha1.ApplicationDestination{ @@ -278,17 +279,17 @@ func TestProjectServer(t *testing.T) { res, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) - require.NoError(t, err) + assert.NoError(t, err) assert.ElementsMatch(t, res.Spec.Destinations, updatedProj.Spec.Destinations) }) t.Run("TestDeleteProjectSuccessful", func(t *testing.T) { argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) _, err := projectServer.Delete(context.Background(), &project.ProjectQuery{Name: "test"}) - require.NoError(t, err) + assert.Nil(t, err) }) t.Run("TestDeleteDefaultProjectFailure", func(t *testing.T) { @@ -297,7 +298,7 @@ func TestProjectServer(t *testing.T) { Spec: v1alpha1.AppProjectSpec{}, } argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&defaultProj), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&defaultProj), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) _, err := projectServer.Delete(context.Background(), &project.ProjectQuery{Name: defaultProj.Name}) statusCode, _ := status.FromError(err) @@ -311,11 +312,11 @@ func TestProjectServer(t *testing.T) { } argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) _, err := projectServer.Delete(context.Background(), &project.ProjectQuery{Name: "test"}) - require.Error(t, err) + assert.NotNil(t, err) statusCode, _ := status.FromError(err) assert.Equal(t, codes.InvalidArgument, statusCode.Code()) }) @@ -338,7 +339,7 @@ func TestProjectServer(t *testing.T) { projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}} argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) _, err := projectServer.CreateToken(ctx, &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1}) assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = permission denied: projects, update, test") }) @@ -348,9 +349,9 @@ func TestProjectServer(t *testing.T) { projectWithRole := existingProj.DeepCopy() projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName, Groups: []string{"my-group"}}} argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) _, err := projectServer.CreateToken(ctx, &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1}) - require.NoError(t, err) + assert.NoError(t, err) }) _ = enforcer.SetBuiltinPolicy(`p, role:admin, projects, update, *, allow`) @@ -362,18 +363,18 @@ func TestProjectServer(t *testing.T) { sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", nil, session.NewUserStateStorage(nil)) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 100}) - require.NoError(t, err) + assert.NoError(t, err) claims, _, err := sessionMgr.Parse(tokenResponse.Token) - require.NoError(t, err) + assert.NoError(t, err) mapClaims, err := jwtutil.MapClaims(claims) subject, ok := mapClaims["sub"].(string) assert.True(t, ok) expectedSubject := fmt.Sprintf(JWTTokenSubFormat, projectWithRole.Name, tokenName) assert.Equal(t, expectedSubject, subject) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("TestCreateTokenWithIDSuccessfully", func(t *testing.T) { @@ -383,18 +384,18 @@ func TestProjectServer(t *testing.T) { sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", nil, session.NewUserStateStorage(nil)) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id}) - require.NoError(t, err) + assert.NoError(t, err) claims, _, err := sessionMgr.Parse(tokenResponse.Token) - require.NoError(t, err) + assert.NoError(t, err) mapClaims, err := jwtutil.MapClaims(claims) subject, ok := mapClaims["sub"].(string) assert.True(t, ok) expectedSubject := fmt.Sprintf(JWTTokenSubFormat, projectWithRole.Name, tokenName) assert.Equal(t, expectedSubject, subject) - require.NoError(t, err) + assert.NoError(t, err) }) t.Run("TestCreateTokenWithSameIdDeny", func(t *testing.T) { @@ -404,19 +405,19 @@ func TestProjectServer(t *testing.T) { sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", nil, session.NewUserStateStorage(nil)) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id}) - require.NoError(t, err) + assert.NoError(t, err) claims, _, err := sessionMgr.Parse(tokenResponse.Token) - require.NoError(t, err) + assert.NoError(t, err) mapClaims, err := jwtutil.MapClaims(claims) subject, ok := mapClaims["sub"].(string) assert.True(t, ok) expectedSubject := fmt.Sprintf(JWTTokenSubFormat, projectWithRole.Name, tokenName) assert.Equal(t, expectedSubject, subject) - require.NoError(t, err) + assert.NoError(t, err) _, err1 := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id}) expectedErr := fmt.Sprintf("rpc error: code = InvalidArgument desc = rpc error: code = InvalidArgument desc = Token id '%s' has been used. ", id) @@ -433,7 +434,7 @@ func TestProjectServer(t *testing.T) { token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}} projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) _, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: issuedAt}) assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = permission denied: projects, update, test") }) @@ -446,9 +447,9 @@ func TestProjectServer(t *testing.T) { token := v1alpha1.ProjectRole{Name: tokenName, Groups: []string{"my-group"}, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}} projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) _, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: issuedAt}) - require.NoError(t, err) + assert.NoError(t, err) }) _ = enforcer.SetBuiltinPolicy(`p, role:admin, projects, get, *, allow @@ -462,11 +463,11 @@ p, role:admin, projects, update, *, allow`) token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}} projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) _, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: issuedAt}) - require.NoError(t, err) + assert.NoError(t, err) projWithoutToken, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name}) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, projWithoutToken.Spec.Roles, 1) assert.Len(t, projWithoutToken.Spec.Roles[0].JWTTokens, 1) assert.Equal(t, projWithoutToken.Spec.Roles[0].JWTTokens[0].IssuedAt, secondIssuedAt) @@ -486,11 +487,11 @@ p, role:admin, projects, update, *, allow`) token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt, ID: id}, {IssuedAt: secondIssuedAt, ID: secondId}}} projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) _, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: secondIssuedAt, Id: id}) - require.NoError(t, err) + assert.NoError(t, err) projWithoutToken, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name}) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, projWithoutToken.Spec.Roles, 1) assert.Len(t, projWithoutToken.Spec.Roles[0].JWTTokens, 1) assert.Equal(t, projWithoutToken.Spec.Roles[0].JWTTokens[0].IssuedAt, secondIssuedAt) @@ -505,24 +506,25 @@ p, role:admin, projects, update, *, allow`) token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) _, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projWithToken.Name, Role: tokenName}) - require.NoError(t, err) + assert.Nil(t, err) projWithTwoTokens, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name}) - require.NoError(t, err) + assert.Nil(t, err) assert.Len(t, projWithTwoTokens.Spec.Roles, 1) assert.Len(t, projWithTwoTokens.Spec.Roles[0].JWTTokens, 2) }) t.Run("TestAddWildcardSource", func(t *testing.T) { + proj := existingProj.DeepCopy() wildSourceRepo := "*" proj.Spec.SourceRepos = append(proj.Spec.SourceRepos, wildSourceRepo) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj), enforcer, sync.NewKeyLock(), nil, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj), enforcer, sync.NewKeyLock(), nil, policyEnf, projInformer, settingsMgr, argoDB) request := &project.ProjectUpdateRequest{Project: proj} updatedProj, err := projectServer.Update(context.Background(), request) - require.NoError(t, err) + assert.Nil(t, err) assert.Equal(t, wildSourceRepo, updatedProj.Spec.SourceRepos[1]) }) @@ -538,13 +540,13 @@ p, role:admin, projects, update, *, allow`) role.Policies = append(role.Policies, policy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, policyEnf, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, policyEnf, projInformer, settingsMgr, argoDB) request := &project.ProjectUpdateRequest{Project: projWithRole} _, err := projectServer.Update(context.Background(), request) - require.NoError(t, err) + assert.Nil(t, err) t.Log(projWithRole.Spec.Roles[0].Policies[0]) expectedPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, role.Name, action, projWithRole.Name, object, effect) - assert.Equal(t, expectedPolicy, projWithRole.Spec.Roles[0].Policies[0]) + assert.Equal(t, projWithRole.Spec.Roles[0].Policies[0], expectedPolicy) }) t.Run("TestValidatePolicyDuplicatePolicyFailure", func(t *testing.T) { @@ -560,7 +562,7 @@ p, role:admin, projects, update, *, allow`) role.Policies = append(role.Policies, policy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) request := &project.ProjectUpdateRequest{Project: projWithRole} _, err := projectServer.Update(context.Background(), request) expectedErr := fmt.Sprintf("rpc error: code = AlreadyExists desc = policy '%s' already exists for role '%s'", policy, roleName) @@ -580,10 +582,10 @@ p, role:admin, projects, update, *, allow`) role.Policies = append(role.Policies, policy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) request := &project.ProjectUpdateRequest{Project: projWithRole} _, err := projectServer.Update(context.Background(), request) - assert.Contains(t, err.Error(), "object must be of form 'test/*', 'test[/]/' or 'test/'") + assert.Contains(t, err.Error(), "object must be of form 'test/*' or 'test/'") }) t.Run("TestValidateProjectIncorrectProjectInRoleFailure", func(t *testing.T) { @@ -599,7 +601,7 @@ p, role:admin, projects, update, *, allow`) role.Policies = append(role.Policies, invalidPolicy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) request := &project.ProjectUpdateRequest{Project: projWithRole} _, err := projectServer.Update(context.Background(), request) assert.Contains(t, err.Error(), "policy subject must be: 'proj:test:testRole'") @@ -618,7 +620,7 @@ p, role:admin, projects, update, *, allow`) role.Policies = append(role.Policies, invalidPolicy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) request := &project.ProjectUpdateRequest{Project: projWithRole} _, err := projectServer.Update(context.Background(), request) assert.Contains(t, err.Error(), "policy subject must be: 'proj:test:testRole'") @@ -636,7 +638,7 @@ p, role:admin, projects, update, *, allow`) role.Policies = append(role.Policies, invalidPolicy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) request := &project.ProjectUpdateRequest{Project: projWithRole} _, err := projectServer.Update(context.Background(), request) assert.Contains(t, err.Error(), "effect must be: 'allow' or 'deny'") @@ -650,15 +652,15 @@ p, role:admin, projects, update, *, allow`) projWithRole := existingProj.DeepCopy() role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} - noSpacesPolicyTemplate := strings.ReplaceAll(policyTemplate, " ", "") + noSpacesPolicyTemplate := strings.Replace(policyTemplate, " ", "", -1) invalidPolicy := fmt.Sprintf(noSpacesPolicyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect) role.Policies = append(role.Policies, invalidPolicy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) request := &project.ProjectUpdateRequest{Project: projWithRole} updateProj, err := projectServer.Update(context.Background(), request) - require.NoError(t, err) + assert.Nil(t, err) expectedPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect) assert.Equal(t, expectedPolicy, updateProj.Spec.Roles[0].Policies[0]) }) @@ -670,10 +672,10 @@ p, role:admin, projects, update, *, allow`) win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"} projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB) res, err := projectServer.GetSyncWindowsState(ctx, &project.SyncWindowsQuery{Name: projectWithSyncWindows.Name}) - require.NoError(t, err) - assert.Len(t, res.Windows, 1) + assert.NoError(t, err) + assert.Equal(t, 1, len(res.Windows)) }) t.Run("TestGetSyncWindowsStateCannotGetProjectDetails", func(t *testing.T) { @@ -683,7 +685,7 @@ p, role:admin, projects, update, *, allow`) win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"} projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB) res, err := projectServer.GetSyncWindowsState(ctx, &project.SyncWindowsQuery{Name: "incorrect"}) assert.Contains(t, err.Error(), "not found") assert.Nil(t, res) @@ -701,10 +703,11 @@ p, role:admin, projects, update, *, allow`) win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"} projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win) argoDB := db.NewDB("default", settingsMgr, kubeclientset) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB, testEnableEventList) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB) _, err := projectServer.GetSyncWindowsState(ctx, &project.SyncWindowsQuery{Name: projectWithSyncWindows.Name}) assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = permission denied: projects, get, test") }) + } func newEnforcer(kubeclientset *fake.Clientset) *rbac.Enforcer { diff --git a/server/rbacpolicy/rbacpolicy.go b/server/rbacpolicy/rbacpolicy.go index 0be623ae7819f..940f5bfe70844 100644 --- a/server/rbacpolicy/rbacpolicy.go +++ b/server/rbacpolicy/rbacpolicy.go @@ -141,11 +141,7 @@ func (p *RBACPolicyEnforcer) EnforceClaims(claims jwt.Claims, rvals ...interface groups := jwtutil.GetScopeValues(mapClaims, scopes) // Get groups to reduce the amount to checking groups - groupingPolicies, err := enforcer.GetGroupingPolicy() - if err != nil { - log.WithError(err).Error("failed to get grouping policy") - return false - } + groupingPolicies := enforcer.GetGroupingPolicy() for gidx := range groups { for gpidx := range groupingPolicies { // Prefilter user groups by groups defined in the model diff --git a/server/repocreds/repocreds.go b/server/repocreds/repocreds.go index 5c0c819598fb3..a9f34dc22ef32 100644 --- a/server/repocreds/repocreds.go +++ b/server/repocreds/repocreds.go @@ -1,11 +1,11 @@ package repocreds import ( - "context" "reflect" "github.com/argoproj/argo-cd/v2/util/argo" + "context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -93,7 +93,7 @@ func (s *Server) CreateRepositoryCredentials(ctx context.Context, q *repocredspk } else if q.Upsert { return s.UpdateRepositoryCredentials(ctx, &repocredspkg.RepoCredsUpdateRequest{Creds: r}) } else { - return nil, status.Error(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("repository credentials", existing, r)) + return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("repository credentials", existing, r)) } } return &appsv1.RepoCreds{URL: r.URL}, err diff --git a/server/repository/repository.go b/server/repository/repository.go index 2e25f87ce19d1..7787228ceb052 100644 --- a/server/repository/repository.go +++ b/server/repository/repository.go @@ -4,8 +4,6 @@ import ( "context" "fmt" "reflect" - "sort" - "strings" "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/argoproj/gitops-engine/pkg/utils/text" @@ -27,7 +25,6 @@ import ( "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/db" "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/rbac" "github.com/argoproj/argo-cd/v2/util/settings" @@ -68,10 +65,12 @@ func NewServer( } } -var errPermissionDenied = status.Error(codes.PermissionDenied, "permission denied") +var ( + errPermissionDenied = status.Error(codes.PermissionDenied, "permission denied") +) -func (s *Server) getRepo(ctx context.Context, url, project string) (*appsv1.Repository, error) { - repo, err := s.db.GetRepository(ctx, url, project) +func (s *Server) getRepo(ctx context.Context, url string) (*appsv1.Repository, error) { + repo, err := s.db.GetRepository(ctx, url) if err != nil { return nil, errPermissionDenied } @@ -88,9 +87,9 @@ func createRBACObject(project string, repo string) string { // Get the connection state for a given repository URL by connecting to the // repo and evaluate the results. Unless forceRefresh is set to true, the // result may be retrieved out of the cache. -func (s *Server) getConnectionState(ctx context.Context, url string, project string, forceRefresh bool) appsv1.ConnectionState { +func (s *Server) getConnectionState(ctx context.Context, url string, forceRefresh bool) appsv1.ConnectionState { if !forceRefresh { - if connectionState, err := s.cache.GetRepoConnectionState(url, project); err == nil { + if connectionState, err := s.cache.GetRepoConnectionState(url); err == nil { return connectionState } } @@ -100,7 +99,7 @@ func (s *Server) getConnectionState(ctx context.Context, url string, project str ModifiedAt: &now, } var err error - repo, err := s.db.GetRepository(ctx, url, project) + repo, err := s.db.GetRepository(ctx, url) if err == nil { err = s.testRepo(ctx, repo) } @@ -113,7 +112,7 @@ func (s *Server) getConnectionState(ctx context.Context, url string, project str connectionState.Message = fmt.Sprintf("Unable to connect to repository: %v", err) } } - err = s.cache.SetRepoConnectionState(url, project, &connectionState) + err = s.cache.SetRepoConnectionState(url, &connectionState) if err != nil { log.Warnf("getConnectionState cache set error %s: %v", url, err) } @@ -128,7 +127,7 @@ func (s *Server) List(ctx context.Context, q *repositorypkg.RepoQuery) (*appsv1. // Get return the requested configured repository by URL and the state of its connections. func (s *Server) Get(ctx context.Context, q *repositorypkg.RepoQuery) (*appsv1.Repository, error) { - repo, err := getRepository(ctx, s.ListRepositories, q) + repo, err := s.getRepo(ctx, q.Repo) if err != nil { return nil, err } @@ -138,7 +137,7 @@ func (s *Server) Get(ctx context.Context, q *repositorypkg.RepoQuery) (*appsv1.R } // getRepo does not return an error for unconfigured repositories, so we are checking here - exists, err := s.db.RepositoryExists(ctx, q.Repo, repo.Project) + exists, err := s.db.RepositoryExists(ctx, q.Repo) if err != nil { return nil, err } @@ -167,7 +166,7 @@ func (s *Server) Get(ctx context.Context, q *repositorypkg.RepoQuery) (*appsv1.R InheritedCreds: repo.InheritedCreds, } - item.ConnectionState = s.getConnectionState(ctx, item.Repo, item.Project, q.ForceRefresh) + item.ConnectionState = s.getConnectionState(ctx, item.Repo, q.ForceRefresh) return &item, nil } @@ -196,7 +195,6 @@ func (s *Server) ListRepositories(ctx context.Context, q *repositorypkg.RepoQuer EnableLFS: repo.EnableLFS, EnableOCI: repo.EnableOCI, Proxy: repo.Proxy, - NoProxy: repo.NoProxy, Project: repo.Project, ForceHttpBasicAuth: repo.ForceHttpBasicAuth, InheritedCreds: repo.InheritedCreds, @@ -204,22 +202,17 @@ func (s *Server) ListRepositories(ctx context.Context, q *repositorypkg.RepoQuer } } err = kube.RunAllAsync(len(items), func(i int) error { - items[i].ConnectionState = s.getConnectionState(ctx, items[i].Repo, items[i].Project, q.ForceRefresh) + items[i].ConnectionState = s.getConnectionState(ctx, items[i].Repo, q.ForceRefresh) return nil }) if err != nil { return nil, err } - sort.Slice(items, func(i, j int) bool { - first := items[i] - second := items[j] - return strings.Compare(fmt.Sprintf("%s/%s", first.Project, first.Repo), fmt.Sprintf("%s/%s", second.Project, second.Repo)) < 0 - }) return &appsv1.RepositoryList{Items: items}, nil } func (s *Server) ListRefs(ctx context.Context, q *repositorypkg.RepoQuery) (*apiclient.Refs, error) { - repo, err := s.getRepo(ctx, q.Repo, q.GetAppProject()) + repo, err := s.getRepo(ctx, q.Repo) if err != nil { return nil, err } @@ -242,7 +235,7 @@ func (s *Server) ListRefs(ctx context.Context, q *repositorypkg.RepoQuery) (*api // ListApps performs discovery of a git repository for potential sources of applications. Used // as a convenience to the UI for auto-complete. func (s *Server) ListApps(ctx context.Context, q *repositorypkg.RepoAppsQuery) (*repositorypkg.RepoAppsResponse, error) { - repo, err := s.getRepo(ctx, q.Repo, q.GetAppProject()) + repo, err := s.getRepo(ctx, q.Repo) if err != nil { return nil, err } @@ -293,7 +286,7 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta if q.Source == nil { return nil, status.Errorf(codes.InvalidArgument, "missing payload in request") } - repo, err := s.getRepo(ctx, q.Source.RepoURL, q.GetAppProject()) + repo, err := s.getRepo(ctx, q.Source.RepoURL) if err != nil { return nil, err } @@ -320,7 +313,7 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta return nil, errPermissionDenied } // verify caller is not making a request with arbitrary source values which were not in our history - if !isSourceInHistory(app, *q.Source, q.SourceIndex, q.VersionId) { + if !isSourceInHistory(app, *q.Source) { return nil, errPermissionDenied } } @@ -350,16 +343,6 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta if err != nil { return nil, err } - - refSources := make(appsv1.RefTargetRevisionMapping) - if app != nil && app.Spec.HasMultipleSources() { - // Store the map of all sources having ref field into a map for applications with sources field - refSources, err = argo.GetRefSources(ctx, app.Spec.Sources, q.AppProject, s.db.GetRepository, []string{}, false) - if err != nil { - return nil, fmt.Errorf("failed to get ref sources: %w", err) - } - } - return repoClient.GetAppDetails(ctx, &apiclient.RepoServerAppDetailsQuery{ Repo: repo, Source: q.Source, @@ -367,13 +350,12 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta KustomizeOptions: kustomizeOptions, HelmOptions: helmOptions, AppName: q.AppName, - RefSources: refSources, }) } // GetHelmCharts returns list of helm charts in the specified repository func (s *Server) GetHelmCharts(ctx context.Context, q *repositorypkg.RepoQuery) (*apiclient.HelmChartsResponse, error) { - repo, err := s.getRepo(ctx, q.Repo, q.GetAppProject()) + repo, err := s.getRepo(ctx, q.Repo) if err != nil { return nil, err } @@ -429,7 +411,7 @@ func (s *Server) CreateRepository(ctx context.Context, q *repositorypkg.RepoCrea repo, err = s.db.CreateRepository(ctx, r) if status.Convert(err).Code() == codes.AlreadyExists { // act idempotent if existing spec matches new spec - existing, getErr := s.db.GetRepository(ctx, r.Repo, q.Repo.Project) + existing, getErr := s.db.GetRepository(ctx, r.Repo) if getErr != nil { return nil, status.Errorf(codes.Internal, "unable to check existing repository details: %v", getErr) } @@ -443,7 +425,7 @@ func (s *Server) CreateRepository(ctx context.Context, q *repositorypkg.RepoCrea r.Project = q.Repo.Project return s.UpdateRepository(ctx, &repositorypkg.RepoUpdateRequest{Repo: r}) } else { - return nil, status.Error(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("repository", existing, r)) + return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("repository", existing, r)) } } if err != nil { @@ -464,7 +446,7 @@ func (s *Server) UpdateRepository(ctx context.Context, q *repositorypkg.RepoUpda return nil, status.Errorf(codes.InvalidArgument, "missing payload in request") } - repo, err := s.getRepo(ctx, q.Repo.Repo, q.Repo.Project) + repo, err := s.getRepo(ctx, q.Repo.Repo) if err != nil { return nil, err } @@ -489,7 +471,7 @@ func (s *Server) Delete(ctx context.Context, q *repositorypkg.RepoQuery) (*repos // DeleteRepository removes a repository from the configuration func (s *Server) DeleteRepository(ctx context.Context, q *repositorypkg.RepoQuery) (*repositorypkg.RepoResponse, error) { - repo, err := getRepository(ctx, s.ListRepositories, q) + repo, err := s.getRepo(ctx, q.Repo) if err != nil { return nil, err } @@ -499,53 +481,14 @@ func (s *Server) DeleteRepository(ctx context.Context, q *repositorypkg.RepoQuer } // invalidate cache - if err := s.cache.SetRepoConnectionState(repo.Repo, repo.Project, nil); err != nil { + if err := s.cache.SetRepoConnectionState(q.Repo, nil); err == nil { log.Errorf("error invalidating cache: %v", err) } - err = s.db.DeleteRepository(ctx, repo.Repo, repo.Project) + err = s.db.DeleteRepository(ctx, q.Repo) return &repositorypkg.RepoResponse{}, err } -// getRepository fetches a single repository which the user has access to. If only one repository can be found which -// matches the same URL, that will be returned (this is for backward compatibility reasons). If multiple repositories -// are matched, a repository is only returned if it matches the app project of the incoming request. -func getRepository(ctx context.Context, listRepositories func(context.Context, *repositorypkg.RepoQuery) (*v1alpha1.RepositoryList, error), q *repositorypkg.RepoQuery) (*appsv1.Repository, error) { - repositories, err := listRepositories(ctx, q) - if err != nil { - return nil, err - } - - var foundRepos []*v1alpha1.Repository - for _, repo := range repositories.Items { - if git.SameURL(repo.Repo, q.Repo) { - foundRepos = append(foundRepos, repo) - } - } - - if len(foundRepos) == 0 { - return nil, errPermissionDenied - } - - var foundRepo *v1alpha1.Repository - if len(foundRepos) == 1 && q.GetAppProject() == "" { - foundRepo = foundRepos[0] - } else if len(foundRepos) > 0 { - for _, repo := range foundRepos { - if repo.Project == q.GetAppProject() { - foundRepo = repo - break - } - } - } - - if foundRepo == nil { - return nil, fmt.Errorf("repository not found for url %q and project %q", q.Repo, q.GetAppProject()) - } - - return foundRepo, nil -} - // ValidateAccess checks whether access to a repository is possible with the // given URL and credentials. func (s *Server) ValidateAccess(ctx context.Context, q *repositorypkg.RepoAccessQuery) (*repositorypkg.RepoResponse, error) { @@ -616,48 +559,20 @@ 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, index int32, versionId int32) bool { - // We have to check if the spec is within the source or sources split - // and then iterate over the historical - if app.Spec.HasMultipleSources() { - appSources := app.Spec.GetSources() - for _, s := range appSources { - if source.Equals(&s) { - return true - } - } - } else { - appSource := app.Spec.GetSource() - if source.Equals(&appSource) { - return true - } +func isSourceInHistory(app *v1alpha1.Application, source v1alpha1.ApplicationSource) bool { + appSource := app.Spec.GetSource() + if source.Equals(&appSource) { + 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 - // In case of multi source apps, we have to check the specific versionID because users - // could have removed/added new sources and we cannot check all the versions due to that for _, h := range app.Status.History { - // multi source revision - if len(h.Sources) > 0 { - if h.ID == int64(versionId) { - if h.Revisions == nil { - continue - } - h.Sources[index].TargetRevision = h.Revisions[index] - if source.Equals(&h.Sources[index]) { - return true - } - } - } else { // single source revision - h.Source.TargetRevision = h.Revision - if source.Equals(&h.Source) { - return true - } + h.Source.TargetRevision = h.Revision + if source.Equals(&h.Source) { + return true } } - return false } diff --git a/server/repository/repository.proto b/server/repository/repository.proto index 379cbdeabf9cc..6466967702e85 100644 --- a/server/repository/repository.proto +++ b/server/repository/repository.proto @@ -30,10 +30,6 @@ message RepoAppDetailsQuery { github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSource source = 1; string appName = 2; string appProject = 3; - // source index (for multi source apps) - int32 sourceIndex = 4; - // versionId from historical data (for multi source apps) - int32 versionId = 5; } // RepoAppsResponse contains applications of specified repository @@ -47,8 +43,6 @@ message RepoQuery { string repo = 1; // Whether to force a cache refresh on repo's connection state bool forceRefresh = 2; - // App project for query - string appProject = 3; } // RepoAccessQuery is a query for checking access to a repo diff --git a/server/repository/repository_test.go b/server/repository/repository_test.go index 72354633048dc..11667319e57a0 100644 --- a/server/repository/repository_test.go +++ b/server/repository/repository_test.go @@ -9,7 +9,6 @@ import ( "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/status" corev1 "k8s.io/api/core/v1" @@ -136,99 +135,6 @@ 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{ - { - ID: 1, - Revisions: []string{ - "abcdef123567", - }, - 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) { @@ -268,7 +174,7 @@ func TestRepositoryServer(t *testing.T) { s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projInformer, testNamespace, settingsMgr) url := "https://test" - repo, _ := s.getRepo(context.TODO(), url, "") + repo, _ := s.getRepo(context.TODO(), url) assert.Equal(t, repo.Repo, url) }) @@ -282,7 +188,7 @@ func TestRepositoryServer(t *testing.T) { _, err := s.ValidateAccess(context.TODO(), &repository.RepoAccessQuery{ Repo: url, }) - require.NoError(t, err) + assert.Nil(t, err) }) t.Run("Test_Get", func(t *testing.T) { @@ -292,15 +198,14 @@ func TestRepositoryServer(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("ListRepositories", context.TODO()).Return([]*appsv1.Repository{{Repo: url}}, nil) - db.On("GetRepository", context.TODO(), url, "").Return(&appsv1.Repository{Repo: url}, nil) - db.On("RepositoryExists", context.TODO(), url, "").Return(true, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, 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, }) - require.NoError(t, err) + assert.Nil(t, err) assert.Equal(t, repo.Repo, url) }) @@ -317,15 +222,14 @@ func TestRepositoryServer(t *testing.T) { Username: "foo", InheritedCreds: true, } - db.On("ListRepositories", context.TODO()).Return([]*appsv1.Repository{testRepo}, nil) - db.On("GetRepository", context.TODO(), url, "").Return(testRepo, nil) - db.On("RepositoryExists", context.TODO(), url, "").Return(true, nil) + 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, }) - require.NoError(t, err) + assert.Nil(t, err) testRepo.ConnectionState = repo.ConnectionState // overwrite connection state on our test object to simplify comparison below @@ -338,9 +242,8 @@ func TestRepositoryServer(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("ListRepositories", context.TODO()).Return(nil, nil) - db.On("GetRepository", context.TODO(), url, "").Return(nil, errors.New("some error")) - db.On("RepositoryExists", context.TODO(), url, "").Return(true, nil) + db.On("GetRepository", context.TODO(), url).Return(nil, errors.New("some error")) + 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{ @@ -353,13 +256,11 @@ func TestRepositoryServer(t *testing.T) { t.Run("Test_GetWithNotExistRepoShouldReturn404", func(t *testing.T) { repoServerClient := mocks.RepoServerServiceClient{} repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} - repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) url := "https://test" db := &dbmocks.ArgoDB{} - db.On("ListRepositories", context.TODO()).Return([]*appsv1.Repository{{Repo: url}}, nil) - db.On("GetRepository", context.TODO(), url, "").Return(&appsv1.Repository{Repo: url}, nil) - db.On("RepositoryExists", context.TODO(), url, "").Return(false, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) + db.On("RepositoryExists", context.TODO(), url).Return(false, nil) s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr) repo, err := s.Get(context.TODO(), &repository.RepoQuery{ @@ -388,8 +289,8 @@ func TestRepositoryServer(t *testing.T) { Username: "test", }, }) - require.NoError(t, err) - assert.Equal(t, "repo", repo.Repo) + assert.Nil(t, err) + assert.Equal(t, repo.Repo, "repo") }) t.Run("Test_CreateRepositoryWithUpsert", func(t *testing.T) { @@ -398,7 +299,7 @@ func TestRepositoryServer(t *testing.T) { repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), "test", "").Return(&appsv1.Repository{ + db.On("GetRepository", context.TODO(), "test").Return(&appsv1.Repository{ Repo: "test", Username: "test", }, nil) @@ -414,8 +315,8 @@ func TestRepositoryServer(t *testing.T) { Upsert: true, }) - require.NoError(t, err) - assert.Equal(t, "test", repo.Repo) + assert.Nil(t, err) + assert.Equal(t, repo.Repo, "test") }) t.Run("Test_ListRepositories", func(t *testing.T) { @@ -426,14 +327,14 @@ func TestRepositoryServer(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "argocd").Return(nil, nil) + 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{}) - require.NoError(t, err) - assert.Len(t, resp.Items, 2) + assert.NoError(t, err) + assert.Equal(t, 2, len(resp.Items)) }) } @@ -449,7 +350,7 @@ func TestRepositoryServerListApps(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) appLister, projLister := newAppAndProjLister(defaultProj) s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) @@ -472,7 +373,7 @@ func TestRepositoryServerListApps(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, 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) repoServerClient.On("ListApps", context.TODO(), mock.Anything).Return(&apiclient.AppList{ @@ -488,7 +389,7 @@ func TestRepositoryServerListApps(t *testing.T) { AppName: "foo", AppProject: "default", }) - require.NoError(t, err) + assert.NoError(t, err) assert.Len(t, resp.Items, 1) assert.Equal(t, "path/to/dir", resp.Items[0].Path) assert.Equal(t, "Kustomize", resp.Items[0].Type) @@ -503,7 +404,7 @@ func TestRepositoryServerListApps(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, 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) repoServerClient.On("ListApps", context.TODO(), mock.Anything).Return(&apiclient.AppList{ @@ -520,7 +421,7 @@ func TestRepositoryServerListApps(t *testing.T) { AppProject: "default", }) assert.Nil(t, resp) - require.Error(t, err, "repository 'https://test' not permitted in project 'default'") + assert.Error(t, err, "repository 'https://test' not permitted in project 'default'") }) } @@ -536,7 +437,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) appLister, projLister := newAppAndProjLister(defaultProj) s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) @@ -548,7 +449,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { AppProject: "default", }) assert.Nil(t, resp) - require.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: repositories, get, https://test") + assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: repositories, get, https://test") }) t.Run("Test_WithoutAppReadPrivileges", func(t *testing.T) { repoServerClient := mocks.RepoServerServiceClient{} @@ -559,7 +460,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) appLister, projLister := newAppAndProjLister(defaultProj) s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) @@ -571,7 +472,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { AppProject: "default", }) assert.Nil(t, resp) - require.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, get, default/newapp") + assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, get, default/newapp") }) t.Run("Test_WithoutCreatePrivileges", func(t *testing.T) { repoServerClient := mocks.RepoServerServiceClient{} @@ -581,7 +482,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) appLister, projLister := newAppAndProjLister(defaultProj) s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) @@ -593,7 +494,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { AppProject: "default", }) assert.Nil(t, resp) - require.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, create, default/newapp") + assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, create, default/newapp") }) t.Run("Test_WithCreatePrivileges", func(t *testing.T) { repoServerClient := mocks.RepoServerServiceClient{} @@ -603,7 +504,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil) - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, 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: "Directory"} @@ -618,7 +519,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { AppName: "newapp", AppProject: "default", }) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, expectedResp, *resp) }) t.Run("Test_RepoNotPermitted", func(t *testing.T) { @@ -628,7 +529,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, 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: "Directory"} @@ -643,7 +544,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { AppName: "newapp", AppProject: "default", }) - require.Error(t, err, "repository 'https://test' not permitted in project 'default'") + assert.Error(t, err, "repository 'https://test' not permitted in project 'default'") assert.Nil(t, resp) }) t.Run("Test_ExistingApp", func(t *testing.T) { @@ -654,7 +555,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil) - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, 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: "Directory"} @@ -663,92 +564,13 @@ 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.GetSourcePtrByIndex(0), + Source: guestbookApp.Spec.GetSourcePtr(), AppName: "guestbook", AppProject: "default", }) - require.NoError(t, err) + 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, "default").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.Len(t, sources, 2) - resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ - Source: &sources[0], - AppName: multiSourceApp001AppName, - AppProject: "default", - }) - require.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", - }) - require.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, "default").Return(&appsv1.Repository{Repo: url0}, nil) - db.On("GetRepository", context.TODO(), url1, "default").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.Len(t, sources, 2) - - resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ - Source: &sources[0], - AppName: multiSourceApp002AppName, - AppProject: "default", - }) - require.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", - }) - require.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} @@ -756,12 +578,12 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "mismatch").Return(&appsv1.Repository{Repo: url}, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp) s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ - Source: guestbookApp.Spec.GetSourcePtrByIndex(0), + Source: guestbookApp.Spec.GetSourcePtr(), AppName: "guestbook", AppProject: "mismatch", }) @@ -775,7 +597,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp) differentSource := guestbookApp.Spec.Source.DeepCopy() differentSource.Helm.ValueFiles = []string{"/etc/passwd"} @@ -796,7 +618,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { url := "https://test" db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil) db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) @@ -812,66 +634,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { AppName: "guestbook", AppProject: "default", }) - require.NoError(t, err) - assert.Equal(t, expectedResp, *resp) - }) - - t.Run("Test_ExistingAppMultiSourceNotInHistory", 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, "default").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) - - differentSource := multiSourceApp001.Spec.Sources[0].DeepCopy() - differentSource.Helm.ValueFiles = []string{"/etc/passwd"} - - s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) - resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ - Source: differentSource, - AppName: multiSourceApp001AppName, - AppProject: "default", - SourceIndex: 0, - VersionId: 1, - }) - assert.Equal(t, errPermissionDenied, err) - assert.Nil(t, resp) - }) - t.Run("Test_ExistingAppMultiSourceInHistory", func(t *testing.T) { - repoServerClient := mocks.RepoServerServiceClient{} - repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} - enforcer := newEnforcer(kubeclientset) - - url := "https://helm.elastic.co" - db := &dbmocks.ArgoDB{} - db.On("GetRepository", context.TODO(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) - db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil) - db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) - db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) - expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"} - repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil) - appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp001) - previousSource := multiSourceApp001.Status.History[0].Sources[0].DeepCopy() - previousSource.TargetRevision = multiSourceApp001.Status.History[0].Revisions[0] - - s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) - resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ - Source: previousSource, - AppName: multiSourceApp001AppName, - AppProject: "default", - SourceIndex: 0, - VersionId: 1, - }) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, expectedResp, *resp) }) } @@ -901,159 +664,3 @@ func newEnforcer(kubeclientset *fake.Clientset) *rbac.Enforcer { }) return enforcer } - -func TestGetRepository(t *testing.T) { - type args struct { - ctx context.Context - listRepositories func(context.Context, *repository.RepoQuery) (*appsv1.RepositoryList, error) - q *repository.RepoQuery - } - tests := []struct { - name string - args args - want *appsv1.Repository - error error - }{ - { - name: "empty project and no repos", - args: args{ - ctx: context.TODO(), - listRepositories: func(ctx context.Context, query *repository.RepoQuery) (*appsv1.RepositoryList, error) { - return &appsv1.RepositoryList{ - Items: []*appsv1.Repository{ - {Repo: "something-else"}, - }, - }, nil - }, - q: &repository.RepoQuery{}, - }, - want: nil, - error: status.Error(codes.PermissionDenied, "permission denied"), - }, - { - name: "empty project and no matching repos", - args: args{ - ctx: context.TODO(), - listRepositories: func(ctx context.Context, query *repository.RepoQuery) (*appsv1.RepositoryList, error) { - return &appsv1.RepositoryList{}, nil - }, - q: &repository.RepoQuery{ - Repo: "foobar", - }, - }, - want: nil, - error: status.Error(codes.PermissionDenied, "permission denied"), - }, - { - name: "empty project + matching repo with an empty project", - args: args{ - ctx: context.TODO(), - listRepositories: func(ctx context.Context, query *repository.RepoQuery) (*appsv1.RepositoryList, error) { - return &appsv1.RepositoryList{ - Items: []*appsv1.Repository{ - {Repo: "foobar", Project: ""}, - }, - }, nil - }, - q: &repository.RepoQuery{ - Repo: "foobar", - AppProject: "", - }, - }, - want: &appsv1.Repository{ - Repo: "foobar", - Project: "", - }, - error: nil, - }, - { - name: "empty project + matching repo with a non-empty project", - args: args{ - ctx: context.TODO(), - listRepositories: func(ctx context.Context, query *repository.RepoQuery) (*appsv1.RepositoryList, error) { - return &appsv1.RepositoryList{ - Items: []*appsv1.Repository{ - {Repo: "foobar", Project: "foobar"}, - }, - }, nil - }, - q: &repository.RepoQuery{ - Repo: "foobar", - AppProject: "", - }, - }, - want: &appsv1.Repository{ - Repo: "foobar", - Project: "foobar", - }, - error: nil, - }, - { - name: "non-empty project + matching repo with an empty project", - args: args{ - ctx: context.TODO(), - listRepositories: func(ctx context.Context, query *repository.RepoQuery) (*appsv1.RepositoryList, error) { - return &appsv1.RepositoryList{ - Items: []*appsv1.Repository{ - {Repo: "foobar", Project: ""}, - }, - }, nil - }, - q: &repository.RepoQuery{ - Repo: "foobar", - AppProject: "foobar", - }, - }, - want: nil, - error: errors.New(`repository not found for url "foobar" and project "foobar"`), - }, - { - name: "non-empty project + matching repo with a matching project", - args: args{ - ctx: context.TODO(), - listRepositories: func(ctx context.Context, query *repository.RepoQuery) (*appsv1.RepositoryList, error) { - return &appsv1.RepositoryList{ - Items: []*appsv1.Repository{ - {Repo: "foobar", Project: "foobar"}, - }, - }, nil - }, - q: &repository.RepoQuery{ - Repo: "foobar", - AppProject: "foobar", - }, - }, - want: &appsv1.Repository{ - Repo: "foobar", - Project: "foobar", - }, - error: nil, - }, - { - name: "non-empty project + matching repo with a non-matching project", - args: args{ - ctx: context.TODO(), - listRepositories: func(ctx context.Context, query *repository.RepoQuery) (*appsv1.RepositoryList, error) { - return &appsv1.RepositoryList{ - Items: []*appsv1.Repository{ - {Repo: "foobar", Project: "something-else"}, - }, - }, nil - }, - q: &repository.RepoQuery{ - Repo: "foobar", - AppProject: "foobar", - }, - }, - want: nil, - error: errors.New(`repository not found for url "foobar" and project "foobar"`), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := getRepository(tt.args.ctx, tt.args.listRepositories, tt.args.q) - assert.Equal(t, tt.error, err) - assert.Equalf(t, tt.want, got, "getRepository(%v, %v) = %v", tt.args.ctx, tt.args.q, got) - }) - } -} diff --git a/server/server.go b/server/server.go index 7ded3951a37c3..54b0fab457d73 100644 --- a/server/server.go +++ b/server/server.go @@ -54,10 +54,8 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apiclient" @@ -125,11 +123,9 @@ import ( "github.com/argoproj/argo-cd/v2/util/webhook" ) -const ( - maxConcurrentLoginRequestsCountEnv = "ARGOCD_MAX_CONCURRENT_LOGIN_REQUESTS_COUNT" - replicasCountEnv = "ARGOCD_API_SERVER_REPLICAS" - renewTokenKey = "renew-token" -) +const maxConcurrentLoginRequestsCountEnv = "ARGOCD_MAX_CONCURRENT_LOGIN_REQUESTS_COUNT" +const replicasCountEnv = "ARGOCD_API_SERVER_REPLICAS" +const renewTokenKey = "renew-token" // ErrNoSession indicates no auth token was supplied as part of a request var ErrNoSession = status.Errorf(codes.Unauthenticated, "no session information") @@ -169,7 +165,6 @@ func init() { // ArgoCDServer is the API server for Argo CD type ArgoCDServer struct { ArgoCDServerOpts - ApplicationSetOpts ssoClientApp *oidc.ClientApp settings *settings_util.ArgoCDSettings @@ -201,55 +196,30 @@ type ArgoCDServer struct { } 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 *dexutil.DexTLSConfig - BaseHRef string - RootPath string - DynamicClientset dynamic.Interface - KubeControllerClientset client.Client - 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 - ApplicationNamespaces []string - EnableProxyExtension bool - WebhookParallelism int - EnableK8sEvent []string -} - -type ApplicationSetOpts struct { - GitSubmoduleEnabled bool - EnableNewGitFileGlobbing bool - ScmRootCAPath string - AllowedScmProviders []string - EnableScmProviders 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) + DisableAuth bool + ContentTypes []string + EnableGZip bool + Insecure bool + StaticAssetsDir string + ListenPort int + ListenHost string + MetricsPort int + MetricsHost string + Namespace string + DexServerAddr string + DexTLSConfig *dexutil.DexTLSConfig + BaseHRef string + RootPath string + KubeClientset kubernetes.Interface + AppClientset appclientset.Interface + RepoClientset repoapiclient.Clientset + Cache *servercache.Cache + RedisClient *redis.Client + TLSConfigCustomizer tlsutil.ConfigCustomizer + XFrameOptions string + ContentSecurityPolicy string + ApplicationNamespaces []string + EnableProxyExtension bool } // initializeDefaultProject creates the default project if it does not already exist @@ -274,7 +244,7 @@ func initializeDefaultProject(opts ArgoCDServerOpts) error { } // NewServer returns a new instance of the Argo CD API server -func NewServer(ctx context.Context, opts ArgoCDServerOpts, appsetOpts ApplicationSetOpts) *ArgoCDServer { +func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer { settingsMgr := settings_util.NewSettingsManager(ctx, opts.KubeClientset, opts.Namespace) settings, err := settingsMgr.InitializeSettings(opts.Insecure) errorsutil.CheckError(err) @@ -327,31 +297,29 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts, appsetOpts Applicatio sg := extension.NewDefaultSettingsGetter(settingsMgr) ag := extension.NewDefaultApplicationGetter(appLister) pg := extension.NewDefaultProjectGetter(projLister, dbInstance) - ug := extension.NewDefaultUserGetter(policyEnf) - em := extension.NewManager(logger, sg, ag, pg, enf, ug) + em := extension.NewManager(logger, sg, ag, pg, enf) a := &ArgoCDServer{ - ArgoCDServerOpts: opts, - ApplicationSetOpts: appsetOpts, - log: logger, - settings: settings, - sessionMgr: sessionMgr, - settingsMgr: settingsMgr, - enf: enf, - projInformer: projInformer, - projLister: projLister, - appInformer: appInformer, - appLister: appLister, - appsetInformer: appsetInformer, - appsetLister: appsetLister, - policyEnforcer: policyEnf, - userStateStorage: userStateStorage, - staticAssets: http.FS(staticFS), - db: dbInstance, - apiFactory: apiFactory, - secretInformer: secretInformer, - configMapInformer: configMapInformer, - extensionManager: em, + ArgoCDServerOpts: opts, + log: logger, + settings: settings, + sessionMgr: sessionMgr, + settingsMgr: settingsMgr, + enf: enf, + projInformer: projInformer, + projLister: projLister, + appInformer: appInformer, + appLister: appLister, + appsetInformer: appsetInformer, + appsetLister: appsetLister, + policyEnforcer: policyEnf, + userStateStorage: userStateStorage, + staticAssets: http.FS(staticFS), + db: dbInstance, + apiFactory: apiFactory, + secretInformer: secretInformer, + configMapInformer: configMapInformer, + extensionManager: em, } err = a.logInClusterWarnings() @@ -491,7 +459,6 @@ func (a *ArgoCDServer) Listen() (*Listeners, error) { } else { dOpts = append(dOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) } - // nolint:staticcheck conn, err := grpc.Dial(fmt.Sprintf("localhost:%d", a.ListenPort), dOpts...) if err != nil { io.Close(mainLn) @@ -516,12 +483,6 @@ 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() @@ -530,9 +491,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, metricsServ) + httpsS = a.newHTTPServer(ctx, a.ListenPort, grpcWebS, appResourceTreeFn, listeners.GatewayConn) } else { - httpS = a.newHTTPServer(ctx, a.ListenPort, grpcWebS, appResourceTreeFn, listeners.GatewayConn, metricsServ) + httpS = a.newHTTPServer(ctx, a.ListenPort, grpcWebS, appResourceTreeFn, listeners.GatewayConn) } if a.RootPath != "" { httpS.Handler = withRootPath(httpS.Handler, a) @@ -546,6 +507,11 @@ func (a *ArgoCDServer) Run(ctx context.Context, listeners *Listeners) { httpsS.Handler = &bug21955Workaround{handler: httpsS.Handler} } + metricsServ := metrics.NewMetricsServer(a.MetricsHost, 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 @@ -555,6 +521,7 @@ func (a *ArgoCDServer) Run(ctx context.Context, listeners *Listeners) { if !a.useTLS() { 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("PATCH")) @@ -650,7 +617,6 @@ func (a *ArgoCDServer) watchSettings() { a.settingsMgr.Subscribe(updateCh) prevURL := a.settings.URL - prevAdditionalURLs := a.settings.AdditionalURLs prevOIDCConfig := a.settings.OIDCConfig() prevDexCfgBytes, err := dexutil.GenerateDexConfigYAML(a.settings, a.DexTLSConfig == nil || a.DexTLSConfig.DisableTLS) errorsutil.CheckError(err) @@ -682,10 +648,6 @@ func (a *ArgoCDServer) watchSettings() { log.Infof("url modified. restarting") break } - if !reflect.DeepEqual(prevAdditionalURLs, a.settings.AdditionalURLs) { - log.Infof("additionalURLs modified. restarting") - break - } if prevGitHubSecret != a.settings.WebhookGitHubSecret { log.Infof("github secret modified. restarting") break @@ -740,7 +702,7 @@ func (a *ArgoCDServer) rbacPolicyLoader(ctx context.Context) { scopes = make([]string, 0) err := yaml.Unmarshal([]byte(scopesStr), &scopes) if err != nil { - return fmt.Errorf("error unmarshalling scopes: %w", err) + return err } } @@ -886,17 +848,12 @@ func newArgoCDServiceSet(a *ArgoCDServer) *ArgoCDServiceSet { projectLock, a.settingsMgr, a.projInformer, - a.ApplicationNamespaces, - a.EnableK8sEvent, - ) + a.ApplicationNamespaces) applicationSetService := applicationset.NewServer( a.db, a.KubeClientset, - a.DynamicClientset, - a.KubeControllerClientset, a.enf, - a.RepoClientset, a.AppClientset, a.appsetInformer, a.appsetLister, @@ -904,16 +861,9 @@ func newArgoCDServiceSet(a *ArgoCDServer) *ArgoCDServiceSet { a.settingsMgr, a.Namespace, projectLock, - a.ApplicationNamespaces, - a.GitSubmoduleEnabled, - a.EnableNewGitFileGlobbing, - a.ScmRootCAPath, - a.AllowedScmProviders, - a.EnableScmProviders, - a.EnableK8sEvent, - ) - - projectService := project.NewServer(a.Namespace, a.KubeClientset, a.AppClientset, a.enf, projectLock, a.sessionMgr, a.policyEnforcer, a.projInformer, a.settingsMgr, a.db, a.EnableK8sEvent) + 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) accountService := account.NewServer(a.sessionMgr, a.settingsMgr, a.enf) @@ -956,7 +906,7 @@ func (a *ArgoCDServer) translateGrpcCookieHeader(ctx context.Context, w http.Res token := sessionResp.Token err := a.setTokenCookie(token, w) if err != nil { - return fmt.Errorf("error setting token cookie from session response: %w", err) + return err } } else if md, ok := runtime.ServerMetadataFromContext(ctx); ok { renewToken := md.HeaderMD[renewTokenKey] @@ -976,7 +926,7 @@ func (a *ArgoCDServer) setTokenCookie(token string, w http.ResponseWriter) error } cookies, err := httputil.MakeCookieMetadata(common.AuthCookieName, token, flags...) if err != nil { - return fmt.Errorf("error creating cookie metadata: %w", err) + return err } for _, cookie := range cookies { w.Header().Add("Set-Cookie", cookie) @@ -1009,7 +959,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, metricsReg HTTPMetricsRegistry) *http.Server { +func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandler http.Handler, appResourceTreeFn application.AppResourceTreeFn, conn *grpc.ClientConn) *http.Server { endpoint := fmt.Sprintf("localhost:%d", port) mux := http.NewServeMux() httpS := http.Server{ @@ -1047,9 +997,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl } mux.Handle("/api/", handler) - terminalOpts := application.TerminalOptions{DisableAuth: a.ArgoCDServerOpts.DisableAuth, Enf: a.enf} - - terminal := application.NewHandler(a.appLister, a.Namespace, a.ApplicationNamespaces, a.db, a.Cache, appResourceTreeFn, a.settings.ExecShells, a.sessionMgr, &terminalOpts). + 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) @@ -1060,7 +1008,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl // 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) + registerExtensions(mux, a) } mustRegisterGWHandler(versionpkg.RegisterVersionServiceHandler, ctx, gwmux, conn) @@ -1086,7 +1034,7 @@ 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.ArgoCDServerOpts.ApplicationNamespaces, a.ArgoCDServerOpts.WebhookParallelism, a.AppClientset, a.settings, a.settingsMgr, a.RepoServerCache, a.Cache, argoDB, a.settingsMgr.GetMaxWebhookPayloadSize()) + acdWebhookHandler := webhook.NewHandler(a.Namespace, a.ArgoCDServerOpts.ApplicationNamespaces, a.AppClientset, a.settings, a.settingsMgr, repocache.NewCache(a.Cache.GetCache(), 24*time.Hour, 3*time.Minute), a.Cache, argoDB, a.settingsMgr.GetMaxWebhookPayloadSize()) mux.HandleFunc("/api/webhook", acdWebhookHandler.Handler) @@ -1094,7 +1042,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl registerDownloadHandlers(mux, "/download") // Serve extensions - extensionsSharedPath := "/tmp/extensions/" + var extensionsSharedPath = "/tmp/extensions/" var extensionsHandler http.Handler = http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) { a.serveExtensions(extensionsSharedPath, writer) @@ -1130,15 +1078,13 @@ func enforceContentTypes(handler http.Handler, types []string) http.Handler { // 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. -func registerExtensions(mux *http.ServeMux, a *ArgoCDServer, metricsReg HTTPMetricsRegistry) { +func registerExtensions(mux *http.ServeMux, a *ArgoCDServer) { 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 extensions: %s", err) @@ -1550,7 +1496,7 @@ func bug21955WorkaroundInterceptor(ctx context.Context, req interface{}, _ *grpc return handler(ctx, req) } -// allowedApplicationNamespacesAsString returns a string containing comma-separated list +// allowedNamespacesAsString returns a string containing comma-separated list // of allowed application namespaces func (a *ArgoCDServer) allowedApplicationNamespacesAsString() string { ns := a.Namespace diff --git a/server/server_norace_test.go b/server/server_norace_test.go index cd73a54f12c41..eaef2e67257d1 100644 --- a/server/server_norace_test.go +++ b/server/server_norace_test.go @@ -13,7 +13,6 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apiclient" @@ -22,6 +21,7 @@ import ( ) func TestUserAgent(t *testing.T) { + // !race: // A data race in go-client's `shared_informer.go`, between `sharedProcessor.run(...)` and itself. Based on // the data race, it APPEARS to be intentional, but in any case it's nothing we are doing in Argo CD @@ -30,7 +30,7 @@ func TestUserAgent(t *testing.T) { s, closer := fakeServer(t) defer closer() lns, err := s.Listen() - require.NoError(t, err) + assert.NoError(t, err) cancelInformer := test.StartInformer(s.projInformer) defer cancelInformer() @@ -45,9 +45,9 @@ func TestUserAgent(t *testing.T) { errorMsg string } currentVersionBytes, err := os.ReadFile("../VERSION") - require.NoError(t, err) + assert.NoError(t, err) currentVersion := strings.TrimSpace(string(currentVersionBytes)) - tests := []testData{ + var tests = []testData{ { // Reject out-of-date user-agent userAgent: fmt.Sprintf("%s/0.10.0", common.ArgoCDUserAgentName), @@ -74,20 +74,21 @@ func TestUserAgent(t *testing.T) { UserAgent: test.userAgent, } clnt, err := apiclient.NewClient(&opts) - require.NoError(t, err) + assert.NoError(t, err) conn, appClnt := clnt.NewApplicationClientOrDie() _, err = appClnt.List(ctx, &applicationpkg.ApplicationQuery{}) if test.errorMsg != "" { - require.Error(t, err) + assert.Error(t, err) assert.Regexp(t, test.errorMsg, err.Error()) } else { - require.NoError(t, err) + assert.NoError(t, err) } _ = conn.Close() } } func Test_StaticHeaders(t *testing.T) { + // !race: // Same as TestUserAgent @@ -96,7 +97,7 @@ func Test_StaticHeaders(t *testing.T) { s, closer := fakeServer(t) defer closer() lns, err := s.Listen() - require.NoError(t, err) + assert.NoError(t, err) cancelInformer := test.StartInformer(s.projInformer) defer cancelInformer() ctx, cancel := context.WithCancel(context.Background()) @@ -111,9 +112,9 @@ 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(http.MethodGet, url, nil) - require.NoError(t, err) + assert.NoError(t, err) resp, err := client.Do(req) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "sameorigin", resp.Header.Get("X-Frame-Options")) assert.Equal(t, "frame-ancestors 'self';", resp.Header.Get("Content-Security-Policy")) } @@ -127,7 +128,7 @@ func Test_StaticHeaders(t *testing.T) { cancelInformer := test.StartInformer(s.projInformer) defer cancelInformer() lns, err := s.Listen() - require.NoError(t, err) + assert.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) defer cancel() s.Init(ctx) @@ -140,9 +141,9 @@ 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(http.MethodGet, url, nil) - require.NoError(t, err) + assert.NoError(t, err) resp, err := client.Do(req) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "deny", resp.Header.Get("X-Frame-Options")) assert.Equal(t, "frame-ancestors 'none';", resp.Header.Get("Content-Security-Policy")) } @@ -156,7 +157,7 @@ func Test_StaticHeaders(t *testing.T) { cancelInformer := test.StartInformer(s.projInformer) defer cancelInformer() lns, err := s.Listen() - require.NoError(t, err) + assert.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) defer cancel() s.Init(ctx) @@ -164,7 +165,7 @@ func Test_StaticHeaders(t *testing.T) { defer func() { time.Sleep(3 * time.Second) }() err = test.WaitForPortListen(fmt.Sprintf("127.0.0.1:%d", s.ListenPort), 10*time.Second) - require.NoError(t, err) + assert.NoError(t, err) // Allow server startup time.Sleep(1 * time.Second) @@ -172,9 +173,9 @@ 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(http.MethodGet, url, nil) - require.NoError(t, err) + assert.NoError(t, err) resp, err := client.Do(req) - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, resp.Header.Get("X-Frame-Options")) assert.Empty(t, resp.Header.Get("Content-Security-Policy")) } diff --git a/server/server_test.go b/server/server_test.go index 7923db7f3e9d6..c4f4153f24d89 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -19,13 +19,9 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/grpc/metadata" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/fake" "sigs.k8s.io/yaml" - dynfake "k8s.io/client-go/dynamic/fake" - clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake" - "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apiclient" "github.com/argoproj/argo-cd/v2/pkg/apiclient/session" @@ -56,12 +52,10 @@ func fakeServer(t *testing.T) (*FakeArgoCDServer, func()) { kubeclientset := fake.NewSimpleClientset(cm, secret) appClientSet := apps.NewSimpleClientset() redis, closer := test.NewInMemoryRedis() + port, err := test.GetFreePort() mockRepoClient := &mocks.Clientset{RepoServerServiceClient: &mocks.RepoServerServiceClient{}} tmpAssetsDir := t.TempDir() - dynamicClient := dynfake.NewSimpleDynamicClient(runtime.NewScheme()) - fakeClient := clientfake.NewClientBuilder().Build() - port, err := test.GetFreePort() if err != nil { panic(err) } @@ -84,13 +78,11 @@ func fakeServer(t *testing.T) (*FakeArgoCDServer, func()) { 1*time.Minute, 1*time.Minute, ), - RedisClient: redis, - RepoClientset: mockRepoClient, - StaticAssetsDir: tmpAssetsDir, - DynamicClientset: dynamicClient, - KubeControllerClientset: fakeClient, + RedisClient: redis, + RepoClientset: mockRepoClient, + StaticAssetsDir: tmpAssetsDir, } - srv := NewServer(context.Background(), argoCDOpts, ApplicationSetOpts{}) + srv := NewServer(context.Background(), argoCDOpts) fakeSrv := &FakeArgoCDServer{srv, tmpAssetsDir} return fakeSrv, closer } @@ -126,7 +118,7 @@ func TestEnforceProjectToken(t *testing.T) { mockRepoClient := &mocks.Clientset{RepoServerServiceClient: &mocks.RepoServerServiceClient{}} t.Run("TestEnforceProjectTokenSuccessful", func(t *testing.T) { - s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}, ApplicationSetOpts{}) + s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}) cancel := test.StartInformer(s.projInformer) defer cancel() claims := jwt.MapClaims{"sub": defaultSub, "iat": defaultIssuedAt} @@ -135,21 +127,21 @@ func TestEnforceProjectToken(t *testing.T) { }) t.Run("TestEnforceProjectTokenWithDiffCreateAtFailure", func(t *testing.T) { - s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}, ApplicationSetOpts{}) + s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}) diffCreateAt := defaultIssuedAt + 1 claims := jwt.MapClaims{"sub": defaultSub, "iat": diffCreateAt} assert.False(t, s.enf.Enforce(claims, "applications", "get", defaultTestObject)) }) t.Run("TestEnforceProjectTokenIncorrectSubFormatFailure", func(t *testing.T) { - s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}, ApplicationSetOpts{}) + s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}) invalidSub := "proj:test" claims := jwt.MapClaims{"sub": invalidSub, "iat": defaultIssuedAt} assert.False(t, s.enf.Enforce(claims, "applications", "get", defaultTestObject)) }) t.Run("TestEnforceProjectTokenNoTokenFailure", func(t *testing.T) { - s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}, ApplicationSetOpts{}) + s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}) nonExistentToken := "fake-token" invalidSub := fmt.Sprintf(subFormat, projectName, nonExistentToken) claims := jwt.MapClaims{"sub": invalidSub, "iat": defaultIssuedAt} @@ -159,7 +151,7 @@ func TestEnforceProjectToken(t *testing.T) { t.Run("TestEnforceProjectTokenNotJWTTokenFailure", func(t *testing.T) { proj := existingProj.DeepCopy() proj.Spec.Roles[0].JWTTokens = nil - s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(proj), RepoClientset: mockRepoClient}, ApplicationSetOpts{}) + s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(proj), RepoClientset: mockRepoClient}) claims := jwt.MapClaims{"sub": defaultSub, "iat": defaultIssuedAt} assert.False(t, s.enf.Enforce(claims, "applications", "get", defaultTestObject)) }) @@ -172,7 +164,7 @@ func TestEnforceProjectToken(t *testing.T) { proj := existingProj.DeepCopy() proj.Spec.Roles[0] = role - s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(proj), RepoClientset: mockRepoClient}, ApplicationSetOpts{}) + s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(proj), RepoClientset: mockRepoClient}) cancel := test.StartInformer(s.projInformer) defer cancel() claims := jwt.MapClaims{"sub": defaultSub, "iat": defaultIssuedAt} @@ -183,7 +175,7 @@ func TestEnforceProjectToken(t *testing.T) { }) t.Run("TestEnforceProjectTokenWithIdSuccessful", func(t *testing.T) { - s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}, ApplicationSetOpts{}) + s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}) cancel := test.StartInformer(s.projInformer) defer cancel() claims := jwt.MapClaims{"sub": defaultSub, "jti": defaultId} @@ -192,12 +184,13 @@ func TestEnforceProjectToken(t *testing.T) { }) t.Run("TestEnforceProjectTokenWithInvalidIdFailure", func(t *testing.T) { - s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}, ApplicationSetOpts{}) + s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}) invalidId := "invalidId" claims := jwt.MapClaims{"sub": defaultSub, "jti": defaultId} res := s.enf.Enforce(claims, "applications", "get", invalidId) assert.False(t, res) }) + } func TestEnforceClaims(t *testing.T) { @@ -276,13 +269,13 @@ func TestInitializingExistingDefaultProject(t *testing.T) { RepoClientset: mockRepoClient, } - argocd := NewServer(context.Background(), argoCDOpts, ApplicationSetOpts{}) + argocd := NewServer(context.Background(), argoCDOpts) assert.NotNil(t, argocd) proj, err := appClientSet.ArgoprojV1alpha1().AppProjects(test.FakeArgoCDNamespace).Get(context.Background(), v1alpha1.DefaultAppProjectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.Nil(t, err) assert.NotNil(t, proj) - assert.Equal(t, v1alpha1.DefaultAppProjectName, proj.Name) + assert.Equal(t, proj.Name, v1alpha1.DefaultAppProjectName) } func TestInitializingNotExistingDefaultProject(t *testing.T) { @@ -299,13 +292,13 @@ func TestInitializingNotExistingDefaultProject(t *testing.T) { RepoClientset: mockRepoClient, } - argocd := NewServer(context.Background(), argoCDOpts, ApplicationSetOpts{}) + argocd := NewServer(context.Background(), argoCDOpts) assert.NotNil(t, argocd) proj, err := appClientSet.ArgoprojV1alpha1().AppProjects(test.FakeArgoCDNamespace).Get(context.Background(), v1alpha1.DefaultAppProjectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.Nil(t, err) assert.NotNil(t, proj) - assert.Equal(t, v1alpha1.DefaultAppProjectName, proj.Name) + assert.Equal(t, proj.Name, v1alpha1.DefaultAppProjectName) } func TestEnforceProjectGroups(t *testing.T) { @@ -341,7 +334,7 @@ func TestEnforceProjectGroups(t *testing.T) { } mockRepoClient := &mocks.Clientset{RepoServerServiceClient: &mocks.RepoServerServiceClient{}} kubeclientset := fake.NewSimpleClientset(test.NewFakeConfigMap(), test.NewFakeSecret()) - s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}, ApplicationSetOpts{}) + s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}) cancel := test.StartInformer(s.projInformer) defer cancel() claims := jwt.MapClaims{ @@ -403,7 +396,7 @@ func TestRevokedToken(t *testing.T) { }, } - s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}, ApplicationSetOpts{}) + s := NewServer(context.Background(), ArgoCDServerOpts{Namespace: test.FakeArgoCDNamespace, KubeClientset: kubeclientset, AppClientset: apps.NewSimpleClientset(&existingProj), RepoClientset: mockRepoClient}) cancel := test.StartInformer(s.projInformer) defer cancel() claims := jwt.MapClaims{"sub": defaultSub, "iat": defaultIssuedAt} @@ -425,7 +418,7 @@ func TestAuthenticate(t *testing.T) { errorMsg string anonymousEnabled bool } - tests := []testData{ + var tests = []testData{ { test: "TestNoSessionAnonymousDisabled", errorMsg: "no session information", @@ -458,11 +451,11 @@ func TestAuthenticate(t *testing.T) { AppClientset: appClientSet, RepoClientset: mockRepoClient, } - argocd := NewServer(context.Background(), argoCDOpts, ApplicationSetOpts{}) + argocd := NewServer(context.Background(), argoCDOpts) ctx := context.Background() if testData.user != "" { token, err := argocd.sessionMgr.Create(testData.user, 0, "abc") - require.NoError(t, err) + assert.NoError(t, err) ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs(apiclient.MetaDataTokenKey, token)) } @@ -470,8 +463,9 @@ func TestAuthenticate(t *testing.T) { if testData.errorMsg != "" { assert.Errorf(t, err, testData.errorMsg) } else { - require.NoError(t, err) + assert.NoError(t, err) } + }) } } @@ -595,7 +589,7 @@ connectors: if withFakeSSO && useDexForSSO { argoCDOpts.DexServerAddr = ts.URL } - argocd = NewServer(context.Background(), argoCDOpts, ApplicationSetOpts{}) + argocd = NewServer(context.Background(), argoCDOpts) 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) @@ -603,6 +597,7 @@ connectors: } func TestGetClaims(t *testing.T) { + defaultExpiry := jwt.NewNumericDate(time.Now().Add(time.Hour * 24)) defaultExpiryUnix := float64(defaultExpiry.Unix()) @@ -614,7 +609,7 @@ func TestGetClaims(t *testing.T) { expectNewToken bool additionalOIDCConfig settings_util.OIDCConfig } - tests := []testData{ + var tests = []testData{ { test: "GetClaims", claims: jwt.MapClaims{ @@ -692,7 +687,7 @@ func TestGetClaims(t *testing.T) { if testDataCopy.expectedErrorContains != "" { assert.ErrorContains(t, err, testDataCopy.expectedErrorContains, "getClaims should have thrown an error and return an error") } else { - require.NoError(t, err) + assert.NoError(t, err) } }) } @@ -710,7 +705,7 @@ func TestAuthenticate_3rd_party_JWTs(t *testing.T) { expectedClaims interface{} useDex bool } - tests := []testData{ + var tests = []testData{ // Dex { test: "anonymous disabled, no audience", @@ -851,7 +846,7 @@ func TestAuthenticate_3rd_party_JWTs(t *testing.T) { if testDataCopy.expectedErrorContains != "" { assert.ErrorContains(t, err, testDataCopy.expectedErrorContains, "Authenticate should have thrown an error and blocked the request") } else { - require.NoError(t, err) + assert.NoError(t, err) } }) } @@ -864,7 +859,7 @@ func TestAuthenticate_no_request_metadata(t *testing.T) { expectedErrorContains string expectedClaims interface{} } - tests := []testData{ + var tests = []testData{ { test: "anonymous disabled", anonymousEnabled: false, @@ -894,7 +889,7 @@ func TestAuthenticate_no_request_metadata(t *testing.T) { if testDataCopy.expectedErrorContains != "" { assert.ErrorContains(t, err, testDataCopy.expectedErrorContains, "Authenticate should have thrown an error and blocked the request") } else { - require.NoError(t, err) + assert.NoError(t, err) } }) } @@ -907,7 +902,7 @@ func TestAuthenticate_no_SSO(t *testing.T) { expectedErrorMessage string expectedClaims interface{} } - tests := []testData{ + var tests = []testData{ { test: "anonymous disabled", anonymousEnabled: false, @@ -943,7 +938,7 @@ func TestAuthenticate_no_SSO(t *testing.T) { if testDataCopy.expectedErrorMessage != "" { assert.ErrorContains(t, err, testDataCopy.expectedErrorMessage, "Authenticate should have thrown an error and blocked the request") } else { - require.NoError(t, err) + assert.NoError(t, err) } }) } @@ -957,7 +952,7 @@ func TestAuthenticate_bad_request_metadata(t *testing.T) { expectedErrorMessage string expectedClaims interface{} } - tests := []testData{ + var tests = []testData{ { test: "anonymous disabled, empty metadata", anonymousEnabled: false, @@ -1048,7 +1043,7 @@ func TestAuthenticate_bad_request_metadata(t *testing.T) { if testDataCopy.expectedErrorMessage != "" { assert.ErrorContains(t, err, testDataCopy.expectedErrorMessage, "Authenticate should have thrown an error and blocked the request") } else { - require.NoError(t, err) + assert.NoError(t, err) } }) } @@ -1079,16 +1074,16 @@ func TestTranslateGrpcCookieHeader(t *testing.T) { AppClientset: apps.NewSimpleClientset(), RepoClientset: &mocks.Clientset{RepoServerServiceClient: &mocks.RepoServerServiceClient{}}, } - argocd := NewServer(context.Background(), argoCDOpts, ApplicationSetOpts{}) + argocd := NewServer(context.Background(), argoCDOpts) t.Run("TokenIsNotEmpty", func(t *testing.T) { recorder := httptest.NewRecorder() err := argocd.translateGrpcCookieHeader(context.Background(), recorder, &session.SessionResponse{ Token: "xyz", }) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "argocd.token=xyz; path=/; SameSite=lax; httpOnly; Secure", recorder.Result().Header.Get("Set-Cookie")) - assert.Len(t, recorder.Result().Cookies(), 1) + assert.Equal(t, 1, len(recorder.Result().Cookies())) }) t.Run("TokenIsLongerThan4093", func(t *testing.T) { @@ -1096,9 +1091,9 @@ func TestTranslateGrpcCookieHeader(t *testing.T) { err := argocd.translateGrpcCookieHeader(context.Background(), recorder, &session.SessionResponse{ Token: "abc.xyz." + strings.Repeat("x", 4093), }) - require.NoError(t, err) + assert.NoError(t, err) assert.Regexp(t, "argocd.token=.*; path=/; SameSite=lax; httpOnly; Secure", recorder.Result().Header.Get("Set-Cookie")) - assert.Len(t, recorder.Result().Cookies(), 2) + assert.Equal(t, 2, len(recorder.Result().Cookies())) }) t.Run("TokenIsEmpty", func(t *testing.T) { @@ -1106,9 +1101,10 @@ func TestTranslateGrpcCookieHeader(t *testing.T) { err := argocd.translateGrpcCookieHeader(context.Background(), recorder, &session.SessionResponse{ Token: "", }) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "", recorder.Result().Header.Get("Set-Cookie")) }) + } func TestInitializeDefaultProject_ProjectDoesNotExist(t *testing.T) { @@ -1120,18 +1116,22 @@ func TestInitializeDefaultProject_ProjectDoesNotExist(t *testing.T) { } err := initializeDefaultProject(argoCDOpts) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } proj, err := argoCDOpts.AppClientset.ArgoprojV1alpha1(). AppProjects(test.FakeArgoCDNamespace).Get(context.Background(), v1alpha1.DefaultAppProjectName, metav1.GetOptions{}) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } - assert.Equal(t, v1alpha1.AppProjectSpec{ + assert.Equal(t, proj.Spec, v1alpha1.AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []v1alpha1.ApplicationDestination{{Server: "*", Namespace: "*"}}, ClusterResourceWhitelist: []metav1.GroupKind{{Group: "*", Kind: "*"}}, - }, proj.Spec) + }) } func TestInitializeDefaultProject_ProjectAlreadyInitialized(t *testing.T) { @@ -1154,23 +1154,26 @@ func TestInitializeDefaultProject_ProjectAlreadyInitialized(t *testing.T) { } err := initializeDefaultProject(argoCDOpts) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } proj, err := argoCDOpts.AppClientset.ArgoprojV1alpha1(). AppProjects(test.FakeArgoCDNamespace).Get(context.Background(), v1alpha1.DefaultAppProjectName, metav1.GetOptions{}) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } assert.Equal(t, proj.Spec, existingDefaultProject.Spec) } func TestOIDCConfigChangeDetection_SecretsChanged(t *testing.T) { - // Given + //Given rawOIDCConfig, err := yaml.Marshal(&settings_util.OIDCConfig{ ClientID: "$k8ssecret:clientid", - ClientSecret: "$k8ssecret:clientsecret", - }) - require.NoError(t, err, "no error expected when marshalling OIDC config") + ClientSecret: "$k8ssecret:clientsecret"}) + assert.NoError(t, err, "no error expected when marshalling OIDC config") originalSecrets := map[string]string{"k8ssecret:clientid": "argocd", "k8ssecret:clientsecret": "sharedargooauthsecret"} @@ -1181,24 +1184,23 @@ func TestOIDCConfigChangeDetection_SecretsChanged(t *testing.T) { assert.Equal(t, originalOIDCConfig.ClientID, originalSecrets["k8ssecret:clientid"], "expected ClientID be replaced by secret value") assert.Equal(t, originalOIDCConfig.ClientSecret, originalSecrets["k8ssecret:clientsecret"], "expected ClientSecret be replaced by secret value") - // When + //When newSecrets := map[string]string{"k8ssecret:clientid": "argocd", "k8ssecret:clientsecret": "a!Better!Secret"} argoSettings.Secrets = newSecrets result := checkOIDCConfigChange(originalOIDCConfig, &argoSettings) - // Then - assert.True(t, result, "secrets have changed, expect interpolated OIDCConfig to change") + //Then + assert.Equal(t, result, true, "secrets have changed, expect interpolated OIDCConfig to change") } func TestOIDCConfigChangeDetection_ConfigChanged(t *testing.T) { - // Given + //Given rawOIDCConfig, err := yaml.Marshal(&settings_util.OIDCConfig{ Name: "argocd", ClientID: "$k8ssecret:clientid", - ClientSecret: "$k8ssecret:clientsecret", - }) + ClientSecret: "$k8ssecret:clientsecret"}) - require.NoError(t, err, "no error expected when marshalling OIDC config") + assert.NoError(t, err, "no error expected when marshalling OIDC config") originalSecrets := map[string]string{"k8ssecret:clientid": "argocd", "k8ssecret:clientsecret": "sharedargooauthsecret"} @@ -1209,49 +1211,46 @@ func TestOIDCConfigChangeDetection_ConfigChanged(t *testing.T) { assert.Equal(t, originalOIDCConfig.ClientID, originalSecrets["k8ssecret:clientid"], "expected ClientID be replaced by secret value") assert.Equal(t, originalOIDCConfig.ClientSecret, originalSecrets["k8ssecret:clientsecret"], "expected ClientSecret be replaced by secret value") - // When + //When newRawOICDConfig, err := yaml.Marshal(&settings_util.OIDCConfig{ Name: "cat", ClientID: "$k8ssecret:clientid", - ClientSecret: "$k8ssecret:clientsecret", - }) + ClientSecret: "$k8ssecret:clientsecret"}) - require.NoError(t, err, "no error expected when marshalling OIDC config") + assert.NoError(t, err, "no error expected when marshalling OIDC config") argoSettings.OIDCConfigRAW = string(newRawOICDConfig) result := checkOIDCConfigChange(originalOIDCConfig, &argoSettings) - // Then - assert.True(t, result, "no error expected since OICD config created") + //Then + assert.Equal(t, result, true, "no error expected since OICD config created") } func TestOIDCConfigChangeDetection_ConfigCreated(t *testing.T) { - // Given + //Given argoSettings := settings_util.ArgoCDSettings{OIDCConfigRAW: ""} originalOIDCConfig := argoSettings.OIDCConfig() - // When + //When newRawOICDConfig, err := yaml.Marshal(&settings_util.OIDCConfig{ Name: "cat", ClientID: "$k8ssecret:clientid", - ClientSecret: "$k8ssecret:clientsecret", - }) - require.NoError(t, err, "no error expected when marshalling OIDC config") + ClientSecret: "$k8ssecret:clientsecret"}) + assert.NoError(t, err, "no error expected when marshalling OIDC config") newSecrets := map[string]string{"k8ssecret:clientid": "argocd", "k8ssecret:clientsecret": "sharedargooauthsecret"} argoSettings.OIDCConfigRAW = string(newRawOICDConfig) argoSettings.Secrets = newSecrets result := checkOIDCConfigChange(originalOIDCConfig, &argoSettings) - // Then - assert.True(t, result, "no error expected since new OICD config created") + //Then + assert.Equal(t, result, true, "no error expected since new OICD config created") } func TestOIDCConfigChangeDetection_ConfigDeleted(t *testing.T) { - // Given + //Given rawOIDCConfig, err := yaml.Marshal(&settings_util.OIDCConfig{ ClientID: "$k8ssecret:clientid", - ClientSecret: "$k8ssecret:clientsecret", - }) - require.NoError(t, err, "no error expected when marshalling OIDC config") + ClientSecret: "$k8ssecret:clientsecret"}) + assert.NoError(t, err, "no error expected when marshalling OIDC config") originalSecrets := map[string]string{"k8ssecret:clientid": "argocd", "k8ssecret:clientsecret": "sharedargooauthsecret"} @@ -1262,22 +1261,21 @@ func TestOIDCConfigChangeDetection_ConfigDeleted(t *testing.T) { assert.Equal(t, originalOIDCConfig.ClientID, originalSecrets["k8ssecret:clientid"], "expected ClientID be replaced by secret value") assert.Equal(t, originalOIDCConfig.ClientSecret, originalSecrets["k8ssecret:clientsecret"], "expected ClientSecret be replaced by secret value") - // When + //When argoSettings.OIDCConfigRAW = "" argoSettings.Secrets = make(map[string]string) result := checkOIDCConfigChange(originalOIDCConfig, &argoSettings) - // Then - assert.True(t, result, "no error expected since OICD config deleted") + //Then + assert.Equal(t, result, true, "no error expected since OICD config deleted") } func TestOIDCConfigChangeDetection_NoChange(t *testing.T) { - // Given + //Given rawOIDCConfig, err := yaml.Marshal(&settings_util.OIDCConfig{ ClientID: "$k8ssecret:clientid", - ClientSecret: "$k8ssecret:clientsecret", - }) - require.NoError(t, err, "no error expected when marshalling OIDC config") + ClientSecret: "$k8ssecret:clientsecret"}) + assert.NoError(t, err, "no error expected when marshalling OIDC config") originalSecrets := map[string]string{"k8ssecret:clientid": "argocd", "k8ssecret:clientsecret": "sharedargooauthsecret"} @@ -1288,11 +1286,11 @@ func TestOIDCConfigChangeDetection_NoChange(t *testing.T) { assert.Equal(t, originalOIDCConfig.ClientID, originalSecrets["k8ssecret:clientid"], "expected ClientID be replaced by secret value") assert.Equal(t, originalOIDCConfig.ClientSecret, originalSecrets["k8ssecret:clientsecret"], "expected ClientSecret be replaced by secret value") - // When + //When result := checkOIDCConfigChange(originalOIDCConfig, &argoSettings) - // Then - assert.False(t, result, "no error since no config change") + //Then + assert.Equal(t, result, false, "no error since no config change") } func TestIsMainJsBundle(t *testing.T) { @@ -1395,9 +1393,9 @@ func TestCacheControlHeaders(t *testing.T) { if testCase.createFile { tmpFile, err := os.Create(fp) - require.NoError(t, err) + assert.NoError(t, err) err = tmpFile.Close() - require.NoError(t, err) + assert.NoError(t, err) } handler(rr, req) @@ -1409,7 +1407,6 @@ func TestCacheControlHeaders(t *testing.T) { }) } } - func TestReplaceBaseHRef(t *testing.T) { testCases := []struct { name string diff --git a/server/session/ratelimiter_test.go b/server/session/ratelimiter_test.go index 1642f1b8d400e..69a6a2e78f606 100644 --- a/server/session/ratelimiter_test.go +++ b/server/session/ratelimiter_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" util "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/session" @@ -15,18 +14,18 @@ func TestRateLimiter(t *testing.T) { limiter := NewLoginRateLimiter(10) for i := 0; i < 10; i++ { closer, err := limiter() - require.NoError(t, err) + assert.NoError(t, err) closers = append(closers, closer) } // 11 request should fail _, err := limiter() assert.Equal(t, err, session.InvalidLoginErr) - if !assert.Len(t, closers, 10) { + if !assert.Equal(t, len(closers), 10) { return } // complete one request - require.NoError(t, closers[0].Close()) + assert.NoError(t, closers[0].Close()) _, err = limiter() - require.NoError(t, err) + assert.NoError(t, err) } diff --git a/server/session/session.go b/server/session/session.go index 780d66c123779..c3837d7f4af4f 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -67,6 +67,7 @@ func (s *Server) Create(_ context.Context, q *session.SessionCreateRequest) (*se fmt.Sprintf("%s:%s", q.Username, settings.AccountCapabilityLogin), int64(argoCDSettings.UserSessionDuration.Seconds()), uniqueId.String()) + if err != nil { return nil, err } diff --git a/server/settings/settings.go b/server/settings/settings.go index a598b5284f743..131ddc1924b27 100644 --- a/server/settings/settings.go +++ b/server/settings/settings.go @@ -112,7 +112,6 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin TrackingMethod: trackingMethod, ExecEnabled: argoCDSettings.ExecEnabled, AppsInAnyNamespaceEnabled: s.appsInAnyNamespaceEnabled, - ImpersonationEnabled: argoCDSettings.ImpersonationEnabled, } if sessionmgr.LoggedIn(ctx) || s.disableAuth { diff --git a/server/settings/settings.proto b/server/settings/settings.proto index 4b2396d5a22a4..a6aa97120c8de 100644 --- a/server/settings/settings.proto +++ b/server/settings/settings.proto @@ -42,7 +42,6 @@ message Settings { bool execEnabled = 22; string controllerNamespace = 23; bool appsInAnyNamespaceEnabled = 24; - bool impersonationEnabled = 25; } message GoogleAnalyticsConfig { diff --git a/sonar-project.properties b/sonar-project.properties index 21dad92e68837..e2f34fd2313d0 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -19,10 +19,7 @@ sonar.host.url=https://sonarcloud.io sonar.coverage.exclusions=**/*.pb.go,**/*.pb.gw.go,**/mocks/**,**/*.ts*,**/vendor/**,**/openapi_generated.go,**/*_test.go,**/*_generated*,test/**,pkg/client/**,pkg/apiclient/**,docs/** # Exclude following set of patterns from code analysis -sonar.go.exclusions=**/vendor/**,**/*.pb.go,**/*_test.go,**/*.pb.gw.go,**/mocks/**,**/openapi_generated.go,**/*_generated*.go,docs/** +sonar.go.exclusions=**/vendor/**,*/*.pb.go,**/*_test.go,**/*.pb.gw.go,**/mocks/**,**/openapi_generated.go,**/*_generated*.go,docs/** # Exclude following set of patterns from duplication detection sonar.cpd.exclusions=**/*.pb.go,**/*.g.cs,**/*.gw.go,**/mocks/*,docs/** - -# Exclude test manifests from analysis -sonar.kubernetes.exclusions=controller/testdata/**,test/**,util/kustomize/testdata/** diff --git a/test/container/Dockerfile b/test/container/Dockerfile index ad22c720bbb81..8536f09ecd7f0 100644 --- a/test/container/Dockerfile +++ b/test/container/Dockerfile @@ -1,20 +1,20 @@ -FROM docker.io/library/redis:7.4.0@sha256:eadf354977d428e347d93046bb1a5569d701e8deb68f090215534a99dbcb23b9 as redis +FROM docker.io/library/redis:7.0.11@sha256:f50031a49f41e493087fb95f96fdb3523bb25dcf6a3f0b07c588ad3cdbe1d0aa 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 # 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:22.9.0@sha256:cbe2d5f94110cea9817dd8c5809d05df49b4bd1aac5203f3594d88665ad37988 as node +FROM docker.io/library/node:20.7.0@sha256:f08c20b9f9c55dd47b1841793f0ee480c5395aa165cd02edfd68b068ed64bfb5 as node -FROM docker.io/library/golang:1.23@sha256:2fe82a3f3e006b4f2a316c6a21f62b66e1330ae211d039bb8d1128e12ed57bf1 as golang +FROM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca as golang -FROM docker.io/library/registry:2.8@sha256:ac0192b549007e22998eb74e8d8488dcfe70f1489520c3b144a6047ac5efbe90 as registry +FROM docker.io/library/registry:2.8@sha256:41f413c22d6156587e2a51f3e80c09808b8c70e82be149b82b5e0196a88d49b4 as registry -FROM docker.io/bitnami/kubectl:1.31@sha256:da4a9868e20d941636087cb8624a4bb441f5249d69e8f3d27e53c7d4d280a5f3 as kubectl +FROM docker.io/bitnami/kubectl:1.27@sha256:670fe3f50d45c0511bb0f2af018e2fc082ac8cdfaea02dba4e32866296036926 as kubectl -FROM docker.io/library/ubuntu:24.04@sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15 +FROM docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install --fix-missing -y \ @@ -49,7 +49,7 @@ ENV GOPATH /go COPY hack/install.sh hack/tool-versions.sh go.* ./ COPY hack/installers installers -RUN ./install.sh helm && \ +RUN ./install.sh helm-linux && \ ./install.sh kustomize && \ ./install.sh codegen-tools && \ ./install.sh codegen-go-tools && \ @@ -72,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.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/ && \ +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/ && \ rm -rf /usr/lib/linux-gnu/ # Copy registry binaries to the image @@ -85,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.22 /opt/yarn-v1.22.22 +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 @@ -93,8 +93,7 @@ COPY ./test/container/entrypoint.sh /usr/local/bin ARG UID # Prepare user configuration & build environments -RUN userdel -r ubuntu && \ - useradd -l -u ${UID} -d /home/user -s /bin/bash user && \ +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 && \ @@ -102,7 +101,6 @@ RUN userdel -r ubuntu && \ chgrp -R user /home/user && \ HOME=/home/user git config --global user.name "ArgoCD Test User" && \ HOME=/home/user git config --global user.email "noreply@example.com" && \ - HOME=/home/user git config --global --add safe.directory '*' && \ mkdir -p /go/pkg && \ mkdir -p /var/run/sshd && \ mkdir -p /root/.ssh && \ @@ -113,8 +111,8 @@ RUN userdel -r ubuntu && \ 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.22/bin/yarn /usr/local/bin/yarn && \ - ln -s /opt/yarn-v1.22.22/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 4cebac203f76d..ef5100e71bab3 100644 --- a/test/container/Procfile +++ b/test/container/Procfile @@ -1,6 +1,6 @@ controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}" api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} " -dex: sh -c "test $ARGOCD_IN_CI = true && exit 0; ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.41.1 serve /dex.yaml" +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.37.0 serve /dex.yaml" redis: sh -c "/usr/local/bin/redis-server --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}" repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_BINARY_NAME=argocd-repo-server $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}" ui: sh -c "test $ARGOCD_IN_CI = true && exit 0; cd ui && ARGOCD_E2E_YARN_HOST=0.0.0.0 ${ARGOCD_E2E_YARN_CMD:-yarn} start" diff --git a/test/e2e/accounts_test.go b/test/e2e/accounts_test.go index 7f3f056a952c9..54eba790af2c5 100644 --- a/test/e2e/accounts_test.go +++ b/test/e2e/accounts_test.go @@ -2,12 +2,12 @@ package e2e import ( "context" + "strings" "testing" "github.com/argoproj/pkg/errors" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -28,13 +28,13 @@ func TestCreateAndUseAccount(t *testing.T) { Then(). And(func(account *account.Account, err error) { assert.Equal(t, account.Name, ctx.GetName()) - assert.Equal(t, []string{"login"}, account.Capabilities) + assert.Equal(t, account.Capabilities, []string{"login"}) }). When(). Login(). Then(). CurrentUser(func(user *session.GetUserInfoResponse, err error) { - assert.True(t, user.LoggedIn) + assert.Equal(t, user.LoggedIn, true) assert.Equal(t, user.Username, ctx.GetName()) }) } @@ -49,7 +49,7 @@ func TestCanIGetLogsAllowNoSwitch(t *testing.T) { CanIGetLogs(). Then(). AndCLIOutput(func(output string, err error) { - assert.Contains(t, output, "yes") + assert.True(t, strings.Contains(output, "yes")) }) } @@ -64,7 +64,7 @@ func TestCanIGetLogsDenySwitchOn(t *testing.T) { CanIGetLogs(). Then(). AndCLIOutput(func(output string, err error) { - assert.Contains(t, output, "no") + assert.True(t, strings.Contains(output, "no")) }) } @@ -92,7 +92,7 @@ func TestCanIGetLogsAllowSwitchOn(t *testing.T) { CanIGetLogs(). Then(). AndCLIOutput(func(output string, err error) { - assert.Contains(t, output, "yes") + assert.True(t, strings.Contains(output, "yes")) }) } @@ -107,7 +107,7 @@ func TestCanIGetLogsAllowSwitchOff(t *testing.T) { CanIGetLogs(). Then(). AndCLIOutput(func(output string, err error) { - assert.Contains(t, output, "yes") + assert.True(t, strings.Contains(output, "yes")) }) } @@ -142,9 +142,9 @@ test true login, apiKey`, output) defer io.Close(closer) info, err := client.GetUserInfo(context.Background(), &session.GetUserInfoRequest{}) - require.NoError(t, err) + assert.NoError(t, err) - assert.Equal(t, "test", info.Username) + assert.Equal(t, info.Username, "test") } func TestLoginBadCredentials(t *testing.T) { @@ -161,7 +161,9 @@ func TestLoginBadCredentials(t *testing.T) { for _, r := range requests { _, err := sessionClient.Create(context.Background(), &r) - require.Error(t, err) + if !assert.Error(t, err) { + return + } errStatus, ok := status.FromError(err) if !assert.True(t, ok) { return diff --git a/test/e2e/admin_test.go b/test/e2e/admin_test.go deleted file mode 100644 index a896888b1b55f..0000000000000 --- a/test/e2e/admin_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package e2e - -import ( - "context" - "testing" - - "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - v1 "k8s.io/apimachinery/pkg/apis/meta/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/admin" - . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/admin/utils" - appfixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" -) - -func TestBackupExportImport(t *testing.T) { - var exportRawOutput string - ctx := Given(t) - // Create application in argocd namespace - appctx := appfixture.GivenWithSameState(t) - - // Create application in test namespace - appctx. - Path(guestbookPath). - Name("exported-app1"). - When(). - CreateApp(). - Then(). - And(func(app *Application) { - assert.Equal(t, "exported-app1", app.Name) - assert.Equal(t, fixture.TestNamespace(), app.Namespace) - }) - - // Create app in other namespace - appctx. - Path(guestbookPath). - Name("exported-app-other-namespace"). - SetAppNamespace(fixture.AppNamespace()). - When(). - CreateApp(). - Then(). - And(func(app *Application) { - assert.Equal(t, "exported-app-other-namespace", app.Name) - assert.Equal(t, fixture.AppNamespace(), app.Namespace) - }) - - ctx. - When(). - RunExport(). - Then(). - AndCLIOutput(func(output string, err error) { - require.NoError(t, err, "export finished with error") - exportRawOutput = output - }). - AndExportedResources(func(exportResources *ExportedResources, err error) { - require.NoError(t, err, "export format not valid") - assert.True(t, exportResources.HasResource(kube.NewResourceKey("", "ConfigMap", "", "argocd-cm")), "argocd-cm not found in export") - assert.True(t, exportResources.HasResource(kube.NewResourceKey(ApplicationSchemaGroupVersionKind.Group, ApplicationSchemaGroupVersionKind.Kind, "", "exported-app1")), "test namespace application not in export") - assert.True(t, exportResources.HasResource(kube.NewResourceKey(ApplicationSchemaGroupVersionKind.Group, ApplicationSchemaGroupVersionKind.Kind, fixture.AppNamespace(), "exported-app-other-namespace")), "app namespace application not in export") - }) - - // Test import - clean state - ctx = Given(t) - - ctx. - When(). - RunImport(exportRawOutput). - Then(). - AndCLIOutput(func(output string, err error) { - require.NoError(t, err, "import finished with error") - _, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.TestNamespace()).Get(context.Background(), "exported-app1", v1.GetOptions{}) - require.NoError(t, err, "failed getting test namespace application after import") - _, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.AppNamespace()).Get(context.Background(), "exported-app-other-namespace", v1.GetOptions{}) - require.NoError(t, err, "failed getting app namespace application after import") - }) -} diff --git a/test/e2e/app_autosync_ns_test.go b/test/e2e/app_autosync_ns_test.go index 3dbf465df49b5..53e87db87d931 100644 --- a/test/e2e/app_autosync_ns_test.go +++ b/test/e2e/app_autosync_ns_test.go @@ -96,6 +96,7 @@ func TestNSAutoSyncSelfHealEnabled(t *testing.T) { Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - assert.Empty(t, app.Status.Conditions) + assert.Len(t, app.Status.Conditions, 0) }) + } diff --git a/test/e2e/app_autosync_test.go b/test/e2e/app_autosync_test.go index 67081fe98adae..6e3c1443285e5 100644 --- a/test/e2e/app_autosync_test.go +++ b/test/e2e/app_autosync_test.go @@ -90,6 +90,7 @@ func TestAutoSyncSelfHealEnabled(t *testing.T) { Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - assert.Empty(t, app.Status.Conditions) + assert.Len(t, app.Status.Conditions, 0) }) + } diff --git a/test/e2e/app_deletion_test.go b/test/e2e/app_deletion_test.go index 9158dddffa06a..1194edcb37df3 100644 --- a/test/e2e/app_deletion_test.go +++ b/test/e2e/app_deletion_test.go @@ -67,18 +67,3 @@ 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_k8s_events_test.go b/test/e2e/app_k8s_events_test.go deleted file mode 100644 index 7438adfe7001c..0000000000000 --- a/test/e2e/app_k8s_events_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package e2e - -import ( - "context" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/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" -) - -// resource.includeEventLabelKeys keys set in argocd-cm -func TestLabelsOnAppK8sEvents(t *testing.T) { - expectedLabels := map[string]string{"app": "test", "environment": "dev"} - - Given(t). - Timeout(60). - Path("two-nice-pods"). - When(). - SetParamInSettingConfigMap("resource.includeEventLabelKeys", "app,team,env*"). - SetParamInSettingConfigMap("resource.excludeEventLabelKeys", "team"). - CreateApp("--label=app=test", "--label=environment=dev", "--label=team=A", "--label=tier=ui"). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - And(func(app *Application) { - events, err := KubeClientset.CoreV1().Events(app.Namespace).List(context.Background(), metav1.ListOptions{ - FieldSelector: fmt.Sprintf("involvedObject.name=%s,involvedObject.kind=Application", app.Name), - }) - require.NoError(t, err) - for _, event := range events.Items { - for k, v := range event.Labels { - ev, found := expectedLabels[k] - assert.True(t, found) - assert.Equal(t, ev, v) - } - } - }) -} - -// resource.includeEventLabelKeys keys not set in argocd-cm -func TestNoLabelsOnAppK8sEvents(t *testing.T) { - Given(t). - Timeout(60). - Path("two-nice-pods"). - When(). - CreateApp("--label=app=test", "--label=environment=dev", "--label=team=A", "--label=tier=ui"). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - And(func(app *Application) { - events, err := KubeClientset.CoreV1().Events(app.Namespace).List(context.Background(), metav1.ListOptions{ - FieldSelector: fmt.Sprintf("involvedObject.name=%s,involvedObject.kind=Application", app.Name), - }) - require.NoError(t, err) - for _, event := range events.Items { - assert.Nil(t, event.Labels) - } - }) -} diff --git a/test/e2e/app_management_ns_test.go b/test/e2e/app_management_ns_test.go index d4717f57d0f7f..496513d22f202 100644 --- a/test/e2e/app_management_ns_test.go +++ b/test/e2e/app_management_ns_test.go @@ -23,7 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" "github.com/argoproj/argo-cd/v2/common" applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" @@ -91,7 +91,7 @@ func TestNamespacedGetLogsDenySwitchOn(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { _, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "permission denied") }) } @@ -145,19 +145,20 @@ func TestNamespacedGetLogsAllowSwitchOnNS(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Pod") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Service") - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) + } func TestNamespacedGetLogsAllowSwitchOff(t *testing.T) { @@ -203,17 +204,17 @@ func TestNamespacedGetLogsAllowSwitchOff(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Pod") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Service") - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) } @@ -294,7 +295,7 @@ func TestNamespacedAppCreation(t *testing.T) { And(func(app *Application) { // app should be listed output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, ctx.AppQualifiedName()) }). When(). @@ -342,7 +343,7 @@ func TestNamespacedAppCreationWithoutForceUpdate(t *testing.T) { And(func(_ *Application) { // app should be listed output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, ctx.AppQualifiedName()) }). When(). @@ -367,7 +368,7 @@ func TestNamespacedDeleteAppResource(t *testing.T) { And(func(_ *Application) { // app should be listed if _, err := RunCli("app", "delete-resource", ctx.AppQualifiedName(), "--kind", "Service", "--resource-name", "guestbook-ui"); err != nil { - require.NoError(t, err) + assert.NoError(t, err) } }). Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). @@ -451,7 +452,7 @@ func TestNamespacedAppDeletion(t *testing.T) { Expect(NamespacedEvent(AppNamespace(), EventReasonResourceDeleted, "delete")) output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, output, ctx.AppQualifiedName()) } @@ -539,15 +540,16 @@ func TestNamespacedAppRollbackSuccessful(t *testing.T) { // sync app and make sure it reaches InSync state _, err = RunCli("app", "rollback", app.QualifiedName(), "1") require.NoError(t, err) + }). Expect(NamespacedEvent(AppNamespace(), EventReasonOperationStarted, "rollback")). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { assert.Equal(t, SyncStatusCodeSynced, app.Status.Sync.Status) require.NotNil(t, app.Status.OperationState.SyncResult) - assert.Len(t, app.Status.OperationState.SyncResult.Resources, 2) + assert.Equal(t, 2, len(app.Status.OperationState.SyncResult.Resources)) assert.Equal(t, OperationSucceeded, app.Status.OperationState.Phase) - assert.Len(t, app.Status.History, 3) + assert.Equal(t, 3, len(app.Status.History)) }) } @@ -590,9 +592,9 @@ func TestNamespacedManipulateApplicationResources(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { manifests, err := RunCli("app", "manifests", ctx.AppQualifiedName(), "--source", "live") - require.NoError(t, err) + assert.NoError(t, err) resources, err := kube.SplitYAML([]byte(manifests)) - require.NoError(t, err) + assert.NoError(t, err) index := -1 for i := range resources { @@ -601,31 +603,31 @@ func TestNamespacedManipulateApplicationResources(t *testing.T) { break } } - assert.Greater(t, index, -1) + assert.True(t, index > -1) deployment := resources[index] closer, client, err := ArgoCDClientset.NewApplicationClient() - require.NoError(t, err) + assert.NoError(t, err) defer io.Close(closer) _, err = client.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{ Name: &app.Name, - AppNamespace: ptr.To(AppNamespace()), - Group: ptr.To(deployment.GroupVersionKind().Group), - Kind: ptr.To(deployment.GroupVersionKind().Kind), - Version: ptr.To(deployment.GroupVersionKind().Version), - Namespace: ptr.To(deployment.GetNamespace()), - ResourceName: ptr.To(deployment.GetName()), + AppNamespace: pointer.String(AppNamespace()), + Group: pointer.String(deployment.GroupVersionKind().Group), + Kind: pointer.String(deployment.GroupVersionKind().Kind), + Version: pointer.String(deployment.GroupVersionKind().Version), + Namespace: pointer.String(deployment.GetNamespace()), + ResourceName: pointer.String(deployment.GetName()), }) - require.NoError(t, err) + assert.NoError(t, err) }). Expect(SyncStatusIs(SyncStatusCodeOutOfSync)) } func TestNamespacedAppWithSecrets(t *testing.T) { closer, client, err := ArgoCDClientset.NewApplicationClient() - require.NoError(t, err) + assert.NoError(t, err) defer io.Close(closer) ctx := Given(t) @@ -641,18 +643,18 @@ func TestNamespacedAppWithSecrets(t *testing.T) { And(func(app *Application) { res := FailOnErr(client.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{ Namespace: &app.Spec.Destination.Namespace, - AppNamespace: ptr.To(AppNamespace()), - Kind: ptr.To(kube.SecretKind), - Group: ptr.To(""), + AppNamespace: pointer.String(AppNamespace()), + Kind: pointer.String(kube.SecretKind), + Group: pointer.String(""), Name: &app.Name, - Version: ptr.To("v1"), - ResourceName: ptr.To("test-secret"), + Version: pointer.String("v1"), + ResourceName: pointer.String("test-secret"), })).(*applicationpkg.ApplicationResourceResponse) assetSecretDataHidden(t, res.GetManifest()) manifests, err := client.GetManifests(context.Background(), &applicationpkg.ApplicationManifestQuery{ Name: &app.Name, - AppNamespace: ptr.To(AppNamespace()), + AppNamespace: pointer.String(AppNamespace()), }) errors.CheckError(err) @@ -685,7 +687,7 @@ func TestNamespacedAppWithSecrets(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). And(func(app *Application) { diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName()) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, diffOutput, "username: ++++++++") assert.Contains(t, diffOutput, "password: ++++++++++++") @@ -697,7 +699,7 @@ func TestNamespacedAppWithSecrets(t *testing.T) { app.Spec.IgnoreDifferences = []ResourceIgnoreDifferences{{ Kind: kube.SecretKind, JSONPointers: []string{"/data"}, }} - FailOnErr(client.UpdateSpec(context.Background(), &applicationpkg.ApplicationUpdateSpecRequest{Name: &app.Name, AppNamespace: ptr.To(AppNamespace()), Spec: &app.Spec})) + FailOnErr(client.UpdateSpec(context.Background(), &applicationpkg.ApplicationUpdateSpecRequest{Name: &app.Name, AppNamespace: pointer.String(AppNamespace()), Spec: &app.Spec})) }). When(). Refresh(RefreshTypeNormal). @@ -739,7 +741,7 @@ func TestNamespacedResourceDiffing(t *testing.T) { // Patch deployment _, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Patch(context.Background(), "guestbook-ui", types.JSONPatchType, []byte(`[{ "op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "test" }]`), metav1.PatchOptions{}) - require.NoError(t, err) + assert.NoError(t, err) }). When(). Refresh(RefreshTypeNormal). @@ -747,7 +749,7 @@ func TestNamespacedResourceDiffing(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). And(func(app *Application) { diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", "testdata/guestbook") - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, diffOutput, fmt.Sprintf("===== apps/Deployment %s/guestbook-ui ======", DeploymentNamespace())) }). Given(). @@ -760,7 +762,7 @@ func TestNamespacedResourceDiffing(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", "testdata/guestbook") - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, diffOutput) }). Given(). @@ -778,7 +780,7 @@ func TestNamespacedResourceDiffing(t *testing.T) { Sync(). And(func() { output, err := RunWithStdin(testdata.SSARevisionHistoryDeployment, "", "kubectl", "apply", "-n", DeploymentNamespace(), "--server-side=true", "--field-manager=revision-history-manager", "--validate=false", "--force-conflicts", "-f", "-") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "serverside-applied") }). Refresh(RefreshTypeNormal). @@ -805,12 +807,12 @@ func TestNamespacedResourceDiffing(t *testing.T) { }]`). And(func() { deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, int32(3), *deployment.Spec.RevisionHistoryLimit) }). And(func() { output, err := RunWithStdin(testdata.SSARevisionHistoryDeployment, "", "kubectl", "apply", "-n", DeploymentNamespace(), "--server-side=true", "--field-manager=revision-history-manager", "--validate=false", "--force-conflicts", "-f", "-") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "serverside-applied") }). Then(). @@ -819,13 +821,13 @@ func TestNamespacedResourceDiffing(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, int32(1), *deployment.Spec.RevisionHistoryLimit) }). When().Sync().Then().Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, int32(1), *deployment.Spec.RevisionHistoryLimit) }) } @@ -897,7 +899,7 @@ func testNSEdgeCasesApplicationResources(t *testing.T, appPath string, statusCod And(func(app *Application) { diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", path.Join("testdata", appPath)) assert.Empty(t, diffOutput) - require.NoError(t, err) + assert.NoError(t, err) }) } @@ -922,36 +924,36 @@ func TestNamespacedResourceAction(t *testing.T) { Sync(). Then(). And(func(app *Application) { + closer, client, err := ArgoCDClientset.NewApplicationClient() - require.NoError(t, err) + assert.NoError(t, err) defer io.Close(closer) actions, err := client.ListResourceActions(context.Background(), &applicationpkg.ApplicationResourceRequest{ Name: &app.Name, - AppNamespace: ptr.To(AppNamespace()), - Group: ptr.To("apps"), - Kind: ptr.To("Deployment"), - Version: ptr.To("v1"), - Namespace: ptr.To(DeploymentNamespace()), - ResourceName: ptr.To("guestbook-ui"), + AppNamespace: pointer.String(AppNamespace()), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("guestbook-ui"), }) - require.NoError(t, err) + 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: ptr.To("apps"), - Kind: ptr.To("Deployment"), - Version: ptr.To("v1"), - Namespace: ptr.To(DeploymentNamespace()), - ResourceName: ptr.To("guestbook-ui"), - Action: ptr.To("sample"), - AppNamespace: ptr.To(AppNamespace()), + _, err = client.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{Name: &app.Name, + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("guestbook-ui"), + Action: pointer.String("sample"), + AppNamespace: pointer.String(AppNamespace()), }) - require.NoError(t, err) + assert.NoError(t, err) deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "test", deployment.Labels["sample"]) }) @@ -973,7 +975,7 @@ func TestNamespacedSyncResourceByLabel(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { _, err := RunCli("app", "sync", ctx.AppQualifiedName(), "--label", "this-label=does-not-exist") - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "level=fatal") }) } @@ -1042,10 +1044,10 @@ func TestNamespacedNoLocalSyncWithAutosyncEnabled(t *testing.T) { Then(). And(func(app *Application) { _, err := RunCli("app", "set", app.QualifiedName(), "--sync-policy", "automated") - require.NoError(t, err) + assert.NoError(t, err) _, err = RunCli("app", "sync", app.QualifiedName(), "--local", guestbookPathLocal) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "Cannot use local sync") }) } @@ -1061,11 +1063,11 @@ func TestNamespacedLocalSyncDryRunWithASEnabled(t *testing.T) { Then(). And(func(app *Application) { _, err := RunCli("app", "set", app.QualifiedName(), "--sync-policy", "automated") - require.NoError(t, err) + assert.NoError(t, err) appBefore := app.DeepCopy() _, err = RunCli("app", "sync", app.QualifiedName(), "--dry-run", "--local-repo-root", ".", "--local", guestbookPathLocal) - require.NoError(t, err) + assert.NoError(t, err) appAfter := app.DeepCopy() assert.True(t, reflect.DeepEqual(appBefore, appAfter)) @@ -1091,10 +1093,11 @@ func TestNamespacedSyncAsync(t *testing.T) { func assertNSResourceActions(t *testing.T, appName string, successful bool) { assertError := func(err error, message string) { if successful { - require.NoError(t, err) + assert.NoError(t, err) } else { - require.Error(t, err) - assert.Contains(t, err.Error(), message) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), message) + } } } @@ -1105,15 +1108,15 @@ func assertNSResourceActions(t *testing.T, appName string, successful bool) { require.NoError(t, err) logs, err := cdClient.PodLogs(context.Background(), &applicationpkg.ApplicationPodLogsQuery{ - Group: ptr.To("apps"), - Kind: ptr.To("Deployment"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), Name: &appName, - AppNamespace: ptr.To(AppNamespace()), - Namespace: ptr.To(DeploymentNamespace()), - Container: ptr.To(""), - SinceSeconds: ptr.To(int64(0)), - TailLines: ptr.To(int64(0)), - Follow: ptr.To(false), + AppNamespace: pointer.String(AppNamespace()), + Namespace: pointer.String(DeploymentNamespace()), + Container: pointer.String(""), + SinceSeconds: pointer.Int64(0), + TailLines: pointer.Int64(0), + Follow: pointer.Bool(false), }) require.NoError(t, err) _, err = logs.Recv() @@ -1123,44 +1126,44 @@ func assertNSResourceActions(t *testing.T, appName string, successful bool) { _, err = cdClient.ListResourceEvents(context.Background(), &applicationpkg.ApplicationResourceEventsQuery{ Name: &appName, - AppNamespace: ptr.To(AppNamespace()), - ResourceName: ptr.To("guestbook-ui"), - ResourceNamespace: ptr.To(DeploymentNamespace()), - ResourceUID: ptr.To(string(deploymentResource.UID)), + AppNamespace: pointer.String(AppNamespace()), + ResourceName: pointer.String("guestbook-ui"), + ResourceNamespace: pointer.String(DeploymentNamespace()), + ResourceUID: pointer.String(string(deploymentResource.UID)), }) assertError(err, fmt.Sprintf("%s not found as part of application %s", "guestbook-ui", appName)) _, err = cdClient.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{ Name: &appName, - AppNamespace: ptr.To(AppNamespace()), - ResourceName: ptr.To("guestbook-ui"), - Namespace: ptr.To(DeploymentNamespace()), - Version: ptr.To("v1"), - Group: ptr.To("apps"), - Kind: ptr.To("Deployment"), + AppNamespace: pointer.String(AppNamespace()), + ResourceName: pointer.String("guestbook-ui"), + Namespace: pointer.String(DeploymentNamespace()), + Version: pointer.String("v1"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), }) assertError(err, expectedError) _, err = cdClient.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{ Name: &appName, - AppNamespace: ptr.To(AppNamespace()), - ResourceName: ptr.To("guestbook-ui"), - Namespace: ptr.To(DeploymentNamespace()), - Version: ptr.To("v1"), - Group: ptr.To("apps"), - Kind: ptr.To("Deployment"), - Action: ptr.To("restart"), + AppNamespace: pointer.String(AppNamespace()), + ResourceName: pointer.String("guestbook-ui"), + Namespace: pointer.String(DeploymentNamespace()), + Version: pointer.String("v1"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + Action: pointer.String("restart"), }) assertError(err, expectedError) _, err = cdClient.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{ Name: &appName, - AppNamespace: ptr.To(AppNamespace()), - ResourceName: ptr.To("guestbook-ui"), - Namespace: ptr.To(DeploymentNamespace()), - Version: ptr.To("v1"), - Group: ptr.To("apps"), - Kind: ptr.To("Deployment"), + AppNamespace: pointer.String(AppNamespace()), + ResourceName: pointer.String("guestbook-ui"), + Namespace: pointer.String(DeploymentNamespace()), + Version: pointer.String("v1"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), }) assertError(err, expectedError) } @@ -1222,8 +1225,8 @@ func TestNamespacedPermissions(t *testing.T) { defer io.Close(closer) tree, err := cdClient.ResourceTree(context.Background(), &applicationpkg.ResourcesQuery{ApplicationName: &app.Name, AppNamespace: &app.Namespace}) require.NoError(t, err) - assert.Empty(t, tree.Nodes) - assert.Empty(t, tree.OrphanedNodes) + assert.Len(t, tree.Nodes, 0) + assert.Len(t, tree.OrphanedNodes, 0) }). When(). // add missing permissions but deny management of Deployment kind @@ -1311,6 +1314,7 @@ func TestNamespacedPermissionDeniedWithScopedRepo(t *testing.T) { CreateApp(). Then(). Expect(Error("", "is not permitted in project")) + } // make sure that if we deleted a resource from the app, it is not pruned if annotated with Prune=false @@ -1339,6 +1343,7 @@ func TestNamespacedSyncOptionPruneFalse(t *testing.T) { // make sure that if we have an invalid manifest, we can add it if we disable validation, we get a server error rather than a client error func TestNamespacedSyncOptionValidateFalse(t *testing.T) { + Given(t). Path("crd-validation"). SetTrackingMethod("annotation"). @@ -1397,6 +1402,7 @@ func TestNamespacedCompareOptionIgnoreExtraneous(t *testing.T) { } func TestNamespacedSelfManagedApps(t *testing.T) { + Given(t). Path("self-managed-app"). SetTrackingMethod("annotation"). @@ -1425,7 +1431,7 @@ func TestNamespacedSelfManagedApps(t *testing.T) { lastReconciledAt = reconciledAt } - assert.Less(t, reconciledCount, 3, "Application was reconciled too many times") + assert.True(t, reconciledCount < 3, "Application was reconciled too many times") }) } @@ -1477,7 +1483,7 @@ func TestNamespacedOrphanedResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: ptr.To(true)}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true)}, SourceNamespaces: []string{AppNamespace()}, }). SetTrackingMethod("annotation"). @@ -1502,14 +1508,14 @@ func TestNamespacedOrphanedResource(t *testing.T) { Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")). And(func(app *Application) { output, err := RunCli("app", "resources", app.QualifiedName()) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "orphaned-configmap") }). Given(). ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: ptr.To(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}}, SourceNamespaces: []string{AppNamespace()}, }). When(). @@ -1518,14 +1524,14 @@ func TestNamespacedOrphanedResource(t *testing.T) { Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")). And(func(app *Application) { output, err := RunCli("app", "resources", app.QualifiedName()) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "orphaned-configmap") }). Given(). ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: ptr.To(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}}, SourceNamespaces: []string{AppNamespace()}, }). When(). @@ -1535,14 +1541,14 @@ func TestNamespacedOrphanedResource(t *testing.T) { Expect(NoConditions()). And(func(app *Application) { output, err := RunCli("app", "resources", app.QualifiedName()) - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, output, "orphaned-configmap") }). Given(). ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: ptr.To(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(). @@ -1552,7 +1558,7 @@ func TestNamespacedOrphanedResource(t *testing.T) { Expect(NoConditions()). And(func(app *Application) { output, err := RunCli("app", "resources", app.QualifiedName()) - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, output, "orphaned-configmap") }). Given(). @@ -1628,8 +1634,7 @@ func TestNamespacedNotPermittedResources(t *testing.T) { SourceNamespaces: []string{AppNamespace()}, NamespaceResourceBlacklist: []metav1.GroupKind{ {Group: "", Kind: "Service"}, - }, - }). + }}). And(func() { FailOnErr(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Create(context.Background(), ingress, metav1.CreateOptions{})) FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Create(context.Background(), svc, metav1.CreateOptions{})) @@ -1647,9 +1652,9 @@ func TestNamespacedNotPermittedResources(t *testing.T) { _, hasIngress := statusByKind[kube.IngressKind] assert.False(t, hasIngress, "Ingress is prohibited not managed object and should be even visible to user") serviceStatus := statusByKind[kube.ServiceKind] - assert.Equal(t, SyncStatusCodeUnknown, serviceStatus.Status, "Service is prohibited managed resource so should be set to Unknown") + assert.Equal(t, serviceStatus.Status, SyncStatusCodeUnknown, "Service is prohibited managed resource so should be set to Unknown") deploymentStatus := statusByKind[kube.DeploymentKind] - assert.Equal(t, SyncStatusCodeOutOfSync, deploymentStatus.Status) + assert.Equal(t, deploymentStatus.Status, SyncStatusCodeOutOfSync) }). When(). Delete(true). @@ -1677,7 +1682,7 @@ func TestNamespacedSyncWithInfos(t *testing.T) { _, err := RunCli("app", "sync", app.QualifiedName(), "--info", fmt.Sprintf("%s=%s", expectedInfo[0].Name, expectedInfo[0].Value), "--info", fmt.Sprintf("%s=%s", expectedInfo[1].Name, expectedInfo[1].Value)) - require.NoError(t, err) + assert.NoError(t, err) }). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { @@ -1701,8 +1706,8 @@ func TestNamespacedCreateAppWithNoNameSpaceForGlobalResource(t *testing.T) { And(func(app *Application) { time.Sleep(500 * time.Millisecond) app, err := AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{}) - require.NoError(t, err) - assert.Empty(t, app.Status.Conditions) + assert.NoError(t, err) + assert.Len(t, app.Status.Conditions, 0) }) } @@ -1726,8 +1731,8 @@ func TestNamespacedCreateAppWithNoNameSpaceWhenRequired(t *testing.T) { require.NoError(t, err) assert.Len(t, updatedApp.Status.Conditions, 2) - assert.Equal(t, ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[0].Type) - assert.Equal(t, ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[1].Type) + assert.Equal(t, updatedApp.Status.Conditions[0].Type, ApplicationConditionInvalidSpecError) + assert.Equal(t, updatedApp.Status.Conditions[1].Type, ApplicationConditionInvalidSpecError) }) } @@ -1752,8 +1757,8 @@ func TestNamespacedCreateAppWithNoNameSpaceWhenRequired2(t *testing.T) { require.NoError(t, err) assert.Len(t, updatedApp.Status.Conditions, 2) - assert.Equal(t, ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[0].Type) - assert.Equal(t, ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[1].Type) + assert.Equal(t, updatedApp.Status.Conditions[0].Type, ApplicationConditionInvalidSpecError) + assert.Equal(t, updatedApp.Status.Conditions[1].Type, ApplicationConditionInvalidSpecError) }) } @@ -1765,7 +1770,7 @@ func TestNamespacedListResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: ptr.To(true)}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true)}, SourceNamespaces: []string{AppNamespace()}, }). Path(guestbookPath). @@ -1788,19 +1793,19 @@ func TestNamespacedListResource(t *testing.T) { Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")). And(func(app *Application) { output, err := RunCli("app", "resources", app.QualifiedName()) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "orphaned-configmap") assert.Contains(t, output, "guestbook-ui") }). And(func(app *Application) { output, err := RunCli("app", "resources", app.QualifiedName(), "--orphaned=true") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "orphaned-configmap") assert.NotContains(t, output, "guestbook-ui") }). And(func(app *Application) { output, err := RunCli("app", "resources", app.QualifiedName(), "--orphaned=false") - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, output, "orphaned-configmap") assert.Contains(t, output, "guestbook-ui") }). @@ -1832,7 +1837,7 @@ func TestNamespacedNamespaceAutoCreation(t *testing.T) { defer func() { if !t.Skipped() { _, err := Run("", "kubectl", "delete", "namespace", updatedNamespace) - require.NoError(t, err) + assert.NoError(t, err) } }() Given(t). @@ -1860,7 +1865,7 @@ func TestNamespacedNamespaceAutoCreation(t *testing.T) { And(func(app *Application) { // Verify delete app does not delete the namespace auto created output, err := Run("", "kubectl", "get", "namespace", updatedNamespace) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, updatedNamespace) }) } @@ -1876,7 +1881,7 @@ func TestNamespacedNamespaceAutoCreationWithMetadata(t *testing.T) { defer func() { if !t.Skipped() { _, err := Run("", "kubectl", "delete", "namespace", updatedNamespace) - require.NoError(t, err) + assert.NoError(t, err) } }() ctx := Given(t) @@ -1892,8 +1897,7 @@ func TestNamespacedNamespaceAutoCreationWithMetadata(t *testing.T) { ManagedNamespaceMetadata: &ManagedNamespaceMetadata{ Labels: map[string]string{"foo": "bar"}, Annotations: map[string]string{"bar": "bat"}, - }, - } + }} }). Then(). Expect(NoNamespace(updatedNamespace)). @@ -1927,6 +1931,7 @@ func TestNamespacedNamespaceAutoCreationWithMetadata(t *testing.T) { Then(). Expect(Success("")). Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) { + delete(ns.Labels, "kubernetes.io/metadata.name") delete(ns.Labels, "argocd.argoproj.io/tracking-id") delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration") @@ -1969,7 +1974,7 @@ func TestNamespacedNamespaceAutoCreationWithMetadataAndNsManifest(t *testing.T) defer func() { if !t.Skipped() { _, err := Run("", "kubectl", "delete", "namespace", namespace) - require.NoError(t, err) + assert.NoError(t, err) } }() @@ -1986,8 +1991,7 @@ func TestNamespacedNamespaceAutoCreationWithMetadataAndNsManifest(t *testing.T) ManagedNamespaceMetadata: &ManagedNamespaceMetadata{ Labels: map[string]string{"foo": "bar", "abc": "123"}, Annotations: map[string]string{"bar": "bat"}, - }, - } + }} }). Then(). Expect(NoNamespace(namespace)). @@ -2023,7 +2027,7 @@ func TestNamespacedNamespaceAutoCreationWithPreexistingNs(t *testing.T) { defer func() { if !t.Skipped() { _, err := Run("", "kubectl", "delete", "namespace", updatedNamespace) - require.NoError(t, err) + assert.NoError(t, err) } }() @@ -2045,7 +2049,7 @@ metadata: errors.CheckError(err) _, err = Run("", "kubectl", "apply", "-f", tmpFile.Name()) - require.NoError(t, err) + assert.NoError(t, err) ctx := Given(t) ctx. @@ -2060,8 +2064,7 @@ metadata: ManagedNamespaceMetadata: &ManagedNamespaceMetadata{ Labels: map[string]string{"foo": "bar"}, Annotations: map[string]string{"bar": "bat"}, - }, - } + }} }). Then(). Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) { @@ -2098,6 +2101,7 @@ metadata: Then(). Expect(Success("")). Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) { + assert.Empty(t, app.Status.Conditions) delete(ns.Labels, "kubernetes.io/metadata.name") @@ -2118,6 +2122,7 @@ metadata: Then(). Expect(Success("")). Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) { + assert.Empty(t, app.Status.Conditions) delete(ns.Labels, "kubernetes.io/metadata.name") @@ -2162,14 +2167,16 @@ func TestNamespacedCreateDisableValidation(t *testing.T) { And(func(app *Application) { _, err := RunCli("app", "create", app.QualifiedName(), "--upsert", "--validate=false", "--repo", RepoURL(RepoURLTypeFile), "--path", "baddir2", "--project", app.Spec.Project, "--dest-server", KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace()) - require.NoError(t, err) + assert.NoError(t, err) }). When(). AppSet("--path", "baddir3", "--validate=false") + } func TestNamespacedCreateFromPartialFile(t *testing.T) { - partialApp := `metadata: + partialApp := + `metadata: labels: labels.local/from-file: file labels.local/from-args: file @@ -2253,45 +2260,44 @@ definitions: // tests resource actions on a CRD using status subresource And(func(app *Application) { _, err := RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "StatusSubResource", "update-both") - require.NoError(t, err) + assert.NoError(t, err) text := FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.spec.foo}")).(string) assert.Equal(t, "update-both", text) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.status.bar}")).(string) assert.Equal(t, "update-both", text) _, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "StatusSubResource", "update-spec") - require.NoError(t, err) + assert.NoError(t, err) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.spec.foo}")).(string) assert.Equal(t, "update-spec", text) _, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "StatusSubResource", "update-status") - require.NoError(t, err) + assert.NoError(t, err) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.status.bar}")).(string) assert.Equal(t, "update-status", text) }). // tests resource actions on a CRD *not* using status subresource And(func(app *Application) { _, err := RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "NonStatusSubResource", "update-both") - require.NoError(t, err) + assert.NoError(t, err) text := FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.spec.foo}")).(string) assert.Equal(t, "update-both", text) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.status.bar}")).(string) assert.Equal(t, "update-both", text) _, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "NonStatusSubResource", "update-spec") - require.NoError(t, err) + assert.NoError(t, err) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.spec.foo}")).(string) assert.Equal(t, "update-spec", text) _, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "NonStatusSubResource", "update-status") - require.NoError(t, err) + assert.NoError(t, err) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.status.bar}")).(string) assert.Equal(t, "update-status", text) }) } func TestNamespacedAppLogs(t *testing.T) { - t.SkipNow() // Too flaky. https://github.com/argoproj/argo-cd/issues/13834 SkipOnEnv(t, "OPENSHIFT") Given(t). SetAppNamespace(AppNamespace()). @@ -2304,17 +2310,17 @@ func TestNamespacedAppLogs(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { out, err := RunCliWithRetry(5, "app", "logs", app.QualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(5, "app", "logs", app.QualifiedName(), "--kind", "Pod") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(5, "app", "logs", app.QualifiedName(), "--kind", "Service") - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) } @@ -2361,14 +2367,14 @@ func TestNamespacedSyncOptionReplace(t *testing.T) { Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - assert.Equal(t, "configmap/my-map created", app.Status.OperationState.SyncResult.Resources[0].Message) + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map created") }). When(). Sync(). Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - assert.Equal(t, "configmap/my-map replaced", app.Status.OperationState.SyncResult.Resources[0].Message) + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map replaced") }) } @@ -2384,14 +2390,14 @@ func TestNamespacedSyncOptionReplaceFromCLI(t *testing.T) { Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - assert.Equal(t, "configmap/my-map created", app.Status.OperationState.SyncResult.Resources[0].Message) + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map created") }). When(). Sync(). Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - assert.Equal(t, "configmap/my-map replaced", app.Status.OperationState.SyncResult.Resources[0].Message) + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map replaced") }) } @@ -2438,7 +2444,7 @@ func TestNamespacedDisableManifestGeneration(t *testing.T) { Refresh(RefreshTypeHard). Then(). And(func(app *Application) { - assert.Equal(t, ApplicationSourceTypeKustomize, app.Status.SourceType) + assert.Equal(t, app.Status.SourceType, ApplicationSourceTypeKustomize) }). When(). And(func() { @@ -2453,7 +2459,7 @@ func TestNamespacedDisableManifestGeneration(t *testing.T) { time.Sleep(1 * time.Second) }). And(func(app *Application) { - assert.Equal(t, ApplicationSourceTypeDirectory, app.Status.SourceType) + assert.Equal(t, app.Status.SourceType, ApplicationSourceTypeDirectory) }) } diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index 67e1b4b03397c..257e8a27ecbd0 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "reflect" + "regexp" "testing" "time" @@ -24,7 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" "github.com/argoproj/argo-cd/v2/common" applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" @@ -99,7 +100,7 @@ func TestGetLogsDenySwitchOn(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { _, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "permission denied") }) } @@ -150,19 +151,20 @@ func TestGetLogsAllowSwitchOn(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Pod") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Service") - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) + } func TestGetLogsAllowSwitchOff(t *testing.T) { @@ -206,17 +208,17 @@ func TestGetLogsAllowSwitchOff(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Pod") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Service") - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) } @@ -400,7 +402,7 @@ func TestAppCreation(t *testing.T) { And(func(_ *Application) { // app should be listed output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, Name()) }). When(). @@ -445,7 +447,7 @@ func TestAppCreationWithoutForceUpdate(t *testing.T) { And(func(_ *Application) { // app should be listed output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, Name()) }). When(). @@ -455,45 +457,6 @@ func TestAppCreationWithoutForceUpdate(t *testing.T) { Expect(Error("", "existing application spec is different, use upsert flag to force update")) } -// Test designed to cover #15126. -// The issue occurs in the controller, when a valuesObject field that contains non-strings (eg, a nested map) gets -// merged/patched. -// Note: Failure is observed by the test timing out, because the controller cannot 'merge' the patch. -func TestPatchValuesObject(t *testing.T) { - Given(t). - Timeout(30). - Path("helm"). - When(). - // app should be auto-synced once created - CreateFromFile(func(app *Application) { - app.Spec.Source.Helm = &ApplicationSourceHelm{ - ValuesObject: &runtime.RawExtension{ - // Setup by using nested YAML objects, which is what causes the patch error: - // "unable to find api field in struct RawExtension for the json field "some"" - Raw: []byte(`{"some": {"foo": "bar"}}`), - }, - } - }). - Then(). - When(). - PatchApp(`[{ - "op": "add", - "path": "/spec/source/helm/valuesObject", - "value": {"some":{"foo":"bar","new":"field"}} - }]`). - Refresh(RefreshTypeNormal). - Sync(). - Then(). - Expect(Success("")). - Expect(OperationPhaseIs(OperationSucceeded)). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - Expect(NoConditions()). - And(func(app *Application) { - // Check that the patch was a success. - assert.Equal(t, `{"some":{"foo":"bar","new":"field"}}`, string(app.Spec.Source.Helm.ValuesObject.Raw)) - }) -} - func TestDeleteAppResource(t *testing.T) { ctx := Given(t) @@ -507,7 +470,7 @@ func TestDeleteAppResource(t *testing.T) { And(func(_ *Application) { // app should be listed if _, err := RunCli("app", "delete-resource", Name(), "--kind", "Service", "--resource-name", "guestbook-ui"); err != nil { - require.NoError(t, err) + assert.NoError(t, err) } }). Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). @@ -529,6 +492,7 @@ func TestPatchHttp(t *testing.T) { 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" @@ -601,7 +565,7 @@ func TestAppDeletion(t *testing.T) { Expect(Event(EventReasonResourceDeleted, "delete")) output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, output, Name()) } @@ -681,15 +645,16 @@ func TestAppRollbackSuccessful(t *testing.T) { // sync app and make sure it reaches InSync state _, err = RunCli("app", "rollback", app.Name, "1") require.NoError(t, err) + }). Expect(Event(EventReasonOperationStarted, "rollback")). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { assert.Equal(t, SyncStatusCodeSynced, app.Status.Sync.Status) require.NotNil(t, app.Status.OperationState.SyncResult) - assert.Len(t, app.Status.OperationState.SyncResult.Resources, 2) + assert.Equal(t, 2, len(app.Status.OperationState.SyncResult.Resources)) assert.Equal(t, OperationSucceeded, app.Status.OperationState.Phase) - assert.Len(t, app.Status.History, 3) + assert.Equal(t, 3, len(app.Status.History)) }) } @@ -725,9 +690,9 @@ func TestManipulateApplicationResources(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { manifests, err := RunCli("app", "manifests", app.Name, "--source", "live") - require.NoError(t, err) + assert.NoError(t, err) resources, err := kube.SplitYAML([]byte(manifests)) - require.NoError(t, err) + assert.NoError(t, err) index := -1 for i := range resources { @@ -736,40 +701,40 @@ func TestManipulateApplicationResources(t *testing.T) { break } } - assert.Greater(t, index, -1) + assert.True(t, index > -1) deployment := resources[index] closer, client, err := ArgoCDClientset.NewApplicationClient() - require.NoError(t, err) + assert.NoError(t, err) defer io.Close(closer) _, err = client.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{ Name: &app.Name, - Group: ptr.To(deployment.GroupVersionKind().Group), - Kind: ptr.To(deployment.GroupVersionKind().Kind), - Version: ptr.To(deployment.GroupVersionKind().Version), - Namespace: ptr.To(deployment.GetNamespace()), - ResourceName: ptr.To(deployment.GetName()), + Group: pointer.String(deployment.GroupVersionKind().Group), + Kind: pointer.String(deployment.GroupVersionKind().Kind), + Version: pointer.String(deployment.GroupVersionKind().Version), + Namespace: pointer.String(deployment.GetNamespace()), + ResourceName: pointer.String(deployment.GetName()), }) - require.NoError(t, err) + assert.NoError(t, err) }). Expect(SyncStatusIs(SyncStatusCodeOutOfSync)) } func assetSecretDataHidden(t *testing.T, manifest string) { secret, err := UnmarshalToUnstructured(manifest) - require.NoError(t, err) + assert.NoError(t, err) _, hasStringData, err := unstructured.NestedMap(secret.Object, "stringData") - require.NoError(t, err) + assert.NoError(t, err) assert.False(t, hasStringData) secretData, hasData, err := unstructured.NestedMap(secret.Object, "data") - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, hasData) for _, v := range secretData { - assert.Regexp(t, `[*]*`, v) + assert.Regexp(t, regexp.MustCompile(`[*]*`), v) } var lastAppliedConfigAnnotation string annotations := secret.GetAnnotations() @@ -783,7 +748,7 @@ func assetSecretDataHidden(t *testing.T, manifest string) { func TestAppWithSecrets(t *testing.T) { closer, client, err := ArgoCDClientset.NewApplicationClient() - require.NoError(t, err) + assert.NoError(t, err) defer io.Close(closer) Given(t). @@ -796,11 +761,11 @@ func TestAppWithSecrets(t *testing.T) { And(func(app *Application) { res := FailOnErr(client.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{ Namespace: &app.Spec.Destination.Namespace, - Kind: ptr.To(kube.SecretKind), - Group: ptr.To(""), + Kind: pointer.String(kube.SecretKind), + Group: pointer.String(""), Name: &app.Name, - Version: ptr.To("v1"), - ResourceName: ptr.To("test-secret"), + Version: pointer.String("v1"), + ResourceName: pointer.String("test-secret"), })).(*applicationpkg.ApplicationResourceResponse) assetSecretDataHidden(t, res.GetManifest()) @@ -836,7 +801,7 @@ func TestAppWithSecrets(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). And(func(app *Application) { diffOutput, err := RunCli("app", "diff", app.Name) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, diffOutput, "username: ++++++++") assert.Contains(t, diffOutput, "password: ++++++++++++") @@ -887,7 +852,7 @@ func TestResourceDiffing(t *testing.T) { // Patch deployment _, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Patch(context.Background(), "guestbook-ui", types.JSONPatchType, []byte(`[{ "op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "test" }]`), metav1.PatchOptions{}) - require.NoError(t, err) + assert.NoError(t, err) }). When(). Refresh(RefreshTypeNormal). @@ -895,7 +860,7 @@ func TestResourceDiffing(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). And(func(app *Application) { diffOutput, err := RunCli("app", "diff", app.Name, "--local", "testdata", "--server-side-generate") - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, diffOutput, fmt.Sprintf("===== apps/Deployment %s/guestbook-ui ======", DeploymentNamespace())) }). Given(). @@ -908,7 +873,7 @@ func TestResourceDiffing(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { diffOutput, err := RunCli("app", "diff", app.Name, "--local", "testdata", "--server-side-generate") - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, diffOutput) }). Given(). @@ -926,7 +891,7 @@ func TestResourceDiffing(t *testing.T) { Sync(). And(func() { output, err := RunWithStdin(testdata.SSARevisionHistoryDeployment, "", "kubectl", "apply", "-n", DeploymentNamespace(), "--server-side=true", "--field-manager=revision-history-manager", "--validate=false", "--force-conflicts", "-f", "-") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "serverside-applied") }). Refresh(RefreshTypeNormal). @@ -953,12 +918,12 @@ func TestResourceDiffing(t *testing.T) { }]`). And(func() { deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, int32(3), *deployment.Spec.RevisionHistoryLimit) }). And(func() { output, err := RunWithStdin(testdata.SSARevisionHistoryDeployment, "", "kubectl", "apply", "-n", DeploymentNamespace(), "--server-side=true", "--field-manager=revision-history-manager", "--validate=false", "--force-conflicts", "-f", "-") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "serverside-applied") }). Then(). @@ -967,13 +932,13 @@ func TestResourceDiffing(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, int32(1), *deployment.Spec.RevisionHistoryLimit) }). When().Sync().Then().Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, int32(1), *deployment.Spec.RevisionHistoryLimit) }) } @@ -1038,7 +1003,7 @@ func testEdgeCasesApplicationResources(t *testing.T, appPath string, statusCode And(func(app *Application) { diffOutput, err := RunCli("app", "diff", app.Name, "--local", "testdata", "--server-side-generate") assert.Empty(t, diffOutput) - require.NoError(t, err) + assert.NoError(t, err) }) } @@ -1058,34 +1023,34 @@ func TestOldStyleResourceAction(t *testing.T) { Sync(). Then(). And(func(app *Application) { + closer, client, err := ArgoCDClientset.NewApplicationClient() - require.NoError(t, err) + assert.NoError(t, err) defer io.Close(closer) actions, err := client.ListResourceActions(context.Background(), &applicationpkg.ApplicationResourceRequest{ Name: &app.Name, - Group: ptr.To("apps"), - Kind: ptr.To("Deployment"), - Version: ptr.To("v1"), - Namespace: ptr.To(DeploymentNamespace()), - ResourceName: ptr.To("guestbook-ui"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("guestbook-ui"), }) - require.NoError(t, err) + 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: ptr.To("apps"), - Kind: ptr.To("Deployment"), - Version: ptr.To("v1"), - Namespace: ptr.To(DeploymentNamespace()), - ResourceName: ptr.To("guestbook-ui"), - Action: ptr.To("sample"), + _, err = client.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{Name: &app.Name, + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("guestbook-ui"), + Action: pointer.String("sample"), }) - require.NoError(t, err) + assert.NoError(t, err) deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "test", deployment.Labels["sample"]) }) @@ -1157,41 +1122,40 @@ func TestNewStyleResourceActionPermitted(t *testing.T) { NamespaceResourceWhitelist: []metav1.GroupKind{ {Group: "batch", Kind: "Job"}, {Group: "batch", Kind: "CronJob"}, - }, - }). + }}). When(). CreateApp(). Sync(). Then(). And(func(app *Application) { + closer, client, err := ArgoCDClientset.NewApplicationClient() - require.NoError(t, err) + assert.NoError(t, err) defer io.Close(closer) actions, err := client.ListResourceActions(context.Background(), &applicationpkg.ApplicationResourceRequest{ Name: &app.Name, - Group: ptr.To("batch"), - Kind: ptr.To("CronJob"), - Version: ptr.To("v1"), - Namespace: ptr.To(DeploymentNamespace()), - ResourceName: ptr.To("hello"), + Group: pointer.String("batch"), + Kind: pointer.String("CronJob"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("hello"), }) - require.NoError(t, err) + 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: ptr.To("batch"), - Kind: ptr.To("CronJob"), - Version: ptr.To("v1"), - Namespace: ptr.To(DeploymentNamespace()), - ResourceName: ptr.To("hello"), - Action: ptr.To("sample"), + _, 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"), }) - require.NoError(t, err) + assert.NoError(t, err) _, err = KubeClientset.BatchV1().Jobs(DeploymentNamespace()).Get(context.Background(), "hello-123", metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) }) } @@ -1268,46 +1232,45 @@ func TestNewStyleResourceActionMixedOk(t *testing.T) { NamespaceResourceWhitelist: []metav1.GroupKind{ {Group: "batch", Kind: "Job"}, {Group: "batch", Kind: "CronJob"}, - }, - }). + }}). When(). CreateApp(). Sync(). Then(). And(func(app *Application) { + closer, client, err := ArgoCDClientset.NewApplicationClient() - require.NoError(t, err) + assert.NoError(t, err) defer io.Close(closer) actions, err := client.ListResourceActions(context.Background(), &applicationpkg.ApplicationResourceRequest{ Name: &app.Name, - Group: ptr.To("batch"), - Kind: ptr.To("CronJob"), - Version: ptr.To("v1"), - Namespace: ptr.To(DeploymentNamespace()), - ResourceName: ptr.To("hello"), + Group: pointer.String("batch"), + Kind: pointer.String("CronJob"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("hello"), }) - require.NoError(t, err) + 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: ptr.To("batch"), - Kind: ptr.To("CronJob"), - Version: ptr.To("v1"), - Namespace: ptr.To(DeploymentNamespace()), - ResourceName: ptr.To("hello"), - Action: ptr.To("sample"), + _, 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"), }) - require.NoError(t, err) + assert.NoError(t, err) // Assert new Job was created _, err = KubeClientset.BatchV1().Jobs(DeploymentNamespace()).Get(context.Background(), "hello-123", metav1.GetOptions{}) - require.NoError(t, err) + 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"]) - require.NoError(t, err) + assert.NoError(t, err) }) } @@ -1324,7 +1287,7 @@ func TestSyncResourceByLabel(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { _, err := RunCli("app", "sync", app.Name, "--label", "this-label=does-not-exist") - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "level=fatal") }) } @@ -1342,7 +1305,7 @@ func TestSyncResourceByProject(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { _, err := RunCli("app", "sync", app.Name, "--project", "this-project-does-not-exist") - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "level=fatal") }) } @@ -1404,10 +1367,10 @@ func TestNoLocalSyncWithAutosyncEnabled(t *testing.T) { Then(). And(func(app *Application) { _, err := RunCli("app", "set", app.Name, "--sync-policy", "automated") - require.NoError(t, err) + assert.NoError(t, err) _, err = RunCli("app", "sync", app.Name, "--local", guestbookPathLocal) - require.Error(t, err) + assert.Error(t, err) }) } @@ -1420,11 +1383,11 @@ func TestLocalSyncDryRunWithAutosyncEnabled(t *testing.T) { Then(). And(func(app *Application) { _, err := RunCli("app", "set", app.Name, "--sync-policy", "automated") - require.NoError(t, err) + assert.NoError(t, err) appBefore := app.DeepCopy() _, err = RunCli("app", "sync", app.Name, "--dry-run", "--local-repo-root", ".", "--local", guestbookPathLocal) - require.NoError(t, err) + assert.NoError(t, err) appAfter := app.DeepCopy() assert.True(t, reflect.DeepEqual(appBefore, appAfter)) @@ -1448,10 +1411,11 @@ func TestSyncAsync(t *testing.T) { func assertResourceActions(t *testing.T, appName string, successful bool) { assertError := func(err error, message string) { if successful { - require.NoError(t, err) + assert.NoError(t, err) } else { - require.Error(t, err) - assert.Contains(t, err.Error(), message) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), message) + } } } @@ -1462,14 +1426,14 @@ func assertResourceActions(t *testing.T, appName string, successful bool) { require.NoError(t, err) logs, err := cdClient.PodLogs(context.Background(), &applicationpkg.ApplicationPodLogsQuery{ - Group: ptr.To("apps"), - Kind: ptr.To("Deployment"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), Name: &appName, - Namespace: ptr.To(DeploymentNamespace()), - Container: ptr.To(""), - SinceSeconds: ptr.To(int64(0)), - TailLines: ptr.To(int64(0)), - Follow: ptr.To(false), + Namespace: pointer.String(DeploymentNamespace()), + Container: pointer.String(""), + SinceSeconds: pointer.Int64(0), + TailLines: pointer.Int64(0), + Follow: pointer.Bool(false), }) require.NoError(t, err) _, err = logs.Recv() @@ -1479,40 +1443,40 @@ func assertResourceActions(t *testing.T, appName string, successful bool) { _, err = cdClient.ListResourceEvents(context.Background(), &applicationpkg.ApplicationResourceEventsQuery{ Name: &appName, - ResourceName: ptr.To("guestbook-ui"), - ResourceNamespace: ptr.To(DeploymentNamespace()), - ResourceUID: ptr.To(string(deploymentResource.UID)), + ResourceName: pointer.String("guestbook-ui"), + ResourceNamespace: pointer.String(DeploymentNamespace()), + ResourceUID: pointer.String(string(deploymentResource.UID)), }) assertError(err, fmt.Sprintf("%s not found as part of application %s", "guestbook-ui", appName)) _, err = cdClient.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{ Name: &appName, - ResourceName: ptr.To("guestbook-ui"), - Namespace: ptr.To(DeploymentNamespace()), - Version: ptr.To("v1"), - Group: ptr.To("apps"), - Kind: ptr.To("Deployment"), + ResourceName: pointer.String("guestbook-ui"), + Namespace: pointer.String(DeploymentNamespace()), + Version: pointer.String("v1"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), }) assertError(err, expectedError) _, err = cdClient.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{ Name: &appName, - ResourceName: ptr.To("guestbook-ui"), - Namespace: ptr.To(DeploymentNamespace()), - Version: ptr.To("v1"), - Group: ptr.To("apps"), - Kind: ptr.To("Deployment"), - Action: ptr.To("restart"), + ResourceName: pointer.String("guestbook-ui"), + Namespace: pointer.String(DeploymentNamespace()), + Version: pointer.String("v1"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + Action: pointer.String("restart"), }) assertError(err, expectedError) _, err = cdClient.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{ Name: &appName, - ResourceName: ptr.To("guestbook-ui"), - Namespace: ptr.To(DeploymentNamespace()), - Version: ptr.To("v1"), - Group: ptr.To("apps"), - Kind: ptr.To("Deployment"), + ResourceName: pointer.String("guestbook-ui"), + Namespace: pointer.String(DeploymentNamespace()), + Version: pointer.String("v1"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), }) assertError(err, expectedError) } @@ -1573,8 +1537,8 @@ func TestPermissions(t *testing.T) { fmt.Printf("APP NAME: %s\n", appName) tree, err := cdClient.ResourceTree(context.Background(), &applicationpkg.ResourcesQuery{ApplicationName: &appName, AppNamespace: &appNs}) require.NoError(t, err) - assert.Empty(t, tree.Nodes) - assert.Empty(t, tree.OrphanedNodes) + assert.Len(t, tree.Nodes, 0) + assert.Len(t, tree.OrphanedNodes, 0) }). When(). // add missing permissions but deny management of Deployment kind @@ -1737,6 +1701,7 @@ func TestSyncOptionPruneFalse(t *testing.T) { // make sure that if we have an invalid manifest, we can add it if we disable validation, we get a server error rather than a client error func TestSyncOptionValidateFalse(t *testing.T) { + Given(t). Path("crd-validation"). When(). @@ -1825,6 +1790,7 @@ func TestSourceNamespaceCanBeMigratedToManagedNamespaceWithoutBeingPrunedOrOutOf } func TestSelfManagedApps(t *testing.T) { + Given(t). Path("self-managed-app"). When(). @@ -1851,7 +1817,7 @@ func TestSelfManagedApps(t *testing.T) { lastReconciledAt = reconciledAt } - assert.Less(t, reconciledCount, 3, "Application was reconciled too many times") + assert.True(t, reconciledCount < 3, "Application was reconciled too many times") }) } @@ -1899,7 +1865,7 @@ func TestOrphanedResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: ptr.To(true)}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true)}, }). Path(guestbookPath). When(). @@ -1921,14 +1887,14 @@ func TestOrphanedResource(t *testing.T) { Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")). And(func(app *Application) { output, err := RunCli("app", "resources", app.Name) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "orphaned-configmap") }). Given(). ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: ptr.To(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}}, }). When(). Refresh(RefreshTypeNormal). @@ -1936,14 +1902,14 @@ func TestOrphanedResource(t *testing.T) { Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")). And(func(app *Application) { output, err := RunCli("app", "resources", app.Name) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "orphaned-configmap") }). Given(). ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: ptr.To(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}}, }). When(). Refresh(RefreshTypeNormal). @@ -1952,14 +1918,14 @@ func TestOrphanedResource(t *testing.T) { Expect(NoConditions()). And(func(app *Application) { output, err := RunCli("app", "resources", app.Name) - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, output, "orphaned-configmap") }). Given(). ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: ptr.To(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap", Name: "orphaned-configmap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap", Name: "orphaned-configmap"}}}, }). When(). Refresh(RefreshTypeNormal). @@ -1968,7 +1934,7 @@ func TestOrphanedResource(t *testing.T) { Expect(NoConditions()). And(func(app *Application) { output, err := RunCli("app", "resources", app.Name) - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, output, "orphaned-configmap") }). Given(). @@ -2042,8 +2008,7 @@ func TestNotPermittedResources(t *testing.T) { Destinations: []ApplicationDestination{{Namespace: DeploymentNamespace(), Server: "*"}}, NamespaceResourceBlacklist: []metav1.GroupKind{ {Group: "", Kind: "Service"}, - }, - }). + }}). And(func() { FailOnErr(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Create(context.Background(), ingress, metav1.CreateOptions{})) FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Create(context.Background(), svc, metav1.CreateOptions{})) @@ -2061,9 +2026,9 @@ func TestNotPermittedResources(t *testing.T) { _, hasIngress := statusByKind[kube.IngressKind] assert.False(t, hasIngress, "Ingress is prohibited not managed object and should be even visible to user") serviceStatus := statusByKind[kube.ServiceKind] - assert.Equal(t, SyncStatusCodeUnknown, serviceStatus.Status, "Service is prohibited managed resource so should be set to Unknown") + assert.Equal(t, serviceStatus.Status, SyncStatusCodeUnknown, "Service is prohibited managed resource so should be set to Unknown") deploymentStatus := statusByKind[kube.DeploymentKind] - assert.Equal(t, SyncStatusCodeOutOfSync, deploymentStatus.Status) + assert.Equal(t, deploymentStatus.Status, SyncStatusCodeOutOfSync) }). When(). Delete(true). @@ -2089,7 +2054,7 @@ func TestSyncWithInfos(t *testing.T) { _, err := RunCli("app", "sync", app.Name, "--info", fmt.Sprintf("%s=%s", expectedInfo[0].Name, expectedInfo[0].Value), "--info", fmt.Sprintf("%s=%s", expectedInfo[1].Name, expectedInfo[1].Value)) - require.NoError(t, err) + assert.NoError(t, err) }). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { @@ -2111,8 +2076,8 @@ func TestCreateAppWithNoNameSpaceForGlobalResource(t *testing.T) { And(func(app *Application) { time.Sleep(500 * time.Millisecond) app, err := AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{}) - require.NoError(t, err) - assert.Empty(t, app.Status.Conditions) + assert.NoError(t, err) + assert.Len(t, app.Status.Conditions, 0) }) } @@ -2134,8 +2099,8 @@ func TestCreateAppWithNoNameSpaceWhenRequired(t *testing.T) { require.NoError(t, err) assert.Len(t, updatedApp.Status.Conditions, 2) - assert.Equal(t, ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[0].Type) - assert.Equal(t, ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[1].Type) + assert.Equal(t, updatedApp.Status.Conditions[0].Type, ApplicationConditionInvalidSpecError) + assert.Equal(t, updatedApp.Status.Conditions[1].Type, ApplicationConditionInvalidSpecError) }) } @@ -2158,8 +2123,8 @@ func TestCreateAppWithNoNameSpaceWhenRequired2(t *testing.T) { require.NoError(t, err) assert.Len(t, updatedApp.Status.Conditions, 2) - assert.Equal(t, ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[0].Type) - assert.Equal(t, ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[1].Type) + assert.Equal(t, updatedApp.Status.Conditions[0].Type, ApplicationConditionInvalidSpecError) + assert.Equal(t, updatedApp.Status.Conditions[1].Type, ApplicationConditionInvalidSpecError) }) } @@ -2169,7 +2134,7 @@ func TestListResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: ptr.To(true)}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true)}, }). Path(guestbookPath). When(). @@ -2191,19 +2156,19 @@ func TestListResource(t *testing.T) { Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")). And(func(app *Application) { output, err := RunCli("app", "resources", app.Name) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "orphaned-configmap") assert.Contains(t, output, "guestbook-ui") }). And(func(app *Application) { output, err := RunCli("app", "resources", app.Name, "--orphaned=true") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "orphaned-configmap") assert.NotContains(t, output, "guestbook-ui") }). And(func(app *Application) { output, err := RunCli("app", "resources", app.Name, "--orphaned=false") - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, output, "orphaned-configmap") assert.Contains(t, output, "guestbook-ui") }). @@ -2234,7 +2199,7 @@ func TestNamespaceAutoCreation(t *testing.T) { defer func() { if !t.Skipped() { _, err := Run("", "kubectl", "delete", "namespace", updatedNamespace) - require.NoError(t, err) + assert.NoError(t, err) } }() Given(t). @@ -2246,7 +2211,7 @@ func TestNamespaceAutoCreation(t *testing.T) { And(func(app *Application) { // Make sure the namespace we are about to update to does not exist _, err := Run("", "kubectl", "get", "namespace", updatedNamespace) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "not found") }). When(). @@ -2265,7 +2230,7 @@ func TestNamespaceAutoCreation(t *testing.T) { And(func(app *Application) { // Verify delete app does not delete the namespace auto created output, err := Run("", "kubectl", "get", "namespace", updatedNamespace) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, updatedNamespace) }) } @@ -2294,14 +2259,16 @@ func TestCreateDisableValidation(t *testing.T) { And(func(app *Application) { _, err := RunCli("app", "create", app.Name, "--upsert", "--validate=false", "--repo", RepoURL(RepoURLTypeFile), "--path", "baddir2", "--project", app.Spec.Project, "--dest-server", KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace()) - require.NoError(t, err) + assert.NoError(t, err) }). When(). AppSet("--path", "baddir3", "--validate=false") + } func TestCreateFromPartialFile(t *testing.T) { - partialApp := `metadata: + partialApp := + `metadata: labels: labels.local/from-file: file labels.local/from-args: file @@ -2381,45 +2348,44 @@ definitions: // tests resource actions on a CRD using status subresource And(func(app *Application) { _, err := RunCli("app", "actions", "run", app.Name, "--kind", "StatusSubResource", "update-both") - require.NoError(t, err) + assert.NoError(t, err) text := FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.spec.foo}")).(string) assert.Equal(t, "update-both", text) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.status.bar}")).(string) assert.Equal(t, "update-both", text) _, err = RunCli("app", "actions", "run", app.Name, "--kind", "StatusSubResource", "update-spec") - require.NoError(t, err) + assert.NoError(t, err) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.spec.foo}")).(string) assert.Equal(t, "update-spec", text) _, err = RunCli("app", "actions", "run", app.Name, "--kind", "StatusSubResource", "update-status") - require.NoError(t, err) + assert.NoError(t, err) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.status.bar}")).(string) assert.Equal(t, "update-status", text) }). // tests resource actions on a CRD *not* using status subresource And(func(app *Application) { _, err := RunCli("app", "actions", "run", app.Name, "--kind", "NonStatusSubResource", "update-both") - require.NoError(t, err) + assert.NoError(t, err) text := FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.spec.foo}")).(string) assert.Equal(t, "update-both", text) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.status.bar}")).(string) assert.Equal(t, "update-both", text) _, err = RunCli("app", "actions", "run", app.Name, "--kind", "NonStatusSubResource", "update-spec") - require.NoError(t, err) + assert.NoError(t, err) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.spec.foo}")).(string) assert.Equal(t, "update-spec", text) _, err = RunCli("app", "actions", "run", app.Name, "--kind", "NonStatusSubResource", "update-status") - require.NoError(t, err) + assert.NoError(t, err) text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.status.bar}")).(string) assert.Equal(t, "update-status", text) }) } func TestAppLogs(t *testing.T) { - t.SkipNow() // Too flaky. https://github.com/argoproj/argo-cd/issues/13834 SkipOnEnv(t, "OPENSHIFT") Given(t). Path("guestbook-logs"). @@ -2430,17 +2396,17 @@ func TestAppLogs(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Pod") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Service") - require.NoError(t, err) + assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) } @@ -2483,14 +2449,14 @@ func TestSyncOptionReplace(t *testing.T) { Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - assert.Equal(t, "configmap/my-map created", app.Status.OperationState.SyncResult.Resources[0].Message) + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map created") }). When(). Sync(). Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - assert.Equal(t, "configmap/my-map replaced", app.Status.OperationState.SyncResult.Resources[0].Message) + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map replaced") }) } @@ -2504,14 +2470,14 @@ func TestSyncOptionReplaceFromCLI(t *testing.T) { Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - assert.Equal(t, "configmap/my-map created", app.Status.OperationState.SyncResult.Resources[0].Message) + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map created") }). When(). Sync(). Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - assert.Equal(t, "configmap/my-map replaced", app.Status.OperationState.SyncResult.Resources[0].Message) + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map replaced") }) } @@ -2554,7 +2520,7 @@ func TestDisableManifestGeneration(t *testing.T) { Refresh(RefreshTypeHard). Then(). And(func(app *Application) { - assert.Equal(t, ApplicationSourceTypeKustomize, app.Status.SourceType) + assert.Equal(t, app.Status.SourceType, ApplicationSourceTypeKustomize) }). When(). And(func() { @@ -2568,7 +2534,7 @@ func TestDisableManifestGeneration(t *testing.T) { time.Sleep(1 * time.Second) }). And(func(app *Application) { - assert.Equal(t, ApplicationSourceTypeDirectory, app.Status.SourceType) + assert.Equal(t, app.Status.SourceType, ApplicationSourceTypeDirectory) }) } @@ -2879,47 +2845,46 @@ func TestAnnotationTrackingExtraResources(t *testing.T) { Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(health.HealthStatusHealthy)) + } -func TestCreateConfigMapsAndWaitForUpdate(t *testing.T) { +// Test designed to cover #15126. +// The issue occurs in the controller, when a valuesObject field that contains non-strings (eg, a nested map) gets +// merged/patched. +// Note: Failure is observed by the test timing out, because the controller cannot 'merge' the patch. +func TestPatchValuesObject(t *testing.T) { + Given(t). - Path("config-map"). + Timeout(30). + Path("helm"). When(). - CreateApp(). - Sync(). - Then(). - And(func(app *Application) { - _, err := RunCli("app", "set", app.Name, "--sync-policy", "automated") - require.NoError(t, err) + // app should be auto-synced once created + CreateFromFile(func(app *Application) { + app.Spec.Source.Helm = &ApplicationSourceHelm{ + ValuesObject: &runtime.RawExtension{ + // Setup by using nested YAML objects, which is what causes the patch error: + // "unable to find api field in struct RawExtension for the json field "some"" + Raw: []byte(`{"some": {"foo": "bar"}}`), + }, + } }). + Then(). When(). - AddFile("other-configmap.yaml", ` -apiVersion: v1 -kind: ConfigMap -metadata: - name: other-map - annotations: - argocd.argoproj.io/sync-wave: "1" -data: - foo2: bar2`). - AddFile("yet-another-configmap.yaml", ` -apiVersion: v1 -kind: ConfigMap -metadata: - name: yet-another-map - annotations: - argocd.argoproj.io/sync-wave: "2" -data: - foo3: bar3`). - PatchFile("kustomization.yaml", `[{"op": "add", "path": "/resources/-", "value": "other-configmap.yaml"}, {"op": "add", "path": "/resources/-", "value": "yet-another-configmap.yaml"}]`). + PatchApp(`[{ + "op": "add", + "path": "/spec/source/helm/valuesObject", + "value": {"some":{"foo":"bar","new":"field"}} + }]`). Refresh(RefreshTypeNormal). - Wait(). + Sync(). Then(). + Expect(Success("")). Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). - Expect(HealthIs(health.HealthStatusHealthy)). - Expect(ResourceHealthWithNamespaceIs("ConfigMap", "other-map", DeploymentNamespace(), health.HealthStatusHealthy)). - Expect(ResourceSyncStatusWithNamespaceIs("ConfigMap", "other-map", DeploymentNamespace(), SyncStatusCodeSynced)). - Expect(ResourceHealthWithNamespaceIs("ConfigMap", "yet-another-map", DeploymentNamespace(), health.HealthStatusHealthy)). - Expect(ResourceSyncStatusWithNamespaceIs("ConfigMap", "yet-another-map", DeploymentNamespace(), SyncStatusCodeSynced)) + Expect(NoConditions()). + And(func(app *Application) { + // Check that the patch was a success. + assert.Equal(t, `{"some":{"foo":"bar","new":"field"}}`, string(app.Spec.Source.Helm.ValuesObject.Raw)) + }) + } diff --git a/test/e2e/app_multiple_sources_test.go b/test/e2e/app_multiple_sources_test.go index fd5f2d8d5fb69..69290edf2a856 100644 --- a/test/e2e/app_multiple_sources_test.go +++ b/test/e2e/app_multiple_sources_test.go @@ -1,11 +1,9 @@ package e2e import ( - "fmt" "testing" "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" @@ -40,7 +38,7 @@ func TestMultiSourceAppCreation(t *testing.T) { And(func(_ *Application) { // app should be listed output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, Name()) }). Expect(Success("")). @@ -75,7 +73,6 @@ func TestMultiSourceAppWithHelmExternalValueFiles(t *testing.T) { }, }, }} - fmt.Printf("sources: %v\n", sources) ctx := Given(t) ctx. Sources(sources). @@ -95,7 +92,7 @@ func TestMultiSourceAppWithHelmExternalValueFiles(t *testing.T) { And(func(_ *Application) { // app should be listed output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, Name()) }). Expect(Success("")). @@ -143,7 +140,7 @@ func TestMultiSourceAppWithSourceOverride(t *testing.T) { And(func(_ *Application) { // app should be listed output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, Name()) }). Expect(Success("")). @@ -163,7 +160,7 @@ func TestMultiSourceAppWithSourceOverride(t *testing.T) { // check if label was added to the pod to make sure resource was taken from the later source output, err := Run("", "kubectl", "describe", "pods", "pod-1", "-n", DeploymentNamespace()) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "foo=bar") }) } diff --git a/test/e2e/app_namespaces_test.go b/test/e2e/app_namespaces_test.go index 20e878a4685f1..033c34e9a70d3 100644 --- a/test/e2e/app_namespaces_test.go +++ b/test/e2e/app_namespaces_test.go @@ -6,7 +6,6 @@ import ( . "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -38,7 +37,7 @@ func TestAppCreationInOtherNamespace(t *testing.T) { And(func(_ *Application) { // app should be listed output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, ctx.AppName()) }). When(). diff --git a/test/e2e/applicationset_test.go b/test/e2e/applicationset_test.go index 5df36d591b1d9..23aa8578c907e 100644 --- a/test/e2e/applicationset_test.go +++ b/test/e2e/applicationset_test.go @@ -15,14 +15,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "github.com/argoproj/pkg/rand" - "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/stretchr/testify/require" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" @@ -31,29 +28,32 @@ import ( "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) -var ExpectedConditions = []v1alpha1.ApplicationSetCondition{ - { - Type: v1alpha1.ApplicationSetConditionErrorOccurred, - Status: v1alpha1.ApplicationSetConditionStatusFalse, - Message: "Successfully generated parameters for all Applications", - Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, - }, - { - Type: v1alpha1.ApplicationSetConditionParametersGenerated, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - Message: "Successfully generated parameters for all Applications", - Reason: v1alpha1.ApplicationSetReasonParametersGenerated, - }, - { - Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - Message: "ApplicationSet up to date", - Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, - }, -} +var ( + ExpectedConditions = []v1alpha1.ApplicationSetCondition{ + { + Type: v1alpha1.ApplicationSetConditionErrorOccurred, + Status: v1alpha1.ApplicationSetConditionStatusFalse, + Message: "Successfully generated parameters for all Applications", + Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, + }, + { + Type: v1alpha1.ApplicationSetConditionParametersGenerated, + Status: v1alpha1.ApplicationSetConditionStatusTrue, + Message: "Successfully generated parameters for all Applications", + Reason: v1alpha1.ApplicationSetReasonParametersGenerated, + }, + { + Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, + Status: v1alpha1.ApplicationSetConditionStatusTrue, + Message: "ApplicationSet up to date", + Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, + }, + } +) func TestSimpleListGeneratorExternalNamespace(t *testing.T) { - externalNamespace := string(utils.ArgoCDExternalNamespace) + + var externalNamespace = string(utils.ArgoCDExternalNamespace) expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -85,11 +85,10 @@ func TestSimpleListGeneratorExternalNamespace(t *testing.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, - }, + CreateNamespace(externalNamespace).Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator-external", + Namespace: externalNamespace, + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -151,11 +150,13 @@ func TestSimpleListGeneratorExternalNamespace(t *testing.T) { // Delete the ApplicationSet, and verify it deletes the Applications When(). Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + } func TestSimpleListGeneratorExternalNamespaceNoConflict(t *testing.T) { - externalNamespace := string(utils.ArgoCDExternalNamespace) - externalNamespace2 := string(utils.ArgoCDExternalNamespace2) + + var externalNamespace = string(utils.ArgoCDExternalNamespace) + var externalNamespace2 = string(utils.ArgoCDExternalNamespace2) expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -212,11 +213,10 @@ func TestSimpleListGeneratorExternalNamespaceNoConflict(t *testing.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, - }, + CreateNamespace(externalNamespace2).Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator-external", + Namespace: externalNamespace2, + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -247,11 +247,10 @@ func TestSimpleListGeneratorExternalNamespaceNoConflict(t *testing.T) { }).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, - }, + CreateNamespace(externalNamespace).Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator-external", + Namespace: externalNamespace, + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -340,6 +339,7 @@ func TestSimpleListGeneratorExternalNamespaceNoConflict(t *testing.T) { } func TestSimpleListGenerator(t *testing.T) { + expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: application.ApplicationKind, @@ -368,10 +368,9 @@ func TestSimpleListGenerator(t *testing.T) { Given(t). // Create a ListGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-list-generator", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{cluster}}-guestbook"}, @@ -428,9 +427,11 @@ func TestSimpleListGenerator(t *testing.T) { // 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, @@ -459,10 +460,9 @@ func TestSimpleListGeneratorGoTemplate(t *testing.T) { Given(t). // Create a ListGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-list-generator", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -520,9 +520,11 @@ func TestSimpleListGeneratorGoTemplate(t *testing.T) { // Delete the ApplicationSet, and verify it deletes the Applications When(). Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + } func TestRenderHelmValuesObject(t *testing.T) { + expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: application.ApplicationKind, @@ -555,10 +557,9 @@ func TestRenderHelmValuesObject(t *testing.T) { Given(t). // Create a ListGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-values-object", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "test-values-object", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -595,9 +596,11 @@ func TestRenderHelmValuesObject(t *testing.T) { // 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, @@ -653,10 +656,9 @@ func TestTemplatePatch(t *testing.T) { Given(t). // Create a ListGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "patch-template", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "patch-template", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -722,94 +724,11 @@ func TestTemplatePatch(t *testing.T) { // Delete the ApplicationSet, and verify it deletes the Applications When(). Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) -} - -func TestUpdateHelmValuesObject(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":{"foo":"bar"}}`), - }, - }, - }, - 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-patch", - }, - 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(ApplicationSetHasConditions("test-values-object-patch", ExpectedConditions)). - When(). - // Update the app spec with some knew ValuesObject to force a merge - Update(func(as *argov1alpha1.ApplicationSet) { - as.Spec.Template.Spec.Source.Helm.ValuesObject = &runtime.RawExtension{ - Raw: []byte(`{"some":{"foo":"bar"}}`), - } - }). - Then(). - Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). - When(). - // Delete the ApplicationSet, and verify it deletes the Applications - Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{expectedApp})) } func TestSyncPolicyCreateUpdate(t *testing.T) { + expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: "Application", @@ -838,17 +757,13 @@ func TestSyncPolicyCreateUpdate(t *testing.T) { Given(t). // Create a ListGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "sync-policy-create-update", - }, + 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", - Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, - }, + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook-sync-policy-create-update"}, Spec: argov1alpha1.ApplicationSpec{ Project: "default", Source: &argov1alpha1.ApplicationSource{ @@ -916,14 +831,14 @@ func TestSyncPolicyCreateUpdate(t *testing.T) { // verify the ApplicationSet status conditions were set correctly Expect(ApplicationSetHasConditions("sync-policy-create-update", ExpectedConditions)). - // Delete the ApplicationSet, and verify it not deletes the Applications - // As policy is create-update, AppSet controller will remove all generated applications's ownerReferences on delete AppSet - // So AppSet deletion will be reflected, but all the applications it generates will still exist + // Delete the ApplicationSet, and verify it deletes the Applications When(). - Delete().Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + } func TestSyncPolicyCreateDelete(t *testing.T) { + expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: "Application", @@ -951,10 +866,9 @@ func TestSyncPolicyCreateDelete(t *testing.T) { Given(t). // Create a ListGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "sync-policy-create-delete", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "sync-policy-create-delete", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -1020,9 +934,11 @@ func TestSyncPolicyCreateDelete(t *testing.T) { // Delete the ApplicationSet When(). Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewNamespace})) + } func TestSyncPolicyCreateOnly(t *testing.T) { + expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: "Application", @@ -1050,17 +966,13 @@ func TestSyncPolicyCreateOnly(t *testing.T) { Given(t). // Create a ListGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "sync-policy-create-only", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "sync-policy-create-only", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ - Name: "{{.cluster}}-guestbook-sync-policy-create-only", - Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, - }, + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook-sync-policy-create-only"}, Spec: argov1alpha1.ApplicationSpec{ Project: "default", Source: &argov1alpha1.ApplicationSource{ @@ -1119,11 +1031,10 @@ func TestSyncPolicyCreateOnly(t *testing.T) { // verify the ApplicationSet status conditions were set correctly Expect(ApplicationSetHasConditions("sync-policy-create-only", ExpectedConditions)). - // Delete the ApplicationSet, and verify it not deletes the Applications - // As policy is create-update, AppSet controller will remove all generated applications's ownerReferences on delete AppSet - // So AppSet deletion will be reflected, but all the applications it generates will still exist + // Delete the ApplicationSet, and verify it deletes the Applications When(). - Delete().Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})) + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewNamespace})) + } func TestSimpleGitDirectoryGenerator(t *testing.T) { @@ -1165,10 +1076,9 @@ func TestSimpleGitDirectoryGenerator(t *testing.T) { Given(t). When(). // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-git-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{path.basename}}"}, @@ -1236,459 +1146,7 @@ func TestSimpleGitDirectoryGenerator(t *testing.T) { Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) } -func TestSimpleGitDirectoryGeneratorGoTemplate(t *testing.T) { - generateExpectedApp := func(name string) argov1alpha1.Application { - return argov1alpha1.Application{ - TypeMeta: metav1.TypeMeta{ - Kind: application.ApplicationKind, - APIVersion: "argoproj.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - 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: name, - }, - Destination: argov1alpha1.ApplicationDestination{ - Server: "https://kubernetes.default.svc", - Namespace: name, - }, - }, - } - } - - expectedApps := []argov1alpha1.Application{ - generateExpectedApp("kustomize-guestbook"), - generateExpectedApp("helm-guestbook"), - generateExpectedApp("ksonnet-guestbook"), - } - - var expectedAppsNewNamespace []argov1alpha1.Application - var expectedAppsNewMetadata []argov1alpha1.Application - - Given(t). - When(). - // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator", - }, - Spec: v1alpha1.ApplicationSetSpec{ - GoTemplate: true, - Template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.path.basename}}"}, - Spec: argov1alpha1.ApplicationSpec{ - Project: "default", - Source: &argov1alpha1.ApplicationSource{ - RepoURL: "https://github.com/argoproj/argocd-example-apps.git", - TargetRevision: "HEAD", - Path: "{{.path.path}}", - }, - Destination: argov1alpha1.ApplicationDestination{ - Server: "https://kubernetes.default.svc", - Namespace: "{{.path.basename}}", - }, - }, - }, - Generators: []v1alpha1.ApplicationSetGenerator{ - { - Git: &v1alpha1.GitGenerator{ - RepoURL: "https://github.com/argoproj/argocd-example-apps.git", - Directories: []v1alpha1.GitDirectoryGeneratorItem{ - { - Path: "*guestbook*", - }, - }, - }, - }, - }, - }, - }).Then().Expect(ApplicationsExist(expectedApps)). - - // Update the ApplicationSet template namespace, and verify it updates the Applications - When(). - And(func() { - for _, expectedApp := range expectedApps { - newExpectedApp := expectedApp.DeepCopy() - newExpectedApp.Spec.Destination.Namespace = "guestbook2" - expectedAppsNewNamespace = append(expectedAppsNewNamespace, *newExpectedApp) - } - }). - Update(func(appset *v1alpha1.ApplicationSet) { - appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" - }).Then().Expect(ApplicationsExist(expectedAppsNewNamespace)). - - // Update the metadata fields in the appset template, and make sure it propagates to the apps - When(). - And(func() { - for _, expectedApp := range expectedAppsNewNamespace { - expectedAppNewMetadata := expectedApp.DeepCopy() - expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} - expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} - expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata) - } - }). - 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(expectedAppsNewMetadata)). - - // verify the ApplicationSet status conditions were set correctly - Expect(ApplicationSetHasConditions("simple-git-generator", ExpectedConditions)). - - // Delete the ApplicationSet, and verify it deletes the Applications - When(). - Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) -} - -func TestSimpleGitDirectoryGeneratorGPGEnabledUnsignedCommits(t *testing.T) { - fixture.SkipOnEnv(t, "GPG") - expectedErrorMessage := `error generating params from git: error getting directories from repo: error retrieving Git Directories: rpc error: code = Unknown desc = permission denied` - expectedConditionsParamsError := []v1alpha1.ApplicationSetCondition{ - { - Type: v1alpha1.ApplicationSetConditionErrorOccurred, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError, - }, - { - Type: v1alpha1.ApplicationSetConditionParametersGenerated, - Status: v1alpha1.ApplicationSetConditionStatusFalse, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonErrorOccurred, - }, - { - Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, - Status: v1alpha1.ApplicationSetConditionStatusFalse, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError, - }, - } - generateExpectedApp := func(name string) argov1alpha1.Application { - return argov1alpha1.Application{ - TypeMeta: metav1.TypeMeta{ - Kind: application.ApplicationKind, - APIVersion: "argoproj.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - 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: name, - }, - Destination: argov1alpha1.ApplicationDestination{ - Server: "https://kubernetes.default.svc", - Namespace: name, - }, - }, - } - } - - expectedApps := []argov1alpha1.Application{ - generateExpectedApp("guestbook"), - } - project := "gpg" - - fixture.EnsureCleanState(t) - Given(t). - Project(project). - When(). - // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator", - }, - Spec: v1alpha1.ApplicationSetSpec{ - Template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{path.basename}}"}, - Spec: argov1alpha1.ApplicationSpec{ - Project: project, - 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{ - { - Git: &v1alpha1.GitGenerator{ - RepoURL: "https://github.com/argoproj/argocd-example-apps.git", - Directories: []v1alpha1.GitDirectoryGeneratorItem{ - { - Path: guestbookPath, - }, - }, - }, - }, - }, - }, - }). - Then().Expect(ApplicationsDoNotExist(expectedApps)). - // verify the ApplicationSet error status conditions were set correctly - Expect(ApplicationSetHasConditions("simple-git-generator", expectedConditionsParamsError)). - When(). - Delete().Then().Expect(ApplicationsDoNotExist(expectedApps)) -} - -func TestSimpleGitDirectoryGeneratorGPGEnabledWithoutKnownKeys(t *testing.T) { - fixture.SkipOnEnv(t, "GPG") - expectedErrorMessage := `error generating params from git: error getting directories from repo: error retrieving Git Directories: rpc error: code = Unknown desc = permission denied` - expectedConditionsParamsError := []v1alpha1.ApplicationSetCondition{ - { - Type: v1alpha1.ApplicationSetConditionErrorOccurred, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError, - }, - { - Type: v1alpha1.ApplicationSetConditionParametersGenerated, - Status: v1alpha1.ApplicationSetConditionStatusFalse, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonErrorOccurred, - }, - { - Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, - Status: v1alpha1.ApplicationSetConditionStatusFalse, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError, - }, - } - generateExpectedApp := func(name string) argov1alpha1.Application { - return argov1alpha1.Application{ - TypeMeta: metav1.TypeMeta{ - Kind: application.ApplicationKind, - APIVersion: "argoproj.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - 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: name, - }, - Destination: argov1alpha1.ApplicationDestination{ - Server: "https://kubernetes.default.svc", - Namespace: name, - }, - }, - } - } - - expectedApps := []argov1alpha1.Application{ - generateExpectedApp("guestbook"), - } - - project := "gpg" - - str, _ := rand.RandString(1) - - Given(t). - Project(project). - Path(guestbookPath). - When(). - AddSignedFile("test.yaml", str).IgnoreErrors(). - IgnoreErrors(). - // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator", - }, - Spec: v1alpha1.ApplicationSetSpec{ - Template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{path.basename}}"}, - Spec: argov1alpha1.ApplicationSpec{ - Project: project, - 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}}", - }, - // Automatically create resources - SyncPolicy: &argov1alpha1.SyncPolicy{ - Automated: &argov1alpha1.SyncPolicyAutomated{}, - }, - }, - }, - Generators: []v1alpha1.ApplicationSetGenerator{ - { - Git: &v1alpha1.GitGenerator{ - RepoURL: "https://github.com/argoproj/argocd-example-apps.git", - Directories: []v1alpha1.GitDirectoryGeneratorItem{ - { - Path: guestbookPath, - }, - }, - }, - }, - }, - }, - }).Then(). - // verify the ApplicationSet error status conditions were set correctly - Expect(ApplicationSetHasConditions("simple-git-generator", expectedConditionsParamsError)). - Expect(ApplicationsDoNotExist(expectedApps)). - When(). - Delete().Then().Expect(ApplicationsDoNotExist(expectedApps)) -} - -func TestSimpleGitFilesGenerator(t *testing.T) { - generateExpectedApp := func(name string) argov1alpha1.Application { - return argov1alpha1.Application{ - TypeMeta: metav1.TypeMeta{ - Kind: application.ApplicationKind, - APIVersion: "argoproj.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - 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", - }, - }, - } - } - - expectedApps := []argov1alpha1.Application{ - generateExpectedApp("engineering-dev-guestbook"), - generateExpectedApp("engineering-prod-guestbook"), - } - - var expectedAppsNewNamespace []argov1alpha1.Application - var expectedAppsNewMetadata []argov1alpha1.Application - - Given(t). - When(). - // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator", - }, - Spec: v1alpha1.ApplicationSetSpec{ - Template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{cluster.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{ - Server: "https://kubernetes.default.svc", - Namespace: "guestbook", - }, - }, - }, - Generators: []v1alpha1.ApplicationSetGenerator{ - { - Git: &v1alpha1.GitGenerator{ - RepoURL: "https://github.com/argoproj/applicationset.git", - Files: []v1alpha1.GitFileGeneratorItem{ - { - Path: "examples/git-generator-files-discovery/cluster-config/**/config.json", - }, - }, - }, - }, - }, - }, - }).Then().Expect(ApplicationsExist(expectedApps)). - - // Update the ApplicationSet template namespace, and verify it updates the Applications - When(). - And(func() { - for _, expectedApp := range expectedApps { - newExpectedApp := expectedApp.DeepCopy() - newExpectedApp.Spec.Destination.Namespace = "guestbook2" - expectedAppsNewNamespace = append(expectedAppsNewNamespace, *newExpectedApp) - } - }). - Update(func(appset *v1alpha1.ApplicationSet) { - appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" - }).Then().Expect(ApplicationsExist(expectedAppsNewNamespace)). - - // Update the metadata fields in the appset template, and make sure it propagates to the apps - When(). - And(func() { - for _, expectedApp := range expectedAppsNewNamespace { - expectedAppNewMetadata := expectedApp.DeepCopy() - expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} - expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} - expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata) - } - }). - 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(expectedAppsNewMetadata)). - - // verify the ApplicationSet status conditions were set correctly - Expect(ApplicationSetHasConditions("simple-git-generator", ExpectedConditions)). - - // Delete the ApplicationSet, and verify it deletes the Applications - When(). - Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) -} - -func TestSimpleGitFilesGeneratorGPGEnabledUnsignedCommits(t *testing.T) { - fixture.SkipOnEnv(t, "GPG") - expectedErrorMessage := `error generating params from git: error retrieving Git files: rpc error: code = Unknown desc = permission denied` - expectedConditionsParamsError := []v1alpha1.ApplicationSetCondition{ - { - Type: v1alpha1.ApplicationSetConditionErrorOccurred, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError, - }, - { - Type: v1alpha1.ApplicationSetConditionParametersGenerated, - Status: v1alpha1.ApplicationSetConditionStatusFalse, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonErrorOccurred, - }, - { - Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, - Status: v1alpha1.ApplicationSetConditionStatusFalse, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError, - }, - } - project := "gpg" +func TestSimpleGitDirectoryGeneratorGoTemplate(t *testing.T) { generateExpectedApp := func(name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -1701,94 +1159,105 @@ func TestSimpleGitFilesGeneratorGPGEnabledUnsignedCommits(t *testing.T) { Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ - Project: project, + Project: "default", Source: &argov1alpha1.ApplicationSource{ RepoURL: "https://github.com/argoproj/argocd-example-apps.git", TargetRevision: "HEAD", - Path: "guestbook", + Path: name, }, Destination: argov1alpha1.ApplicationDestination{ Server: "https://kubernetes.default.svc", - Namespace: "guestbook", + Namespace: name, }, }, } } expectedApps := []argov1alpha1.Application{ - generateExpectedApp("engineering-dev-guestbook"), - generateExpectedApp("engineering-prod-guestbook"), + generateExpectedApp("kustomize-guestbook"), + generateExpectedApp("helm-guestbook"), + generateExpectedApp("ksonnet-guestbook"), } - fixture.EnsureCleanState(t) + var expectedAppsNewNamespace []argov1alpha1.Application + var expectedAppsNewMetadata []argov1alpha1.Application + Given(t). - Project(project). When(). // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-git-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{cluster.name}}-guestbook"}, + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.path.basename}}"}, Spec: argov1alpha1.ApplicationSpec{ - Project: project, + Project: "default", Source: &argov1alpha1.ApplicationSource{ RepoURL: "https://github.com/argoproj/argocd-example-apps.git", TargetRevision: "HEAD", - Path: "guestbook", + Path: "{{.path.path}}", }, Destination: argov1alpha1.ApplicationDestination{ Server: "https://kubernetes.default.svc", - Namespace: "guestbook", + Namespace: "{{.path.basename}}", }, }, }, Generators: []v1alpha1.ApplicationSetGenerator{ { Git: &v1alpha1.GitGenerator{ - RepoURL: "https://github.com/argoproj/applicationset.git", - Files: []v1alpha1.GitFileGeneratorItem{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + Directories: []v1alpha1.GitDirectoryGeneratorItem{ { - Path: "examples/git-generator-files-discovery/cluster-config/**/config.json", + Path: "*guestbook*", }, }, }, }, }, }, - }).Then().Expect(ApplicationsDoNotExist(expectedApps)). - // verify the ApplicationSet error status conditions were set correctly - Expect(ApplicationSetHasConditions("simple-git-generator", expectedConditionsParamsError)). + }).Then().Expect(ApplicationsExist(expectedApps)). + + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + for _, expectedApp := range expectedApps { + newExpectedApp := expectedApp.DeepCopy() + newExpectedApp.Spec.Destination.Namespace = "guestbook2" + expectedAppsNewNamespace = append(expectedAppsNewNamespace, *newExpectedApp) + } + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist(expectedAppsNewNamespace)). + + // Update the metadata fields in the appset template, and make sure it propagates to the apps + When(). + And(func() { + for _, expectedApp := range expectedAppsNewNamespace { + expectedAppNewMetadata := expectedApp.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} + expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata) + } + }). + 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(expectedAppsNewMetadata)). + + // verify the ApplicationSet status conditions were set correctly + Expect(ApplicationSetHasConditions("simple-git-generator", ExpectedConditions)). + + // Delete the ApplicationSet, and verify it deletes the Applications When(). - Delete().Then().Expect(ApplicationsDoNotExist(expectedApps)) + Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) } -func TestSimpleGitFilesGeneratorGPGEnabledWithoutKnownKeys(t *testing.T) { - fixture.SkipOnEnv(t, "GPG") - expectedErrorMessage := `error generating params from git: error retrieving Git files: rpc error: code = Unknown desc = permission denied` - expectedConditionsParamsError := []v1alpha1.ApplicationSetCondition{ - { - Type: v1alpha1.ApplicationSetConditionErrorOccurred, - Status: v1alpha1.ApplicationSetConditionStatusTrue, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError, - }, - { - Type: v1alpha1.ApplicationSetConditionParametersGenerated, - Status: v1alpha1.ApplicationSetConditionStatusFalse, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonErrorOccurred, - }, - { - Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, - Status: v1alpha1.ApplicationSetConditionStatusFalse, - Message: expectedErrorMessage, - Reason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError, - }, - } - project := "gpg" +func TestSimpleGitFilesGenerator(t *testing.T) { + generateExpectedApp := func(name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -1801,7 +1270,7 @@ func TestSimpleGitFilesGeneratorGPGEnabledWithoutKnownKeys(t *testing.T) { Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ - Project: project, + Project: "default", Source: &argov1alpha1.ApplicationSource{ RepoURL: "https://github.com/argoproj/argocd-example-apps.git", TargetRevision: "HEAD", @@ -1815,30 +1284,25 @@ func TestSimpleGitFilesGeneratorGPGEnabledWithoutKnownKeys(t *testing.T) { } } - str, _ := rand.RandString(1) - expectedApps := []argov1alpha1.Application{ generateExpectedApp("engineering-dev-guestbook"), generateExpectedApp("engineering-prod-guestbook"), } - fixture.EnsureCleanState(t) + var expectedAppsNewNamespace []argov1alpha1.Application + var expectedAppsNewMetadata []argov1alpha1.Application + Given(t). - Project(project). - Path(guestbookPath). When(). - AddSignedFile("test.yaml", str).IgnoreErrors(). - IgnoreErrors(). // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-git-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{cluster.name}}-guestbook"}, Spec: argov1alpha1.ApplicationSpec{ - Project: project, + Project: "default", Source: &argov1alpha1.ApplicationSource{ RepoURL: "https://github.com/argoproj/argocd-example-apps.git", TargetRevision: "HEAD", @@ -1863,15 +1327,46 @@ func TestSimpleGitFilesGeneratorGPGEnabledWithoutKnownKeys(t *testing.T) { }, }, }, - }).Then(). - // verify the ApplicationSet error status conditions were set correctly - Expect(ApplicationSetHasConditions("simple-git-generator", expectedConditionsParamsError)). - Expect(ApplicationsDoNotExist(expectedApps)). + }).Then().Expect(ApplicationsExist(expectedApps)). + + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + for _, expectedApp := range expectedApps { + newExpectedApp := expectedApp.DeepCopy() + newExpectedApp.Spec.Destination.Namespace = "guestbook2" + expectedAppsNewNamespace = append(expectedAppsNewNamespace, *newExpectedApp) + } + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist(expectedAppsNewNamespace)). + + // Update the metadata fields in the appset template, and make sure it propagates to the apps + When(). + And(func() { + for _, expectedApp := range expectedAppsNewNamespace { + expectedAppNewMetadata := expectedApp.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} + expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata) + } + }). + 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(expectedAppsNewMetadata)). + + // verify the ApplicationSet status conditions were set correctly + Expect(ApplicationSetHasConditions("simple-git-generator", ExpectedConditions)). + + // Delete the ApplicationSet, and verify it deletes the Applications When(). - Delete().Then().Expect(ApplicationsDoNotExist(expectedApps)) + Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) } func TestSimpleGitFilesGeneratorGoTemplate(t *testing.T) { + generateExpectedApp := func(name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -1909,10 +1404,9 @@ func TestSimpleGitFilesGeneratorGoTemplate(t *testing.T) { Given(t). When(). // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-git-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -1982,14 +1476,14 @@ func TestSimpleGitFilesGeneratorGoTemplate(t *testing.T) { } func TestSimpleGitFilesPreserveResourcesOnDeletion(t *testing.T) { + Given(t). When(). CreateNamespace(utils.ApplicationsResourcesNamespace). // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-git-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{cluster.name}}-guestbook"}, @@ -2042,14 +1536,14 @@ func TestSimpleGitFilesPreserveResourcesOnDeletion(t *testing.T) { } func TestSimpleGitFilesPreserveResourcesOnDeletionGoTemplate(t *testing.T) { + Given(t). When(). CreateNamespace(utils.ApplicationsResourcesNamespace). // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-git-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -2307,6 +1801,7 @@ func testServerWithPort(t *testing.T, port int, handler http.Handler) *httptest. } func TestSimpleSCMProviderGenerator(t *testing.T) { + ts := testServerWithPort(t, 8341, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { githubSCMMockHandler(t)(w, r) })) @@ -2342,10 +1837,9 @@ func TestSimpleSCMProviderGenerator(t *testing.T) { Given(t). // Create an SCMProviderGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-scm-provider-generator", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-scm-provider-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ repository }}-guestbook"}, @@ -2417,10 +1911,9 @@ func TestSimpleSCMProviderGeneratorGoTemplate(t *testing.T) { Given(t). // Create an SCMProviderGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-scm-provider-generator", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-scm-provider-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -2487,10 +1980,9 @@ func TestSCMProviderGeneratorSCMProviderNotAllowed(t *testing.T) { Given(t). // Create an SCMProviderGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "scm-provider-generator-scm-provider-not-allowed", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "scm-provider-generator-scm-provider-not-allowed", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -2528,7 +2020,7 @@ func TestSCMProviderGeneratorSCMProviderNotAllowed(t *testing.T) { And(func() { // app should be listed output, err := fixture.RunCli("appset", "get", "scm-provider-generator-scm-provider-not-allowed") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "scm provider not allowed") }) } @@ -2560,10 +2052,9 @@ func TestCustomApplicationFinalizers(t *testing.T) { Given(t). // Create a ListGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-list-generator", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ @@ -2627,10 +2118,9 @@ func TestCustomApplicationFinalizersGoTemplate(t *testing.T) { Given(t). // Create a ListGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-list-generator", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -2676,7 +2166,6 @@ func githubPullMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request _, err := io.WriteString(w, `[ { "number": 1, - "title": "title1", "labels": [ { "name": "preview" @@ -2689,10 +2178,7 @@ func githubPullMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request "head": { "ref": "pull-request", "sha": "824a5c987fdfb2b0629e9dbf5f31636c69ba4772" - }, - "user": { - "login": "testName" - } + } } ]`) if err != nil { @@ -2705,6 +2191,7 @@ func githubPullMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request } func TestSimplePullRequestGenerator(t *testing.T) { + ts := testServerWithPort(t, 8343, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { githubPullMockHandler(t)(w, r) })) @@ -2741,10 +2228,9 @@ func TestSimplePullRequestGenerator(t *testing.T) { Given(t). // Create an PullRequestGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-pull-request-generator", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-pull-request-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "guestbook-{{ number }}"}, @@ -2820,17 +2306,15 @@ func TestSimplePullRequestGeneratorGoTemplate(t *testing.T) { Given(t). // Create an PullRequestGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-pull-request-generator", - }, + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-pull-request-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ Name: "guestbook-{{ .number }}", - Labels: map[string]string{"app": "{{index .labels 0}}"}, - }, + Labels: map[string]string{"app": "{{index .labels 0}}"}}, Spec: argov1alpha1.ApplicationSpec{ Project: "default", Source: &argov1alpha1.ApplicationSource{ @@ -2866,6 +2350,7 @@ func TestSimplePullRequestGeneratorGoTemplate(t *testing.T) { } func TestPullRequestGeneratorNotAllowedSCMProvider(t *testing.T) { + expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: application.ApplicationKind, @@ -2898,17 +2383,15 @@ func TestPullRequestGeneratorNotAllowedSCMProvider(t *testing.T) { Given(t). // Create an PullRequestGenerator-based ApplicationSet - When().Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pull-request-generator-not-allowed-scm", - }, + 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}}"}, - }, + Labels: map[string]string{"app": "{{index .labels 0}}"}}, Spec: argov1alpha1.ApplicationSpec{ Project: "default", Source: &argov1alpha1.ApplicationSource{ @@ -2944,7 +2427,7 @@ func TestPullRequestGeneratorNotAllowedSCMProvider(t *testing.T) { And(func() { // app should be listed output, err := fixture.RunCli("appset", "get", "pull-request-generator-not-allowed-scm") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, output, "scm provider not allowed") }) } @@ -2986,10 +2469,9 @@ func TestGitGeneratorPrivateRepo(t *testing.T) { Given(t). When(). // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator-private", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-git-generator-private", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{path.basename}}"}, @@ -3062,10 +2544,9 @@ func TestGitGeneratorPrivateRepoGoTemplate(t *testing.T) { Given(t). When(). // Create a GitGenerator-based ApplicationSet - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-git-generator-private", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-git-generator-private", + }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -3101,3 +2582,88 @@ func TestGitGeneratorPrivateRepoGoTemplate(t *testing.T) { When(). Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) } + +func TestUpdateHelmValuesObject(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":{"foo":"bar"}}`), + }, + }, + }, + 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-patch", + }, + 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(ApplicationSetHasConditions("test-values-object-patch", ExpectedConditions)). + When(). + // Update the app spec with some knew ValuesObject to force a merge + Update(func(as *argov1alpha1.ApplicationSet) { + as.Spec.Template.Spec.Source.Helm.ValuesObject = &runtime.RawExtension{ + Raw: []byte(`{"some":{"foo":"bar"}}`), + } + }). + Then(). + Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + When(). + // Delete the ApplicationSet, and verify it deletes the Applications + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{expectedApp})) +} diff --git a/test/e2e/cli_test.go b/test/e2e/cli_test.go index 1472116be7e93..028d3d516764e 100644 --- a/test/e2e/cli_test.go +++ b/test/e2e/cli_test.go @@ -6,7 +6,6 @@ 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" @@ -20,7 +19,7 @@ func TestCliAppCommand(t *testing.T) { CreateApp(). And(func() { output, err := RunCli("app", "sync", Name(), "--timeout", "90") - require.NoError(t, err) + assert.NoError(t, err) vars := map[string]interface{}{"Name": Name(), "Namespace": DeploymentNamespace()} assert.Contains(t, NormalizeOutput(output), Tmpl(`Pod {{.Namespace}} pod Synced Progressing pod/pod created`, vars)) assert.Contains(t, NormalizeOutput(output), Tmpl(`Pod {{.Namespace}} hook Succeeded Sync pod/hook created`, vars)) @@ -30,9 +29,9 @@ func TestCliAppCommand(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(_ *Application) { output, err := RunCli("app", "list") - require.NoError(t, err) + assert.NoError(t, err) expected := Tmpl( - `{{.Name}} https://kubernetes.default.svc {{.Namespace}} default Synced Healthy Manual `, + `{{.Name}} https://kubernetes.default.svc {{.Namespace}} default Synced Healthy `, 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 aa73e36aea796..1d5699e23503d 100644 --- a/test/e2e/cluster_generator_test.go +++ b/test/e2e/cluster_generator_test.go @@ -17,7 +17,8 @@ import ( ) func TestSimpleClusterGeneratorExternalNamespace(t *testing.T) { - externalNamespace := string(utils.ArgoCDExternalNamespace) + + var externalNamespace = string(utils.ArgoCDExternalNamespace) expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -52,10 +53,9 @@ func TestSimpleClusterGeneratorExternalNamespace(t *testing.T) { CreateClusterSecret("my-secret", "cluster1", "https://kubernetes.default.svc"). SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). CreateNamespace(externalNamespace). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-cluster-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, @@ -119,6 +119,7 @@ func TestSimpleClusterGeneratorExternalNamespace(t *testing.T) { } func TestSimpleClusterGenerator(t *testing.T) { + expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: application.ApplicationKind, @@ -150,10 +151,9 @@ func TestSimpleClusterGenerator(t *testing.T) { // Create a ClusterGenerator-based ApplicationSet When(). CreateClusterSecret("my-secret", "cluster1", "https://kubernetes.default.svc"). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-cluster-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, @@ -275,10 +275,9 @@ func TestClusterGeneratorWithLocalCluster(t *testing.T) { Given(t). // Create a ClusterGenerator-based ApplicationSet When(). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "in-cluster-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "in-cluster-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, @@ -330,6 +329,7 @@ func TestClusterGeneratorWithLocalCluster(t *testing.T) { } func TestSimpleClusterGeneratorAddingCluster(t *testing.T) { + expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: application.ApplicationKind, @@ -366,10 +366,9 @@ func TestSimpleClusterGeneratorAddingCluster(t *testing.T) { // Create a ClusterGenerator-based ApplicationSet When(). CreateClusterSecret("my-secret", "cluster1", "https://kubernetes.default.svc"). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-cluster-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, @@ -412,6 +411,7 @@ func TestSimpleClusterGeneratorAddingCluster(t *testing.T) { } func TestSimpleClusterGeneratorDeletingCluster(t *testing.T) { + expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: application.ApplicationKind, @@ -449,10 +449,9 @@ func TestSimpleClusterGeneratorDeletingCluster(t *testing.T) { When(). CreateClusterSecret("my-secret", "cluster1", "https://kubernetes.default.svc"). CreateClusterSecret("my-secret2", "cluster2", "https://kubernetes.default.svc"). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-cluster-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, diff --git a/test/e2e/cluster_objects_test.go b/test/e2e/cluster_objects_test.go index 59ee43d3974b6..4299a35c55c00 100644 --- a/test/e2e/cluster_objects_test.go +++ b/test/e2e/cluster_objects_test.go @@ -6,7 +6,6 @@ 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" @@ -26,7 +25,7 @@ func TestClusterRoleBinding(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { diffOutput, err := RunCli("app", "diff", app.Name, "--revision=HEAD") - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, diffOutput) }). When(). @@ -38,7 +37,7 @@ func TestClusterRoleBinding(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { diffOutput, err := RunCli("app", "diff", app.Name, "--revision=HEAD") - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, diffOutput) }) } diff --git a/test/e2e/cluster_test.go b/test/e2e/cluster_test.go index d09df7d0cc812..96415f4f5541a 100644 --- a/test/e2e/cluster_test.go +++ b/test/e2e/cluster_test.go @@ -3,6 +3,7 @@ package e2e import ( "fmt" "net/url" + "strings" "testing" "time" @@ -152,7 +153,7 @@ func TestClusterListDenied(t *testing.T) { List(). Then(). AndCLIOutput(func(output string, err error) { - assert.Equal(t, "SERVER NAME VERSION STATUS MESSAGE PROJECT", output) + assert.Equal(t, output, "SERVER NAME VERSION STATUS MESSAGE PROJECT") }) } @@ -170,8 +171,8 @@ func TestClusterSet(t *testing.T) { GetByName("in-cluster"). Then(). AndCLIOutput(func(output string, err error) { - assert.Contains(t, output, "namespace-edit-1") - assert.Contains(t, output, "namespace-edit-2") + assert.True(t, strings.Contains(output, "namespace-edit-1")) + assert.True(t, strings.Contains(output, "namespace-edit-2")) }) } @@ -198,7 +199,7 @@ func TestClusterNameInRestAPI(t *testing.T) { err := DoHttpJsonRequest("GET", "/api/v1/clusters/in-cluster?id.type=name", &cluster) require.NoError(t, err) - assert.Equal(t, "in-cluster", cluster.Name) + assert.Equal(t, cluster.Name, "in-cluster") assert.Contains(t, cluster.Server, "https://kubernetes.default.svc") err = DoHttpJsonRequest("PUT", @@ -216,7 +217,7 @@ func TestClusterURLInRestAPI(t *testing.T) { err := DoHttpJsonRequest("GET", fmt.Sprintf("/api/v1/clusters/%s", clusterURL), &cluster) require.NoError(t, err) - assert.Equal(t, "in-cluster", cluster.Name) + assert.Equal(t, cluster.Name, "in-cluster") assert.Contains(t, cluster.Server, "https://kubernetes.default.svc") err = DoHttpJsonRequest("PUT", diff --git a/test/e2e/clusterdecisiongenerator_e2e_test.go b/test/e2e/clusterdecisiongenerator_e2e_test.go index 3ee4a30509cbb..5f0d6ff6ae3c7 100644 --- a/test/e2e/clusterdecisiongenerator_e2e_test.go +++ b/test/e2e/clusterdecisiongenerator_e2e_test.go @@ -18,7 +18,8 @@ import ( var tenSec = int64(10) func TestSimpleClusterDecisionResourceGeneratorExternalNamespace(t *testing.T) { - externalNamespace := string(utils.ArgoCDExternalNamespace) + + var externalNamespace = string(utils.ArgoCDExternalNamespace) expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -64,10 +65,9 @@ func TestSimpleClusterDecisionResourceGeneratorExternalNamespace(t *testing.T) { StatusUpdatePlacementDecision("my-placementdecision", clusterList). CreateNamespace(externalNamespace). SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-cluster-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, @@ -128,6 +128,7 @@ func TestSimpleClusterDecisionResourceGeneratorExternalNamespace(t *testing.T) { } func TestSimpleClusterDecisionResourceGenerator(t *testing.T) { + expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: application.ApplicationKind, @@ -170,10 +171,9 @@ func TestSimpleClusterDecisionResourceGenerator(t *testing.T) { CreatePlacementDecisionConfigMap("my-configmap"). CreatePlacementDecision("my-placementdecision"). StatusUpdatePlacementDecision("my-placementdecision", clusterList). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-cluster-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, @@ -230,6 +230,7 @@ func TestSimpleClusterDecisionResourceGenerator(t *testing.T) { } func TestSimpleClusterDecisionResourceGeneratorAddingCluster(t *testing.T) { + expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: application.ApplicationKind, @@ -281,10 +282,9 @@ func TestSimpleClusterDecisionResourceGeneratorAddingCluster(t *testing.T) { CreatePlacementDecisionConfigMap("my-configmap"). CreatePlacementDecision("my-placementdecision"). StatusUpdatePlacementDecision("my-placementdecision", clusterList). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-cluster-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, @@ -325,6 +325,7 @@ func TestSimpleClusterDecisionResourceGeneratorAddingCluster(t *testing.T) { } func TestSimpleClusterDecisionResourceGeneratorDeletingClusterSecret(t *testing.T) { + expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: application.ApplicationKind, @@ -377,10 +378,9 @@ func TestSimpleClusterDecisionResourceGeneratorDeletingClusterSecret(t *testing. CreatePlacementDecisionConfigMap("my-configmap"). CreatePlacementDecision("my-placementdecision"). StatusUpdatePlacementDecision("my-placementdecision", clusterList). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-cluster-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, @@ -422,6 +422,7 @@ func TestSimpleClusterDecisionResourceGeneratorDeletingClusterSecret(t *testing. } func TestSimpleClusterDecisionResourceGeneratorDeletingClusterFromResource(t *testing.T) { + expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ Kind: application.ApplicationKind, @@ -481,10 +482,9 @@ func TestSimpleClusterDecisionResourceGeneratorDeletingClusterFromResource(t *te CreatePlacementDecisionConfigMap("my-configmap"). CreatePlacementDecision("my-placementdecision"). StatusUpdatePlacementDecision("my-placementdecision", clusterList). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-cluster-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, diff --git a/test/e2e/custom_tool_test.go b/test/e2e/custom_tool_test.go index d5433977afa3f..7370fb5478ad3 100644 --- a/test/e2e/custom_tool_test.go +++ b/test/e2e/custom_tool_test.go @@ -67,12 +67,12 @@ func TestCustomToolWithGitCredsTemplate(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitUsername}") - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, output) }). And(func(app *Application) { output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitPassword}") - require.NoError(t, err) + assert.NoError(t, err) assert.Empty(t, output) }) } @@ -107,18 +107,18 @@ func TestCustomToolWithEnv(t *testing.T) { }). And(func(app *Application) { output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.Bar}") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "baz", output) }). And(func(app *Application) { output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.Foo}") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "bar", output) }). And(func(app *Application) { expectedKubeVersion := GetVersions().ServerVersion.Format("%s.%s") output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeVersion}") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, expectedKubeVersion, output) }). And(func(app *Application) { @@ -127,7 +127,7 @@ func TestCustomToolWithEnv(t *testing.T) { sort.Strings(expectedApiVersionSlice) output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeApiVersion}") - require.NoError(t, err) + assert.NoError(t, err) outputSlice := strings.Split(output, ",") sort.Strings(outputSlice) @@ -174,7 +174,7 @@ func startCMPServer(t *testing.T, configFile string) { t.Setenv("ARGOCD_PLUGINSOCKFILEPATH", pluginSockFilePath) if _, err := os.Stat(pluginSockFilePath); os.IsNotExist(err) { // path/to/whatever does not exist - err := os.Mkdir(pluginSockFilePath, 0o700) + err := os.Mkdir(pluginSockFilePath, 0700) require.NoError(t, err) } FailOnErr(RunWithStdin("", "", "../../dist/argocd", "--config-dir-path", configFile)) @@ -261,13 +261,13 @@ func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) { }). And(func(app *Application) { output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.Bar}") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "baz", output) }). And(func(app *Application) { expectedKubeVersion := GetVersions().ServerVersion.Format("%s.%s") output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeVersion}") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, expectedKubeVersion, output) }). And(func(app *Application) { @@ -276,7 +276,7 @@ func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) { sort.Strings(expectedApiVersionSlice) output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeApiVersion}") - require.NoError(t, err) + assert.NoError(t, err) outputSlice := strings.Split(output, ",") sort.Strings(outputSlice) @@ -303,7 +303,7 @@ func TestPruneResourceFromCMP(t *testing.T) { Expect(DoesNotExist()). AndAction(func() { _, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "deployment", "guestbook-ui") - require.Error(t, err) + assert.Error(t, err) }) } diff --git a/test/e2e/declarative_test.go b/test/e2e/declarative_test.go index 0b1649b527f20..3f1c1a20e1037 100644 --- a/test/e2e/declarative_test.go +++ b/test/e2e/declarative_test.go @@ -55,14 +55,13 @@ func TestDeclarativeInvalidProject(t *testing.T) { Expect(HealthIs(health.HealthStatusUnknown)). Expect(SyncStatusIs(SyncStatusCodeUnknown)). Expect(Condition(ApplicationConditionInvalidSpecError, "Application referencing project garbage which does not exist")) - // TODO: you can`t delete application with invalid project due to enforcment that was recently added, // in https://github.com/argoproj/argo-cd/security/advisories/GHSA-2gvw-w6fj-7m3c - // When(). - // Delete(false). - // Then(). - // Expect(Success("")). - // Expect(DoesNotExist()) + //When(). + //Delete(false). + //Then(). + //Expect(Success("")). + //Expect(DoesNotExist()) } func TestDeclarativeInvalidRepoURL(t *testing.T) { diff --git a/test/e2e/deployment_test.go b/test/e2e/deployment_test.go index 083a2a60d9b3d..20e79c2aff56c 100644 --- a/test/e2e/deployment_test.go +++ b/test/e2e/deployment_test.go @@ -9,7 +9,6 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -66,7 +65,7 @@ func TestDeploymentWithAnnotationTrackingMode(t *testing.T) { Then(). And(func(app *Application) { out, err := RunCli("app", "manifests", ctx.AppName()) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, fmt.Sprintf(`annotations: argocd.argoproj.io/tracking-id: %s:apps/Deployment:%s/nginx-deployment `, ctx.AppName(), DeploymentNamespace())) @@ -89,7 +88,7 @@ func TestDeploymentWithLabelTrackingMode(t *testing.T) { Then(). And(func(app *Application) { out, err := RunCli("app", "manifests", ctx.AppName()) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, fmt.Sprintf(`labels: app: nginx app.kubernetes.io/instance: %s @@ -112,7 +111,7 @@ func TestDeploymentWithoutTrackingMode(t *testing.T) { Then(). And(func(app *Application) { out, err := RunCli("app", "manifests", ctx.AppName()) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, fmt.Sprintf(`labels: app: nginx app.kubernetes.io/instance: %s @@ -125,9 +124,11 @@ func TestDeploymentWithoutTrackingMode(t *testing.T) { // 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 @@ -147,7 +148,9 @@ func TestDeployToKubernetesAPIURLWithQueryParameter(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(health.HealthStatusHealthy)) } + } + } // This test verifies that Argo CD can: @@ -155,10 +158,12 @@ func TestDeployToKubernetesAPIURLWithQueryParameter(t *testing.T) { // 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 @@ -169,6 +174,7 @@ func TestArgoCDSupportsMultipleServiceAccountsWithDifferingRBACOnSameCluster(t * } 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)] @@ -191,12 +197,14 @@ func TestArgoCDSupportsMultipleServiceAccountsWithDifferingRBACOnSameCluster(t * 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, @@ -259,6 +267,7 @@ func buildArgoCDClusterSecret(secretName, secretNamespace, clusterName, clusterS // - 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{ @@ -266,12 +275,12 @@ func createNamespaceScopedUser(t *testing.T, username string, clusterScopedSecre }, } _, err := KubeClientset.CoreV1().Namespaces().Create(context.Background(), &ns, metav1.CreateOptions{}) - require.NoError(t, err) + 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) - require.NoError(t, err) + assert.Nil(t, err) // Create a Role that allows the ServiceAccount to read/write all within the Namespace role := rbacv1.Role{ @@ -286,7 +295,7 @@ func createNamespaceScopedUser(t *testing.T, username string, clusterScopedSecre }}, } _, err = KubeClientset.RbacV1().Roles(role.Namespace).Create(context.Background(), &role, metav1.CreateOptions{}) - require.NoError(t, err) + assert.Nil(t, err) // Bind the Role with the ServiceAccount in the Namespace roleBinding := rbacv1.RoleBinding{ @@ -306,11 +315,11 @@ func createNamespaceScopedUser(t *testing.T, username string, clusterScopedSecre }, } _, err = KubeClientset.RbacV1().RoleBindings(roleBinding.Namespace).Create(context.Background(), &roleBinding, metav1.CreateOptions{}) - require.NoError(t, err) + assert.Nil(t, err) // Retrieve the bearer token from the ServiceAccount token, err := clusterauth.GetServiceAccountBearerToken(KubeClientset, ns.Name, serviceAccountName, time.Second*60) - require.NoError(t, err) + 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. @@ -318,10 +327,11 @@ func createNamespaceScopedUser(t *testing.T, username string, clusterScopedSecre clusterRole, clusterRoleBinding := generateReadOnlyClusterRoleandBindingForServiceAccount(username, username) _, err := KubeClientset.RbacV1().ClusterRoles().Create(context.Background(), &clusterRole, metav1.CreateOptions{}) - require.NoError(t, err) + assert.Nil(t, err) _, err = KubeClientset.RbacV1().ClusterRoleBindings().Create(context.Background(), &clusterRoleBinding, metav1.CreateOptions{}) - require.NoError(t, err) + assert.Nil(t, err) + } // Build the Argo CD Cluster Secret by using the service account token, and extracting needed values from kube config @@ -333,10 +343,10 @@ func createNamespaceScopedUser(t *testing.T, username string, clusterScopedSecre } jsonStringBytes, err := json.Marshal(clusterSecretConfigJSON) - require.NoError(t, err) + assert.Nil(t, err) _, apiURL, err := extractKubeConfigValues() - require.NoError(t, err) + assert.Nil(t, err) clusterResourcesField := "" namespacesField := "" @@ -354,7 +364,7 @@ func createNamespaceScopedUser(t *testing.T, username string, clusterScopedSecre // Finally, create the Cluster secret in the Argo CD E2E namespace _, err = KubeClientset.CoreV1().Secrets(secret.Namespace).Create(context.Background(), &secret, metav1.CreateOptions{}) - require.NoError(t, err) + assert.Nil(t, err) } // extractKubeConfigValues returns contents of the local environment's kubeconfig, using standard path resolution mechanism. @@ -363,6 +373,7 @@ func createNamespaceScopedUser(t *testing.T, username string, clusterScopedSecre // - server name (within the kubeconfig) // - error func extractKubeConfigValues() (string, string, error) { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() config, err := loadingRules.Load() @@ -384,6 +395,7 @@ func extractKubeConfigValues() (string, string, error) { paths := loadingRules.Precedence { + // For all the kubeconfig paths, look for one that exists for _, path := range paths { _, err = os.Stat(path) @@ -392,6 +404,7 @@ func extractKubeConfigValues() (string, string, error) { kubeConfigDefault = path break } // Otherwise, continue. + } if kubeConfigDefault == "" { diff --git a/test/e2e/fixture/admin/actions.go b/test/e2e/fixture/admin/actions.go deleted file mode 100644 index 4519d228f9c1a..0000000000000 --- a/test/e2e/fixture/admin/actions.go +++ /dev/null @@ -1,67 +0,0 @@ -package admin - -import ( - "github.com/argoproj/argo-cd/v2/test/e2e/fixture" -) - -// this implements the "when" part of given/when/then -// -// none of the func implement error checks, and that is complete intended, you should check for errors -// using the Then() -type Actions struct { - context *Context - ignoreErrors bool - lastOutput string - lastError error -} - -func (a *Actions) prepareExportCommand() []string { - a.context.t.Helper() - args := []string{"export", "--application-namespaces", fixture.AppNamespace()} - - return args -} - -func (a *Actions) prepareImportCommand() []string { - a.context.t.Helper() - args := []string{"import", "--application-namespaces", fixture.AppNamespace(), "-"} - - return args -} - -func (a *Actions) RunExport() *Actions { - a.context.t.Helper() - a.runCli(a.prepareExportCommand()...) - return a -} - -func (a *Actions) RunImport(stdin string) *Actions { - a.context.t.Helper() - a.runCliWithStdin(stdin, a.prepareImportCommand()...) - return a -} - -func (a *Actions) IgnoreErrors() *Actions { - a.ignoreErrors = true - return a -} - -func (a *Actions) DoNotIgnoreErrors() *Actions { - a.ignoreErrors = false - return a -} - -func (a *Actions) runCli(args ...string) { - a.context.t.Helper() - a.lastOutput, a.lastError = RunCli(args...) -} - -func (a *Actions) runCliWithStdin(stdin string, args ...string) { - a.context.t.Helper() - a.lastOutput, a.lastError = RunCliWithStdin(stdin, args...) -} - -func (a *Actions) Then() *Consequences { - a.context.t.Helper() - return &Consequences{a.context, a} -} diff --git a/test/e2e/fixture/admin/consequences.go b/test/e2e/fixture/admin/consequences.go deleted file mode 100644 index bc65f3a532794..0000000000000 --- a/test/e2e/fixture/admin/consequences.go +++ /dev/null @@ -1,37 +0,0 @@ -package admin - -import ( - . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/admin/utils" -) - -// this implements the "then" part of given/when/then -type Consequences struct { - context *Context - actions *Actions -} - -func (c *Consequences) And(block func()) *Consequences { - c.context.t.Helper() - block() - return c -} - -func (c *Consequences) AndCLIOutput(block func(output string, err error)) *Consequences { - c.context.t.Helper() - block(c.actions.lastOutput, c.actions.lastError) - return c -} - -// For use after running export with the exported resources desirialized -func (c *Consequences) AndExportedResources(block func(resources *ExportedResources, err error)) { - result, err := GetExportedResourcesFromOutput(c.actions.lastOutput) - block(&result, err) -} - -func (c *Consequences) Given() *Context { - return c.context -} - -func (c *Consequences) When() *Actions { - return c.actions -} diff --git a/test/e2e/fixture/admin/context.go b/test/e2e/fixture/admin/context.go deleted file mode 100644 index aed58cb1a7b79..0000000000000 --- a/test/e2e/fixture/admin/context.go +++ /dev/null @@ -1,41 +0,0 @@ -package admin - -import ( - "testing" - - "github.com/argoproj/argo-cd/v2/test/e2e/fixture" - "github.com/argoproj/argo-cd/v2/util/env" -) - -// this implements the "given" part of given/when/then -type Context struct { - t *testing.T - // seconds - timeout int - name string -} - -func Given(t *testing.T) *Context { - fixture.EnsureCleanState(t) - return GivenWithSameState(t) -} - -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", 20, 0, 180) - return &Context{ - t: t, - name: fixture.Name(), - timeout: timeout, - } -} - -func (c *Context) And(block func()) *Context { - block() - return c -} - -func (c *Context) When() *Actions { - return &Actions{context: c} -} diff --git a/test/e2e/fixture/admin/fixture.go b/test/e2e/fixture/admin/fixture.go deleted file mode 100644 index 92216c58d42fe..0000000000000 --- a/test/e2e/fixture/admin/fixture.go +++ /dev/null @@ -1,15 +0,0 @@ -package admin - -import ( - "github.com/argoproj/argo-cd/v2/test/e2e/fixture" -) - -// For admin CLI with kubernetes context -func RunCli(args ...string) (string, error) { - return RunCliWithStdin("", args...) -} - -func RunCliWithStdin(stdin string, args ...string) (string, error) { - args = append([]string{"admin", "--namespace", fixture.TestNamespace()}, args...) - return fixture.RunCliWithStdin(stdin, true, args...) -} diff --git a/test/e2e/fixture/admin/utils/backup.go b/test/e2e/fixture/admin/utils/backup.go deleted file mode 100644 index 79bd890518603..0000000000000 --- a/test/e2e/fixture/admin/utils/backup.go +++ /dev/null @@ -1,48 +0,0 @@ -package utils - -import ( - "fmt" - "strings" - - kube "github.com/argoproj/gitops-engine/pkg/utils/kube" - yaml "gopkg.in/yaml.v3" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -type ExportedResources []unstructured.Unstructured - -func GetExportedResourcesFromOutput(output string) (ExportedResources, error) { - var resources []unstructured.Unstructured - docs := strings.Split(output, "---") - - for _, doc := range docs { - doc = strings.TrimSpace(doc) - if len(doc) == 0 { - continue - } - - var resourceData map[string]interface{} - - if err := yaml.Unmarshal([]byte(doc), &resourceData); err != nil { - return nil, fmt.Errorf("error unmarshaling YAML: %w", err) - } - - resource := unstructured.Unstructured{Object: resourceData} - resources = append(resources, resource) - } - - return resources, nil -} - -func (e ExportedResources) HasResource(resource kube.ResourceKey) bool { - for _, res := range e { - if res.GetObjectKind().GroupVersionKind().Group == resource.Group && - res.GetKind() == resource.Kind && - res.GetName() == resource.Name && - res.GetNamespace() == resource.Namespace { - return true - } - } - - return false -} diff --git a/test/e2e/fixture/app/actions.go b/test/e2e/fixture/app/actions.go index 1d013b6628963..f4fd167db1024 100644 --- a/test/e2e/fixture/app/actions.go +++ b/test/e2e/fixture/app/actions.go @@ -106,7 +106,6 @@ func (a *Actions) CreateFromPartialFile(data string, flags ...string) *Actions { a.runCli(args...) return a } - func (a *Actions) CreateFromFile(handler func(app *Application), flags ...string) *Actions { a.context.t.Helper() app := &Application{ @@ -301,9 +300,9 @@ func (a *Actions) PatchApp(patch string) *Actions { func (a *Actions) PatchAppHttp(patch string) *Actions { a.context.t.Helper() var application Application - patchType := "merge" - appName := a.context.AppQualifiedName() - appNamespace := a.context.AppNamespace() + var patchType = "merge" + var appName = a.context.AppQualifiedName() + var appNamespace = a.context.AppNamespace() patchRequest := &client.ApplicationPatchRequest{ Name: &appName, PatchType: &patchType, @@ -418,12 +417,6 @@ 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...) diff --git a/test/e2e/fixture/app/context.go b/test/e2e/fixture/app/context.go index 2225cac54c61d..41c8dbd17bcad 100644 --- a/test/e2e/fixture/app/context.go +++ b/test/e2e/fixture/app/context.go @@ -100,7 +100,7 @@ func (c *Context) AppNamespace() string { func (c *Context) SetAppNamespace(namespace string) *Context { c.appNamespace = namespace - // fixture.SetParamInSettingConfigMap("application.resourceTrackingMethod", "annotation") + //fixture.SetParamInSettingConfigMap("application.resourceTrackingMethod", "annotation") return c } diff --git a/test/e2e/fixture/app/expectation.go b/test/e2e/fixture/app/expectation.go index b5e83a664085c..c7cf20ab27729 100644 --- a/test/e2e/fixture/app/expectation.go +++ b/test/e2e/fixture/app/expectation.go @@ -111,6 +111,7 @@ func StatusExists() Expectation { func Namespace(name string, block func(app *Application, ns *v1.Namespace)) Expectation { return func(c *Consequences) (state, string) { ns, err := namespace(name) + if err != nil { return failed, fmt.Sprintf("namespace not found %s", err.Error()) } @@ -133,7 +134,6 @@ func ResourceSyncStatusIs(kind, resource string, expected SyncStatusCode) Expect return simple(actual == expected, fmt.Sprintf("resource '%s/%s' sync status should be %s, is %s", kind, resource, expected, actual)) } } - func ResourceSyncStatusWithNamespaceIs(kind, resource, namespace string, expected SyncStatusCode) Expectation { return func(c *Consequences) (state, string) { actual := c.resource(kind, resource, namespace).Status @@ -143,32 +143,16 @@ func ResourceSyncStatusWithNamespaceIs(kind, resource, namespace string, expecte func ResourceHealthIs(kind, resource string, expected health.HealthStatusCode) Expectation { return func(c *Consequences) (state, string) { - var actual health.HealthStatusCode - resourceHealth := c.resource(kind, resource, "").Health - if resourceHealth != nil { - actual = resourceHealth.Status - } else { - // Some resources like ConfigMap may not have health status when they are okay - actual = health.HealthStatusHealthy - } + actual := c.resource(kind, resource, "").Health.Status return simple(actual == expected, fmt.Sprintf("resource '%s/%s' health should be %s, is %s", kind, resource, expected, actual)) } } - func ResourceHealthWithNamespaceIs(kind, resource, namespace string, expected health.HealthStatusCode) Expectation { return func(c *Consequences) (state, string) { - var actual health.HealthStatusCode - resourceHealth := c.resource(kind, resource, namespace).Health - if resourceHealth != nil { - actual = resourceHealth.Status - } else { - // Some resources like ConfigMap may not have health status when they are okay - actual = health.HealthStatusHealthy - } + actual := c.resource(kind, resource, namespace).Health.Status return simple(actual == expected, fmt.Sprintf("resource '%s/%s' health should be %s, is %s", kind, resource, expected, actual)) } } - func ResourceResultNumbering(num int) Expectation { return func(c *Consequences) (state, string) { actualNum := len(c.app().Status.OperationState.SyncResult.Resources) @@ -232,19 +216,6 @@ 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() @@ -284,6 +255,7 @@ func pods() (*v1.PodList, error) { func NoNamespace(name string) Expectation { return func(c *Consequences) (state, string) { _, err := namespace(name) + if err != nil { return succeeded, "namespace not found" } diff --git a/test/e2e/fixture/applicationsets/actions.go b/test/e2e/fixture/applicationsets/actions.go index 1a5b214f75482..0b167c2b1a734 100644 --- a/test/e2e/fixture/applicationsets/actions.go +++ b/test/e2e/fixture/applicationsets/actions.go @@ -79,13 +79,15 @@ func (a *Actions) SwitchToArgoCDNamespace() *Actions { // 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 { + fixtureClient := utils.GetE2EFixtureK8sClient() var serviceAccountName string // Look for a service account matching '*application-controller*' - err := wait.PollUntilContextTimeout(context.Background(), 500*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (bool, error) { - serviceAccountList, err := fixtureClient.KubeClientset.CoreV1().ServiceAccounts(fixture.TestNamespace()).List(ctx, metav1.ListOptions{}) + err := wait.Poll(500*time.Millisecond, 30*time.Second, func() (bool, error) { + + 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 @@ -152,6 +154,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(fixture.TestNamespace()).Delete(context.Background(), secretName, metav1.DeleteOptions{}) a.describeAction = fmt.Sprintf("deleting cluster Secret '%s'", secretName) @@ -163,6 +166,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(fixture.TestNamespace()).Delete(context.Background(), configMapName, metav1.DeleteOptions{}) a.describeAction = fmt.Sprintf("deleting configMap '%s'", configMapName) @@ -174,6 +178,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(fixture.TestNamespace()).Delete(context.Background(), placementDecisionName, metav1.DeleteOptions{}) a.describeAction = fmt.Sprintf("deleting placement decision '%s'", placementDecisionName) @@ -444,6 +449,7 @@ func (a *Actions) get() (*v1alpha1.ApplicationSet, error) { } return &appSet, nil + } // Update retrieves the latest copy the ApplicationSet, then allows the caller to mutate it via 'toUpdate', with @@ -456,6 +462,7 @@ func (a *Actions) Update(toUpdate func(*v1alpha1.ApplicationSet)) *Actions { var mostRecentError error for start := time.Now(); time.Since(start) < timeout; time.Sleep(3 * time.Second) { + appSet, err := a.get() mostRecentError = err if err == nil { @@ -521,9 +528,3 @@ func (a *Actions) runCli(args ...string) { a.lastOutput, a.lastError = fixture.RunCli(args...) a.verifyAction() } - -func (a *Actions) AddSignedFile(fileName, fileContents string) *Actions { - a.context.t.Helper() - fixture.AddSignedFile(a.context.path+"/"+fileName, fileContents) - return a -} diff --git a/test/e2e/fixture/applicationsets/consequences.go b/test/e2e/fixture/applicationsets/consequences.go index 3da461c1c9e5e..db614f3cf3075 100644 --- a/test/e2e/fixture/applicationsets/consequences.go +++ b/test/e2e/fixture/applicationsets/consequences.go @@ -27,6 +27,7 @@ func (c *Consequences) Expect(e Expectation) *Consequences { } func (c *Consequences) ExpectWithDuration(e Expectation, timeout time.Duration) *Consequences { + // this invocation makes sure this func is not reported as the cause of the failure - we are a "test helper" c.context.t.Helper() var message string @@ -74,6 +75,7 @@ func (c *Consequences) app(name string) *v1alpha1.Application { } func (c *Consequences) apps() []v1alpha1.Application { + var namespace string if c.context.switchToNamespace != "" { namespace = string(c.context.switchToNamespace) @@ -93,6 +95,7 @@ func (c *Consequences) apps() []v1alpha1.Application { } func (c *Consequences) applicationSet(applicationSetName string) *v1alpha1.ApplicationSet { + fixtureClient := utils.GetE2EFixtureK8sClient() var appSetClientSet dynamic.ResourceInterface diff --git a/test/e2e/fixture/applicationsets/context.go b/test/e2e/fixture/applicationsets/context.go index daa2474ea01d5..c10b2c99bfe5f 100644 --- a/test/e2e/fixture/applicationsets/context.go +++ b/test/e2e/fixture/applicationsets/context.go @@ -5,7 +5,7 @@ import ( "time" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" - "github.com/argoproj/argo-cd/v2/test/e2e/fixture/gpgkeys" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" ) // Context implements the "given" part of given/when/then @@ -16,12 +16,10 @@ type Context struct { name string namespace string switchToNamespace utils.ExternalNamespace - project string - path string } func Given(t *testing.T) *Context { - utils.EnsureCleanState(t) + EnsureCleanState(t) return &Context{t: t} } @@ -40,18 +38,3 @@ func (c *Context) And(block func()) *Context { block() return c } - -func (c *Context) Project(project string) *Context { - c.project = project - return c -} - -func (c *Context) Path(path string) *Context { - c.path = path - return c -} - -func (c *Context) GPGPublicKeyAdded() *Context { - gpgkeys.AddGPGPublicKey() - return c -} diff --git a/test/e2e/fixture/applicationsets/expectation.go b/test/e2e/fixture/applicationsets/expectation.go index fc8de495fe70c..990ad5f33dbfb 100644 --- a/test/e2e/fixture/applicationsets/expectation.go +++ b/test/e2e/fixture/applicationsets/expectation.go @@ -59,6 +59,7 @@ func Error(message, err string) Expectation { // equivalent to provided values. func ApplicationsExist(expectedApps []v1alpha1.Application) Expectation { return func(c *Consequences) (state, string) { + for _, expectedApp := range expectedApps { foundApp := c.app(expectedApp.Name) if foundApp == nil { @@ -66,13 +67,16 @@ func ApplicationsExist(expectedApps []v1alpha1.Application) Expectation { } if !appsAreEqual(expectedApp, *foundApp) { + diff, err := getDiff(filterFields(expectedApp), filterFields(*foundApp)) if err != nil { return failed, err.Error() } return pending, fmt.Sprintf("apps are not equal: '%s', diff: %s\n", expectedApp.QualifiedName(), diff) + } + } return succeeded, "all apps successfully found" @@ -83,6 +87,7 @@ func ApplicationsExist(expectedApps []v1alpha1.Application) Expectation { // equivalent to provided values. func ApplicationSetHasConditions(applicationSetName string, expectedConditions []v1alpha1.ApplicationSetCondition) Expectation { return func(c *Consequences) (state, string) { + // retrieve the application set foundApplicationSet := c.applicationSet(applicationSetName) if foundApplicationSet == nil { @@ -103,6 +108,7 @@ func ApplicationSetHasConditions(applicationSetName string, expectedConditions [ // ApplicationsDoNotExist checks that each of the 'expectedApps' no longer exist in the namespace func ApplicationsDoNotExist(expectedApps []v1alpha1.Application) Expectation { return func(c *Consequences) (state, string) { + for _, expectedApp := range expectedApps { foundApp := c.app(expectedApp.Name) if foundApp != nil { @@ -139,12 +145,14 @@ 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 v1alpha1.Application) (string, error) { + bytes, _, err := diff.CreateTwoWayMergePatch(orig, new, orig) if err != nil { return "", err } return string(bytes), nil + } // getConditionDiff returns a string containing a comparison result of two ApplicationSetCondition (for test output/debug purposes) @@ -164,10 +172,12 @@ func getConditionDiff(orig, new []v1alpha1.ApplicationSetCondition) (string, err } return string(bytes), nil + } // filterFields returns a copy of Application, but with unnecessary (for testing) fields removed func filterFields(input v1alpha1.Application) v1alpha1.Application { + spec := input.Spec metaCopy := input.ObjectMeta.DeepCopy() @@ -200,6 +210,7 @@ func filterFields(input v1alpha1.Application) v1alpha1.Application { // filterConditionFields returns a copy of ApplicationSetCondition, but with unnecessary (for testing) fields removed func filterConditionFields(input *[]v1alpha1.ApplicationSetCondition) *[]v1alpha1.ApplicationSetCondition { + var filteredConditions []v1alpha1.ApplicationSetCondition for _, condition := range *input { newCondition := &v1alpha1.ApplicationSetCondition{ diff --git a/test/e2e/fixture/applicationsets/utils/fixture.go b/test/e2e/fixture/applicationsets/utils/fixture.go index 1ff0fbbfd6137..0074fe76bf5c8 100644 --- a/test/e2e/fixture/applicationsets/utils/fixture.go +++ b/test/e2e/fixture/applicationsets/utils/fixture.go @@ -81,11 +81,12 @@ func TestNamespace() string { return GetEnvWithDefault("ARGOCD_E2E_NAMESPACE", ArgoCDNamespace) } -// GetE2EFixtureK8sClient initializes the Kubernetes clients (if needed), and returns the most recently initialized value. +// 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() { + // set-up variables config := getKubeConfig("", clientcmd.ConfigOverrides{}) @@ -100,12 +101,14 @@ func GetE2EFixtureK8sClient() *E2EFixtureK8sClient { 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 in a 'clean' state, before a test is run. +// EnsureCleanSlate ensures that the Kubernetes resources on the cluster are are in a 'clean' state, before a test is run. func EnsureCleanState(t *testing.T) { + start := time.Now() fixtureClient := GetE2EFixtureK8sClient() @@ -188,6 +191,7 @@ func EnsureCleanState(t *testing.T) { } func waitForExpectedClusterState() error { + fixtureClient := GetE2EFixtureK8sClient() SetProjectSpec(fixtureClient, "default", v1alpha1.AppProjectSpec{ @@ -225,6 +229,7 @@ func waitForExpectedClusterState() error { return fmt.Errorf("Waiting for list of Applications to be size zero: %d", len(appList.Items)) } return nil // Pass + }, time.Now().Add(60*time.Second)); err != nil { return err } @@ -268,13 +273,14 @@ func cleanUpNamespace(fixtureClient *E2EFixtureK8sClient, namespace string) erro msg = err.Error() } - return fmt.Errorf("%s", msg) + return fmt.Errorf(msg) } // waitForSuccess waits for the condition to return a non-error value. // Returns if condition returns nil, or the expireTime has elapsed (in which // case the last error will be returned) func waitForSuccess(condition func() error, expireTime time.Time) error { + var mostRecentError error for { @@ -296,6 +302,7 @@ func waitForSuccess(condition func() error, expireTime time.Time) error { time.Sleep(500 * time.Millisecond) } return mostRecentError + } // getKubeConfig creates new kubernetes client config using specified config path and config overrides variables @@ -312,6 +319,7 @@ func getKubeConfig(configPath string, overrides clientcmd.ConfigOverrides) *rest // creates e2e tests fixture: ensures that Application CRD is installed, creates temporal namespace, starts repo and api server, // configure currently available cluster. func init() { + // ensure we log all shell execs log.SetLevel(log.DebugLevel) } @@ -366,10 +374,12 @@ func ToUnstructured(obj interface{}) (*unstructured.Unstructured, error) { // // Note: This only applies to tests that use the GitHub API (different from GitHub's Git service) func IsGitHubAPISkippedTest(t *testing.T) bool { + if strings.TrimSpace(os.Getenv("GITHUB_TOKEN")) == "" { t.Skip("Skipping this test, as the GITHUB_TOKEN is not set. Please ensure this test passes locally, with your own GITHUB_TOKEN.") return true } return false + } diff --git a/test/e2e/fixture/certs/certs.go b/test/e2e/fixture/certs/certs.go index 40db06a18f45d..b9dcea1a7624d 100644 --- a/test/e2e/fixture/certs/certs.go +++ b/test/e2e/fixture/certs/certs.go @@ -23,9 +23,9 @@ func AddCustomCACert() { errors.FailOnErr(fixture.RunCli(args...)) certData, err := os.ReadFile(caCertPath) errors.CheckError(err) - err = os.WriteFile(fixture.TmpDir+"/app/config/tls/localhost", certData, 0o644) + err = os.WriteFile(fixture.TmpDir+"/app/config/tls/localhost", certData, 0644) errors.CheckError(err) - err = os.WriteFile(fixture.TmpDir+"/app/config/tls/127.0.0.1", certData, 0o644) + err = os.WriteFile(fixture.TmpDir+"/app/config/tls/127.0.0.1", certData, 0644) errors.CheckError(err) } else { args := []string{"cert", "add-tls", "argocd-e2e-server", "--from", caCertPath} @@ -33,6 +33,7 @@ func AddCustomCACert() { fixture.RestartAPIServer() fixture.RestartRepoServer() } + } // AddCustomSSHKnownHostsKeys adds SSH known hosts data to the Argo CD server @@ -51,7 +52,7 @@ func AddCustomSSHKnownHostsKeys() { if fixture.IsLocal() { knownHostsData, err := os.ReadFile(knownHostsPath) errors.CheckError(err) - err = os.WriteFile(fixture.TmpDir+"/app/config/ssh/ssh_known_hosts", knownHostsData, 0o644) + err = os.WriteFile(fixture.TmpDir+"/app/config/ssh/ssh_known_hosts", knownHostsData, 0644) errors.CheckError(err) } else { fixture.RestartAPIServer() diff --git a/test/e2e/fixture/cluster/actions.go b/test/e2e/fixture/cluster/actions.go index ac114bad0cdf9..0613c9a22cf15 100644 --- a/test/e2e/fixture/cluster/actions.go +++ b/test/e2e/fixture/cluster/actions.go @@ -3,15 +3,15 @@ package cluster import ( "context" "errors" + "fmt" "log" "strings" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/clusterauth" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster" "github.com/argoproj/argo-cd/v2/test/e2e/fixture" @@ -57,9 +57,10 @@ func (a *Actions) Create(args ...string) *Actions { }, Upsert: a.context.upsert, }) + if err != nil { if !a.ignoreErrors { - log.Fatalf("Failed to upsert cluster %v", err.Error()) + log.Fatalf(fmt.Sprintf("Failed to upsert cluster %v", err.Error())) } a.lastError = errors.New(err.Error()) } diff --git a/test/e2e/fixture/fixture.go b/test/e2e/fixture/fixture.go index 24d0e4ce74d71..f8dd60cb74974 100644 --- a/test/e2e/fixture/fixture.go +++ b/test/e2e/fixture/fixture.go @@ -231,6 +231,7 @@ func init() { for scanner.Scan() { testsRun[scanner.Text()] = true } + } func loginAs(username, password string) { @@ -567,7 +568,7 @@ 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 successful + // Register this test after it has been run & was successfull t.Cleanup(func() { RecordTestRun(t) }) @@ -741,25 +742,21 @@ func RunCliWithRetry(maxRetries int, args ...string) (string, error) { } func RunCli(args ...string) (string, error) { - return RunCliWithStdin("", false, args...) + return RunCliWithStdin("", args...) } -func RunCliWithStdin(stdin string, isKubeConextOnlyCli bool, args ...string) (string, error) { +func RunCliWithStdin(stdin string, args ...string) (string, error) { if plainText { args = append(args, "--plaintext") } - // For commands executed with Kubernetes context server argument causes a conflict (for those commands server argument is for KubeAPI server), also authentication is not required - if !isKubeConextOnlyCli { - args = append(args, "--server", apiServerAddress, "--auth-token", token) - } - - args = append(args, "--insecure") + args = append(args, "--server", apiServerAddress, "--auth-token", token, "--insecure") return RunWithStdin(stdin, "", "../../dist/argocd", args...) } func Patch(path string, jsonPatch string) { + log.WithFields(log.Fields{"path": path, "jsonPatch": jsonPatch}).Info("patching") filename := filepath.Join(repoDirectory(), path) @@ -787,7 +784,7 @@ func Patch(path string, jsonPatch string) { CheckError(err) } - CheckError(os.WriteFile(filename, bytes, 0o644)) + CheckError(os.WriteFile(filename, bytes, 0644)) FailOnErr(Run(repoDirectory(), "git", "diff")) FailOnErr(Run(repoDirectory(), "git", "commit", "-am", "patch")) if IsRemote() { @@ -796,6 +793,7 @@ func Patch(path string, jsonPatch string) { } func Delete(path string) { + log.WithFields(log.Fields{"path": path}).Info("deleting") CheckError(os.Remove(filepath.Join(repoDirectory(), path))) @@ -810,10 +808,11 @@ func Delete(path string) { func WriteFile(path, contents string) { log.WithFields(log.Fields{"path": path}).Info("adding") - CheckError(os.WriteFile(filepath.Join(repoDirectory(), path), []byte(contents), 0o644)) + CheckError(os.WriteFile(filepath.Join(repoDirectory(), path), []byte(contents), 0644)) } func AddFile(path, contents string) { + WriteFile(path, contents) FailOnErr(Run(repoDirectory(), "git", "diff")) @@ -861,6 +860,7 @@ func AddTag(name string) { // create the resource by creating using "kubectl apply", with bonus templating func Declarative(filename string, values interface{}) (string, error) { + bytes, err := os.ReadFile(path.Join("testdata", filename)) CheckError(err) @@ -1001,7 +1001,7 @@ func RecordTestRun(t *testing.T) { return } log.Infof("Registering test execution at %s", rf) - f, err := os.OpenFile(rf, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) + f, err := os.OpenFile(rf, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { t.Fatalf("could not open record file %s: %v", rf, err) } @@ -1015,11 +1015,3 @@ func RecordTestRun(t *testing.T) { t.Fatalf("could not write to %s: %v", rf, err) } } - -func GetApiServerAddress() string { - return apiServerAddress -} - -func GetToken() string { - return token -} diff --git a/test/e2e/fixture/gpgkeys/gpgkeys.go b/test/e2e/fixture/gpgkeys/gpgkeys.go index 0751f67ad2cec..339c6e474dd93 100644 --- a/test/e2e/fixture/gpgkeys/gpgkeys.go +++ b/test/e2e/fixture/gpgkeys/gpgkeys.go @@ -19,7 +19,7 @@ func AddGPGPublicKey() { if fixture.IsLocal() { keyData, err := os.ReadFile(keyPath) errors.CheckError(err) - err = os.WriteFile(fmt.Sprintf("%s/app/config/gpg/source/%s", fixture.TmpDir, fixture.GpgGoodKeyID), keyData, 0o644) + err = os.WriteFile(fmt.Sprintf("%s/app/config/gpg/source/%s", fixture.TmpDir, fixture.GpgGoodKeyID), keyData, 0644) errors.CheckError(err) } else { fixture.RestartRepoServer() diff --git a/test/e2e/fixture/repos/repos.go b/test/e2e/fixture/repos/repos.go index e64f6e20fe8fa..70eb574265554 100644 --- a/test/e2e/fixture/repos/repos.go +++ b/test/e2e/fixture/repos/repos.go @@ -137,10 +137,8 @@ func AddHelmHTTPSCredentialsTLSClientCert() { // AddHelmoOCICredentialsWithoutUserPass adds credentials for Helm OIC repo to context func AddHelmoOCICredentialsWithoutUserPass() { - args := []string{ - "repocreds", "add", fixture.RepoURL(fixture.RepoURLTypeHelmOCI), - "--enable-oci", "--type", "helm", - } + args := []string{"repocreds", "add", fixture.RepoURL(fixture.RepoURLTypeHelmOCI), + "--enable-oci", "--type", "helm"} errors.FailOnErr(fixture.RunCli(args...)) } @@ -174,4 +172,5 @@ func PushChartToOCIRegistry(chartPathName, chartName, chartVersion string) { fmt.Sprintf("%s/%s-%s.tgz", tempDest, chartName, chartVersion), fmt.Sprintf("oci://%s", fixture.HelmOCIRegistryURL), )) + } diff --git a/test/e2e/git_test.go b/test/e2e/git_test.go deleted file mode 100644 index 7c92274dab56a..0000000000000 --- a/test/e2e/git_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package e2e - -import ( - "strings" - "testing" - - v1 "k8s.io/api/core/v1" - - "github.com/argoproj/argo-cd/v2/test/e2e/fixture" - - . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" -) - -func TestGitSemverResolutionNotUsingConstraint(t *testing.T) { - Given(t). - Path("deployment"). - CustomSSHKnownHostsAdded(). - SSHRepoURLAdded(true). - RepoURLType(fixture.RepoURLTypeSSH). - Revision("v0.1.0"). - When(). - AddTag("v0.1.0"). - CreateApp(). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)) -} - -func TestGitSemverResolutionNotUsingConstraintWithLeadingZero(t *testing.T) { - Given(t). - Path("deployment"). - CustomSSHKnownHostsAdded(). - SSHRepoURLAdded(true). - RepoURLType(fixture.RepoURLTypeSSH). - Revision("0.1.0"). - When(). - AddTag("0.1.0"). - CreateApp(). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)) -} - -func TestGitSemverResolutionUsingConstraint(t *testing.T) { - Given(t). - Path("deployment"). - CustomSSHKnownHostsAdded(). - SSHRepoURLAdded(true). - RepoURLType(fixture.RepoURLTypeSSH). - Revision("v0.1.*"). - When(). - AddTag("v0.1.0"). - CreateApp(). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - When(). - PatchFile("deployment.yaml", `[ - {"op": "replace", "path": "/metadata/name", "value": "new-app"}, - {"op": "replace", "path": "/spec/replicas", "value": 1} -]`). - AddTag("v0.1.2"). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - Expect(Pod(func(p v1.Pod) bool { return strings.HasPrefix(p.Name, "new-app") })) -} - -func TestGitSemverResolutionUsingConstraintWithLeadingZero(t *testing.T) { - Given(t). - Path("deployment"). - CustomSSHKnownHostsAdded(). - SSHRepoURLAdded(true). - RepoURLType(fixture.RepoURLTypeSSH). - Revision("0.1.*"). - When(). - AddTag("0.1.0"). - CreateApp(). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - When(). - PatchFile("deployment.yaml", `[ - {"op": "replace", "path": "/metadata/name", "value": "new-app"}, - {"op": "replace", "path": "/spec/replicas", "value": 1} -]`). - AddTag("0.1.2"). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - Expect(Pod(func(p v1.Pod) bool { return strings.HasPrefix(p.Name, "new-app") })) -} diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 9a95829c33c50..5fd774ea0c46d 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -135,13 +135,13 @@ func TestHelmIgnoreMissingValueFiles(t *testing.T) { Then(). And(func(app *Application) { assert.Equal(t, []string{"does-not-exist-values.yaml"}, app.Spec.GetSource().Helm.ValueFiles) - assert.False(t, app.Spec.GetSource().Helm.IgnoreMissingValueFiles) + assert.Equal(t, false, app.Spec.GetSource().Helm.IgnoreMissingValueFiles) }). When(). AppSet("--ignore-missing-value-files"). Then(). And(func(app *Application) { - assert.True(t, app.Spec.GetSource().Helm.IgnoreMissingValueFiles) + assert.Equal(t, true, app.Spec.GetSource().Helm.IgnoreMissingValueFiles) }). When(). Sync(). @@ -153,7 +153,7 @@ func TestHelmIgnoreMissingValueFiles(t *testing.T) { AppUnSet("--ignore-missing-value-files"). Then(). And(func(app *Application) { - assert.False(t, app.Spec.GetSource().Helm.IgnoreMissingValueFiles) + assert.Equal(t, false, app.Spec.GetSource().Helm.IgnoreMissingValueFiles) }). When(). IgnoreErrors(). @@ -362,64 +362,9 @@ func TestKubeVersion(t *testing.T) { "-o", "jsonpath={.data.kubeVersion}")).(string) // Capabilities.KubeVersion defaults to 1.9.0, we assume here you are running a later version assert.LessOrEqual(t, GetVersions().ServerVersion.Format("v%s.%s.0"), kubeVersion) - }). - When(). - // Make sure override works. - AppSet("--helm-kube-version", "999.999.999"). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - And(func(app *Application) { - assert.Equal(t, "v999.999.999", FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", - "-o", "jsonpath={.data.kubeVersion}")).(string)) - }) -} - -// make sure api versions gets passed down to resources -func TestApiVersions(t *testing.T) { - SkipOnEnv(t, "HELM") - Given(t). - Path("helm-api-versions"). - When(). - CreateApp(). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - And(func(app *Application) { - apiVersions := FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", - "-o", "jsonpath={.data.apiVersions}")).(string) - // The v1 API shouldn't be going anywhere. - assert.Contains(t, apiVersions, "v1") - }). - When(). - // Make sure override works. - AppSet("--helm-api-versions", "v1/MyTestResource"). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - And(func(app *Application) { - apiVersions := FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", - "-o", "jsonpath={.data.apiVersions}")).(string) - assert.Contains(t, apiVersions, "v1/MyTestResource") }) } -func TestHelmNamespaceOverride(t *testing.T) { - SkipOnEnv(t, "HELM") - Given(t). - Path("helm-namespace"). - When(). - CreateApp(). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - When(). - AppSet("--helm-namespace", "does-not-exist"). - Then(). - // The app should go out of sync, because the resource's target namespace changed. - Expect(SyncStatusIs(SyncStatusCodeOutOfSync)) -} - func TestHelmValuesHiddenDirectory(t *testing.T) { SkipOnEnv(t, "HELM") Given(t). diff --git a/test/e2e/hook_test.go b/test/e2e/hook_test.go index 5fe2248051737..2db8ff87795ad 100644 --- a/test/e2e/hook_test.go +++ b/test/e2e/hook_test.go @@ -7,7 +7,6 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -66,9 +65,10 @@ func TestPostDeleteHook(t *testing.T) { assert.Len(t, hooks.Items, 1) assert.Equal(t, "hook", hooks.Items[0].Name) }) + } -// make sure that hooks do not appear in "argocd app diff" +// make sure that that hooks do not appear in "argocd app diff" func TestHookDiff(t *testing.T) { Given(t). Path("hook"). @@ -77,7 +77,7 @@ func TestHookDiff(t *testing.T) { Then(). And(func(_ *Application) { output, err := RunCli("app", "diff", Name()) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, output, "name: pod") assert.NotContains(t, output, "name: hook") }) @@ -420,7 +420,7 @@ func TestAutomaticallyNamingUnnamedHook(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { resources := app.Status.OperationState.SyncResult.Resources - assert.Len(t, resources, 3) + assert.Equal(t, 3, len(resources)) // make sure we don't use the same name assert.Contains(t, resources[0].Name, "presync") assert.Contains(t, resources[2].Name, "postsync") diff --git a/test/e2e/jsonnet_test.go b/test/e2e/jsonnet_test.go index 3f1113b4207a5..cad88f34a0048 100644 --- a/test/e2e/jsonnet_test.go +++ b/test/e2e/jsonnet_test.go @@ -6,7 +6,6 @@ import ( . "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/argoproj/gitops-engine/pkg/utils/kube" "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" @@ -24,9 +23,9 @@ func TestJsonnetAppliedCorrectly(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { manifests, err := RunCli("app", "manifests", app.Name, "--source", "live") - require.NoError(t, err) + assert.NoError(t, err) resources, err := kube.SplitYAML([]byte(manifests)) - require.NoError(t, err) + assert.NoError(t, err) index := -1 for i := range resources { @@ -36,7 +35,7 @@ func TestJsonnetAppliedCorrectly(t *testing.T) { } } - assert.Greater(t, index, -1) + assert.True(t, index > -1) deployment := resources[index] assert.Equal(t, "jsonnet-guestbook-ui", deployment.GetName()) @@ -54,9 +53,9 @@ func TestJsonnetTlaParameterAppliedCorrectly(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { manifests, err := RunCli("app", "manifests", app.Name, "--source", "live") - require.NoError(t, err) + assert.NoError(t, err) resources, err := kube.SplitYAML([]byte(manifests)) - require.NoError(t, err) + assert.NoError(t, err) index := -1 for i := range resources { @@ -66,7 +65,7 @@ func TestJsonnetTlaParameterAppliedCorrectly(t *testing.T) { } } - assert.Greater(t, index, -1) + assert.True(t, index > -1) deployment := resources[index] assert.Equal(t, "testing-tla", deployment.GetName()) @@ -88,7 +87,6 @@ func TestJsonnetTlaEnv(t *testing.T) { assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.bar}")).(string)) }) } - func TestJsonnetExtVarEnv(t *testing.T) { Given(t). Path("jsonnet-ext-var"). diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go index 923b9d46fee9e..862e55c9e9502 100644 --- a/test/e2e/kustomize_test.go +++ b/test/e2e/kustomize_test.go @@ -7,17 +7,15 @@ 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" - . "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/errors" - . "github.com/argoproj/argo-cd/v2/util/errors" ) func TestKustomize2AppSource(t *testing.T) { + patchLabelMatchesFor := func(kind string) func(app *Application) { return func(app *Application) { name := "k2-patched-guestbook-ui-deploy1" @@ -25,7 +23,7 @@ func TestKustomize2AppSource(t *testing.T) { "", "kubectl", "-n="+fixture.DeploymentNamespace(), "get", kind, name, "-ojsonpath={.metadata.labels.patched-by}") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, "argo-cd", labelValue, "wrong value of 'patched-by' label of %s %s", kind, name) } } @@ -100,13 +98,14 @@ func TestSyncStatusOptionIgnore(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - assert.Len(t, app.Status.Resources, 2) + assert.Equal(t, 2, len(app.Status.Resources)) for _, resourceStatus := range app.Status.Resources { // new map in-sync if resourceStatus.Name != oldMap { assert.Contains(t, resourceStatus.Name, "my-map-") // make sure we've a new map with changed name assert.Equal(t, SyncStatusCodeSynced, resourceStatus.Status) + } else { assert.Equal(t, SyncStatusCodeOutOfSync, resourceStatus.Status) } @@ -192,7 +191,7 @@ func TestKustomizeReplicas2AppSource(t *testing.T) { "", "kubectl", "-n="+fixture.DeploymentNamespace(), "get", kind, name, "-ojsonpath={.spec.replicas}") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, strconv.Itoa(deploymentReplicas), replicas, "wrong value of replicas %s %s", kind, name) } } @@ -257,7 +256,7 @@ func TestKustomizeUnsetOverride(t *testing.T) { assert.Contains(t, app.Spec.GetSource().Kustomize.Images, KustomizeImage("alpine:bar")) }). When(). - // AppUnSet("--kustomize-image=alpine"). + //AppUnSet("--kustomize-image=alpine"). AppUnSet("--kustomize-image", "alpine", "--kustomize-image", "alpine"). Then(). And(func(app *Application) { @@ -288,88 +287,3 @@ func TestKustomizeUnsetOverrideDeployment(t *testing.T) { assert.Nil(t, app.Spec.Source.Kustomize) }) } - -// make sure kube-version gets passed down to resources -func TestKustomizeKubeVersion(t *testing.T) { - Given(t). - Path("kustomize-kube-version"). - And(func() { - errors.FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", - "-n", fixture.TestNamespace(), - "-p", `{ "data": { "kustomize.buildOptions": "--enable-helm" } }`)) - }). - When(). - CreateApp(). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - And(func(app *Application) { - kubeVersion := FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", - "-o", "jsonpath={.data.kubeVersion}")).(string) - // Capabilities.KubeVersion defaults to 1.9.0, we assume here you are running a later version - assert.LessOrEqual(t, GetVersions().ServerVersion.Format("v%s.%s.0"), kubeVersion) - }). - When(). - // Make sure override works. - AppSet("--kustomize-kube-version", "999.999.999"). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - And(func(app *Application) { - assert.Equal(t, "v999.999.999", FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", - "-o", "jsonpath={.data.kubeVersion}")).(string)) - }) -} - -// make sure api versions gets passed down to resources -func TestKustomizeApiVersions(t *testing.T) { - Given(t). - Path("kustomize-api-versions"). - And(func() { - errors.FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", - "-n", fixture.TestNamespace(), - "-p", `{ "data": { "kustomize.buildOptions": "--enable-helm" } }`)) - }). - When(). - CreateApp(). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - And(func(app *Application) { - apiVersions := FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", - "-o", "jsonpath={.data.apiVersions}")).(string) - // The v1 API shouldn't be going anywhere. - assert.Contains(t, apiVersions, "v1") - }). - When(). - // Make sure override works. - AppSet("--kustomize-api-versions", "v1/MyTestResource"). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - And(func(app *Application) { - apiVersions := FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", - "-o", "jsonpath={.data.apiVersions}")).(string) - assert.Contains(t, apiVersions, "v1/MyTestResource") - }) -} - -func TestKustomizeNamespaceOverride(t *testing.T) { - Given(t). - Path("kustomize-kube-version"). - And(func() { - errors.FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", - "-n", fixture.TestNamespace(), - "-p", `{ "data": { "kustomize.buildOptions": "--enable-helm" } }`)) - }). - When(). - CreateApp(). - Sync(). - Then(). - Expect(SyncStatusIs(SyncStatusCodeSynced)). - When(). - AppSet("--kustomize-namespace", "does-not-exist"). - Then(). - // The app should go out of sync, because the resource's target namespace changed. - Expect(SyncStatusIs(SyncStatusCodeOutOfSync)) -} diff --git a/test/e2e/matrix_e2e_test.go b/test/e2e/matrix_e2e_test.go index e6f8781fcfcea..9fc023ecaaf69 100644 --- a/test/e2e/matrix_e2e_test.go +++ b/test/e2e/matrix_e2e_test.go @@ -58,10 +58,9 @@ func TestListMatrixGenerator(t *testing.T) { Given(t). // Create a ClusterGenerator-based ApplicationSet When(). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "matrix-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "matrix-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{values.name}}-{{path.basename}}"}, @@ -185,10 +184,9 @@ func TestClusterMatrixGenerator(t *testing.T) { When(). CreateClusterSecret("my-secret", "cluster1", "https://kubernetes.default.svc"). CreateClusterSecret("my-secret2", "cluster2", "https://kubernetes.default.svc"). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "matrix-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "matrix-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-{{path.basename}}"}, @@ -309,10 +307,9 @@ func TestMatrixTerminalMatrixGeneratorSelector(t *testing.T) { Given(t). // Create ApplicationSet with LabelSelector on an ApplicationSetTerminalGenerator When(). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "matrix-generator-nested-matrix", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "matrix-generator-nested-matrix", + }, Spec: v1alpha1.ApplicationSetSpec{ ApplyNestedSelectors: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -457,10 +454,9 @@ func TestMatrixTerminalMergeGeneratorSelector(t *testing.T) { Given(t). // Create ApplicationSet with LabelSelector on an ApplicationSetTerminalGenerator When(). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "matrix-generator-nested-merge", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "matrix-generator-nested-merge", + }, Spec: v1alpha1.ApplicationSetSpec{ ApplyNestedSelectors: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -529,6 +525,7 @@ func TestMatrixTerminalMergeGeneratorSelector(t *testing.T) { // 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{ diff --git a/test/e2e/merge_e2e_test.go b/test/e2e/merge_e2e_test.go index 970996aee9819..9ad148b65b985 100644 --- a/test/e2e/merge_e2e_test.go +++ b/test/e2e/merge_e2e_test.go @@ -54,10 +54,9 @@ func TestListMergeGenerator(t *testing.T) { Given(t). // Create a ClusterGenerator-based ApplicationSet When(). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "merge-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "merge-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{path.basename}}-{{name-suffix}}"}, @@ -182,10 +181,9 @@ func TestClusterMergeGenerator(t *testing.T) { When(). CreateClusterSecret("my-secret", "cluster1", "https://kubernetes.default.svc"). CreateClusterSecret("my-secret2", "cluster2", "https://kubernetes.default.svc"). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "merge-generator", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "merge-generator", + }, Spec: v1alpha1.ApplicationSetSpec{ Template: v1alpha1.ApplicationSetTemplate{ ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-{{path.basename}}-{{values.name-suffix}}"}, @@ -320,10 +318,9 @@ func TestMergeTerminalMergeGeneratorSelector(t *testing.T) { Given(t). // Create ApplicationSet with LabelSelector on an ApplicationSetTerminalGenerator When(). - Create(v1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "merge-generator-nested-merge", - }, + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "merge-generator-nested-merge", + }, Spec: v1alpha1.ApplicationSetSpec{ ApplyNestedSelectors: true, Template: v1alpha1.ApplicationSetTemplate{ @@ -393,6 +390,7 @@ func TestMergeTerminalMergeGeneratorSelector(t *testing.T) { // 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{ @@ -435,6 +433,7 @@ func TestMergeTerminalMergeGeneratorSelector(t *testing.T) { } func toAPIExtensionsJSON(t *testing.T, g interface{}) *apiextensionsv1.JSON { + resVal, err := json.Marshal(g) if err != nil { t.Error("unable to unmarshal json", g) diff --git a/test/e2e/multiarch-container/Dockerfile b/test/e2e/multiarch-container/Dockerfile index ad49d66dc585c..d4d49adc0d746 100644 --- a/test/e2e/multiarch-container/Dockerfile +++ b/test/e2e/multiarch-container/Dockerfile @@ -1,2 +1,2 @@ -FROM docker.io/library/busybox@sha256:c230832bd3b0be59a6c47ed64294f9ce71e91b327957920b6929a0caa8353140 +FROM docker.io/library/busybox@sha256:2376a0c12759aa1214ba83e771ff252c7b1663216b192fbe5e0fb364e952f85c 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 e4dd855a107e3..eebe4d8991ae5 100644 --- a/test/e2e/notification_test.go +++ b/test/e2e/notification_test.go @@ -3,12 +3,10 @@ package e2e import ( "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "k8s.io/utils/ptr" - "github.com/argoproj/argo-cd/v2/pkg/apiclient/notification" notifFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/notification" + "github.com/stretchr/testify/assert" + "k8s.io/utils/pointer" ) func TestNotificationsListServices(t *testing.T) { @@ -16,8 +14,8 @@ func TestNotificationsListServices(t *testing.T) { ctx.When(). SetParamInNotificationConfigMap("service.webhook.test", "url: https://test.example.com"). Then().Services(func(services *notification.ServiceList, err error) { - require.NoError(t, err) - assert.Equal(t, []*notification.Service{{Name: ptr.To("test")}}, services.Items) + assert.Nil(t, err) + assert.Equal(t, []*notification.Service{{Name: pointer.String("test")}}, services.Items) }) } @@ -26,8 +24,8 @@ func TestNotificationsListTemplates(t *testing.T) { ctx.When(). 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) { - require.NoError(t, err) - assert.Equal(t, []*notification.Template{{Name: ptr.To("app-created")}}, templates.Items) + assert.Nil(t, err) + assert.Equal(t, []*notification.Template{{Name: pointer.String("app-created")}}, templates.Items) }) } @@ -36,7 +34,7 @@ func TestNotificationsListTriggers(t *testing.T) { ctx.When(). 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) { - require.NoError(t, err) - assert.Equal(t, []*notification.Trigger{{Name: ptr.To("on-created")}}, triggers.Items) + assert.Nil(t, err) + 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 f00c4e3eeba62..fb8886a21dbd4 100644 --- a/test/e2e/project_management_test.go +++ b/test/e2e/project_management_test.go @@ -10,11 +10,10 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" - "k8s.io/utils/ptr" + "k8s.io/utils/pointer" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/test/e2e/fixture" @@ -29,7 +28,7 @@ func assertProjHasEvent(t *testing.T, a *v1alpha1.AppProject, message string, re "involvedObject.namespace": fixture.TestNamespace(), }).String(), }) - require.NoError(t, err) + assert.NoError(t, err) for i := range list.Items { event := list.Items[i] @@ -50,12 +49,12 @@ func TestProjectCreation(t *testing.T) { "-d", "https://192.168.99.100:8443,service", "-s", "https://github.com/argoproj/argo-cd.git", "--orphaned-resources") - require.NoError(t, err) + assert.Nil(t, err) proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) - assert.Len(t, proj.Spec.Destinations, 2) + assert.Equal(t, 2, len(proj.Spec.Destinations)) assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.Destinations[0].Server) assert.Equal(t, "default", proj.Spec.Destinations[0].Namespace) @@ -63,7 +62,7 @@ func TestProjectCreation(t *testing.T) { assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.Destinations[1].Server) assert.Equal(t, "service", proj.Spec.Destinations[1].Namespace) - assert.Len(t, proj.Spec.SourceRepos, 1) + assert.Equal(t, 1, len(proj.Spec.SourceRepos)) assert.Equal(t, "https://github.com/argoproj/argo-cd.git", proj.Spec.SourceRepos[0]) assert.NotNil(t, proj.Spec.OrphanedResources) @@ -77,19 +76,19 @@ func TestProjectCreation(t *testing.T) { proj.ResourceVersion = "" data, err := json.Marshal(proj) stdinString := string(data) - require.NoError(t, err) + assert.NoError(t, err) // fail without upsert flag - _, err = fixture.RunCliWithStdin(stdinString, false, "proj", "create", + _, err = fixture.RunCliWithStdin(stdinString, "proj", "create", "-f", "-") - require.Error(t, err) + assert.Error(t, err) // succeed with the upsert flag - _, err = fixture.RunCliWithStdin(stdinString, false, "proj", "create", + _, err = fixture.RunCliWithStdin(stdinString, "proj", "create", "-f", "-", "--upsert") - require.NoError(t, err) + assert.NoError(t, err) proj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, newDescription, proj.Spec.Description) } @@ -99,10 +98,10 @@ func TestProjectDeletion(t *testing.T) { projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( context.Background(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) - require.NoError(t, err) + assert.NoError(t, err) _, err = fixture.RunCli("proj", "delete", projectName) - require.NoError(t, err) + assert.NoError(t, err) _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.True(t, errors.IsNotFound(err)) @@ -115,19 +114,19 @@ func TestSetProject(t *testing.T) { projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( context.Background(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) - require.NoError(t, err) + assert.NoError(t, err) _, err = fixture.RunCli("proj", "set", projectName, "--description", "updated description", "-d", "https://192.168.99.100:8443,default", "-d", "https://192.168.99.100:8443,service", "--orphaned-resources-warn=false") - require.NoError(t, err) + assert.NoError(t, err) proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) - assert.Len(t, proj.Spec.Destinations, 2) + assert.Equal(t, 2, len(proj.Spec.Destinations)) assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.Destinations[0].Server) assert.Equal(t, "default", proj.Spec.Destinations[0].Namespace) @@ -155,6 +154,7 @@ func TestAddProjectDestination(t *testing.T) { "https://192.168.99.100:8443", "test1", ) + if err != nil { t.Fatalf("Unable to add project destination %v", err) } @@ -163,27 +163,27 @@ func TestAddProjectDestination(t *testing.T) { "https://192.168.99.100:8443", "test1", ) - require.Error(t, err) - assert.Contains(t, err.Error(), "already defined") + assert.Error(t, err) + assert.True(t, strings.Contains(err.Error(), "already defined")) _, err = fixture.RunCli("proj", "add-destination", projectName, "!*", "test1", ) - require.Error(t, err) - assert.Contains(t, err.Error(), "server has an invalid format, '!*'") + assert.Error(t, err) + assert.True(t, strings.Contains(err.Error(), "server has an invalid format, '!*'")) _, err = fixture.RunCli("proj", "add-destination", projectName, "https://192.168.99.100:8443", "!*", ) - require.Error(t, err) - assert.Contains(t, err.Error(), "namespace has an invalid format, '!*'") + assert.Error(t, err) + assert.True(t, strings.Contains(err.Error(), "namespace has an invalid format, '!*'")) proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) - assert.Len(t, proj.Spec.Destinations, 1) + assert.Equal(t, 1, len(proj.Spec.Destinations)) assert.Equal(t, "https://192.168.99.100:8443", proj.Spec.Destinations[0].Server) assert.Equal(t, "test1", proj.Spec.Destinations[0].Namespace) @@ -205,14 +205,15 @@ func TestAddProjectDestinationWithName(t *testing.T) { "test1", "--name", ) + if err != nil { t.Fatalf("Unable to add project destination %v", err) } proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) - assert.Len(t, proj.Spec.Destinations, 1) + assert.Equal(t, 1, len(proj.Spec.Destinations)) assert.Equal(t, "", proj.Spec.Destinations[0].Server) assert.Equal(t, "in-cluster", proj.Spec.Destinations[0].Name) @@ -233,6 +234,7 @@ func TestRemoveProjectDestination(t *testing.T) { }}, }, }, metav1.CreateOptions{}) + if err != nil { t.Fatalf("Unable to create project %v", err) } @@ -241,6 +243,7 @@ func TestRemoveProjectDestination(t *testing.T) { "https://192.168.99.100:8443", "test", ) + if err != nil { t.Fatalf("Unable to remove project destination %v", err) } @@ -249,7 +252,7 @@ func TestRemoveProjectDestination(t *testing.T) { "https://192.168.99.100:8443", "test1", ) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "does not exist") proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) @@ -257,7 +260,7 @@ func TestRemoveProjectDestination(t *testing.T) { t.Fatalf("Unable to get project %v", err) } assert.Equal(t, projectName, proj.Name) - assert.Empty(t, proj.Spec.Destinations) + assert.Equal(t, 0, len(proj.Spec.Destinations)) assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) } @@ -272,17 +275,18 @@ func TestAddProjectSource(t *testing.T) { } _, err = fixture.RunCli("proj", "add-source", projectName, "https://github.com/argoproj/argo-cd.git") + if err != nil { t.Fatalf("Unable to add project source %v", err) } _, err = fixture.RunCli("proj", "add-source", projectName, "https://github.com/argoproj/argo-cd.git") - require.NoError(t, err) + assert.Nil(t, err) proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) - assert.Len(t, proj.Spec.SourceRepos, 1) + assert.Equal(t, 1, len(proj.Spec.SourceRepos)) assert.Equal(t, "https://github.com/argoproj/argo-cd.git", proj.Spec.SourceRepos[0]) } @@ -298,19 +302,19 @@ func TestRemoveProjectSource(t *testing.T) { }, }, metav1.CreateOptions{}) - require.NoError(t, err) + assert.NoError(t, err) _, err = fixture.RunCli("proj", "remove-source", projectName, "https://github.com/argoproj/argo-cd.git") - require.NoError(t, err) + assert.NoError(t, err) _, err = fixture.RunCli("proj", "remove-source", projectName, "https://github.com/argoproj/argo-cd.git") - require.NoError(t, err) + assert.NoError(t, err) proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) - assert.Empty(t, proj.Spec.SourceRepos) + assert.Equal(t, 0, len(proj.Spec.SourceRepos)) assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) } @@ -320,7 +324,6 @@ func TestUseJWTToken(t *testing.T) { projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) appName := "app-" + strconv.FormatInt(time.Now().Unix(), 10) roleName := "roleTest" - roleName2 := "roleTest2" testApp := &v1alpha1.Application{ ObjectMeta: metav1.ObjectMeta{ Name: appName, @@ -347,50 +350,42 @@ func TestUseJWTToken(t *testing.T) { SourceRepos: []string{"*"}, }, }, metav1.CreateOptions{}) - require.NoError(t, err) + assert.Nil(t, err) _, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.TestNamespace()).Create(context.Background(), testApp, metav1.CreateOptions{}) - require.NoError(t, err) + assert.NoError(t, err) _, err = fixture.RunCli("proj", "role", "create", projectName, roleName) - require.NoError(t, err) + assert.NoError(t, err) roleGetResult, err := fixture.RunCli("proj", "role", "get", projectName, roleName) - require.NoError(t, err) + assert.NoError(t, err) assert.True(t, strings.HasSuffix(roleGetResult, "ID ISSUED-AT EXPIRES-AT")) _, err = fixture.RunCli("proj", "role", "create-token", projectName, roleName) - require.NoError(t, err) - - // Create second role with kubectl, to test that it will not affect 1st role - _, err = fixture.Run("", "kubectl", "patch", "appproject", projectName, "--type", "merge", - "-n", fixture.TestNamespace(), - "-p", fmt.Sprintf(`{"spec":{"roles":[{"name":"%s"},{"name":"%s"}]}}`, roleName, roleName2)) - require.NoError(t, err) - - _, err = fixture.RunCli("proj", "role", "create-token", projectName, roleName2) - require.NoError(t, err) + assert.NoError(t, err) for _, action := range []string{"get", "update", "sync", "create", "override", "*"} { _, err = fixture.RunCli("proj", "role", "add-policy", projectName, roleName, "-a", action, "-o", "*", "-p", "allow") - require.NoError(t, err) + assert.NoError(t, err) } newProj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + 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) roleGetResult, err = fixture.RunCli("proj", "role", "get", projectName, roleName) - require.NoError(t, err) - assert.Contains(t, roleGetResult, strconv.FormatInt(newProj.Status.JWTTokensByRole[roleName].Items[0].IssuedAt, 10)) + assert.NoError(t, err) + assert.True(t, strings.Contains(roleGetResult, strconv.FormatInt(newProj.Status.JWTTokensByRole[roleName].Items[0].IssuedAt, 10))) _, err = fixture.RunCli("proj", "role", "delete-token", projectName, roleName, strconv.FormatInt(newProj.Status.JWTTokensByRole[roleName].Items[0].IssuedAt, 10)) - require.NoError(t, err) + assert.NoError(t, err) newProj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Nil(t, newProj.Status.JWTTokensByRole[roleName].Items) assert.Nil(t, newProj.Spec.Roles[0].JWTTokens) + } func TestAddOrphanedIgnore(t *testing.T) { @@ -409,6 +404,7 @@ func TestAddOrphanedIgnore(t *testing.T) { "--name", "name", ) + if err != nil { t.Fatalf("Unable to add resource to orphaned ignore %v", err) } @@ -419,13 +415,13 @@ func TestAddOrphanedIgnore(t *testing.T) { "--name", "name", ) - require.Error(t, err) - assert.Contains(t, err.Error(), "already defined") + assert.Error(t, err) + assert.True(t, strings.Contains(err.Error(), "already defined")) proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) - assert.Len(t, proj.Spec.OrphanedResources.Ignore, 1) + assert.Equal(t, 1, len(proj.Spec.OrphanedResources.Ignore)) assert.Equal(t, "group", proj.Spec.OrphanedResources.Ignore[0].Group) assert.Equal(t, "kind", proj.Spec.OrphanedResources.Ignore[0].Kind) @@ -441,11 +437,12 @@ func TestRemoveOrphanedIgnore(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: projectName}, Spec: v1alpha1.AppProjectSpec{ OrphanedResources: &v1alpha1.OrphanedResourcesMonitorSettings{ - Warn: ptr.To(true), + Warn: pointer.Bool(true), Ignore: []v1alpha1.OrphanedResourceKey{{Group: "group", Kind: "kind", Name: "name"}}, }, }, }, metav1.CreateOptions{}) + if err != nil { t.Fatalf("Unable to create project %v", err) } @@ -456,6 +453,7 @@ func TestRemoveOrphanedIgnore(t *testing.T) { "--name", "name", ) + if err != nil { t.Fatalf("Unable to remove resource from orphaned ignore list %v", err) } @@ -466,7 +464,7 @@ func TestRemoveOrphanedIgnore(t *testing.T) { "--name", "name", ) - require.Error(t, err) + assert.Error(t, err) assert.Contains(t, err.Error(), "does not exist") proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) @@ -474,7 +472,7 @@ func TestRemoveOrphanedIgnore(t *testing.T) { t.Fatalf("Unable to get project %v", err) } assert.Equal(t, projectName, proj.Name) - assert.Empty(t, proj.Spec.OrphanedResources.Ignore) + assert.Equal(t, 0, len(proj.Spec.OrphanedResources.Ignore)) assertProjHasEvent(t, proj, "update", argo.EventReasonResourceUpdated) } @@ -547,7 +545,7 @@ func createAndConfigGlobalProject() error { func TestGetVirtualProjectNoMatch(t *testing.T) { fixture.EnsureCleanState(t) err := createAndConfigGlobalProject() - require.NoError(t, err) + assert.NoError(t, err) // Create project which does not match global project settings projectName := "proj-" + fixture.Name() @@ -556,15 +554,15 @@ func TestGetVirtualProjectNoMatch(t *testing.T) { "-d", fmt.Sprintf("%s,*", v1alpha1.KubernetesInternalAPIServerAddr), "-s", "*", "--orphaned-resources") - require.NoError(t, err) + assert.NoError(t, err) proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) // 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()) - require.NoError(t, err) + assert.NoError(t, err) // Waiting for the app to be successfully created. // Else the sync would fail to retrieve the app resources. @@ -572,17 +570,18 @@ func TestGetVirtualProjectNoMatch(t *testing.T) { // 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)) - require.NoError(t, err) + assert.NoError(t, err) // 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)) - require.NoError(t, err) + assert.NoError(t, err) + } func TestGetVirtualProjectMatch(t *testing.T) { fixture.EnsureCleanState(t) err := createAndConfigGlobalProject() - require.NoError(t, err) + assert.NoError(t, err) // Create project which matches global project settings projectName := "proj-" + fixture.Name() @@ -591,20 +590,20 @@ func TestGetVirtualProjectMatch(t *testing.T) { "-d", fmt.Sprintf("%s,*", v1alpha1.KubernetesInternalAPIServerAddr), "-s", "*", "--orphaned-resources") - require.NoError(t, err) + assert.NoError(t, err) proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) - require.NoError(t, err) + assert.NoError(t, err) // 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.TestNamespace()).Update(context.Background(), proj, metav1.UpdateOptions{}) - require.NoError(t, err) + assert.NoError(t, err) // 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()) - require.NoError(t, err) + assert.NoError(t, err) // Waiting for the app to be successfully created. // Else the sync would fail to retrieve the app resources. @@ -612,10 +611,11 @@ func TestGetVirtualProjectMatch(t *testing.T) { // 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)) - require.Error(t, err) + 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 _, 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_management_test.go b/test/e2e/repo_management_test.go index c49eb2eeba794..70b14d5682299 100644 --- a/test/e2e/repo_management_test.go +++ b/test/e2e/repo_management_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -21,15 +20,15 @@ func TestAddRemovePublicRepo(t *testing.T) { app.Given(t).And(func() { repoUrl := fixture.RepoURL(fixture.RepoURLTypeFile) _, err := fixture.RunCli("repo", "add", repoUrl) - require.NoError(t, err) + assert.NoError(t, err) conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient() - require.NoError(t, err) + assert.NoError(t, err) defer argoio.Close(conn) repo, err := repoClient.ListRepositories(context.Background(), &repositorypkg.RepoQuery{}) - require.NoError(t, err) + assert.Nil(t, err) exists := false for i := range repo.Items { if repo.Items[i].Repo == repoUrl { @@ -40,10 +39,10 @@ func TestAddRemovePublicRepo(t *testing.T) { assert.True(t, exists) _, err = fixture.RunCli("repo", "rm", repoUrl) - require.NoError(t, err) + assert.NoError(t, err) repo, err = repoClient.ListRepositories(context.Background(), &repositorypkg.RepoQuery{}) - require.NoError(t, err) + assert.NoError(t, err) exists = false for i := range repo.Items { if repo.Items[i].Repo == repoUrl { @@ -67,7 +66,7 @@ func TestGetRepoWithInheritedCreds(t *testing.T) { // Then, we remove username/password so that the repo inherits the credentials from our repocreds conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient() - require.NoError(t, err) + assert.NoError(t, err) defer argoio.Close(conn) _, err = repoClient.UpdateRepository(context.Background(), &repositorypkg.RepoUpdateRequest{ @@ -75,15 +74,15 @@ func TestGetRepoWithInheritedCreds(t *testing.T) { Repo: repoUrl, }, }) - require.NoError(t, err) + assert.NoError(t, err) // CLI output should indicate that repo has inherited credentials out, err := fixture.RunCli("repo", "get", repoUrl) - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "inherited") _, err = fixture.RunCli("repo", "rm", repoUrl) - require.NoError(t, err) + assert.NoError(t, err) }) } @@ -92,13 +91,13 @@ func TestUpsertExistingRepo(t *testing.T) { fixture.SetRepos(settings.RepositoryCredentials{URL: fixture.RepoURL(fixture.RepoURLTypeFile)}) repoUrl := fixture.RepoURL(fixture.RepoURLTypeFile) _, err := fixture.RunCli("repo", "add", repoUrl) - require.NoError(t, err) + assert.NoError(t, err) _, err = fixture.RunCli("repo", "add", repoUrl, "--username", fixture.GitUsername, "--password", fixture.GitPassword) - require.Error(t, err) + assert.Error(t, err) _, err = fixture.RunCli("repo", "add", repoUrl, "--upsert", "--username", fixture.GitUsername, "--password", fixture.GitPassword) - require.NoError(t, err) + assert.NoError(t, err) }) } @@ -111,15 +110,15 @@ func TestAddRemoveHelmRepo(t *testing.T) { "--password", fixture.GitPassword, "--tls-client-cert-path", repos.CertPath, "--tls-client-cert-key-path", repos.CertKeyPath) - require.NoError(t, err) + assert.NoError(t, err) conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient() - require.NoError(t, err) + assert.NoError(t, err) defer argoio.Close(conn) repo, err := repoClient.ListRepositories(context.Background(), &repositorypkg.RepoQuery{}) - require.NoError(t, err) + assert.NoError(t, err) exists := false for i := range repo.Items { if repo.Items[i].Repo == fixture.RepoURL(fixture.RepoURLTypeHelm) { @@ -130,10 +129,10 @@ func TestAddRemoveHelmRepo(t *testing.T) { assert.True(t, exists) _, err = fixture.RunCli("repo", "rm", fixture.RepoURL(fixture.RepoURLTypeHelm)) - require.NoError(t, err) + assert.NoError(t, err) repo, err = repoClient.ListRepositories(context.Background(), &repositorypkg.RepoQuery{}) - require.NoError(t, err) + assert.NoError(t, err) exists = false for i := range repo.Items { if repo.Items[i].Repo == fixture.RepoURL(fixture.RepoURLTypeHelm) { @@ -143,6 +142,7 @@ func TestAddRemoveHelmRepo(t *testing.T) { } assert.False(t, exists) }) + } func TestAddHelmRepoInsecureSkipVerify(t *testing.T) { @@ -156,16 +156,22 @@ func TestAddHelmRepoInsecureSkipVerify(t *testing.T) { "--tls-client-cert-path", repos.CertPath, "--tls-client-cert-key-path", repos.CertKeyPath) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient() - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } defer argoio.Close(conn) repo, err := repoClient.ListRepositories(context.Background(), &repositorypkg.RepoQuery{}) - require.NoError(t, err) + if !assert.NoError(t, err) { + return + } exists := false for i := range repo.Items { @@ -176,4 +182,5 @@ func TestAddHelmRepoInsecureSkipVerify(t *testing.T) { } assert.True(t, exists) }) + } diff --git a/test/e2e/scoped_repository_test.go b/test/e2e/scoped_repository_test.go index d1da08b0a434d..275deb0c20f63 100644 --- a/test/e2e/scoped_repository_test.go +++ b/test/e2e/scoped_repository_test.go @@ -1,6 +1,7 @@ package e2e import ( + "strings" "testing" "github.com/argoproj/argo-cd/v2/test/e2e/fixture" @@ -31,10 +32,10 @@ func TestCreateRepositoryWithProject(t *testing.T) { Then(). And(func(r *Repository, err error) { assert.Equal(t, r.Repo, path) - assert.Equal(t, "argo-project", r.Project) + assert.Equal(t, r.Project, "argo-project") prjConsequence.And(func(projectResponse *project.DetailedProjectsResponse, err error) { - assert.Len(t, projectResponse.Repositories, 1) + assert.Equal(t, len(projectResponse.Repositories), 1) assert.Equal(t, projectResponse.Repositories[0].Repo, path) }) }) @@ -56,7 +57,7 @@ func TestCreateRepositoryNonAdminUserPermissionDenied(t *testing.T) { Create(). Then(). AndCLIOutput(func(output string, err error) { - assert.Contains(t, err.Error(), "PermissionDenied desc = permission denied: repositories, create") + assert.True(t, strings.Contains(err.Error(), "PermissionDenied desc = permission denied: repositories, create")) }) } @@ -83,7 +84,7 @@ func TestCreateRepositoryNonAdminUserWithWrongProject(t *testing.T) { Create(). Then(). AndCLIOutput(func(output string, err error) { - assert.Contains(t, err.Error(), "PermissionDenied desc = permission denied: repositories, create") + assert.True(t, strings.Contains(err.Error(), "PermissionDenied desc = permission denied: repositories, create")) }) } @@ -120,13 +121,13 @@ func TestDeleteRepositoryRbacAllowed(t *testing.T) { Then(). And(func(r *Repository, err error) { assert.Equal(t, r.Repo, path) - assert.Equal(t, "argo-project", r.Project) + assert.Equal(t, r.Project, "argo-project") }). When(). Delete(). Then(). AndCLIOutput(func(output string, err error) { - assert.Contains(t, output, "Repository 'https://github.com/argoproj/argo-cd.git' removed") + assert.True(t, strings.Contains(output, "Repository 'https://github.com/argoproj/argo-cd.git' removed")) }) } @@ -163,14 +164,14 @@ func TestDeleteRepositoryRbacDenied(t *testing.T) { Then(). And(func(r *Repository, err error) { assert.Equal(t, r.Repo, path) - assert.Equal(t, "argo-project", r.Project) + assert.Equal(t, r.Project, "argo-project") }). When(). IgnoreErrors(). Delete(). Then(). AndCLIOutput(func(output string, err error) { - assert.Contains(t, err.Error(), "PermissionDenied desc = permission denied: repositories, delete") + assert.True(t, strings.Contains(err.Error(), "PermissionDenied desc = permission denied: repositories, delete")) }) } @@ -189,8 +190,9 @@ func TestDeleteRepository(t *testing.T) { Delete(). Then(). And(func(r *Repository, err error) { - assert.Equal(t, "repo not found", err.Error()) + assert.Equal(t, err.Error(), "repo not found") }) + } func TestListRepoCLIOutput(t *testing.T) { diff --git a/test/e2e/sync_waves_test.go b/test/e2e/sync_waves_test.go index 8d0ee14e487d1..ac5db15eee57d 100644 --- a/test/e2e/sync_waves_test.go +++ b/test/e2e/sync_waves_test.go @@ -9,8 +9,6 @@ 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) { @@ -102,46 +100,3 @@ 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/sync_with_impersonate_test.go b/test/e2e/sync_with_impersonate_test.go deleted file mode 100644 index 7bb57af1156d3..0000000000000 --- a/test/e2e/sync_with_impersonate_test.go +++ /dev/null @@ -1,309 +0,0 @@ -package e2e - -import ( - "context" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - v1 "k8s.io/api/core/v1" - rbac "k8s.io/api/rbac/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - - "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" -) - -func TestSyncWithImpersonateDisable(t *testing.T) { - Given(t). - Path("guestbook"). - When(). - SetParamInSettingConfigMap("application.sync.impersonation.enabled", "false"). - CreateFromFile(func(app *v1alpha1.Application) { - app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}} - }). - Then(). - Expect(SyncStatusIs(v1alpha1.SyncStatusCodeSynced)) -} - -func TestSyncWithImpersonateDefaultNamespaceServiceAccountNoRBAC(t *testing.T) { - Given(t). - Path("guestbook"). - When(). - SetParamInSettingConfigMap("application.sync.impersonation.enabled", "true"). - CreateFromFile(func(app *v1alpha1.Application) { - app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}} - }). - Then(). - Expect(SyncStatusIs(v1alpha1.SyncStatusCodeOutOfSync)) -} - -func TestSyncWithImpersonateDefaultNamespaceServiceAccountWithRBAC(t *testing.T) { - roleName := "default-sa-role" - Given(t). - Path("guestbook"). - When(). - SetParamInSettingConfigMap("application.sync.impersonation.enabled", "true"). - CreateFromFile(func(app *v1alpha1.Application) { - app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}} - }). - And(func() { - err := createTestRole(roleName, fixture.DeploymentNamespace(), []rbac.PolicyRule{ - { - APIGroups: []string{"apps", ""}, - Resources: []string{"deployments"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"services"}, - Verbs: []string{"*"}, - }, - }) - require.NoError(t, err) - err = createTestRoleBinding(roleName, "default", fixture.DeploymentNamespace()) - require.NoError(t, err) - }). - Then(). - Expect(SyncStatusIs(v1alpha1.SyncStatusCodeOutOfSync)) -} - -func TestSyncWithImpersonateWithSyncServiceAccount(t *testing.T) { - projectName := "sync-test-project" - serviceAccountName := "test-account" - roleName := "test-account-sa-role" - Given(t). - SetTrackingMethod("annotation"). - Path("guestbook"). - When(). - SetParamInSettingConfigMap("application.sync.impersonation.enabled", "true"). - And(func() { - destinationServiceAccounts := []v1alpha1.ApplicationDestinationServiceAccount{ - { - Server: "*", - Namespace: fixture.DeploymentNamespace(), - DefaultServiceAccount: serviceAccountName, - }, - { - Server: "*", - Namespace: fixture.DeploymentNamespace(), - DefaultServiceAccount: "false-serviceAccount", - }, - } - err := createTestServiceAccount(serviceAccountName, fixture.DeploymentNamespace()) - require.NoError(t, err) - err = createTestAppProject(projectName, fixture.TestNamespace(), destinationServiceAccounts) - require.NoError(t, err) - err = createTestRole(roleName, fixture.DeploymentNamespace(), []rbac.PolicyRule{ - { - APIGroups: []string{"apps", ""}, - Resources: []string{"deployments"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"services"}, - Verbs: []string{"*"}, - }, - }) - require.NoError(t, err) - - err = createTestRoleBinding(roleName, serviceAccountName, fixture.DeploymentNamespace()) - require.NoError(t, err) - }). - CreateFromFile(func(app *v1alpha1.Application) { - app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}} - app.Spec.Project = projectName - }). - Then(). - Expect(SyncStatusIs(v1alpha1.SyncStatusCodeSynced)) -} - -func TestSyncWithImpersonateWithFalseServiceAccount(t *testing.T) { - projectName := "false-test-project" - serviceAccountName := "test-account" - roleName := "test-account-sa-role" - Given(t). - SetTrackingMethod("annotation"). - Path("guestbook"). - When(). - SetParamInSettingConfigMap("application.sync.impersonation.enabled", "true"). - And(func() { - destinationServiceAccounts := []v1alpha1.ApplicationDestinationServiceAccount{ - { - Server: "*", - Namespace: fixture.DeploymentNamespace(), - DefaultServiceAccount: "false-serviceAccount", - }, - { - Server: "*", - Namespace: fixture.DeploymentNamespace(), - DefaultServiceAccount: serviceAccountName, - }, - } - err := createTestServiceAccount(serviceAccountName, fixture.DeploymentNamespace()) - require.NoError(t, err) - err = createTestAppProject(projectName, fixture.TestNamespace(), destinationServiceAccounts) - require.NoError(t, err) - err = createTestRole(roleName, fixture.DeploymentNamespace(), []rbac.PolicyRule{ - { - APIGroups: []string{"apps", ""}, - Resources: []string{"deployments"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"services"}, - Verbs: []string{"*"}, - }, - }) - require.NoError(t, err) - - err = createTestRoleBinding(roleName, serviceAccountName, fixture.DeploymentNamespace()) - require.NoError(t, err) - }). - CreateFromFile(func(app *v1alpha1.Application) { - app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}} - app.Spec.Project = projectName - }). - Then(). - Expect(SyncStatusIs(v1alpha1.SyncStatusCodeOutOfSync)) -} - -func TestSyncWithNegationApplicationDestinationNamespace(t *testing.T) { - projectName := "nagation-test-project" - serviceAccountName := "test-account" - roleName := "test-account-sa-role" - Given(t). - SetTrackingMethod("annotation"). - Path("guestbook"). - When(). - SetParamInSettingConfigMap("application.sync.impersonation.enabled", "true"). - And(func() { - destinationServiceAccounts := []v1alpha1.ApplicationDestinationServiceAccount{ - { - Server: "*", - Namespace: fixture.DeploymentNamespace(), - DefaultServiceAccount: serviceAccountName, - }, - } - err := createTestServiceAccount(serviceAccountName, fixture.DeploymentNamespace()) - require.NoError(t, err) - err = createTestAppProject(projectName, fixture.TestNamespace(), destinationServiceAccounts) - require.NoError(t, err) - err = createTestRole(roleName, fixture.DeploymentNamespace(), []rbac.PolicyRule{ - { - APIGroups: []string{"apps", ""}, - Resources: []string{"deployments"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"services"}, - Verbs: []string{"*"}, - }, - }) - require.NoError(t, err) - err = createTestRoleBinding(roleName, serviceAccountName, fixture.DeploymentNamespace()) - require.NoError(t, err) - }). - CreateFromFile(func(app *v1alpha1.Application) { - app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}} - app.Spec.Project = projectName - }). - Then(). - Expect(SyncStatusIs(v1alpha1.SyncStatusCodeSynced)). - When(). - And(func() { - patch := []byte(fmt.Sprintf(`{"spec": {"destinations": [{"namespace": "%s"}]}}`, "!"+fixture.DeploymentNamespace())) - - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Patch(context.Background(), projectName, types.MergePatchType, patch, metav1.PatchOptions{}) - require.NoError(t, err) - }). - Refresh(v1alpha1.RefreshTypeNormal). - Then(). - Expect(SyncStatusIs(v1alpha1.SyncStatusCodeUnknown)) -} - -// createTestAppProject creates a test AppProject resource. -func createTestAppProject(name, namespace string, destinationServiceAccounts []v1alpha1.ApplicationDestinationServiceAccount) error { - appProject := &v1alpha1.AppProject{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: v1alpha1.AppProjectSpec{ - SourceRepos: []string{"*"}, - SourceNamespaces: []string{"*"}, - Destinations: []v1alpha1.ApplicationDestination{ - { - Server: "*", - Namespace: "*", - }, - }, - ClusterResourceWhitelist: []metav1.GroupKind{ - { - Group: "*", - Kind: "*", - }, - }, - DestinationServiceAccounts: destinationServiceAccounts, - }, - } - - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(namespace).Create(context.Background(), appProject, metav1.CreateOptions{}) - return err -} - -// createTestRole creates a test Role resource. -func createTestRole(roleName, namespace string, rules []rbac.PolicyRule) error { - role := &rbac.Role{ - ObjectMeta: metav1.ObjectMeta{ - Name: roleName, - Namespace: namespace, - }, - Rules: rules, - } - - _, err := fixture.KubeClientset.RbacV1().Roles(namespace).Create(context.Background(), role, metav1.CreateOptions{}) - return err -} - -// createTestRoleBinding creates a test RoleBinding resource. -func createTestRoleBinding(roleName, serviceAccountName, namespace string) error { - roleBinding := &rbac.RoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: roleName + "-binding", - }, - Subjects: []rbac.Subject{ - { - Kind: "ServiceAccount", - Name: serviceAccountName, - Namespace: namespace, - }, - }, - RoleRef: rbac.RoleRef{ - Kind: "Role", - Name: roleName, - APIGroup: "rbac.authorization.k8s.io", - }, - } - - _, err := fixture.KubeClientset.RbacV1().RoleBindings(namespace).Create(context.Background(), roleBinding, metav1.CreateOptions{}) - return err -} - -// createTestServiceAccount creates a test ServiceAccount resource. -func createTestServiceAccount(name, namespace string) error { - serviceAccount := &v1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - } - - _, err := fixture.KubeClientset.CoreV1().ServiceAccounts(namespace).Create(context.Background(), serviceAccount, metav1.CreateOptions{}) - return err -} diff --git a/test/e2e/testdata/helm-api-versions/Chart.yaml b/test/e2e/testdata/helm-api-versions/Chart.yaml deleted file mode 100644 index 4859bf0726b5f..0000000000000 --- a/test/e2e/testdata/helm-api-versions/Chart.yaml +++ /dev/null @@ -1,3 +0,0 @@ -version: 1.0.0 -name: helm-api-versions -kubeVersion: ">=1.0.0" \ No newline at end of file diff --git a/test/e2e/testdata/helm-api-versions/templates/config-map.yaml b/test/e2e/testdata/helm-api-versions/templates/config-map.yaml deleted file mode 100644 index 5ec9e07025b61..0000000000000 --- a/test/e2e/testdata/helm-api-versions/templates/config-map.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: my-map -data: - apiVersions: | - {{.Capabilities.APIVersions | toJson}} diff --git a/test/e2e/testdata/helm-namespace/Chart.yaml b/test/e2e/testdata/helm-namespace/Chart.yaml deleted file mode 100644 index a3becea5ac11b..0000000000000 --- a/test/e2e/testdata/helm-namespace/Chart.yaml +++ /dev/null @@ -1,3 +0,0 @@ -apiVersion: v2 -version: 1.0.0 -name: helm \ No newline at end of file diff --git a/test/e2e/testdata/helm-namespace/baz.yaml b/test/e2e/testdata/helm-namespace/baz.yaml deleted file mode 100644 index 26a745ddb6d38..0000000000000 --- a/test/e2e/testdata/helm-namespace/baz.yaml +++ /dev/null @@ -1 +0,0 @@ -a: b diff --git a/test/e2e/testdata/helm-namespace/templates/config-map.yaml b/test/e2e/testdata/helm-namespace/templates/config-map.yaml deleted file mode 100644 index 776432f4d8265..0000000000000 --- a/test/e2e/testdata/helm-namespace/templates/config-map.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: my-map - namespace: {{.Release.Namespace}} -data: - foo: bar \ No newline at end of file diff --git a/test/e2e/testdata/helm-namespace/values.yaml b/test/e2e/testdata/helm-namespace/values.yaml deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/e2e/testdata/kustomize-api-versions/helm-chart/Chart.yaml b/test/e2e/testdata/kustomize-api-versions/helm-chart/Chart.yaml deleted file mode 100644 index 62a5a8dccc5ad..0000000000000 --- a/test/e2e/testdata/kustomize-api-versions/helm-chart/Chart.yaml +++ /dev/null @@ -1,3 +0,0 @@ -version: 1.0.0 -name: helm-kube-version -kubeVersion: ">=1.0.0" \ No newline at end of file diff --git a/test/e2e/testdata/kustomize-api-versions/helm-chart/templates/config-map.yaml b/test/e2e/testdata/kustomize-api-versions/helm-chart/templates/config-map.yaml deleted file mode 100644 index 1237da4ffd0be..0000000000000 --- a/test/e2e/testdata/kustomize-api-versions/helm-chart/templates/config-map.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: my-map -data: - apiVersions: | - {{.Capabilities.APIVersions}} diff --git a/test/e2e/testdata/kustomize-api-versions/helm-chart/values.yaml b/test/e2e/testdata/kustomize-api-versions/helm-chart/values.yaml deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/e2e/testdata/kustomize-api-versions/kustomization.yaml b/test/e2e/testdata/kustomize-api-versions/kustomization.yaml deleted file mode 100644 index ed97d250133a7..0000000000000 --- a/test/e2e/testdata/kustomize-api-versions/kustomization.yaml +++ /dev/null @@ -1,7 +0,0 @@ -helmGlobals: - chartHome: . - -helmCharts: -- releaseName: test - name: helm-chart - version: v1.0.0 diff --git a/test/e2e/testdata/kustomize-kube-version/helm-chart/Chart.yaml b/test/e2e/testdata/kustomize-kube-version/helm-chart/Chart.yaml deleted file mode 100644 index 62a5a8dccc5ad..0000000000000 --- a/test/e2e/testdata/kustomize-kube-version/helm-chart/Chart.yaml +++ /dev/null @@ -1,3 +0,0 @@ -version: 1.0.0 -name: helm-kube-version -kubeVersion: ">=1.0.0" \ No newline at end of file diff --git a/test/e2e/testdata/kustomize-kube-version/helm-chart/templates/config-map.yaml b/test/e2e/testdata/kustomize-kube-version/helm-chart/templates/config-map.yaml deleted file mode 100644 index 34ee852195ea4..0000000000000 --- a/test/e2e/testdata/kustomize-kube-version/helm-chart/templates/config-map.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: my-map -data: - kubeVersion: {{.Capabilities.KubeVersion}} diff --git a/test/e2e/testdata/kustomize-kube-version/helm-chart/values.yaml b/test/e2e/testdata/kustomize-kube-version/helm-chart/values.yaml deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/e2e/testdata/kustomize-kube-version/kustomization.yaml b/test/e2e/testdata/kustomize-kube-version/kustomization.yaml deleted file mode 100644 index ed97d250133a7..0000000000000 --- a/test/e2e/testdata/kustomize-kube-version/kustomization.yaml +++ /dev/null @@ -1,7 +0,0 @@ -helmGlobals: - chartHome: . - -helmCharts: -- releaseName: test - name: helm-chart - version: v1.0.0 diff --git a/test/e2e/testdata/syncwaves-prune-order/README.md b/test/e2e/testdata/syncwaves-prune-order/README.md deleted file mode 100644 index 92a62fdfe109d..0000000000000 --- a/test/e2e/testdata/syncwaves-prune-order/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## 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 deleted file mode 100644 index f801a3992aa37..0000000000000 --- a/test/e2e/testdata/syncwaves-prune-order/pod.yaml +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index 9512644b731db..0000000000000 --- a/test/e2e/testdata/syncwaves-prune-order/rbac.yaml +++ /dev/null @@ -1,37 +0,0 @@ -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/user_info_test.go b/test/e2e/user_info_test.go index 3e852c030f221..db352b816e82a 100644 --- a/test/e2e/user_info_test.go +++ b/test/e2e/user_info_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" ) @@ -14,7 +13,7 @@ func TestUserInfo(t *testing.T) { output, err := RunCli("account", "get-user-info") - require.NoError(t, err) + assert.NoError(t, err) assert.Equal(t, `Logged In: true Username: admin Issuer: argocd diff --git a/test/fixture/path/files.go b/test/fixture/path/files.go index 5a7a2ae380277..5c5f6120a7ec6 100644 --- a/test/fixture/path/files.go +++ b/test/fixture/path/files.go @@ -10,6 +10,7 @@ import ( // CopyDir copies the contents of a directory from 'src' to 'dest' func CopyDir(src string, dest string) error { + mode, err := os.Stat(src) if err != nil { return err diff --git a/test/manifests_test.go b/test/manifests_test.go index 9c30714041bcb..ce62b175d79b3 100644 --- a/test/manifests_test.go +++ b/test/manifests_test.go @@ -8,7 +8,6 @@ import ( argoexec "github.com/argoproj/pkg/exec" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/argoproj/argo-cd/v2/test/fixture/test" ) @@ -16,7 +15,7 @@ import ( func TestKustomizeVersion(t *testing.T) { test.CIOnly(t) out, err := argoexec.RunCommand("kustomize", argoexec.CmdOpts{}, "version") - require.NoError(t, err) + assert.NoError(t, err) assert.Contains(t, out, "v5.", "kustomize should be version 5") } @@ -42,5 +41,5 @@ func TestBuildManifests(t *testing.T) { _, err = argoexec.RunCommand("kustomize", argoexec.CmdOpts{}, "build", dirName) return err }) - require.NoError(t, err) + assert.NoError(t, err) } diff --git a/test/remote/Dockerfile b/test/remote/Dockerfile index fa649805767cc..8d03d1321d25b 100644 --- a/test/remote/Dockerfile +++ b/test/remote/Dockerfile @@ -1,6 +1,6 @@ -ARG BASE_IMAGE=docker.io/library/ubuntu:24.04@sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15 +ARG BASE_IMAGE=docker.io/library/ubuntu:22.04 -FROM docker.io/library/golang:1.23.1@sha256:2fe82a3f3e006b4f2a316c6a21f62b66e1330ae211d039bb8d1128e12ed57bf1 AS go +FROM docker.io/library/golang:1.21.3@sha256:02d7116222536a5cf0fcf631f90b507758b669648e0f20186d2dc94a9b419a9b AS go RUN go install github.com/mattn/goreman@latest && \ go install github.com/kisielk/godepgraph@latest diff --git a/test/testutil.go b/test/testutil.go index 3ad755bfdca69..34264772fa54f 100644 --- a/test/testutil.go +++ b/test/testutil.go @@ -14,8 +14,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/tools/cache" "sigs.k8s.io/yaml" - - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) // StartInformer is a helper to start an informer, wait for its cache to sync and return a cancel func @@ -86,15 +84,6 @@ 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) diff --git a/tools/cmd-docs/main.go b/tools/cmd-docs/main.go index 27c26195c851c..aace315302d4b 100644 --- a/tools/cmd-docs/main.go +++ b/tools/cmd-docs/main.go @@ -54,4 +54,5 @@ func main() { if err != nil { log.Fatal(err) } + } diff --git a/ui-test/Dockerfile b/ui-test/Dockerfile index c0fbd1be9b711..a5a77710eca52 100644 --- a/ui-test/Dockerfile +++ b/ui-test/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/library/node:22.9.0@sha256:cbe2d5f94110cea9817dd8c5809d05df49b4bd1aac5203f3594d88665ad37988 as node +FROM docker.io/library/node:20.7.0@sha256:f08c20b9f9c55dd47b1841793f0ee480c5395aa165cd02edfd68b068ed64bfb5 as node RUN apt-get update && apt-get install --no-install-recommends -y \ software-properties-common diff --git a/ui-test/package.json b/ui-test/package.json index a37fb07511461..1875e31b6fd62 100644 --- a/ui-test/package.json +++ b/ui-test/package.json @@ -12,21 +12,21 @@ "author": "Keith Chong", "license": "Apache-2.0", "dependencies": { - "@types/selenium-webdriver": "^4.1.26", - "assert": "^2.1.0", - "chromedriver": "^129.0.0", - "selenium-webdriver": "^4.24.1" + "@types/selenium-webdriver": "^4.0.9", + "assert": "^2.0.0", + "chromedriver": "^94.0.0", + "selenium-webdriver": "^4.0.0-alpha.7" }, "devDependencies": { - "@types/mocha": "^10.0.8", - "@types/node": "^22.5.5", - "dotenv": "^16.4.5", - "mocha": "^10.7.3", - "prettier": "^2.8.8", + "@types/mocha": "^8.0.3", + "@types/node": "^14.14.2", + "dotenv": "^8.2.0", + "mocha": "^8.2.0", + "prettier": "^1.18.2", "tslint": "^6.1.3", "tslint-config-prettier": "^1.18.0", "tslint-plugin-prettier": "^2.0.1", - "typescript": "^5.6.2", - "yarn": "^1.22.22" + "typescript": "^4.0.3", + "yarn": "^1.22.10" } } diff --git a/ui-test/yarn.lock b/ui-test/yarn.lock index e937e6532293e..b80910028fb7f 100644 --- a/ui-test/yarn.lock +++ b/ui-test/yarn.lock @@ -23,47 +23,51 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@bazel/runfiles@^5.8.1": - version "5.8.1" - resolved "https://registry.yarnpkg.com/@bazel/runfiles/-/runfiles-5.8.1.tgz#737d5b3dc9739767054820265cfe432a80564c82" - integrity sha512-NDdfpdQ6rZlylgv++iMn5FkObC/QlBQvipinGLSOguTYpRywmieOyJ29XHvUilspwTFSILWpoE9CqMGkHXug1g== +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" -"@testim/chrome-version@^1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.4.tgz#86e04e677cd6c05fa230dd15ac223fa72d1d7090" - integrity sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g== +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@tootallnate/quickjs-emscripten@^0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" - integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" -"@types/mocha@^10.0.8": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.8.tgz#a7eff5816e070c3b4d803f1d3cd780c4e42934a1" - integrity sha512-HfMcUmy9hTMJh66VNcmeC9iVErIZJli2bszuXc6julh5YGuRb/W5OnkHjwLNYdFlMis0sY3If5SEAp+PktdJjw== +"@testim/chrome-version@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.0.7.tgz#0cd915785ec4190f08a3a6acc9b61fc38fb5f1a9" + integrity sha512-8UT/J+xqCYfn3fKtOznAibsHpiuDshCb0fwgWxRazTT19Igp9ovoXMPhXyLD6m3CKQGTMHgqoxaFfMWaL40Rnw== -"@types/node@*", "@types/node@^22.5.5": - version "22.5.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.5.tgz#52f939dd0f65fc552a4ad0b392f3c466cc5d7a44" - integrity sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA== - dependencies: - undici-types "~6.19.2" +"@types/mocha@^8.0.3": + version "8.2.0" + resolved "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz" + integrity sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ== -"@types/selenium-webdriver@^4.1.26": - version "4.1.26" - resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.1.26.tgz#09c696a341cf8cfc1641cded11d14813350b6ca9" - integrity sha512-PUgqsyNffal0eAU0bzGlh37MJo558aporAPZoKqBeB/pF7zhKl1S3zqza0GpwFqgoigNxWhEIJzru75eeYco/w== - dependencies: - "@types/node" "*" - "@types/ws" "*" +"@types/node@*": + version "16.10.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.3.tgz#7a8f2838603ea314d1d22bb3171d899e15c57bd5" + integrity sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ== -"@types/ws@*": - version "8.5.10" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" - integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== - dependencies: - "@types/node" "*" +"@types/node@^14.14.2": + version "14.14.14" + resolved "https://registry.npmjs.org/@types/node/-/node-14.14.14.tgz" + integrity sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ== + +"@types/selenium-webdriver@^4.0.9": + version "4.0.10" + resolved "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-4.0.10.tgz" + integrity sha512-Xavn3fE+uM2aeIHtefIwpy0zAf2HQOyip/jU7ZR0ailt/B0ww/TJ6yMnfZ5pM0F4+Kx+9AQSnxQio3P5QAl1yQ== "@types/yauzl@^2.9.1": version "2.9.2" @@ -72,41 +76,59 @@ dependencies: "@types/node" "*" -agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" - integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz" + integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: - debug "^4.3.4" + debug "4" -ansi-colors@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" - integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0: +ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" -anymatch@~3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -118,38 +140,25 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - array-filter@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz" integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= -assert@^2.1.0: +array-union@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" - integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== - dependencies: - call-bind "^1.0.2" - is-nan "^1.3.2" - object-is "^1.1.5" - object.assign "^4.1.4" - util "^0.12.5" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -ast-types@^0.13.4: - version "0.13.4" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" - integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== +assert@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz" + integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== dependencies: - tslib "^2.0.1" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + es6-object-assign "^1.1.0" + is-nan "^1.2.1" + object-is "^1.0.1" + util "^0.12.0" available-typed-arrays@^1.0.2: version "1.0.2" @@ -158,25 +167,18 @@ available-typed-arrays@^1.0.2: dependencies: array-filter "^1.0.0" -axios@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" - integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== +axios@^0.21.2: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== dependencies: - follow-redirects "^1.15.6" - form-data "^4.0.0" - proxy-from-env "^1.1.0" + follow-redirects "^1.14.0" balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -basic-ftp@^5.0.2: - version "5.0.5" - resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" - integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== - binary-extensions@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz" @@ -190,23 +192,16 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: - fill-range "^7.1.1" + fill-range "^7.0.1" -browser-stdout@^1.3.1: +browser-stdout@1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== buffer-crc32@~0.2.3: @@ -219,16 +214,18 @@ builtin-modules@^1.1.1: resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== +call-bind@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz" + integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.0.0" + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0: version "6.2.0" @@ -244,50 +241,55 @@ chalk@^2.0.0, chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== +chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@^3.5.3: - version "3.6.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" - integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== +chokidar@3.4.3: + version "3.4.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz" + integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== dependencies: - anymatch "~3.1.2" + anymatch "~3.1.1" braces "~3.0.2" - glob-parent "~5.1.2" + glob-parent "~5.1.0" is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.6.0" + readdirp "~3.5.0" optionalDependencies: - fsevents "~2.3.2" + fsevents "~2.1.2" -chromedriver@^129.0.0: - version "129.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-129.0.0.tgz#28d7ede5ab372b868ac0db5efff7036646b4d603" - integrity sha512-B1ccqD6hDjNrw94FeqdynIotn1ZV/TnFrkRz2Rync2kzSnq6D6IrSkN1w5Pnuvnc98QhN2xujxDXxkqEqy/PWg== +chromedriver@^94.0.0: + version "94.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-94.0.0.tgz#f6a3533976ba72413a01672954040c3544ea9d30" + integrity sha512-x4hK7R7iOyAhdLHJEcOyGBW/oa2kno6AqpHVLd+n3G7c2Vk9XcAXMz84XhNItqykJvTc6E3z/JRIT1eHYH//Eg== dependencies: - "@testim/chrome-version" "^1.1.4" - axios "^1.7.4" - compare-versions "^6.1.0" + "@testim/chrome-version" "^1.0.7" + axios "^0.21.2" + del "^6.0.0" extract-zip "^2.0.1" - proxy-agent "^6.4.0" + https-proxy-agent "^5.0.0" proxy-from-env "^1.1.0" - tcp-port-used "^1.0.2" + tcp-port-used "^1.0.1" -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" color-convert@^1.9.0: version "1.9.3" @@ -313,23 +315,11 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - commander@^2.12.1: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -compare-versions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.1.0.tgz#3f2131e3ae93577df111dba133e6db876ffe127a" - integrity sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -340,17 +330,19 @@ core-util-is@~1.0.0: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -data-uri-to-buffer@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b" - integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== +debug@4, debug@^4.1.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" -debug@4, debug@^4.1.1, debug@^4.3.4, debug@^4.3.5: - version "4.3.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" - integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== +debug@4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== dependencies: - ms "^2.1.3" + ms "2.1.2" debug@4.3.1: version "4.3.1" @@ -359,6 +351,11 @@ debug@4.3.1: dependencies: ms "2.1.2" +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + decamelize@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" @@ -369,15 +366,6 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -define-data-property@^1.0.1, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - define-properties@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" @@ -385,48 +373,41 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" -define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -degenerator@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" - integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== +del@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" + integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== dependencies: - ast-types "^0.13.4" - escodegen "^2.1.0" - esprima "^4.0.1" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" -diff@^4.0.1: +diff@4.0.2, diff@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diff@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" - integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" -dotenv@^16.4.5: - version "16.4.5" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" - integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== +dotenv@^8.2.0: + version "8.2.0" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== end-of-stream@^1.1.0: version "1.4.4" @@ -453,18 +434,6 @@ es-abstract@^1.18.0-next.1: string.prototype.trimend "^1.0.1" string.prototype.trimstart "^1.0.1" -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" @@ -474,32 +443,21 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -escalade@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" - integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +es6-object-assign@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz" + integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escodegen@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" - integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionalDependencies: - source-map "~0.6.1" - eslint-plugin-prettier@^2.2.0: version "2.7.0" resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz" @@ -508,21 +466,11 @@ eslint-plugin-prettier@^2.2.0: fast-diff "^1.1.1" jest-docblock "^21.0.0" -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0: version "4.0.1" resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - extract-zip@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" @@ -539,6 +487,24 @@ fast-diff@^1.1.1: resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== +fast-glob@^3.1.1: + version "3.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -546,89 +512,71 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" -find-up@^5.0.0: +find-up@5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" path-exists "^4.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + flat@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -follow-redirects@^1.15.6: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== +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== foreach@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -fs-extra@^11.2.0: - version "11.2.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" - integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== function-bind@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -get-caller-file@^2.0.5: +get-caller-file@^2.0.1: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== +get-intrinsic@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz" + integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg== dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" get-stream@^5.1.0: version "5.2.0" @@ -637,27 +585,17 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-uri@^6.0.1: - version "6.0.3" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.3.tgz#0d26697bc13cf91092e519aa63aa60ee5b6f385a" - integrity sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw== - dependencies: - basic-ftp "^5.0.2" - data-uri-to-buffer "^6.0.2" - debug "^4.3.4" - fs-extra "^11.2.0" - -glob-parent@~5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.0: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@7.1.6, glob@^7.1.1: + version "7.1.6" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -666,28 +604,39 @@ glob@^7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== +glob@^7.1.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^5.0.1" + minimatch "^3.0.4" once "^1.3.0" + path-is-absolute "^1.0.0" -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== +globby@^11.0.1: + version "11.0.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" + integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== dependencies: - get-intrinsic "^1.1.3" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" -graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graceful-fs@^4.2.4: + version "4.2.8" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" + integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== has-flag@^3.0.0: version "3.0.0" @@ -699,28 +648,11 @@ has-flag@^4.0.0: resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - has@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" @@ -728,39 +660,34 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hasown@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -he@^1.2.0: +he@1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: - version "7.0.2" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" - integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== - dependencies: - agent-base "^7.1.0" - debug "^4.3.4" - -https-proxy-agent@^7.0.2, https-proxy-agent@^7.0.3: - version "7.0.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" - integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== dependencies: - agent-base "^7.0.2" + agent-base "6" debug "4" +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + immediate@~3.0.5: version "3.0.6" resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz" integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -774,14 +701,6 @@ inherits@2, inherits@^2.0.3, inherits@~2.0.3: resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ip-address@^9.0.5: - version "9.0.5" - resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" - integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== - dependencies: - jsbn "1.1.0" - sprintf-js "^1.1.3" - ip-regex@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" @@ -823,10 +742,10 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-generator-function@^1.0.7: version "1.0.8" @@ -847,12 +766,11 @@ is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-nan@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" - integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== +is-nan@^1.2.1: + version "1.3.0" + resolved "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz" + integrity sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ== dependencies: - call-bind "^1.0.0" define-properties "^1.1.3" is-negative-zero@^2.0.0: @@ -865,6 +783,16 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" @@ -895,11 +823,6 @@ is-typed-array@^1.1.3: foreach "^2.0.5" has-symbols "^1.0.1" -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - is-url@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" @@ -919,6 +842,11 @@ isarray@~1.0.0: resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + jest-docblock@^21.0.0: version "21.2.0" resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz" @@ -929,7 +857,7 @@ js-tokens@^4.0.0: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: +js-yaml@3.14.0, js-yaml@^3.13.1: version "3.14.0" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz" integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== @@ -937,36 +865,15 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -jsbn@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" - integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -jszip@^3.10.1: - version "3.10.1" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" - integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== +jszip@^3.5.0: + version "3.7.1" + resolved "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz" + integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg== dependencies: lie "~3.3.0" pako "~1.0.2" readable-stream "~2.3.6" - setimmediate "^1.0.5" + set-immediate-shim "~1.0.1" lie@~3.3.0: version "3.3.0" @@ -980,6 +887,14 @@ lines-and-columns@^1.1.6: resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" @@ -987,45 +902,33 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== +log-symbols@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -lru-cache@^7.14.1: - version "7.18.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + chalk "^4.0.0" -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: - mime-db "1.52.0" + braces "^3.0.1" + picomatch "^2.2.3" -minimatch@^3.0.4: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1, minimatch@^5.1.6: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -1038,46 +941,46 @@ mkdirp@^0.5.3: dependencies: minimist "^1.2.5" -mocha@^10.7.3: - version "10.7.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" - integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A== - dependencies: - ansi-colors "^4.1.3" - browser-stdout "^1.3.1" - chokidar "^3.5.3" - debug "^4.3.5" - diff "^5.2.0" - escape-string-regexp "^4.0.0" - find-up "^5.0.0" - glob "^8.1.0" - he "^1.2.0" - js-yaml "^4.1.0" - log-symbols "^4.1.0" - minimatch "^5.1.6" - ms "^2.1.3" - serialize-javascript "^6.0.2" - strip-json-comments "^3.1.1" - supports-color "^8.1.1" - workerpool "^6.5.1" - yargs "^16.2.0" - yargs-parser "^20.2.9" - yargs-unparser "^2.0.0" +mocha@^8.2.0: + version "8.2.1" + resolved "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz" + integrity sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.4.3" + debug "4.2.0" + diff "4.0.2" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.1.6" + growl "1.10.5" + he "1.2.0" + js-yaml "3.14.0" + log-symbols "4.0.0" + minimatch "3.0.4" + ms "2.1.2" + nanoid "3.1.12" + serialize-javascript "5.0.1" + strip-json-comments "3.1.1" + supports-color "7.2.0" + which "2.0.2" + wide-align "1.1.3" + workerpool "6.0.2" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "2.0.0" ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -netmask@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" - integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== +nanoid@3.1.12: + version "3.1.12" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz" + integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" @@ -1089,27 +992,27 @@ object-inspect@^1.8.0: resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz" integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== -object-is@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" - integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== +object-is@^1.0.1: + version "1.1.4" + resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.4.tgz" + integrity sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg== dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" + call-bind "^1.0.0" + define-properties "^1.1.3" object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.1, object.assign@^4.1.4: - version "4.1.5" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" - integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== +object.assign@^4.1.1: + version "4.1.2" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: - call-bind "^1.0.5" - define-properties "^1.2.1" - has-symbols "^1.0.3" + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" object-keys "^1.1.1" once@^1.3.0, once@^1.3.1, once@^1.4.0: @@ -1119,6 +1022,13 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" @@ -1126,6 +1036,13 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + p-locate@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" @@ -1133,33 +1050,28 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -pac-proxy-agent@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz#6b9ddc002ec3ff0ba5fdf4a8a21d363bcc612d75" - integrity sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A== - dependencies: - "@tootallnate/quickjs-emscripten" "^0.23.0" - agent-base "^7.0.2" - debug "^4.3.4" - get-uri "^6.0.1" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.2" - pac-resolver "^7.0.0" - socks-proxy-agent "^8.0.2" - -pac-resolver@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6" - integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg== +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: - degenerator "^5.0.0" - netmask "^2.0.2" + aggregate-error "^3.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== pako@~1.0.2: version "1.0.11" resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" @@ -1175,6 +1087,11 @@ path-parse@^1.0.6: resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -1185,35 +1102,21 @@ picomatch@^2.0.4: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -picomatch@^2.2.1: +picomatch@^2.2.1, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -prettier@^2.8.8: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^1.18.2: + version "1.19.1" + resolved "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -proxy-agent@^6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d" - integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ== - dependencies: - agent-base "^7.0.2" - debug "^4.3.4" - http-proxy-agent "^7.0.1" - https-proxy-agent "^7.0.3" - lru-cache "^7.14.1" - pac-proxy-agent "^7.0.1" - proxy-from-env "^1.1.0" - socks-proxy-agent "^8.0.2" - proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -1227,6 +1130,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" @@ -1247,10 +1155,10 @@ readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== dependencies: picomatch "^2.2.1" @@ -1259,6 +1167,11 @@ require-directory@^2.1.1: resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + resolve@^1.3.2: version "1.19.0" resolved "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz" @@ -1267,95 +1180,95 @@ resolve@^1.3.2: is-core-module "^2.1.0" path-parse "^1.0.6" -safe-buffer@^5.1.0, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^2.6.3, rimraf@^2.7.1: + version "2.7.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -selenium-webdriver@^4.24.1: - version "4.24.1" - resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.24.1.tgz#4315214420cc26dddaa21ae26863dd452aec2829" - integrity sha512-fcK5BTI/54cSqIhiVtrd9li1YL6LW109yIwuVw6V+FlVE6y4riGiX2qdZxVzHq+sm2TJyps+D2sjzXrpDZe1Og== +selenium-webdriver@^4.0.0-alpha.7: + version "4.0.0-alpha.8" + resolved "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.8.tgz" + integrity sha512-yPSaiWySZTEbxuuWQMDqdXh3H3N4Aiw/bSUjpkKMPWWCysfPqUncrq6FewBqdxWD1wQKzy5yWaQMGsgTY/0rCQ== dependencies: - "@bazel/runfiles" "^5.8.1" - jszip "^3.10.1" - tmp "^0.2.3" - ws "^8.18.0" + jszip "^3.5.0" + rimraf "^2.7.1" + tmp "^0.1.0" + ws "^7.3.1" semver@^5.3.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -serialize-javascript@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" - integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== +serialize-javascript@5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== dependencies: randombytes "^2.1.0" -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== - -smart-buffer@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" - integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== - -socks-proxy-agent@^8.0.2: - version "8.0.3" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz#6b2da3d77364fde6292e810b496cb70440b9b89d" - integrity sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A== - dependencies: - agent-base "^7.1.1" - debug "^4.3.4" - socks "^2.7.1" - -socks@^2.7.1: - version "2.8.3" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" - integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== - dependencies: - ip-address "^9.0.5" - smart-buffer "^4.2.0" +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +set-immediate-shim@~1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= -sprintf-js@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" - integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" string.prototype.trimend@^1.0.1: version "1.0.3" @@ -1380,40 +1293,40 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: - ansi-regex "^5.0.1" + ansi-regex "^4.1.0" -strip-json-comments@^3.1.1: +strip-json-comments@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: +supports-color@7.2.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -supports-color@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: - has-flag "^4.0.0" + has-flag "^3.0.0" -tcp-port-used@^1.0.2: +tcp-port-used@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea" integrity sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA== @@ -1421,10 +1334,12 @@ tcp-port-used@^1.0.2: debug "4.3.1" is2 "^2.0.6" -tmp@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" - integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== +tmp@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" to-regex-range@^5.0.1: version "5.0.1" @@ -1438,11 +1353,6 @@ tslib@^1.13.0, tslib@^1.7.1, tslib@^1.8.1: resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - tslint-config-prettier@^1.18.0: version "1.18.0" resolved "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz" @@ -1483,37 +1393,33 @@ tsutils@^2.29.0: dependencies: tslib "^1.8.1" -typescript@^5.6.2: - version "5.6.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0" - integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw== - -undici-types@~6.19.2: - version "6.19.8" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" - integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== - -universalify@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" - integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== +typescript@^4.0.3: + version "4.1.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util@^0.12.5: - version "0.12.5" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" - integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== +util@^0.12.0: + version "0.12.3" + resolved "https://registry.npmjs.org/util/-/util-0.12.3.tgz" + integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog== dependencies: inherits "^2.0.3" is-arguments "^1.0.4" is-generator-function "^1.0.7" is-typed-array "^1.1.3" + safe-buffer "^5.1.2" which-typed-array "^1.1.2" +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + which-typed-array@^1.1.2: version "1.1.4" resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz" @@ -1527,43 +1433,60 @@ which-typed-array@^1.1.2: has-symbols "^1.0.1" is-typed-array "^1.1.3" -workerpool@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" - integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== +which@2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +workerpool@6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz" + integrity sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q== + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -ws@^8.18.0: - version "8.18.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== +ws@^7.3.1: + version "7.5.3" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz" + integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +y18n@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== -yargs-parser@^20.2.2, yargs-parser@^20.2.9: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" -yargs-unparser@^2.0.0: +yargs-unparser@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== dependencies: camelcase "^6.0.0" @@ -1571,23 +1494,26 @@ yargs-unparser@^2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== +yargs@13.3.2: + version "13.3.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yarn@^1.22.22: - version "1.22.22" - resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.22.tgz#ac34549e6aa8e7ead463a7407e1c7390f61a6610" - integrity sha512-prL3kGtyG7o9Z9Sv8IPfBNrWTDmXB4Qbes8A9rEzt6wkJV8mUvoirjU0Mp3GGAU06Y0XQyA3/2/RQFVuK7MTfg== + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + 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== yauzl@^2.10.0: version "2.10.0" diff --git a/ui/.nvmrc b/ui/.nvmrc index a8d3ff91fa10d..376d26203e61e 100644 --- a/ui/.nvmrc +++ b/ui/.nvmrc @@ -1 +1 @@ -v21.6.1 +v20.7.0 diff --git a/ui/.prettierrc b/ui/.prettierrc index 13a9584287dd1..9e18402b642d4 100644 --- a/ui/.prettierrc +++ b/ui/.prettierrc @@ -6,6 +6,5 @@ "tabWidth": 4, "jsxBracketSameLine": true, "quoteProps": "consistent", - "arrowParens": "avoid", - "trailingComma": "none" + "arrowParens": "avoid" } diff --git a/ui/README.md b/ui/README.md index aa6393fb9196f..aec1971e6797d 100644 --- a/ui/README.md +++ b/ui/README.md @@ -22,25 +22,4 @@ Make sure your code passes the lint checks: ``` yarn lint --fix -``` - -If you are using VSCode, add this configuration to `.vscode/settings.json` in the root of this repository to identify and fix lint issues automatically before you save file. - -Install [Eslint Extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) in VSCode. - -`.vscode/settings.json` -```json -{ - "eslint.format.enable": true, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "always" - }, - "eslint.workingDirectories": [ - { - "directory": "./ui", - "!cwd": false - } - ], - "eslint.experimental.useFlatConfig": true -} -``` +``` \ No newline at end of file diff --git a/ui/eslint.config.mjs b/ui/eslint.config.mjs deleted file mode 100644 index 45cca74acdcf7..0000000000000 --- a/ui/eslint.config.mjs +++ /dev/null @@ -1,37 +0,0 @@ -import globals from 'globals'; -import pluginJs from '@eslint/js'; -import tseslint from 'typescript-eslint'; -import pluginReactConfig from 'eslint-plugin-react/configs/recommended.js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; - -export default [ - {languageOptions: {globals: globals.browser}}, - pluginJs.configs.recommended, - ...tseslint.configs.recommended, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/ban-types': 'off', - '@typescript-eslint/no-var-requires': 'off' - } - }, - { - settings: { - react: { - version: 'detect' - } - }, - ...pluginReactConfig, - rules: { - 'react/display-name': 'off', - 'react/no-string-refs': 'off' - } - }, - eslintPluginPrettierRecommended, - { - files: ['./src/**/*.{ts,tsx}'] - }, - { - ignores: ['dist', 'assets', '**/*.config.js', '__mocks__', 'coverage', '**/*.test.{ts,tsx}'] - } -]; diff --git a/ui/package.json b/ui/package.json index e5b5b0a874a47..9c6f1e36e8db0 100644 --- a/ui/package.json +++ b/ui/package.json @@ -6,14 +6,14 @@ "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": "tsc --noEmit --project ./src/app && eslint", - "lint:fix": "eslint --fix", + "lint": "tsc --noEmit --project ./src/app && tslint -p ./src/app", + "lint:fix": "tslint -p ./src/app --fix", "test": "jest" }, "dependencies": { - "@fortawesome/fontawesome-free": "^6.5.2", + "@fortawesome/fontawesome-free": "^6.4.0", "@types/react-virtualized": "^9.21.21", - "@types/superagent": "^8.1.6", + "@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", @@ -22,10 +22,10 @@ "dagre": "^0.8.5", "date-fns": "^2.30.0", "deepmerge": "^3.2.0", - "foundation-sites": "^6.8.1", + "foundation-sites": "^6.7.5", "git-url-parse": "^13.1.0", "history": "^4.7.2", - "js-yaml": "^4.1.0", + "js-yaml": "^3.14.1", "json-merge-patch": "^0.2.3", "lodash-es": "^4.17.21", "minimatch": "^3.1.2", @@ -40,10 +40,10 @@ "react-dom": "^16.9.3", "react-form": "2.16.3", "react-ga": "^2.7.0", - "react-helmet": "^6.1.0", + "react-helmet": "^5.2.0", "react-hot-loader": "^3.1.3", "react-moment": "^0.9.7", - "react-paginate": "^8.2.0", + "react-paginate": "^8.1.4", "react-router": "^4.3.1", "react-router-dom": "^4.2.2", "react-svg-piechart": "^2.4.2", @@ -69,62 +69,57 @@ "@babel/preset-env": "^7.7.1", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.7.2", - "@codecov/webpack-plugin": "^0.0.1-beta.10", - "@eslint/js": "^9.1.1", "@types/classnames": "^2.2.3", "@types/cookie": "^0.5.1", "@types/dagre": "^0.7.40", "@types/deepmerge": "^2.2.0", "@types/git-url-parse": "^9.0.1", - "@types/jest": "^29.5.12", - "@types/js-yaml": "^4.0.9", + "@types/jest": "^24.0.13", + "@types/js-yaml": "^3.11.2", "@types/lodash-es": "^4.17.6", "@types/minimatch": "^3.0.3", - "@types/node": "20.14.12", + "@types/node": "20.6.3", "@types/prop-types": "^15.7.5", "@types/react": "^16.8.5", - "@types/react-autocomplete": "^1.8.10", + "@types/react-autocomplete": "^1.8.4", "@types/react-dom": "^16.9.14", "@types/react-form": "^2.16.0", - "@types/react-helmet": "^6.1.6", - "@types/react-paginate": "^7.1.4", + "@types/react-helmet": "^5.0.17", + "@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": "^29.7.0", + "babel-jest": "^26.6.3", "babel-loader": "^8.0.6", "codecov": "^3.8.3", "copy-webpack-plugin": "^6.1.1", "esbuild-loader": "^2.18.0", - "eslint": "^9.1.1", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-react": "^7.34.1", - "globals": "^15.1.0", - "html-webpack-plugin": "^5.6.0", + "html-webpack-plugin": "^5.5.0", "identity-obj-proxy": "^3.0.0", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", + "jest": "^27.5.1", "jest-junit": "^6.4.0", "jest-transform-css": "^2.0.0", - "monaco-editor-webpack-plugin": "^7.1.0", - "postcss": "^8.4.38", - "prettier": "^3.2.5", + "monaco-editor-webpack-plugin": "^7.0.0", + "postcss": "^8.2.13", + "prettier": "1.19", "raw-loader": "^0.5.1", "react-test-renderer": "16.8.3", "sass": "^1.49.9", - "sass-loader": "^14.2.1", + "sass-loader": "^12.6.0", "source-map-loader": "^0.2.3", "style-loader": "^0.20.1", - "ts-jest": "^29.1.2", - "ts-node": "10.9.2", + "ts-jest": "^27.1.3", + "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.9.5", - "typescript-eslint": "^7.8.0", "webpack": "^5.94.0", "webpack-cli": "^4.9.2", "webpack-dev-server": "^4.7.4", - "yarn": "^1.22.22" + "yarn": "^1.22.10" } } diff --git a/ui/src/app/app.tsx b/ui/src/app/app.tsx index 7a9bfb21635bb..d0a58d3fbdc7f 100644 --- a/ui/src/app/app.tsx +++ b/ui/src/app/app.tsx @@ -19,7 +19,6 @@ import {Banner} from './ui-banner/ui-banner'; import userInfo from './user-info'; import {AuthSettings} from './shared/models'; import {PKCEVerification} from './login/components/pkce-verify'; -import {SystemLevelExtension} from './shared/services/extensions-service'; services.viewPreferences.init(); const bases = document.getElementsByTagName('base'); @@ -27,7 +26,7 @@ const base = bases.length > 0 ? bases[0].getAttribute('href') || '/' : '/'; export const history = createBrowserHistory({basename: base}); requests.setBaseHRef(base); -type Routes = {[path: string]: {component: React.ComponentType>; noLayout?: boolean}}; +type Routes = {[path: string]: {component: React.ComponentType>; noLayout?: boolean; extension?: boolean}}; const routes: Routes = { '/login': {component: login.component as any, noLayout: true}, @@ -99,7 +98,10 @@ requests.onError.subscribe(async err => { } // Query for basehref and remove trailing /. // If basehref is the default `/` it will become an empty string. - const basehref = document.querySelector('head > base').getAttribute('href').replace(/\/$/, ''); + const basehref = document + .querySelector('head > base') + .getAttribute('href') + .replace(/\/$/, ''); if (isSSO) { window.location.href = `${basehref}/auth/login?return_url=${encodeURIComponent(location.href)}`; } else { @@ -135,7 +137,6 @@ export class App extends React.Component< this.navigationManager = new NavigationManager(history); this.navItems = navItems; this.routes = routes; - services.extensions.addEventListener('systemLevel', this.onAddSystemLevelExtension.bind(this)); } public async componentDidMount() { @@ -164,7 +165,32 @@ export class App extends React.Component< document.head.appendChild(link); } - this.setState({...this.state, navItems: this.navItems, routes: this.routes, extensionsLoaded: false, authSettings}); + const systemExtensions = services.extensions.getSystemExtensions(); + const extendedNavItems = this.navItems; + const extendedRoutes = this.routes; + for (const extension of systemExtensions) { + extendedNavItems.push({ + title: extension.title, + path: extension.path, + iconClassName: `fa ${extension.icon}` + }); + const component = () => ( + <> + + {extension.title} - Argo CD + + + + + + ); + extendedRoutes[extension.path] = { + component: component as React.ComponentType>, + extension: true + }; + } + + this.setState({...this.state, navItems: extendedNavItems, routes: extendedRoutes, extensionsLoaded: true, authSettings}); } public render() { @@ -214,7 +240,11 @@ export class App extends React.Component< ) : ( services.viewPreferences.getPreferences()}> {pref => ( - this.setState({showVersionPanel: true})} navItems={this.navItems} pref={pref}> + this.setState({showVersionPanel: true})} + navItems={this.navItems} + pref={pref} + isExtension={route.extension}> @@ -241,28 +271,4 @@ export class App extends React.Component< public getChildContext() { return {history, apis: {popup: this.popupManager, notifications: this.notificationsManager, navigation: this.navigationManager}}; } - - private onAddSystemLevelExtension(extension: SystemLevelExtension) { - const extendedNavItems = this.navItems; - const extendedRoutes = this.routes; - extendedNavItems.push({ - title: extension.title, - path: extension.path, - iconClassName: `fa ${extension.icon}` - }); - const component = () => ( - <> - - {extension.title} - Argo CD - - - - - - ); - extendedRoutes[extension.path] = { - component: component as React.ComponentType> - }; - this.setState({...this.state, navItems: extendedNavItems, routes: extendedRoutes, extensionsLoaded: true}); - } } diff --git a/ui/src/app/applications/components/__snapshots__/utils.test.tsx.snap b/ui/src/app/applications/components/__snapshots__/utils.test.tsx.snap index f580fd6f66b35..c3756c3510277 100644 --- a/ui/src/app/applications/components/__snapshots__/utils.test.tsx.snap +++ b/ui/src/app/applications/components/__snapshots__/utils.test.tsx.snap @@ -1,12 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`ComparisonStatusIcon.OutOfSync 1`] = ` -[ +Array [ { if (app?.spec?.destination?.name && app.spec.destination.name !== '') { @@ -119,11 +116,7 @@ export const ApplicationCreatePanel = (props: { } else { setDestFormat('URL'); } - - return () => { - debouncedOnAppChanged.cancel(); - }; - }, [debouncedOnAppChanged]); + }, []); function normalizeTypeFields(formApi: FormApi, type: models.AppSourceType) { const appToNormalize = formApi.getFormState().values; @@ -187,7 +180,7 @@ export const ApplicationCreatePanel = (props: { 'Cluster name is required' })} defaultValues={app} - formDidUpdate={state => debouncedOnAppChanged(state.values as any)} + formDidUpdate={state => props.onAppChanged(state.values as any)} onSubmit={props.createApp} getApi={props.getFormApi}> {api => { @@ -451,7 +444,7 @@ export const ApplicationCreatePanel = (props: { }} load={async src => { if (src.repoURL && src.targetRevision && (src.path || src.chart)) { - return services.repos.appDetails(src, src.appName, app.spec.project, 0, 0).catch(() => ({ + return services.repos.appDetails(src, src.appName, app.spec.project).catch(() => ({ type: 'Directory', details: {} })); diff --git a/ui/src/app/applications/components/application-deployment-history/application-deployment-history-details.tsx b/ui/src/app/applications/components/application-deployment-history/application-deployment-history-details.tsx deleted file mode 100644 index e98f46fc60e06..0000000000000 --- a/ui/src/app/applications/components/application-deployment-history/application-deployment-history-details.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import * as moment from 'moment'; -import * as React from 'react'; -import * as models from '../../../shared/models'; -import './application-deployment-history.scss'; -import {DataLoader} from 'argo-ui'; -import {Revision} from '../../../shared/components'; -import {services} from '../../../shared/services'; -import {ApplicationParameters} from '../application-parameters/application-parameters'; -import {RevisionMetadataRows} from './revision-metadata-rows'; - -type props = { - app: models.Application; - info: models.RevisionHistory; - index: number; -}; - -export const ApplicationDeploymentHistoryDetails = ({app, info, index}: props) => { - const deployments = (app.status.history || []).slice().reverse(); - const recentDeployments = deployments.map((info, i) => { - const nextDeployedAt = i === 0 ? null : deployments[i - 1].deployedAt; - const runEnd = nextDeployedAt ? moment(nextDeployedAt) : moment(); - return {...info, nextDeployedAt, durationMs: runEnd.diff(moment(info.deployedAt)) / 1000}; - }); - - const [showParameterDetails, setShowParameterDetails] = React.useState(Boolean); - - return ( - <> - {info.sources === undefined ? ( - -
    -
    -
    Revision:
    -
    - -
    -
    -
    - - - - {showParameterDetails && ( - services.repos.appDetails(src, src.appName, app.spec.project, 0, recentDeployments[index].id)}> - {(details: models.RepoAppDetails) => ( -
    - -
    - )} -
    - )} -
    - ) : ( - info.sources.map((source, i) => ( - - {i > 0 ?
    : null} -
    -
    -
    Revision:
    -
    - -
    -
    -
    - - - - {showParameterDetails && ( - services.repos.appDetails(src, src.appName, app.spec.project, i, recentDeployments[index].id)}> - {(details: models.RepoAppDetails) => ( -
    - -
    - )} -
    - )} - - )) - )} - - ); -}; diff --git a/ui/src/app/applications/components/application-deployment-history/application-deployment-history.scss b/ui/src/app/applications/components/application-deployment-history/application-deployment-history.scss index abacf037f48e5..8c31a357529f2 100644 --- a/ui/src/app/applications/components/application-deployment-history/application-deployment-history.scss +++ b/ui/src/app/applications/components/application-deployment-history/application-deployment-history.scss @@ -3,6 +3,7 @@ .application-deployment-history { &__item { + cursor: pointer; position: relative; @include themify($themes) { background: themed('background-2'); @@ -33,10 +34,6 @@ right: 1em; } - &__show-parameter-details { - margin: 20px 0px; - } - .white-box { margin-top: 1em; padding: 0; @@ -46,11 +43,4 @@ .white-box__details p { margin-left: -1em; } - - .separator { - height: 2px; - margin: 1em 0; - width: 100%; - background-color: $argo-color-gray-5; - } } 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 69adefe598216..55734b69ea0c4 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,18 +1,21 @@ -import {DropDownMenu, Duration} from 'argo-ui'; -import {InitiatedBy} from './initiated-by'; +import {DataLoader, DropDownMenu, Duration} from 'argo-ui'; import * as moment from 'moment'; import * as React from 'react'; -import {Timestamp} from '../../../shared/components'; +import {Revision, Timestamp} from '../../../shared/components'; import * as models from '../../../shared/models'; +import {services} from '../../../shared/services'; +import {ApplicationParameters} from '../application-parameters/application-parameters'; +import {RevisionMetadataRows} from './revision-metadata-rows'; import './application-deployment-history.scss'; -import {ApplicationDeploymentHistoryDetails} from './application-deployment-history-details'; export const ApplicationDeploymentHistory = ({ app, rollbackApp, + selectedRollbackDeploymentIndex, selectDeployment }: { app: models.Application; + selectedRollbackDeploymentIndex: number; rollbackApp: (info: models.RevisionHistory) => any; selectDeployment: (index: number) => any; }) => { @@ -39,12 +42,6 @@ export const ApplicationDeploymentHistory = ({
    {(info.deployStartedAt && ) || 'Unknown'}
    -
    -
    - Initiated by: -
    - -

    Active for: @@ -54,7 +51,9 @@ export const ApplicationDeploymentHistory = ({
    +
    Revision:
    +
    ( @@ -72,8 +71,30 @@ export const ApplicationDeploymentHistory = ({
    - - + {selectedRollbackDeploymentIndex === index ? ( + + + services.repos.appDetails(src, src.appName, app.spec.project)}> + {(details: models.RepoAppDetails) => ( +
    + +
    + )} +
    +
    + ) : null}
    ))} 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 deleted file mode 100644 index f691389b5daca..0000000000000 --- a/ui/src/app/applications/components/application-deployment-history/initiated-by.tsx +++ /dev/null @@ -1,6 +0,0 @@ -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 1043d0bfa5659..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 @@ -4,14 +4,10 @@ import {Timestamp} from '../../../shared/components/timestamp'; import {ApplicationSource, RevisionMetadata, ChartDetails} from '../../../shared/models'; import {services} from '../../../shared/services'; -export const RevisionMetadataRows = (props: {applicationName: string; applicationNamespace: string; source: ApplicationSource; index: number; versionId: number}) => { +export const RevisionMetadataRows = (props: {applicationName: string; applicationNamespace: string; source: ApplicationSource}) => { if (props.source.chart) { return ( - - services.applications.revisionChartDetails(input.applicationName, input.applicationNamespace, input.source.targetRevision, input.index, input.versionId) - }> + services.applications.revisionChartDetails(input.applicationName, input.applicationNamespace, input.source.targetRevision)}> {(m: ChartDetails) => (
    @@ -48,9 +44,7 @@ export const RevisionMetadataRows = (props: {applicationName: string; applicatio ); } return ( - services.applications.revisionMetadata(input.applicationName, input.applicationNamespace, input.source.targetRevision, input.index, input.versionId)}> + services.applications.revisionMetadata(input.applicationName, input.applicationNamespace, input.source.targetRevision)}> {(m: RevisionMetadata) => (
    diff --git a/ui/src/app/applications/components/application-details/application-details-app-dropdown.tsx b/ui/src/app/applications/components/application-details/application-details-app-dropdown.tsx index 905785765540c..7ada4d6a66707 100644 --- a/ui/src/app/applications/components/application-details/application-details-app-dropdown.tsx +++ b/ui/src/app/applications/components/application-details/application-details-app-dropdown.tsx @@ -34,7 +34,7 @@ export const ApplicationsDetailsAppDropdown = (props: {appName: string}) => { } /> - services.applications.list([], {fields: ['items.metadata.name', 'items.metadata.namespace']})}> + services.applications.list([], {fields: ['items.metadata.name']})}> {apps => apps.items .filter(app => { @@ -42,7 +42,7 @@ export const ApplicationsDetailsAppDropdown = (props: {appName: string}) => { }) .slice(0, 100) // take top 100 results after filtering to avoid performance issues .map(app => ( -
  • ctx.navigation.goto(`/applications/${app.metadata.namespace}/${app.metadata.name}`)}> +
  • ctx.navigation.goto(`/applications/${app.metadata.name}`)}> {app.metadata.name} {app.metadata.name === props.appName && ' (current)'}
  • )) 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 c364e939054fb..a3e8175591dde 100644 --- a/ui/src/app/applications/components/application-details/application-details.tsx +++ b/ui/src/app/applications/components/application-details/application-details.tsx @@ -24,17 +24,17 @@ import {ResourceDetails} from '../resource-details/resource-details'; import * as AppUtils from '../utils'; import {ApplicationResourceList} from './application-resource-list'; import {Filters, FiltersProps} from './application-resource-filter'; -import {getAppDefaultSource, getAppCurrentVersion, urlPattern} from '../utils'; +import {getAppDefaultSource, urlPattern, helpTip} from '../utils'; import {ChartDetails, ResourceStatus} from '../../../shared/models'; import {ApplicationsDetailsAppDropdown} from './application-details-app-dropdown'; import {useSidebarTarget} from '../../../sidebar/sidebar'; import './application-details.scss'; -import {TopBarActionMenuExt, AppViewExtension, StatusPanelExtension} from '../../../shared/services/extensions-service'; +import {AppViewExtension, StatusPanelExtension} from '../../../shared/services/extensions-service'; interface ApplicationDetailsState { page: number; - revision?: string; // Which type of revision panelto show SYNC_STATUS_REVISION or OPERATION_STATE_REVISION + revision?: string; groupedResources?: ResourceStatus[]; slidingPanelPage?: number; filteredGraph?: any[]; @@ -44,8 +44,6 @@ interface ApplicationDetailsState { extensionsMap?: {[key: string]: AppViewExtension}; statusExtensions?: StatusPanelExtension[]; statusExtensionsMap?: {[key: string]: StatusPanelExtension}; - topBarActionMenuExts?: TopBarActionMenuExt[]; - topBarActionMenuExtsMap?: {[key: string]: TopBarActionMenuExt}; } interface FilterInput { @@ -96,11 +94,6 @@ export class ApplicationDetails extends React.Component { statusExtensionsMap[ext.id] = ext; }); - const topBarActionMenuExts = services.extensions.getActionMenuExtensions(); - const topBarActionMenuExtsMap: {[key: string]: TopBarActionMenuExt} = {}; - topBarActionMenuExts.forEach(ext => { - topBarActionMenuExtsMap[ext.id] = ext; - }); this.state = { page: 0, groupedResources: [], @@ -111,9 +104,7 @@ export class ApplicationDetails extends React.Component - message.split(/\s/).map(part => - urlPattern.test(part) ? ( - - {part}{' '} - - ) : ( - part + ' ' - ) - ); - - const getContentForChart = ( - aRevision: string, - aSourceIndex: number | null, - aVersionId: number | null, - indx: number, - aSource: models.ApplicationSource, - sourceHeader?: JSX.Element - ) => { - const showChartNonMetadataInfo = (aRevision: string, aRepoUrl: string) => { - return ( - <> -
    -
    Revision:
    -
    {aRevision}
    -
    -
    -
    Chart Source:
    -
    {aRepoUrl}
    -
    - - ); - }; - return ( - services.applications.revisionChartDetails(input.metadata.name, input.metadata.namespace, aRevision, aSourceIndex, aVersionId)}> - {(m: ChartDetails) => { - return m ? ( -
    - {sourceHeader && sourceHeader} -
    - {showChartNonMetadataInfo(aRevision, aSource.repoURL)} -
    -
    Helm Chart:
    -
    - {aSource.chart}  - {m.home && ( - { - e.stopPropagation(); - window.open(m.home); - }}> - - - )} -
    -
    - {m.description && ( -
    -
    Description:
    -
    {m.description}
    -
    - )} - {m.maintainers && m.maintainers.length > 0 && ( -
    -
    Maintainers:
    -
    {m.maintainers.join(', ')}
    -
    - )} -
    -
    - ) : ( -
    -
    Source {indx + 1}
    -
    - {showChartNonMetadataInfo(aRevision, aSource.repoURL)} -
    -
    Helm Chart:
    - -
    -
    -
    - ); - }} -
    - ); - }; - - const getContentForNonChart = ( - aRevision: string, - aSourceIndex: number, - aVersionId: number, - indx: number, - aSource: models.ApplicationSource, - sourceHeader?: JSX.Element - ) => { - const showNonMetadataInfo = (aSource: models.ApplicationSource, aRevision: string) => { - return ( - <> -
    -
    -
    SHA:
    -
    - -
    -
    -
    -
    -
    -
    Source:
    -
    {aSource.repoURL}
    -
    -
    - - ); - }; - return ( - services.applications.revisionMetadata(application.metadata.name, application.metadata.namespace, aRevision, aSourceIndex, aVersionId)}> - {metadata => - metadata ? ( -
    - {sourceHeader && sourceHeader} - {showNonMetadataInfo(aSource, aRevision)} -
    -
    -
    Date:
    -
    - -
    -
    -
    -
    -
    -
    Tags:
    -
    {((metadata.tags || []).length > 0 && metadata.tags.join(', ')) || 'No tags'}
    -
    -
    -
    -
    -
    Author:
    -
    {metadata.author}
    -
    -
    -
    -
    -
    Message:
    -
    -
    {renderCommitMessage(metadata.message)}
    -
    -
    -
    -
    - ) : ( -
    -
    Source {indx + 1}
    - {showNonMetadataInfo(aSource, aRevision)} -
    - ) - } -
    - ); - }; - const cont: JSX.Element[] = []; - const sources: models.ApplicationSource[] = application.spec.sources; - if (sources?.length > 0 && revisions) { - revisions.forEach((rev, indx) => { - if (sources[indx].chart) { - cont.push(getContentForChart(rev, indx, getAppCurrentVersion(application), indx, sources[indx],
    Source {indx + 1}
    )); - } else { - cont.push(getContentForNonChart(rev, indx, getAppCurrentVersion(application), indx, sources[indx],
    Source {indx + 1}
    )); - } - }); - return <>{cont}; - } else if (application.spec.source) { - if (source.chart) { - cont.push(getContentForChart(revision, null, null, 0, source)); - } else { - cont.push(getContentForNonChart(revision, null, getAppCurrentVersion(application), 0, source)); - } - return <>{cont}; - } else { - return ( -
    -
    -
    -
    No other information available
    -
    -
    -
    - ); - } - } - public render() { return ( @@ -409,7 +189,7 @@ export class ApplicationDetails extends React.Component Loading...} input={this.props.match.params.name} load={name => - combineLatest([this.loadAppInfo(name, this.props.match.params.appnamespace), services.viewPreferences.getPreferences(), q]).pipe( + combineLatest([this.loadAppInfo(name, this.appNamespace), services.viewPreferences.getPreferences(), q]).pipe( map(items => { const application = items[0].application; const pref = items[1].appDetails; @@ -503,6 +283,17 @@ export class ApplicationDetails extends React.Component + message.split(/\s/).map(part => + urlPattern.test(part) ? ( + + {part}{' '} + + ) : ( + part + ' ' + ) + ); const {Tree, Pods, Network, List} = AppsDetailsViewKey; const zoomNum = (pref.zoom * 100).toFixed(0); const setZoom = (s: number) => { @@ -576,8 +367,7 @@ export class ApplicationDetails extends React.Component @@ -590,14 +380,7 @@ export class ApplicationDetails extends React.Component} ], - actionMenu: { - items: [ - ...this.getApplicationActionMenu(application, true), - ...(this.state.topBarActionMenuExts - ?.filter(ext => ext.shouldDisplay?.(application)) - .map(ext => this.renderActionMenuItem(ext, tree, application, this.setExtensionPanelVisible)) || []) - ] - }, + actionMenu: {items: this.getApplicationActionMenu(application, true)}, tools: (
    @@ -790,12 +573,16 @@ export class ApplicationDetails extends React.Component {data => ( this.selectNode(fullName)} resources={data} nodeMenu={node => - AppUtils.renderResourceMenu(node, application, tree, this.appContext.apis, this.appChanged, () => - this.getApplicationActionMenu(application, false) + AppUtils.renderResourceMenu( + {...node, root: node}, + application, + tree, + this.appContext.apis, + this.appChanged, + () => this.getApplicationActionMenu(application, false) ) } tree={tree} @@ -821,11 +608,10 @@ export class ApplicationDetails extends React.Component {data => ( this.selectNode(fullName)} resources={data} nodeMenu={node => - AppUtils.renderResourceMenu(node, application, tree, this.appContext.apis, this.appChanged, () => + AppUtils.renderResourceMenu({...node, root: node}, application, tree, this.appContext.apis, this.appChanged, () => this.getApplicationActionMenu(application, false) ) } @@ -842,7 +628,6 @@ export class ApplicationDetails extends React.Component this.updateApp(app, query)} selectedNode={selectedNode} - appCxt={this.context} tab={tab} /> @@ -855,6 +640,7 @@ export class ApplicationDetails extends React.Component -1 && ( this.rollbackApplication(info, application)} selectDeployment={i => this.setRollbackPanelVisible(i)} /> @@ -866,33 +652,108 @@ export class ApplicationDetails extends React.Component this.setConditionsStatusVisible(false)}> {conditions && } - this.setState({revision: null})}> - {this.state.revision === 'SYNC_STATUS_REVISION' && - (application.status.sync.revisions || application.status.sync.revision) && - this.getContent(application, source, application.status.sync.revisions, application.status.sync.revision)} - {this.state.revision === 'OPERATION_STATE_REVISION' && - (application.status.operationState.syncResult.revisions || application.status.operationState.syncResult.revision) && - this.getContent( - application, - source, - application.status.operationState.syncResult.revisions, - application.status.operationState.syncResult.revision - )} - - this.setExtensionPanelVisible('')}> - {this.selectedExtension !== '' && activeStatusExt?.flyout && } + this.setState({revision: null})}> + {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(', ')}
    +
    + )} +
    +
    + )} +
    + ) : ( + + services.applications.revisionMetadata(application.metadata.name, application.metadata.namespace, this.state.revision) + }> + {metadata => ( +
    +
    +
    +
    SHA:
    +
    + +
    +
    +
    +
    +
    +
    Date:
    +
    + +
    +
    +
    +
    +
    +
    Tags:
    +
    + {((metadata.tags || []).length > 0 && metadata.tags.join(', ')) || 'No tags'} +
    +
    +
    +
    +
    +
    Author:
    +
    {metadata.author}
    +
    +
    +
    +
    +
    Message:
    +
    +
    {renderCommitMessage(metadata.message)}
    +
    +
    +
    +
    + )} +
    + ))}
    this.setExtensionPanelVisible('')}> - {this.selectedExtension !== '' && activeTopBarActionMenuExt?.flyout && ( - + {this.selectedExtension !== '' && activeExtension && activeExtension.flyout && ( + )} @@ -904,17 +765,12 @@ export class ApplicationDetails extends React.Component ); } - private renderActionMenuItem(ext: TopBarActionMenuExt, tree: appModels.ApplicationTree, application: appModels.Application, showExtension?: (id: string) => any): any { - return { - action: () => this.setExtensionPanelVisible(ext.id), - title: showExtension && showExtension(ext.id)} />, - iconClassName: ext.iconClassName - }; - } + private getApplicationActionMenu(app: appModels.Application, needOverlapLabelOnNarrowScreen: boolean) { const refreshing = app.metadata.annotations && app.metadata.annotations[appModels.AnnotationRefreshKey]; const fullName = AppUtils.nodeKey({group: 'argoproj.io', kind: app.kind, name: app.metadata.name, namespace: app.metadata.namespace}); const ActionMenuItem = (prop: {actionLabel: string}) => {prop.actionLabel}; + const hasMultipleSources = app.spec.sources && app.spec.sources.length > 0; return [ { iconClassName: 'fa fa-info-circle', @@ -940,11 +796,18 @@ export class ApplicationDetails extends React.Component, + title: hasMultipleSources ? ( + + + {helpTip('Rollback is not supported for apps with multiple sources')} + + ) : ( + + ), action: () => { this.setRollbackPanelVisible(0); }, - disabled: !app.status.operationState + disabled: !app.status.operationState || hasMultipleSources }, { iconClassName: 'fa fa-times-circle', diff --git a/ui/src/app/applications/components/application-details/application-resource-filter.tsx b/ui/src/app/applications/components/application-details/application-resource-filter.tsx index 242189ff0a9ef..a3d99f92488f3 100644 --- a/ui/src/app/applications/components/application-details/application-resource-filter.tsx +++ b/ui/src/app/applications/components/application-details/application-resource-filter.tsx @@ -145,7 +145,7 @@ export const Filters = (props: FiltersProps) => { {ResourceFilter({ label: 'HEALTH STATUS', prefix: 'health', - options: ['Progressing', 'Suspended', 'Healthy', 'Degraded', 'Missing', 'Unknown'].map(label => ({ + options: ['Healthy', 'Progressing', 'Degraded', 'Suspended', 'Missing', 'Unknown'].map(label => ({ label, count: getOptionCount(label, 'Health'), icon: diff --git a/ui/src/app/applications/components/application-details/application-resource-list.scss b/ui/src/app/applications/components/application-details/application-resource-list.scss deleted file mode 100644 index 9bc4b17bfe7ed..0000000000000 --- a/ui/src/app/applications/components/application-details/application-resource-list.scss +++ /dev/null @@ -1,13 +0,0 @@ -.application-details__item { - display: flex; - - .application-details__item_text { - overflow: hidden; - text-overflow: ellipsis; - } - - .application-details__external_link { - flex: 0; - min-width: 13px; - } -} diff --git a/ui/src/app/applications/components/application-details/application-resource-list.tsx b/ui/src/app/applications/components/application-details/application-resource-list.tsx index 2230e31bacea1..c5519fc4b6ff9 100644 --- a/ui/src/app/applications/components/application-details/application-resource-list.tsx +++ b/ui/src/app/applications/components/application-details/application-resource-list.tsx @@ -1,45 +1,50 @@ -import {DropDown, Tooltip} from 'argo-ui'; +import {DropDown} from 'argo-ui'; import * as React from 'react'; import * as classNames from 'classnames'; import * as models from '../../../shared/models'; import {ResourceIcon} from '../resource-icon'; import {ResourceLabel} from '../resource-label'; -import {ComparisonStatusIcon, HealthStatusIcon, nodeKey, createdOrNodeKey, isSameNode} from '../utils'; -import {AppDetailsPreferences} from '../../../shared/services'; +import {ComparisonStatusIcon, HealthStatusIcon, nodeKey, createdOrNodeKey} from '../utils'; import {Consumer} from '../../../shared/context'; +import * as _ from 'lodash'; import Moment from 'react-moment'; import {format} from 'date-fns'; -import {ResourceNode} from '../../../shared/models'; -import './application-resource-list.scss'; +import {ResourceNode, ResourceRef} from '../../../shared/models'; -export interface ApplicationResourceListProps { - pref: AppDetailsPreferences; +export const ApplicationResourceList = ({ + resources, + onNodeClick, + nodeMenu, + tree +}: { resources: models.ResourceStatus[]; onNodeClick?: (fullName: string) => any; nodeMenu?: (node: models.ResourceNode) => React.ReactNode; tree?: models.ApplicationTree; -} - -export const ApplicationResourceList = (props: ApplicationResourceListProps) => { - const nodeByKey = new Map(); - props.tree?.nodes?.forEach(res => nodeByKey.set(nodeKey(res), res)); - - const firstParentNode = props.resources.length > 0 && (nodeByKey.get(nodeKey(props.resources[0])) as ResourceNode)?.parentRefs?.[0]; - const isSameParent = firstParentNode && props.resources?.every(x => (nodeByKey.get(nodeKey(x)) as ResourceNode)?.parentRefs?.every(p => isSameNode(p, firstParentNode))); - const isSameKind = props.resources?.every(x => x.group === props.resources[0].group && x.kind === props.resources[0].kind); - const view = props.pref.view; +}) => { + 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 isSameParent ? ( + return Object.keys(parentNode).length > 0 ? (
    Parent Node Info
    Name:
    -
    {firstParentNode.name}
    +
    {parentNode?.name}
    Kind:
    -
    {firstParentNode.kind}
    +
    {parentNode?.kind}
    ) : ( @@ -47,126 +52,119 @@ export const ApplicationResourceList = (props: ApplicationResourceListProps) => ); }; return ( - props.resources.length > 0 && ( -
    - {/* Display only when the view is set to or network */} - {(view === 'tree' || view === 'network') && ( -
    - -
    - )} -
    -
    -
    -
    -
    NAME
    -
    GROUP/KIND
    -
    SYNC ORDER
    -
    NAMESPACE
    - {isSameKind && props.resources[0].kind === 'ReplicaSet' &&
    REVISION
    } -
    CREATED AT
    -
    STATUS
    -
    +
    + {/* Display only when the view is set to or network */} + {(view === 'tree' || view === 'network') && ( +
    + +
    + )} +
    +
    +
    +
    +
    NAME
    +
    GROUP/KIND
    +
    SYNC ORDER
    +
    NAMESPACE
    + {(parentNode.kind === 'Rollout' || parentNode.kind === 'Deployment') &&
    REVISION
    } +
    CREATED AT
    +
    STATUS
    - {props.resources - .sort((first, second) => -createdOrNodeKey(first).localeCompare(createdOrNodeKey(second))) - .map(res => { - const groupkindjoin = [res.group, res.kind].filter(item => !!item).join('/'); - return ( -
    props.onNodeClick && props.onNodeClick(nodeKey(res))}> -
    -
    -
    - -
    -
    {ResourceLabel({kind: res.kind})}
    -
    -
    - -
    - {res.name} - {res.kind === 'Application' && ( - - {ctx => ( - - e.stopPropagation()} - title='Open application'> - - - - )} - - )} -
    -
    - -
    {groupkindjoin}
    -
    - -
    {res.syncWave || '-'}
    -
    - -
    {res.namespace}
    -
    - {isSameKind && - res.kind === 'ReplicaSet' && - ((nodeByKey.get(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.health && ( - - {res.health.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.status && } - {res.hook && } - {props.nodeMenu && ( -
    - ( - - )}> - {() => props.nodeMenu(nodeByKey.get(nodeKey(res)))} - + + )} +
    +
    {[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.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.scss b/ui/src/app/applications/components/application-fullscreen-logs/application-fullscreen-logs.scss index 80b2312a7b3d3..c735215f0cae8 100644 --- a/ui/src/app/applications/components/application-fullscreen-logs/application-fullscreen-logs.scss +++ b/ui/src/app/applications/components/application-fullscreen-logs/application-fullscreen-logs.scss @@ -10,9 +10,4 @@ height: 100%; padding: 20px 30px; background-color: $argo-color-gray-3; - - .theme-dark & { - background-color: #28292a; - color: #fff; - } -} \ No newline at end of file +} 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 03baa4e44e655..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 @@ -15,7 +15,6 @@ export const ApplicationFullscreenLogs = (props: RouteComponentProps<{name: stri const group = q.get('group'); const kind = q.get('kind'); const title = `${podName || `${group}/${kind}/${name}`}:${props.match.params.container}`; - const fullscreen = true; return (
    @@ -29,7 +28,6 @@ export const ApplicationFullscreenLogs = (props: RouteComponentProps<{name: stri kind={kind} name={name} podName={podName} - fullscreen={fullscreen} />
    ); 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 27ab11d776c17..f50e67279cc52 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,5 +1,4 @@ @import 'node_modules/argo-ui/src/styles/config'; -@import 'node_modules/argo-ui/src/styles/theme'; .application-node-info { &__manifest { @@ -7,9 +6,6 @@ .tabs__content { background-color: white; - @include themify($themes){ - background-color: themed('background-2'); - } } &--raw { @@ -41,9 +37,6 @@ label { padding-right: 2em; color: $argo-color-gray-8; - @include themify($themes){ - color: themed('text-2'); - } } } &__err_msg { 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 4d1f2720bf9a9..18ff44e381c55 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 @@ -21,21 +21,7 @@ const RenderContainerState = (props: {container: any}) => { return (
    -
    - {props.container.state?.running ? ( - - - - ) : ( - (props.container.state.terminated && props.container.state.terminated?.exitCode !== 0) || - (lastState && lastState?.exitCode !== 0 && ( - - - - )) - )} - {props.container.name} -
    +
    {props.container.name}
    {state && ( <> diff --git a/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx b/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx index f6284258b238e..0f5bbac2615a2 100644 --- a/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx +++ b/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx @@ -1,4 +1,4 @@ -import {Checkbox, DropDown, Duration, NotificationType, Ticker, HelpIcon} from 'argo-ui'; +import {Checkbox, DropDown, Duration, NotificationType, Ticker} from 'argo-ui'; import * as moment from 'moment'; import * as PropTypes from 'prop-types'; import * as React from 'react'; @@ -15,7 +15,6 @@ interface Props { application: models.Application; operationState: models.OperationState; } -const buildResourceUniqueId = (res: Omit) => `${res.group}-${res.kind}-${res.version}-${res.namespace}-${res.name}`; const Filter = (props: {filters: string[]; setFilters: (f: string[]) => void; options: string[]; title: string; style?: React.CSSProperties}) => { const {filters, setFilters, options, title, style} = props; @@ -94,15 +93,7 @@ export const ApplicationOperationState: React.StatelessComponent = ({appl }); } if (operationState.syncResult) { - operationAttributes.push({ - title: 'REVISION', - value: ( -
    - - {utils.getAppDefaultOperationSyncRevisionExtra(application)} -
    - ) - }); + operationAttributes.push({title: 'REVISION', value: }); } let initiator = ''; if (operationState.operation.initiatedBy) { @@ -127,60 +118,18 @@ export const ApplicationOperationState: React.StatelessComponent = ({appl } } const [filters, setFilters] = React.useState([]); - const [healthFilters, setHealthFilters] = React.useState([]); - const Healths = Object.keys(models.HealthStatuses); const Statuses = Object.keys(models.ResultCodes); const OperationPhases = Object.keys(models.OperationPhases); // const syncPhases = ['PreSync', 'Sync', 'PostSync', 'SyncFail']; // const hookPhases = ['Running', 'Terminating', 'Failed', 'Error', 'Succeeded']; - const resourceHealth = application.status.resources.reduce( - (acc, res) => { - if (res.health) { - acc[buildResourceUniqueId(res)] = res.health; - } - - return acc; - }, - {} as Record - ); - - const combinedHealthSyncResult: models.SyncResourceResult[] = syncResult?.resources?.map(syncResultItem => { - const uniqueResourceName = buildResourceUniqueId(syncResultItem); - - const healthStatus = resourceHealth[uniqueResourceName]; - const syncResultWithHealth: models.SyncResourceResult = { - ...syncResultItem - }; - - if (healthStatus) { - syncResultWithHealth.health = healthStatus; + let filtered: models.ResourceResult[] = []; + if (syncResult) { + if (syncResult.resources && syncResult.resources.length > 0) { + filtered = syncResult.resources.filter(r => filters.length === 0 || filters.includes(getStatus(r))); } - - return syncResultWithHealth; - }); - let filtered: models.SyncResourceResult[] = []; - - if (combinedHealthSyncResult && combinedHealthSyncResult.length > 0) { - filtered = combinedHealthSyncResult.filter(r => { - if (filters.length === 0 && healthFilters.length === 0) { - return true; - } - - let pass = true; - if (filters.length !== 0 && !filters.includes(getStatus(r))) { - pass = false; - } - - if (pass && healthFilters.length !== 0 && !healthFilters.includes(r.health?.status)) { - pass = false; - } - - return pass; - }); } - return (
    @@ -198,7 +147,6 @@ export const ApplicationOperationState: React.StatelessComponent = ({appl
    -
    @@ -210,7 +158,6 @@ export const ApplicationOperationState: React.StatelessComponent = ({appl
    NAMESPACE
    NAME
    STATUS
    -
    HEALTH
    HOOK
    MESSAGE
    @@ -234,16 +181,6 @@ export const ApplicationOperationState: React.StatelessComponent = ({appl
    {getStatus(resource)}
    -
    - {resource.health ? ( -
    - {resource.health?.status} - {resource.health.message && } -
    - ) : ( - <>{'-'} - )} -
    {resource.hookType}
    diff --git a/ui/src/app/applications/components/application-parameters/application-parameters-source.tsx b/ui/src/app/applications/components/application-parameters/application-parameters-source.tsx deleted file mode 100644 index 2d494af941d3d..0000000000000 --- a/ui/src/app/applications/components/application-parameters/application-parameters-source.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import * as classNames from 'classnames'; -import * as React from 'react'; -import {FormApi} from 'react-form'; -import {EditablePanelItem} from '../../../shared/components'; -import {EditableSection} from '../../../shared/components/editable-panel/editable-section'; -import {Consumer} from '../../../shared/context'; -import '../../../shared/components/editable-panel/editable-panel.scss'; - -export interface ApplicationParametersPanelProps { - floatingTitle?: string | React.ReactNode; - titleTop?: string | React.ReactNode; - titleBottom?: string | React.ReactNode; - index: number; - valuesTop?: T; - valuesBottom?: T; - validateTop?: (values: T) => any; - validateBottom?: (values: T) => any; - saveTop?: (input: T, query: {validate?: boolean}) => Promise; - saveBottom?: (input: T, query: {validate?: boolean}) => Promise; - itemsTop?: EditablePanelItem[]; - itemsBottom?: EditablePanelItem[]; - onModeSwitch?: () => any; - viewTop?: string | React.ReactNode; - viewBottom?: string | React.ReactNode; - editTop?: (formApi: FormApi) => React.ReactNode; - editBottom?: (formApi: FormApi) => React.ReactNode; - numberOfSources?: number; - noReadonlyMode?: boolean; - collapsible?: boolean; - deleteSource: () => void; -} - -interface ApplicationParametersPanelState { - editTop: boolean; - editBottom: boolean; - savingTop: boolean; - savingBottom: boolean; -} - -// Currently two editable sections, but can be modified to support N panels in general. This should be part of a white-box, editable-panel. -export class ApplicationParametersSource extends React.Component, ApplicationParametersPanelState> { - constructor(props: ApplicationParametersPanelProps) { - super(props); - this.state = {editTop: !!props.noReadonlyMode, editBottom: !!props.noReadonlyMode, savingTop: false, savingBottom: false}; - } - - public render() { - return ( - - {ctx => ( -
    - {this.props.floatingTitle &&
    {this.props.floatingTitle}
    } - - this.onModeSwitch()} - noReadonlyMode={this.props.noReadonlyMode} - edit={this.props.editTop} - collapsible={this.props.collapsible} - ctx={ctx} - isTopSection={true} - disabledState={this.state.editTop || this.state.editTop === null} - disabledDelete={this.props.numberOfSources <= 1} - updateButtons={editClicked => { - this.setState({editBottom: editClicked}); - }} - deleteSource={this.props.deleteSource} - /> - - {this.props.itemsTop && ( - -
    -

     

    -
    -
    - - )} - - this.onModeSwitch()} - noReadonlyMode={this.props.noReadonlyMode} - edit={this.props.editBottom} - collapsible={this.props.collapsible} - ctx={ctx} - isTopSection={false} - disabledState={this.state.editBottom || this.state.editBottom === null} - updateButtons={editClicked => { - this.setState({editTop: editClicked}); - }} - /> - -
    - )} - - ); - } - - private onModeSwitch() { - if (this.props.onModeSwitch) { - this.props.onModeSwitch(); - } - } -} diff --git a/ui/src/app/applications/components/application-parameters/application-parameters.scss b/ui/src/app/applications/components/application-parameters/application-parameters.scss deleted file mode 100644 index d40c88ac05340..0000000000000 --- a/ui/src/app/applications/components/application-parameters/application-parameters.scss +++ /dev/null @@ -1,88 +0,0 @@ -@import 'node_modules/argo-ui/src/styles/config'; -@import 'node_modules/argo-ui/src/styles/theme'; - -.application-parameters { - &__labels { - line-height: 28px; - display: flex; - align-items: center; - height: 100%; - flex-wrap: wrap; - padding-top: 0.5em; - } - - &__label { - background-color: $argo-color-gray-5; - color: white; - border-radius: 5px; - padding: 4px; - line-height: 14px; - margin: 0.3em 0; - margin-right: 2px; - } - - &__sort-icon { - cursor: pointer; - position: absolute; - font-size: 1.3em; - left: -1em; - - &.fa-sort-up { - top: 10px; - } - - &.fa-sort-down { - bottom: 10px; - } - } - &__remove-icon { - cursor: pointer; - position: absolute; - top: 1em; - right: 1em; - } - - .source-panel-buttons { - margin-bottom: 10px; - } - - .argo-field { - line-height: 1.15; - } - - .white-box__details p { - font-weight: 500; - @include themify($themes) { - color: themed('text-1'); - } - } - - .white-box__details-row .row { - padding-left: 1em; - padding-right: 1em; - } - - .white-box__details-row .row .columns:last-child { - padding-left: 1em; - } - - .select { - padding-bottom: 0; - - .select__value{ - min-height: 28px; - } - } - - .row.application-retry-options { - .columns.application-retry-options__item{ - padding-left: 0; - padding-right: 10px; - } - - .argo-form-row__error-msg { - position: static; - line-height: 1; - } - } -} 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 ec6d67dae46a1..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,4 +1,4 @@ -import {AutocompleteField, DataLoader, ErrorNotification, FormField, FormSelect, getNestedField, NotificationType, SlidingPanel} from 'argo-ui'; +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 {cloneDeep} from 'lodash-es'; @@ -6,20 +6,15 @@ import { ArrayInputField, ArrayValueField, CheckboxField, + EditablePanel, + EditablePanelItem, Expandable, MapValueField, NameValueEditor, StringValueField, NameValue, TagsInputField, - ValueEditor, - Paginate, - RevisionHelpIcon, - Revision, - Repo, - EditablePanel, - EditablePanelItem, - Spinner + ValueEditor } from '../../../shared/components'; import * as models from '../../../shared/models'; import {ApplicationSourceDirectory, Plugin} from '../../../shared/models'; @@ -28,15 +23,8 @@ import {ImageTagFieldEditor} from './kustomize'; import * as kustomize from './kustomize-image'; import {VarsInputField} from './vars-input-field'; import {concatMaps} from '../../../shared/utils'; -import {deleteSourceAction, getAppDefaultSource, helpTip} from '../utils'; +import {getAppDefaultSource} from '../utils'; import * as jsYaml from 'js-yaml'; -import {RevisionFormField} from '../revision-form-field/revision-form-field'; -import classNames from 'classnames'; -import {ApplicationParametersSource} from './application-parameters-source'; - -import './application-parameters.scss'; -import {AppContext} from '../../../shared/context'; -import {SourcePanel} from './source-panel'; const TextWithMetadataField = ReactFormField((props: {metadata: {value: string}; fieldApi: FieldApi; className: string}) => { const { @@ -63,16 +51,6 @@ function overridesFirst(first: {overrideIndex: number; metadata: {name: string}} return first.overrideIndex - second.overrideIndex; } -function processPath(path: string) { - if (path !== null && path !== undefined) { - if (path === '.') { - return '(root)'; - } - return path; - } - return ''; -} - function getParamsEditableItems( app: models.Application, title: string, @@ -144,543 +122,20 @@ function getParamsEditableItems( export const ApplicationParameters = (props: { application: models.Application; - details?: models.RepoAppDetails; + details: models.RepoAppDetails; save?: (application: models.Application, query: {validate?: boolean}) => Promise; noReadonlyMode?: boolean; - pageNumber?: number; - setPageNumber?: (x: number) => any; - collapsedSources?: boolean[]; - handleCollapse?: (i: number, isCollapsed: boolean) => void; - appContext?: AppContext; - tempSource?: models.ApplicationSource; }) => { const app = cloneDeep(props.application); - const source = getAppDefaultSource(app); // For source field - const appSources = app?.spec.sources; + const source = getAppDefaultSource(app); const [removedOverrides, setRemovedOverrides] = React.useState(new Array()); - const collapsible = props.collapsedSources !== undefined && props.handleCollapse !== undefined; - const [createApi, setCreateApi] = React.useState(null); - const [isAddingSource, setIsAddingSource] = React.useState(false); - const [isSavingSource, setIsSavingSource] = React.useState(false); - const [appParamsDeletedState, setAppParamsDeletedState] = React.useState([]); - - if (app.spec.sources?.length > 0 && !props.details) { - // For multi-source case only - return ( -
    -
    - -
    - { - props.setPageNumber(page); - }}> - {data => { - const listOfPanels: JSX.Element[] = []; - data.forEach(appSource => { - const i = app.spec.sources.indexOf(appSource); - listOfPanels.push(getEditablePanelForSources(i, appSource)); - }); - return listOfPanels; - }} - - setIsAddingSource(false)} - header={ -
    - {' '} - -
    - }> - { - setCreateApi(api); - }} - onSubmitFailure={errors => { - props.appContext.apis.notifications.show({ - content: 'Cannot add source: ' + errors.toString(), - type: NotificationType.Warning - }); - }} - updateApp={async updatedAppSource => { - setIsSavingSource(true); - props.application.spec.sources.push(updatedAppSource.spec.source); - try { - await services.applications.update(props.application); - setIsAddingSource(false); - } catch (e) { - props.application.spec.sources.pop(); - props.appContext.apis.notifications.show({ - content: , - type: NotificationType.Error - }); - } finally { - setIsSavingSource(false); - } - }} - /> -
    -
    - ); - } else { - // For the three other references of ApplicationParameters. They are single source. - // Create App, Add source, Rollback and History - let attributes: EditablePanelItem[] = []; - if (props.details) { - return getEditablePanel( - gatherDetails( - 0, - props.details, - attributes, - props.tempSource ? props.tempSource : source, - app, - setRemovedOverrides, - removedOverrides, - appParamsDeletedState, - setAppParamsDeletedState, - false - ), - props.details - ); - } else { - // For single source field, details page where we have to do the load to retrieve repo details - return ( - getSingleSource(application)}> - {(details: models.RepoAppDetails) => { - attributes = []; - const attr = gatherDetails( - 0, - details, - attributes, - source, - app, - setRemovedOverrides, - removedOverrides, - appParamsDeletedState, - setAppParamsDeletedState, - false - ); - return getEditablePanel(attr, details); - }} - - ); - } - } - - // Collapse button is separate - function getEditablePanelForSources(index: number, appSource: models.ApplicationSource): JSX.Element { - return (collapsible && props.collapsedSources[index] === undefined) || props.collapsedSources[index] ? ( -
    { - const currentState = props.collapsedSources[index] !== undefined ? props.collapsedSources[index] : true; - props.handleCollapse(index, !currentState); - }}> -
    - -
    -
    -
    Source {index + 1 + ': ' + appSource.repoURL}
    -
    - {(appSource.path ? 'PATH=' + appSource.path : '') + (appSource.targetRevision ? (appSource.path ? ', ' : '') + 'REVISION=' + appSource.targetRevision : '')} -
    -
    -
    - ) : ( -
    -
    - {collapsible && ( - -
    - { - props.handleCollapse(index, !props.collapsedSources[index]); - }} - /> -
    -
    - )} - getSourceFromAppSources(src, app.metadata.name, app.spec.project, index, 0)}> - {(details: models.RepoAppDetails) => getEditablePanelForOneSource(details, index, app.spec.sources[index])} - -
    -
    - ); - } - - function getEditablePanel(items: EditablePanelItem[], repoAppDetails: models.RepoAppDetails): any { - return ( -
    - { - const updatedSrc = input.spec.source; - - function isDefined(item: any) { - return item !== null && item !== undefined; - } - function isDefinedWithVersion(item: any) { - return item !== null && item !== undefined && item.match(/:/); - } - if (updatedSrc && updatedSrc.helm?.parameters) { - updatedSrc.helm.parameters = updatedSrc.helm.parameters.filter(isDefined); - } - if (updatedSrc && updatedSrc.kustomize?.images) { - updatedSrc.kustomize.images = updatedSrc.kustomize.images.filter(isDefinedWithVersion); - } - - let params = input.spec?.source?.plugin?.parameters; - if (params) { - for (const param of params) { - if (param.map && param.array) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - param.map = param.array.reduce((acc, {name, value}) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @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 && input.spec.source.helm?.valuesObject) { - input.spec.source.helm.valuesObject = jsYaml.load(input.spec.source.helm.values); // Deserialize json - input.spec.source.helm.values = ''; - } - await props.save(input, {}); - setRemovedOverrides(new Array()); - }) - } - values={((repoAppDetails?.plugin || app?.spec?.source?.plugin) && cloneDeep(app)) || app} - validate={updatedApp => { - const errors = {} as any; - - for (const fieldPath of ['spec.source.directory.jsonnet.tlas', 'spec.source.directory.jsonnet.extVars']) { - const invalid = ((getNestedField(updatedApp, fieldPath) || []) as Array).filter(item => !item.name && !item.code); - errors[fieldPath] = invalid.length > 0 ? 'All fields must have name' : null; - } - - if (updatedApp.spec.source && updatedApp.spec.source.helm?.values) { - const parsedValues = jsYaml.load(updatedApp.spec.source.helm.values); - errors['spec.source.helm.values'] = typeof parsedValues === 'object' ? null : 'Values must be a map'; - } - - return errors; - }} - onModeSwitch={ - repoAppDetails?.plugin && - (() => { - setAppParamsDeletedState([]); - }) - } - title={repoAppDetails?.type?.toLocaleUpperCase()} - items={items as EditablePanelItem[]} - noReadonlyMode={props.noReadonlyMode} - hasMultipleSources={false} - /> -
    - ); - } - - function getEditablePanelForOneSource(repoAppDetails: models.RepoAppDetails, ind: number, src: models.ApplicationSource): any { - let floatingTitle: string; - const lowerPanelAttributes: EditablePanelItem[] = []; - const upperPanelAttributes: EditablePanelItem[] = []; - const upperPanel = gatherCoreSourceDetails(ind, upperPanelAttributes, appSources[ind], app); - const lowerPanel = gatherDetails( - ind, - repoAppDetails, - lowerPanelAttributes, - appSources[ind], - app, - setRemovedOverrides, - removedOverrides, - appParamsDeletedState, - setAppParamsDeletedState, - true - ); - - if (repoAppDetails.type === 'Directory') { - floatingTitle = - 'Source ' + - (ind + 1) + - ': TYPE=' + - repoAppDetails.type + - ', URL=' + - src.repoURL + - (repoAppDetails.path ? ', PATH=' + repoAppDetails.path : '') + - (src.targetRevision ? ', TARGET REVISION=' + src.targetRevision : ''); - } else if (repoAppDetails.type === 'Helm') { - floatingTitle = - 'Source ' + - (ind + 1) + - ': TYPE=' + - repoAppDetails.type + - ', URL=' + - src.repoURL + - (src.chart ? ', CHART=' + src.chart + ':' + src.targetRevision : '') + - (src.path ? ', PATH=' + src.path : '') + - (src.targetRevision ? ', REVISION=' + src.targetRevision : ''); - } else if (repoAppDetails.type === 'Kustomize') { - floatingTitle = - 'Source ' + - (ind + 1) + - ': TYPE=' + - repoAppDetails.type + - ', URL=' + - src.repoURL + - (repoAppDetails.path ? ', PATH=' + repoAppDetails.path : '') + - (src.targetRevision ? ', TARGET REVISION=' + src.targetRevision : ''); - } else if (repoAppDetails.type === 'Plugin') { - floatingTitle = - 'Source ' + - (ind + 1) + - ': TYPE=' + - repoAppDetails.type + - ', URL=' + - src.repoURL + - (repoAppDetails.path ? ', PATH=' + repoAppDetails.path : '') + - (src.targetRevision ? ', TARGET REVISION=' + src.targetRevision : ''); - } - return ( - { - const appSrc = input.spec.sources[ind]; - - function isDefined(item: any) { - return item !== null && item !== undefined; - } - function isDefinedWithVersion(item: any) { - return item !== null && item !== undefined && item.match(/:/); - } - - if (appSrc.helm && appSrc.helm.parameters) { - appSrc.helm.parameters = appSrc.helm.parameters.filter(isDefined); - } - if (appSrc.kustomize && appSrc.kustomize.images) { - appSrc.kustomize.images = appSrc.kustomize.images.filter(isDefinedWithVersion); - } - - let params = input.spec?.sources[ind]?.plugin?.parameters; - if (params) { - for (const param of params) { - if (param.map && param.array) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - param.map = param.array.reduce((acc, {name, value}) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - acc[name] = value; - return acc; - }, {}); - delete param.array; - } - } - - params = params.filter(param => !appParamsDeletedState.includes(param.name)); - appSrc.plugin.parameters = params; - } - if (appSrc.helm && appSrc.helm.valuesObject) { - appSrc.helm.valuesObject = jsYaml.load(appSrc.helm.values); // Deserialize json - appSrc.helm.values = ''; - } - - await props.save(input, {}); - setRemovedOverrides(new Array()); - }) - } - valuesTop={(app?.spec?.sources && (repoAppDetails.plugin || app?.spec?.sources[ind]?.plugin) && cloneDeep(app)) || app} - valuesBottom={(app?.spec?.sources && (repoAppDetails.plugin || app?.spec?.sources[ind]?.plugin) && cloneDeep(app)) || app} - validateTop={updatedApp => { - const errors = [] as any; - const repoURL = updatedApp.spec.sources[ind].repoURL; - if (repoURL === null || repoURL.length === 0) { - errors['spec.sources[' + ind + '].repoURL'] = 'The source repo URL cannot be empty'; - } else { - errors['spec.sources[' + ind + '].repoURL'] = null; - } - return errors; - }} - validateBottom={updatedApp => { - const errors = {} as any; - - for (const fieldPath of ['spec.sources[' + ind + '].directory.jsonnet.tlas', 'spec.sources[' + ind + '].directory.jsonnet.extVars']) { - const invalid = ((getNestedField(updatedApp, fieldPath) || []) as Array).filter(item => !item.name && !item.code); - errors[fieldPath] = invalid.length > 0 ? 'All fields must have name' : null; - } - - if (updatedApp.spec.sources[ind].helm?.values) { - const parsedValues = jsYaml.load(updatedApp.spec.sources[ind].helm.values); - errors['spec.sources[' + ind + '].helm.values'] = typeof parsedValues === 'object' ? null : 'Values must be a map'; - } - - return errors; - }} - onModeSwitch={ - repoAppDetails.plugin && - (() => { - setAppParamsDeletedState([]); - }) - } - titleBottom={repoAppDetails.type.toLocaleUpperCase()} - titleTop={'SOURCE ' + (ind + 1)} - floatingTitle={floatingTitle ? floatingTitle : null} - itemsBottom={lowerPanel as EditablePanelItem[]} - itemsTop={upperPanel as EditablePanelItem[]} - noReadonlyMode={props.noReadonlyMode} - collapsible={collapsible} - numberOfSources={app?.spec?.sources.length} - deleteSource={() => { - deleteSourceAction(app, app.spec.sources.at(ind), props.appContext); - }} - /> - ); - } -}; - -function gatherCoreSourceDetails(i: number, attributes: EditablePanelItem[], source: models.ApplicationSource, app: models.Application): EditablePanelItem[] { - const hasMultipleSources = app.spec.sources && app.spec.sources.length > 0; - // eslint-disable-next-line no-prototype-builtins - const isHelm = source.hasOwnProperty('chart'); - const repoUrlField = 'spec.sources[' + i + '].repoURL'; - const sourcesPathField = 'spec.sources[' + i + '].path'; - const refField = 'spec.sources[' + i + '].ref'; - const chartField = 'spec.sources[' + i + '].chart'; - const revisionField = 'spec.sources[' + i + '].targetRevision'; - // For single source apps using the source field, these fields are shown in the Summary tab. - if (hasMultipleSources) { - attributes.push({ - title: 'REPO URL', - view: , - edit: (formApi: FormApi) => - }); - if (isHelm) { - attributes.push({ - title: 'CHART', - view: ( - - {source.chart}:{source.targetRevision} - - ), - edit: (formApi: FormApi) => ( - services.repos.charts(src.repoURL).catch(() => new Array())}> - {(charts: models.HelmChart[]) => ( -
    -
    - chart.name), - filterSuggestions: true - }} - /> -
    - { - const chartInfo = data.charts.find(chart => chart.name === data.chart); - return (chartInfo && chartInfo.versions) || new Array(); - }}> - {(versions: string[]) => ( -
    - - -
    - )} -
    -
    - )} -
    - ) - }); - } else { - attributes.push({ - title: 'TARGET REVISION', - view: , - edit: (formApi: FormApi) => - }); - attributes.push({ - title: 'PATH', - view: ( - - {processPath(source.path)} - - ), - edit: (formApi: FormApi) => - }); - attributes.push({ - title: 'REF', - view: {source.ref}, - edit: (formApi: FormApi) => - }); - } - } - return attributes; -} + let attributes: EditablePanelItem[] = []; + const isValuesObject = source?.helm?.valuesObject; + const helmValues = isValuesObject ? jsYaml.safeDump(source.helm.valuesObject) : source?.helm?.values; + const [appParamsDeletedState, setAppParamsDeletedState] = React.useState([]); -function gatherDetails( - ind: number, - repoDetails: models.RepoAppDetails, - attributes: EditablePanelItem[], - source: models.ApplicationSource, - app: models.Application, - setRemovedOverrides: any, - removedOverrides: any, - appParamsDeletedState: any[], - setAppParamsDeletedState: any, - isMultiSource: boolean -): EditablePanelItem[] { - if (repoDetails.type === 'Kustomize' && repoDetails.kustomize) { + if (props.details.type === 'Kustomize' && props.details.kustomize) { attributes.push({ title: 'VERSION', view: (source.kustomize && source.kustomize.version) || default, @@ -688,12 +143,7 @@ function gatherDetails( services.authService.settings()}> {settings => ((settings.kustomizeVersions || []).length > 0 && ( - + )) || default } @@ -703,28 +153,22 @@ function gatherDetails( attributes.push({ title: 'NAME PREFIX', view: source.kustomize && source.kustomize.namePrefix, - edit: (formApi: FormApi) => ( - - ) + edit: (formApi: FormApi) => }); attributes.push({ title: 'NAME SUFFIX', view: source.kustomize && source.kustomize.nameSuffix, - edit: (formApi: FormApi) => ( - - ) + edit: (formApi: FormApi) => }); attributes.push({ title: 'NAMESPACE', - view: source.kustomize && source.kustomize.namespace, - edit: (formApi: FormApi) => ( - - ) + view: app.spec.source.kustomize && app.spec.source.kustomize.namespace, + edit: (formApi: FormApi) => }); - const srcImages = ((repoDetails && repoDetails.kustomize && repoDetails.kustomize.images) || []).map(val => kustomize.parse(val)); + 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)); if (srcImages.length > 0) { @@ -738,7 +182,7 @@ function gatherDetails( getParamsEditableItems( app, 'IMAGES', - isMultiSource ? 'spec.sources[' + ind + '].kustomize.images' : 'spec.source.kustomize.images', + 'spec.source.kustomize.images', removedOverrides, setRemovedOverrides, distinct(imagesByName.keys(), overridesByName.keys()).map(name => { @@ -755,19 +199,17 @@ function gatherDetails( ) ); } - } else if (repoDetails.type === 'Helm' && repoDetails.helm) { - const isValuesObject = source?.helm?.valuesObject; - const helmValues = isValuesObject ? jsYaml.dump(source.helm.valuesObject) : source?.helm?.values; + } else if (props.details.type === 'Helm' && props.details.helm) { attributes.push({ title: 'VALUES FILES', view: (source.helm && (source.helm.valueFiles || []).join(', ')) || 'No values files selected', edit: (formApi: FormApi) => ( @@ -789,21 +231,21 @@ function gatherDetails( return (
    -                            
    +                            
                             
    ); } }); const paramsByName = new Map(); - (repoDetails.helm.parameters || []).forEach(param => paramsByName.set(param.name, param)); + (props.details.helm.parameters || []).forEach(param => paramsByName.set(param.name, param)); const overridesByName = new Map(); ((source.helm && source.helm.parameters) || []).forEach((override, i) => overridesByName.set(override.name, i)); attributes = attributes.concat( getParamsEditableItems( app, 'PARAMETERS', - isMultiSource ? 'spec.sources[' + ind + '].helm.parameters' : 'spec.source.helm.parameters', + 'spec.source.helm.parameters', removedOverrides, setRemovedOverrides, distinct(paramsByName.keys(), overridesByName.keys()).map(name => { @@ -819,14 +261,14 @@ function gatherDetails( ) ); const fileParamsByName = new Map(); - (repoDetails.helm.fileParameters || []).forEach(param => fileParamsByName.set(param.name, param)); + (props.details.helm.fileParameters || []).forEach(param => fileParamsByName.set(param.name, param)); const fileOverridesByName = new Map(); ((source.helm && source.helm.fileParameters) || []).forEach((override, i) => fileOverridesByName.set(override.name, i)); attributes = attributes.concat( getParamsEditableItems( app, 'PARAMETERS', - isMultiSource ? 'spec.sources[' + ind + '].helm.parameters' : 'spec.source.helm.parameters', + 'spec.source.helm.parameters', removedOverrides, setRemovedOverrides, distinct(fileParamsByName.keys(), fileOverridesByName.keys()).map(name => { @@ -841,19 +283,14 @@ function gatherDetails( }) ) ); - } else if (repoDetails.type === 'Plugin') { + } else if (props.details.type === 'Plugin') { attributes.push({ title: 'NAME', view:
    {ValueEditor(app.spec.source?.plugin?.name, null)}
    , edit: (formApi: FormApi) => ( services.authService.plugins()}> {(plugins: Plugin[]) => ( - p.name)}} - /> + p.name)}} /> )} ) @@ -869,13 +306,11 @@ function gatherDetails( ))}
    ), - edit: (formApi: FormApi) => ( - - ) + edit: (formApi: FormApi) => }); const parametersSet = new Set(); - if (repoDetails?.plugin?.parametersAnnouncement) { - for (const announcement of repoDetails.plugin.parametersAnnouncement) { + if (props.details?.plugin?.parametersAnnouncement) { + for (const announcement of props.details.plugin.parametersAnnouncement) { parametersSet.add(announcement.name); } } @@ -889,7 +324,7 @@ function gatherDetails( parametersSet.delete(key); } parametersSet.forEach(name => { - const announcement = repoDetails.plugin.parametersAnnouncement?.find(param => param.name === 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.'; @@ -923,7 +358,7 @@ function gatherDetails( ), edit: (formApi: FormApi) => ( ( ( + edit: (formApi: FormApi) => }); attributes.push({ title: 'TOP-LEVEL ARGUMENTS', @@ -1030,13 +464,7 @@ function gatherDetails( {i.name}='{i.value}' {i.code && 'code'} )), - edit: (formApi: FormApi) => ( - - ) + edit: (formApi: FormApi) => }); attributes.push({ title: 'EXTERNAL VARIABLES', @@ -1045,51 +473,94 @@ function gatherDetails( {i.name}='{i.value}' {i.code && 'code'} )), - edit: (formApi: FormApi) => ( - - ) + edit: (formApi: FormApi) => }); attributes.push({ title: 'INCLUDE', view: directory && directory.include, - edit: (formApi: FormApi) => ( - - ) + edit: (formApi: FormApi) => }); attributes.push({ title: 'EXCLUDE', view: directory && directory.exclude, - edit: (formApi: FormApi) => ( - - ) + edit: (formApi: FormApi) => }); } - return attributes; -} -// For Sources field. Get one source with index i from the list -async function getSourceFromAppSources(aSource: models.ApplicationSource, name: string, project: string, index: number, version: number) { - const repoDetail = await services.repos.appDetails(aSource, name, project, index, version).catch(() => ({ - type: 'Directory' as models.AppSourceType, - path: aSource.path - })); - return repoDetail; -} + return ( + { + const src = getAppDefaultSource(input); -// Delete when source field is removed -async function getSingleSource(app: models.Application) { - if (app.spec.source) { - const repoDetail = await services.repos.appDetails(getAppDefaultSource(app), app.metadata.name, app.spec.project, 0, 0).catch(() => ({ - type: 'Directory' as models.AppSourceType, - path: getAppDefaultSource(app).path - })); - return repoDetail; - } - return null; -} + function isDefined(item: any) { + return item !== null && item !== undefined; + } + function isDefinedWithVersion(item: any) { + return item !== null && item !== undefined && item.match(/:/); + } + + if (src.helm && src.helm.parameters) { + src.helm.parameters = src.helm.parameters.filter(isDefined); + } + 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={((props.details.plugin || app?.spec?.source?.plugin) && cloneDeep(app)) || app} + validate={updatedApp => { + const errors = {} as any; + + for (const fieldPath of ['spec.source.directory.jsonnet.tlas', 'spec.source.directory.jsonnet.extVars']) { + const invalid = ((getNestedField(updatedApp, fieldPath) || []) as Array).filter(item => !item.name && !item.code); + 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-parameters/kustomize-image.test.ts b/ui/src/app/applications/components/application-parameters/kustomize-image.test.ts index 75106fc301513..471228d780523 100644 --- a/ui/src/app/applications/components/application-parameters/kustomize-image.test.ts +++ b/ui/src/app/applications/components/application-parameters/kustomize-image.test.ts @@ -1,4 +1,4 @@ -import {format, parse} from './kustomize-image'; +import { format, parse } from './kustomize-image'; test('parse image version override', () => { const image = parse('foo/bar:v1.0.0'); @@ -8,7 +8,7 @@ test('parse image version override', () => { }); test('format image version override', () => { - const formatted = format({name: 'foo/bar', newTag: 'v1.0.0'}); + const formatted = format({ name: 'foo/bar', newTag: 'v1.0.0' }); expect(formatted).toBe('foo/bar:v1.0.0'); }); @@ -21,7 +21,7 @@ test('parse image name override', () => { }); test('format image name override', () => { - const formatted = format({name: 'foo/bar', newTag: 'v1.0.0', newName: 'foo/bar1'}); + const formatted = format({ name: 'foo/bar', newTag: 'v1.0.0', newName: 'foo/bar1' }); expect(formatted).toBe('foo/bar=foo/bar1:v1.0.0'); }); @@ -33,6 +33,6 @@ test('parse image digest override', () => { }); test('format image digest override', () => { - const formatted = format({name: 'foo/bar', digest: 'sha:123'}); + const formatted = format({ name: 'foo/bar', digest: 'sha:123' }); expect(formatted).toBe('foo/bar@sha:123'); }); diff --git a/ui/src/app/applications/components/application-parameters/source-panel.scss b/ui/src/app/applications/components/application-parameters/source-panel.scss deleted file mode 100644 index 9ee0b7c0aa785..0000000000000 --- a/ui/src/app/applications/components/application-parameters/source-panel.scss +++ /dev/null @@ -1,18 +0,0 @@ -@import 'node_modules/argo-ui/src/styles/config'; - -.new-source-panel { - - .checkbox-container { - margin: 0.5em ; - } - - pre { - font-family: monospace; - line-height: normal; - white-space: pre; - } - - .row.argo-form-row .columns { - padding-left: 0; - } -} diff --git a/ui/src/app/applications/components/application-parameters/source-panel.tsx b/ui/src/app/applications/components/application-parameters/source-panel.tsx deleted file mode 100644 index 8e750b6e4a9b9..0000000000000 --- a/ui/src/app/applications/components/application-parameters/source-panel.tsx +++ /dev/null @@ -1,375 +0,0 @@ -import {AutocompleteField, DataLoader, DropDownMenu, FormField} from 'argo-ui'; -import * as deepMerge from 'deepmerge'; -import * as React from 'react'; -import {Form, FormApi, FormErrors, Text} from 'react-form'; -import {ApplicationParameters} from '../../../applications/components/application-parameters/application-parameters'; -import {RevisionFormField} from '../../../applications/components/revision-form-field/revision-form-field'; -import {RevisionHelpIcon} from '../../../shared/components'; -import * as models from '../../../shared/models'; -import {services} from '../../../shared/services'; -import './source-panel.scss'; - -// This is similar to what is in application-create-panel.tsx. If the create panel -// is modified to support multi-source apps, then we should refactor and common these up -const appTypes = new Array<{field: string; type: models.AppSourceType}>( - {type: 'Helm', field: 'helm'}, - {type: 'Kustomize', field: 'kustomize'}, - {type: 'Directory', field: 'directory'}, - {type: 'Plugin', field: 'plugin'} -); - -// This is similar to the same function in application-create-panel.tsx. If the create panel -// is modified to support multi-source apps, then we should refactor and common these up -function normalizeAppSource(app: models.Application, type: string): boolean { - const source = app.spec.source; - // eslint-disable-next-line no-prototype-builtins - const repoType = (source.hasOwnProperty('chart') && 'helm') || 'git'; - if (repoType !== type) { - if (type === 'git') { - source.path = source.chart; - delete source.chart; - source.targetRevision = 'HEAD'; - } else { - source.chart = source.path; - delete source.path; - source.targetRevision = ''; - } - return true; - } - return false; -} - -// Use a single source app to represent the 'new source'. This panel will make use of the source field only. -// However, we need to use a template based on an Application so that we can reuse the application-parameters code -const DEFAULT_APP: Partial = { - apiVersion: 'argoproj.io/v1alpha1', - kind: 'Application', - metadata: { - name: '' - }, - spec: { - destination: { - name: '', - namespace: '', - server: '' - }, - source: { - path: '', - repoURL: '', - ref: '', - targetRevision: 'HEAD' - }, - sources: [], - project: '' - } -}; - -export const SourcePanel = (props: { - appCurrent: models.Application; - onSubmitFailure: (error: string) => any; - updateApp: (app: models.Application) => any; - getFormApi: (api: FormApi) => any; -}) => { - const [explicitPathType, setExplicitPathType] = React.useState<{path: string; type: models.AppSourceType}>(null); - const appInEdit = deepMerge(DEFAULT_APP, {}); - - function normalizeTypeFields(formApi: FormApi, type: models.AppSourceType) { - const appToNormalize = formApi.getFormState().values; - for (const item of appTypes) { - if (item.type !== type) { - delete appToNormalize.spec.source[item.field]; - } - } - formApi.setAllValues(appToNormalize); - } - - return ( - - Promise.all([services.repos.list()]).then(([reposInfo]) => ({reposInfo}))}> - {({reposInfo}) => { - const repos = reposInfo.map(info => info.repo).sort(); - return ( -
    -
    { - let samePath = false; - let sameChartVersion = false; - let pathError = null; - let chartError = null; - if (a.spec.source.repoURL && a.spec.source.path) { - props.appCurrent.spec.sources.forEach(source => { - if (source.repoURL === a.spec.source.repoURL && source.path === a.spec.source.path) { - samePath = true; - pathError = 'Provided path in the selected repository URL was already added to this multi-source application'; - } - }); - } - if (a.spec.source.repoURL && a.spec.source.chart) { - props.appCurrent.spec.sources.forEach(source => { - if ( - source.repoURL === a.spec.source.repoURL && - source.chart === a.spec.source.chart && - source.targetRevision === a.spec.source.targetRevision - ) { - sameChartVersion = true; - chartError = - 'Version ' + - source.targetRevision + - ' of chart ' + - source.chart + - ' from the selected repository was already added to this multi-source application'; - } - }); - } - if (!samePath) { - if (!a.spec.source.path && !a.spec.source.chart && !a.spec.source.ref) { - pathError = 'Path or Ref is required'; - } - } - if (!sameChartVersion) { - if (!a.spec.source.chart && !a.spec.source.path && !a.spec.source.ref) { - chartError = 'Chart is required'; - } - } - return { - 'spec.source.repoURL': !a.spec.source.repoURL && 'Repository URL is required', - // eslint-disable-next-line no-prototype-builtins - 'spec.source.targetRevision': !a.spec.source.targetRevision && a.spec.source.hasOwnProperty('chart') && 'Version is required', - 'spec.source.path': pathError, - 'spec.source.chart': chartError - }; - }} - defaultValues={appInEdit} - onSubmitFailure={(errors: FormErrors) => { - let errorString: string = ''; - let i = 0; - for (const key in errors) { - if (errors[key]) { - i++; - errorString = errorString.concat(i + '. ' + errors[key] + ' '); - } - } - props.onSubmitFailure(errorString); - }} - onSubmit={values => { - props.updateApp(values as models.Application); - }} - getApi={props.getFormApi}> - {api => { - // eslint-disable-next-line no-prototype-builtins - const repoType = (api.getFormState().values.spec.source.hasOwnProperty('chart') && 'helm') || 'git'; - const repoInfo = reposInfo.find(info => info.repo === api.getFormState().values.spec.source.repoURL); - if (repoInfo) { - normalizeAppSource(appInEdit, repoInfo.type || 'git'); - } - const sourcePanel = () => ( -
    -

    SOURCE

    -
    -
    - -
    -
    -
    - {(repoInfo && ( - - {(repoInfo.type || 'git').toUpperCase()} - - )) || ( - ( -

    - {repoType.toUpperCase()} -

    - )} - items={['git', 'helm'].map((type: 'git' | 'helm') => ({ - title: type.toUpperCase(), - action: () => { - if (repoType !== type) { - const updatedApp = api.getFormState().values as models.Application; - if (normalizeAppSource(updatedApp, type)) { - api.setAllValues(updatedApp); - } - } - } - }))} - /> - )} -
    -
    -
    - {(repoType === 'git' && ( - - -
    - - (src.repoURL && - (await services.repos - .apps(src.repoURL, src.revision, appInEdit.metadata.name, props.appCurrent.spec.project) - .then(apps => Array.from(new Set(apps.map(item => item.path))).sort()) - .catch(() => new Array()))) || - new Array() - }> - {(apps: string[]) => ( - - )} - -
    -
    - -
    -
    - )) || ( - - (src.repoURL && services.repos.charts(src.repoURL).catch(() => new Array())) || - new Array() - }> - {(charts: models.HelmChart[]) => { - const selectedChart = charts.find(chart => chart.name === api.getFormState().values.spec.source.chart); - return ( -
    -
    - chart.name), - filterSuggestions: true - }} - /> -
    -
    - - -
    -
    - ); - }} -
    - )} -
    - ); - - const typePanel = () => ( - { - if (src.repoURL && src.targetRevision && (src.path || src.chart)) { - return services.repos.appDetails(src, src.appName, props.appCurrent.spec.project, 0, 0).catch(() => ({ - type: 'Directory', - details: {} - })); - } else { - return { - type: 'Directory', - details: {} - }; - } - }}> - {(details: models.RepoAppDetails) => { - const type = (explicitPathType && explicitPathType.path === appInEdit.spec.source.path && explicitPathType.type) || details.type; - if (details.type !== type) { - switch (type) { - case 'Helm': - details = { - type, - path: details.path, - helm: {name: '', valueFiles: [], path: '', parameters: [], fileParameters: []} - }; - break; - case 'Kustomize': - details = {type, path: details.path, kustomize: {path: ''}}; - break; - case 'Plugin': - details = {type, path: details.path, plugin: {name: '', env: []}}; - break; - // Directory - default: - details = {type, path: details.path, directory: {}}; - break; - } - } - return ( - - ( -

    - {type} -

    - )} - items={appTypes.map(item => ({ - title: item.type, - action: () => { - setExplicitPathType({type: item.type, path: appInEdit.spec.source.path}); - normalizeTypeFields(api, item.type); - } - }))} - /> - { - api.setAllValues(updatedApp); - }} - /> -
    - ); - }} -
    - ); - - return ( - - {sourcePanel()} - - {typePanel()} - - ); - }} - -
    - ); - }} -
    -
    - ); -}; 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 caba162b82eba..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 @@ -11,7 +11,7 @@ import {PodViewPreferences, services, ViewPreferences} from '../../../shared/ser import {ResourceTreeNode} from '../application-resource-tree/application-resource-tree'; import {ResourceIcon} from '../resource-icon'; import {ResourceLabel} from '../resource-label'; -import {ComparisonStatusIcon, isYoungerThanXMinutes, HealthStatusIcon, nodeKey, PodHealthIcon} from '../utils'; +import {ComparisonStatusIcon, isYoungerThanXMinutes, HealthStatusIcon, nodeKey, PodHealthIcon, deletePodAction} from '../utils'; import './pod-view.scss'; import {PodTooltip} from './pod-tooltip'; @@ -145,7 +145,9 @@ export class PodView extends React.Component {
    ) : null} - {group.info?.map(infoItem =>
    {infoItem.value}
    )} + {group.info?.map(infoItem => ( +
    {infoItem.value}
    + ))}
    )}
    @@ -157,43 +159,83 @@ export class PodView extends React.Component { )}
    - {group.pods.map( - pod => - this.props.nodeMenu && ( - ( - } - popperOptions={{ - modifiers: { - preventOverflow: { - enabled: true - }, - hide: { - enabled: false - }, - flip: { - enabled: false - } - } - }} - key={pod.metadata.name}> -
    - {isYoungerThanXMinutes(pod, 30) && ( - - )} -
    - -
    -
    -
    - )}> - {() => this.props.nodeMenu(pod)} -
    - ) - )} + {group.pods.map(pod => ( + ( + } + popperOptions={{ + modifiers: { + preventOverflow: { + enabled: true + }, + hide: { + enabled: false + }, + flip: { + enabled: false + } + } + }} + key={pod.metadata.name}> +
    + {isYoungerThanXMinutes(pod, 30) && ( + + )} +
    + +
    +
    +
    + )} + items={[ + { + title: ( + + Info + + ), + action: () => this.props.onItemClick(pod.fullName) + }, + { + title: ( + + Logs + + ), + action: () => { + this.appContext.apis.navigation.goto('.', {node: pod.fullName, tab: 'logs'}, {replace: true}); + } + }, + { + title: ( + + Exec + + ), + action: () => { + this.appContext.apis.navigation.goto('.', {node: pod.fullName, tab: 'exec'}, {replace: true}); + } + }, + { + title: ( + + Delete + + ), + action: () => { + deletePodAction( + pod, + this.appContext, + this.props.app.metadata.name, + this.props.app.metadata.namespace + ); + } + } + ]} + /> + ))}
    PODS
    {(podPrefs.sortMode === 'parentResource' || podPrefs.sortMode === 'topLevelResource') && ( 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 9f3879d617732..0cc459b0dc52b 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 @@ -150,6 +150,10 @@ background-color: themed('pod-cyan') !important; } } + &--nodegroup{ + padding-left: 3.5em; + padding-top: 25px; + } &--lower-section { left: 8px; @@ -428,4 +432,4 @@ } -} +} \ No newline at end of file diff --git a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.test.tsx b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.test.tsx index 45a7797aa9a0f..d9918fb28523d 100644 --- a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.test.tsx +++ b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.test.tsx @@ -1,109 +1,93 @@ -import {compareNodes, describeNode, ResourceTreeNode} from './application-resource-tree'; +import {compareNodes, describeNode, ResourceTreeNode} from "./application-resource-tree"; -test('describeNode.NoImages', () => { - expect( - describeNode({ - kind: 'my-kind', - name: 'my-name', - namespace: 'my-ns', - } as ResourceTreeNode), - ).toBe(`Kind: my-kind +test("describeNode.NoImages", () => { + expect(describeNode({ + kind: "my-kind", + name: "my-name", + namespace: "my-ns", + } as ResourceTreeNode)).toBe(`Kind: my-kind Namespace: my-ns -Name: my-name`); +Name: my-name`) }); -test('describeNode.Images', () => { - expect( - describeNode({ - kind: 'my-kind', - name: 'my-name', - namespace: 'my-ns', - images: ['my-image:v1'], - } as ResourceTreeNode), - ).toBe(`Kind: my-kind +test("describeNode.Images", () => { + expect(describeNode({ + kind: "my-kind", + name: "my-name", + namespace: "my-ns", + images: ['my-image:v1'], + } as ResourceTreeNode)).toBe(`Kind: my-kind Namespace: my-ns Name: my-name Images: -- my-image:v1`); +- my-image:v1`) }); -test('compareNodes', () => { +test("compareNodes", () => { const nodes = [ - { - resourceVersion: '1', - name: 'a', - info: [ - { - name: 'Revision', - value: 'Rev:1', - }, - ], - } as ResourceTreeNode, - { - orphaned: false, - resourceVersion: '1', - name: 'a', - info: [ - { - name: 'Revision', - value: 'Rev:1', - }, - ], - } as ResourceTreeNode, - { - orphaned: false, - resourceVersion: '1', - name: 'b', - info: [ - { - name: 'Revision', - value: 'Rev:1', - }, - ], - } as ResourceTreeNode, - { - orphaned: false, - resourceVersion: '2', - name: 'a', - info: [ - { - name: 'Revision', - value: 'Rev:2', - }, - ], - } as ResourceTreeNode, - { - orphaned: false, - resourceVersion: '2', - name: 'b', - info: [ - { - name: 'Revision', - value: 'Rev:2', - }, - ], - } as ResourceTreeNode, - { - orphaned: true, - resourceVersion: '1', - name: 'a', - info: [ - { - name: 'Revision', - value: 'Rev:1', - }, - ], - } as ResourceTreeNode, + { + resourceVersion: "1", + name: "a", + info: [{ + "name": "Revision", + "value": "Rev:1" + }], + } as ResourceTreeNode, + { + orphaned: false, + resourceVersion: "1", + name: "a", + info: [{ + "name": "Revision", + "value": "Rev:1" + }], + } as ResourceTreeNode, + { + orphaned: false, + resourceVersion: "1", + name: "b", + info: [{ + "name": "Revision", + "value": "Rev:1" + }], + } as ResourceTreeNode, + { + orphaned: false, + resourceVersion: "2", + name: "a", + info: [{ + "name": "Revision", + "value": "Rev:2" + }], + } as ResourceTreeNode, + { + orphaned: false, + resourceVersion: "2", + name: "b", + info: [{ + "name": "Revision", + "value": "Rev:2" + }], + } as ResourceTreeNode, + { + orphaned: true, + resourceVersion: "1", + name: "a", + info: [{ + "name": "Revision", + "value": "Rev:1" + }], + } as ResourceTreeNode, ]; - expect(compareNodes(nodes[0], nodes[1])).toBe(0); - expect(compareNodes(nodes[2], nodes[1])).toBe(1); - expect(compareNodes(nodes[1], nodes[2])).toBe(-1); - expect(compareNodes(nodes[3], nodes[2])).toBe(-1); - expect(compareNodes(nodes[2], nodes[3])).toBe(1); - expect(compareNodes(nodes[4], nodes[3])).toBe(1); - expect(compareNodes(nodes[3], nodes[4])).toBe(-1); - expect(compareNodes(nodes[5], nodes[4])).toBe(1); - expect(compareNodes(nodes[4], nodes[5])).toBe(-1); - expect(compareNodes(nodes[0], nodes[4])).toBe(-1); - expect(compareNodes(nodes[4], nodes[0])).toBe(1); + expect(compareNodes(nodes[0], nodes[1])).toBe(0) + expect(compareNodes(nodes[2], nodes[1])).toBe(1) + expect(compareNodes(nodes[1], nodes[2])).toBe(-1) + expect(compareNodes(nodes[3], nodes[2])).toBe(-1) + expect(compareNodes(nodes[2], nodes[3])).toBe(1) + expect(compareNodes(nodes[4], nodes[3])).toBe(1) + expect(compareNodes(nodes[3], nodes[4])).toBe(-1) + expect(compareNodes(nodes[5], nodes[4])).toBe(1) + expect(compareNodes(nodes[4], nodes[5])).toBe(-1) + expect(compareNodes(nodes[0], nodes[4])).toBe(-1) + expect(compareNodes(nodes[4], nodes[0])).toBe(1) }); 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 0e0dfb9ac12a4..3d5b1782a0e0c 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 @@ -1,4 +1,4 @@ -import {DropDown, Tooltip} from 'argo-ui'; +import {DropDown, DropDownMenu, Tooltip} from 'argo-ui'; import * as classNames from 'classnames'; import * as dagre from 'dagre'; import * as React from 'react'; @@ -15,6 +15,7 @@ import {ResourceLabel} from '../resource-label'; import { BASE_COLORS, ComparisonStatusIcon, + deletePodAction, getAppOverridesCount, HealthStatusIcon, isAppNode, @@ -93,7 +94,15 @@ const NODE_TYPES = { podGroup: 'pod_group' }; // generate lots of colors with different darkness -const TRAFFIC_COLORS = [0, 0.25, 0.4, 0.6].map(darken => BASE_COLORS.map(item => color(item).darken(darken).hex())).reduce((first, second) => first.concat(second), []); +const TRAFFIC_COLORS = [0, 0.25, 0.4, 0.6] + .map(darken => + BASE_COLORS.map(item => + color(item) + .darken(darken) + .hex() + ) + ) + .reduce((first, second) => first.concat(second), []); function getGraphSize(nodes: dagre.Node[]): {width: number; height: number} { let width = 0; @@ -291,7 +300,7 @@ function renderGroupedNodes(props: ApplicationResourceTreeProps, node: {count: n className='application-resource-tree__node-title application-resource-tree__direction-center-left' onClick={() => 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} {node.kind === 'ReplicaSet' ? ( ) : ( - pods.map( - pod => - props.nodeMenu && ( - ( - - {pod.metadata.name} -
    Health: {pod.health}
    - {pod.createdAt && ( - - Created: - - {pod.createdAt} - - ago ({{pod.createdAt}}) - - )} -
    + 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 } - popperOptions={{ - modifiers: { - preventOverflow: { - enabled: true - }, - hide: { - enabled: false - }, - flip: { - enabled: false - } - } - }} - key={pod.metadata.name}> -
    - {isYoungerThanXMinutes(pod, 30) && ( - - )} -
    - -
    -
    - - )}> - {() => props.nodeMenu(pod)} - - ) - ) + } + }} + 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); + } + } + ]} + /> + )) )}
    ); @@ -858,8 +892,7 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) => resourceVersion: props.app.metadata.resourceVersion, group: 'argoproj.io', version: '', - // @ts-expect-error its not any - children: [], + children: Array(), status: props.app.status.sync.status, health: props.app.status.health, uid: props.app.kind + '-' + props.app.metadata.namespace + '-' + props.app.metadata.name, @@ -1002,7 +1035,7 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) => const loadBalancers = root.networkingInfo.ingress.map(ingress => ingress.hostname || ingress.ip); const colorByService = new Map(); (childrenByParentKey.get(treeNodeKey(root)) || []).forEach((child, i) => colorByService.set(treeNodeKey(child), TRAFFIC_COLORS[i % TRAFFIC_COLORS.length])); - (childrenByParentKey.get(treeNodeKey(root)) || []).sort(compareNodes).forEach(child => { + (childrenByParentKey.get(treeNodeKey(root)) || []).sort(compareNodes).forEach((child, i) => { processNode(child, root, [colorByService.get(treeNodeKey(child))]); }); if (root.podGroup && props.showCompactNodes) { @@ -1191,58 +1224,6 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) => }); const graphNodes = graph.nodes(); const size = getGraphSize(graphNodes.map(id => graph.node(id))); - - const resourceTreeRef = React.useRef(); - - const graphMoving = React.useRef({ - enable: false, - x: 0, - y: 0 - }); - - const onGraphDragStart: React.PointerEventHandler = e => { - if (e.target !== resourceTreeRef.current) { - return; - } - - if (!resourceTreeRef.current?.parentElement) { - return; - } - - graphMoving.current.enable = true; - graphMoving.current.x = e.clientX; - graphMoving.current.y = e.clientY; - }; - - const onGraphDragMoving: React.PointerEventHandler = e => { - if (!graphMoving.current.enable) { - return; - } - - if (!resourceTreeRef.current?.parentElement) { - return; - } - - const graphContainer = resourceTreeRef.current?.parentElement; - - const currentPositionX = graphContainer.scrollLeft; - const currentPositionY = graphContainer.scrollTop; - - const scrollLeft = currentPositionX + graphMoving.current.x - e.clientX; - const scrollTop = currentPositionY + graphMoving.current.y - e.clientY; - - graphContainer.scrollTo(scrollLeft, scrollTop); - - graphMoving.current.x = e.clientX; - graphMoving.current.y = e.clientY; - }; - - const onGraphDragEnd: React.PointerEventHandler = e => { - if (graphMoving.current.enable) { - graphMoving.current.enable = false; - e.preventDefault(); - } - }; return ( (graphNodes.length === 0 && ( @@ -1251,11 +1232,6 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) => )) || (
    {graphNodes.map(key => { 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 fb139f273a24c..fbf23c95796bf 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 @@ -7,9 +7,6 @@ label { padding-right: 2em; color: $argo-color-gray-8; - @include themify($themes){ - color: themed('text-2'); - } } } &__diff { @@ -31,8 +28,4 @@ .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-resources-diff/application-resources-diff.tsx b/ui/src/app/applications/components/application-resources-diff/application-resources-diff.tsx index f21dbe326fa41..18eb941981a37 100644 --- a/ui/src/app/applications/components/application-resources-diff/application-resources-diff.tsx +++ b/ui/src/app/applications/components/application-resources-diff/application-resources-diff.tsx @@ -20,8 +20,8 @@ export const ApplicationResourcesDiff = (props: ApplicationResourcesDiffProps) = const diffText = props.states .map(state => { return { - a: state.normalizedLiveState ? jsYaml.dump(state.normalizedLiveState, {indent: 2}) : '', - b: state.predictedLiveState ? jsYaml.dump(state.predictedLiveState, {indent: 2}) : '', + a: state.normalizedLiveState ? jsYaml.safeDump(state.normalizedLiveState, {indent: 2}) : '', + b: state.predictedLiveState ? jsYaml.safeDump(state.predictedLiveState, {indent: 2}) : '', hook: state.hook, // doubles as sort order name: (state.group || '') + '/' + state.kind + '/' + (state.namespace ? state.namespace + '/' : '') + state.name diff --git a/ui/src/app/applications/components/application-resources-diff/individual-diff-section.tsx b/ui/src/app/applications/components/application-resources-diff/individual-diff-section.tsx index 8104e7e232b23..7d82659cbfdc7 100644 --- a/ui/src/app/applications/components/application-resources-diff/individual-diff-section.tsx +++ b/ui/src/app/applications/components/application-resources-diff/individual-diff-section.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import {useState} from 'react'; -import {Diff, Hunk, tokenize, markEdits} from 'react-diff-view'; +import {Diff, Hunk} from 'react-diff-view'; import 'react-diff-view/style/index.css'; import './application-resources-diff.scss'; @@ -15,12 +15,6 @@ export interface IndividualDiffSectionProps { export const IndividualDiffSection = (props: IndividualDiffSectionProps) => { const {file, showPath, whiteBox, viewType} = props; const [collapsed, setCollapsed] = useState(false); - const options = { - highlight: false, - enhancers: [markEdits(file.hunks, {type: 'block'})] - }; - const token = tokenize(file.hunks, options); - return (
    {showPath && ( @@ -30,7 +24,7 @@ export const IndividualDiffSection = (props: IndividualDiffSectionProps) => {

    )} {!collapsed && ( - + {(hunks: any) => hunks.map((hunk: any) => )} )} diff --git a/ui/src/app/applications/components/application-retry-options/application-retry-options.tsx b/ui/src/app/applications/components/application-retry-options/application-retry-options.tsx index c9e6f9265f208..48b72de19551e 100644 --- a/ui/src/app/applications/components/application-retry-options/application-retry-options.tsx +++ b/ui/src/app/applications/components/application-retry-options/application-retry-options.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-prototype-builtins */ import * as React from 'react'; import {FormApi, NestedForm, Text, Form} from 'react-form'; import {Checkbox, FormField} from 'argo-ui'; @@ -8,7 +7,6 @@ import * as models from '../../../shared/models'; import './application-retry-options.scss'; -// eslint-disable-next-line no-useless-escape const durationRegex = /^([\d\.]+[HMS])+$/i; const durationRegexError = 'Should be 1h10m10s/10h10m/10m/10s'; diff --git a/ui/src/app/applications/components/application-retry-view/application-retry-view.tsx b/ui/src/app/applications/components/application-retry-view/application-retry-view.tsx index 0baeca32ce6ee..a97d9d5b98609 100644 --- a/ui/src/app/applications/components/application-retry-view/application-retry-view.tsx +++ b/ui/src/app/applications/components/application-retry-view/application-retry-view.tsx @@ -21,6 +21,6 @@ const retryOptionsView: Array<(initData: models.RetryStrategy) => React.ReactNod ]; export const ApplicationRetryView = ({initValues}: {initValues?: models.RetryStrategy}) => { - const result = !initValues ? 'Retry disabled' : retryOptionsView.map(render => render(initValues)); + const result = !initValues ? 'Retry disabled' : retryOptionsView.map((render, i) => render(initValues)); return
    {result}
    ; }; 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 643e24034d54a..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 @@ -5,10 +5,9 @@ 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, getAppDefaultSyncRevisionExtra, getAppOperationState} from '../utils'; -import {getConditionCategory, HealthStatusIcon, OperationState, syncStatusMessage, getAppDefaultSyncRevision, getAppDefaultOperationSyncRevision} from '../utils'; +import {ApplicationSyncWindowStatusIcon, ComparisonStatusIcon, getAppDefaultSource, getAppOperationState} from '../utils'; +import {getConditionCategory, HealthStatusIcon, OperationState, syncStatusMessage, helpTip} from '../utils'; import {RevisionMetadataPanel} from './revision-metadata-panel'; -import * as utils from '../utils'; import './application-status-panel.scss'; @@ -33,12 +32,13 @@ const sectionLabel = (info: SectionInfo) => ( ); -const sectionHeader = (info: SectionInfo, onClick?: () => any) => { +const sectionHeader = (info: SectionInfo, hasMultipleSources: boolean, onClick?: () => any) => { return (
    {sectionLabel(info)} {onClick && ( - )} @@ -66,13 +66,11 @@ export const ApplicationStatusPanel = ({application, showDiff, showOperation, sh const statusExtensions = services.extensions.getStatusPanelExtensions(); - const revision = getAppDefaultSyncRevision(application); - const operationStateRevision = getAppDefaultOperationSyncRevision(application); const infos = cntByCategory.get('info'); const warnings = cntByCategory.get('warning'); const errors = cntByCategory.get('error'); const source = getAppDefaultSource(application); - const hasMultipleSources = application.spec.sources?.length > 0; + const hasMultipleSources = application.spec.sources && application.spec.sources.length > 0; return (
    @@ -91,7 +89,8 @@ export const ApplicationStatusPanel = ({application, showDiff, showOperation, sh title: 'SYNC STATUS', helpContent: 'Whether or not the version of your app is up to date with your repo. You may wish to sync your app if it is out-of-sync.' }, - () => showMetadataInfo(application.status.sync ? 'SYNC_STATUS_REVISION' : null) + hasMultipleSources, + () => showMetadataInfo(application.status.sync ? application.status.sync.revision : '') )}
    @@ -108,21 +107,16 @@ export const ApplicationStatusPanel = ({application, showDiff, showOperation, sh
    {application.spec.syncPolicy?.automated ? 'Auto sync is enabled.' : 'Auto sync is not enabled.'}
    - {application.status && - application.status.sync && - (hasMultipleSources - ? application.status.sync.revisions && application.status.sync.revisions[0] && application.spec.sources && !application.spec.sources[0].chart - : application.status.sync.revision && !application.spec.source.chart) && ( -
    - -
    - )} + {application.status && application.status.sync && application.status.sync.revision && !application.spec.source.chart && ( +
    + +
    + )}
    {appOperationState && ( @@ -136,33 +130,29 @@ export const ApplicationStatusPanel = ({application, showDiff, showOperation, sh daysSinceLastSynchronized + ' days since last sync. Click for the status of that sync.' }, - () => - showMetadataInfo( - appOperationState.syncResult && (appOperationState.syncResult.revisions || appOperationState.syncResult.revision) - ? 'OPERATION_STATE_REVISION' - : null - ) + hasMultipleSources, + () => showMetadataInfo(appOperationState.syncResult ? appOperationState.syncResult.revision : '') )}
    showOperation && showOperation()}> {' '} - {appOperationState.syncResult && (appOperationState.syncResult.revision || appOperationState.syncResult.revisions) && ( + {appOperationState.syncResult && appOperationState.syncResult.revision && (
    - to {getAppDefaultSyncRevisionExtra(application)} + to
    )}
    +
    {appOperationState.phase}
    - {(appOperationState.syncResult && operationStateRevision && ( + {(appOperationState.syncResult && appOperationState.syncResult.revision && ( )) ||
    {appOperationState.message}
    } diff --git a/ui/src/app/applications/components/application-status-panel/revision-metadata-panel.tsx b/ui/src/app/applications/components/application-status-panel/revision-metadata-panel.tsx index 085958d0f1cf5..fea9a0c8e2c4b 100644 --- a/ui/src/app/applications/components/application-status-panel/revision-metadata-panel.tsx +++ b/ui/src/app/applications/components/application-status-panel/revision-metadata-panel.tsx @@ -3,12 +3,12 @@ import * as React from 'react'; import {Timestamp} from '../../../shared/components/timestamp'; import {services} from '../../../shared/services'; -export const RevisionMetadataPanel = (props: {appName: string; appNamespace: string; type: string; revision: string; versionId: number}) => { +export const RevisionMetadataPanel = (props: {appName: string; appNamespace: string; type: string; revision: string}) => { if (props.type === 'helm') { return ; } return ( - services.applications.revisionMetadata(props.appName, props.appNamespace, props.revision, 0, props.versionId)} errorRenderer={() =>
    }> + services.applications.revisionMetadata(props.appName, props.appNamespace, props.revision)} errorRenderer={() =>
    }> {m => ( Promise; @@ -170,105 +160,104 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => { title: 'CREATED AT', view: formatCreationTimestamp(app.metadata.creationTimestamp) }, - !hasMultipleSources && { + { title: 'REPO URL', view: , - edit: (formApi: FormApi) => + edit: (formApi: FormApi) => + hasMultipleSources ? ( + helpTip('REPO URL is not editable for applications with multiple sources. You can edit them in the "Manifest" tab.') + ) : ( + + ) }, - ...(!hasMultipleSources - ? isHelm - ? [ - { - title: 'CHART', - view: ( - - {source.chart}:{source.targetRevision} - - ), - edit: (formApi: FormApi) => - hasMultipleSources ? ( - helpTip('CHART is not editable for applications with multiple sources. You can edit them in the "Manifest" tab.') - ) : ( - services.repos.charts(src.repoURL).catch(() => new Array())}> - {(charts: models.HelmChart[]) => ( -
    -
    - chart.name), - filterSuggestions: true - }} - /> -
    - { - const chartInfo = data.charts.find(chart => chart.name === data.chart); - return (chartInfo && chartInfo.versions) || new Array(); - }}> - {(versions: string[]) => ( -
    - - -
    - )} -
    + ...(isHelm + ? [ + { + title: 'CHART', + view: ( + + {source.chart}:{source.targetRevision} + + ), + edit: (formApi: FormApi) => + hasMultipleSources ? ( + helpTip('CHART is not editable for applications with multiple sources. You can edit them in the "Manifest" tab.') + ) : ( + services.repos.charts(src.repoURL).catch(() => new Array())}> + {(charts: models.HelmChart[]) => ( +
    +
    + chart.name), + filterSuggestions: true + }} + />
    - )} - - ) - } - ] - : [ - { - title: 'TARGET REVISION', - view: , - edit: (formApi: FormApi) => - hasMultipleSources ? ( - helpTip('TARGET REVISION is not editable for applications with multiple sources. You can edit them in the "Manifest" tab.') - ) : ( - - ) - }, - { - title: 'PATH', - view: ( - - {processPath(source.path)} - - ), - edit: (formApi: FormApi) => - hasMultipleSources ? ( - helpTip('PATH is not editable for applications with multiple sources. You can edit them in the "Manifest" tab.') - ) : ( - - ) - } - ] - : []), + { + const chartInfo = data.charts.find(chart => chart.name === data.chart); + return (chartInfo && chartInfo.versions) || new Array(); + }}> + {(versions: string[]) => ( +
    + + +
    + )} +
    +
    + )} +
    + ) + } + ] + : [ + { + title: 'TARGET REVISION', + view: , + edit: (formApi: FormApi) => + hasMultipleSources ? ( + helpTip('TARGET REVISION is not editable for applications with multiple sources. You can edit them in the "Manifest" tab.') + ) : ( + + ) + }, + { + title: 'PATH', + view: ( + + {source.path ?? ''} + + ), + edit: (formApi: FormApi) => + hasMultipleSources ? ( + helpTip('PATH is not editable for applications with multiple sources. You can edit them in the "Manifest" tab.') + ) : ( + + ) + } + ]), + { title: 'REVISION HISTORY LIMIT', view: app.spec.revisionHistoryLimit, edit: (formApi: FormApi) => (
    - +
    -
    - {urls - .map(item => item.split('|')) - .map((parts, i) => ( - - ))} -
    + {urls.map((url, i) => { + return ( + + {url.title}   + + ); + })} ) }); @@ -494,7 +478,6 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => {
    This is a multi-source app, see the Sources tab for repository URLs and source-related information. : <>} validate={input => ({ 'spec.project': !input.spec.project && 'Project name is required', 'spec.destination.server': !input.spec.destination.server && input.spec.destination.hasOwnProperty('server') && 'Cluster server is required', diff --git a/ui/src/app/applications/components/application-summary/edit-notification-subscriptions.tsx b/ui/src/app/applications/components/application-summary/edit-notification-subscriptions.tsx index 771204edc05f5..1d774cd9cd4df 100644 --- a/ui/src/app/applications/components/application-summary/edit-notification-subscriptions.tsx +++ b/ui/src/app/applications/components/application-summary/edit-notification-subscriptions.tsx @@ -10,7 +10,7 @@ import './edit-notification-subscriptions.scss'; export const NOTIFICATION_SUBSCRIPTION_ANNOTATION_PREFIX = 'notifications.argoproj.io/subscribe'; -export const NOTIFICATION_SUBSCRIPTION_ANNOTATION_REGEX = new RegExp(`^notifications\\.argoproj\\.io/subscribe\\.[a-zA-Z-]{1,100}\\.[a-zA-Z-]{1,100}$`); +export const NOTIFICATION_SUBSCRIPTION_ANNOTATION_REGEX = new RegExp(`^notifications\.argoproj\.io\/subscribe\.[a-zA-Z-]{1,100}\.[a-zA-Z-]{1,100}$`); export type TNotificationSubscription = { trigger: string; @@ -96,22 +96,20 @@ export const useEditNotificationSubscriptions = (annotations: models.Application const onRemoveSubscription = (idx: number) => idx >= 0 && setSubscriptions(subscriptions.filter((_, i) => i !== idx)); - const withNotificationSubscriptions = - (updateApp: ApplicationSummaryProps['updateApp']) => - (...args: Parameters) => { - const app = args[0]; + const withNotificationSubscriptions = (updateApp: ApplicationSummaryProps['updateApp']) => (...args: Parameters) => { + const app = args[0]; - const notificationSubscriptionsRaw = notificationSubscriptionsParser.subscriptionsToAnnotations(subscriptions); + const notificationSubscriptionsRaw = notificationSubscriptionsParser.subscriptionsToAnnotations(subscriptions); - if (Object.keys(notificationSubscriptionsRaw)?.length) { - app.metadata.annotations = { - ...notificationSubscriptionsRaw, - ...(app.metadata.annotations || {}) - }; - } + if (Object.keys(notificationSubscriptionsRaw)?.length) { + app.metadata.annotations = { + ...notificationSubscriptionsRaw, + ...(app.metadata.annotations || {}) + }; + } - return updateApp(app, args[1]); - }; + return updateApp(app, args[1]); + }; const onResetNotificationSubscriptions = () => setSubscriptions(notificationSubscriptionsParser.annotationsToSubscriptions(annotations)); diff --git a/ui/src/app/applications/components/application-summary/edit-notification-subscriptsions.test.ts b/ui/src/app/applications/components/application-summary/edit-notification-subscriptsions.test.ts deleted file mode 100644 index 8cfc93bb29a06..0000000000000 --- a/ui/src/app/applications/components/application-summary/edit-notification-subscriptsions.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {NOTIFICATION_SUBSCRIPTION_ANNOTATION_REGEX} from "./edit-notification-subscriptions"; - -test('rejects incorrect annotations', () => { - expect(NOTIFICATION_SUBSCRIPTION_ANNOTATION_REGEX.test('notifications_argoproj_io/subscribe_a_b')).toEqual(false) - expect(NOTIFICATION_SUBSCRIPTION_ANNOTATION_REGEX.test('notifications.argoproj.io/subscribe.a.b')).toEqual(true) -}) diff --git a/ui/src/app/applications/components/application-urls.test.ts b/ui/src/app/applications/components/application-urls.test.ts index 74a0dfc20e3c0..a3093a5a29c1d 100644 --- a/ui/src/app/applications/components/application-urls.test.ts +++ b/ui/src/app/applications/components/application-urls.test.ts @@ -1,4 +1,4 @@ -import {ExternalLink, ExternalLinks, InvalidExternalLinkError} from './application-urls'; +import { ExternalLink, ExternalLinks, InvalidExternalLinkError } from './application-urls'; test('rejects malicious URLs', () => { expect(() => { @@ -29,19 +29,24 @@ test('allows relative URLs', () => { expect(new ExternalLink('/applications').ref).toEqual('/applications'); }); + test('URLs format', () => { expect(new ExternalLink('https://localhost:8080/applications')).toEqual({ ref: 'https://localhost:8080/applications', title: 'https://localhost:8080/applications', - }); + }) expect(new ExternalLink('title|https://localhost:8080/applications')).toEqual({ ref: 'https://localhost:8080/applications', title: 'title', - }); + }) }); + test('malicious URLs from list to be removed', () => { - const urls: string[] = ['javascript:alert("hi")', 'https://localhost:8080/applications']; + const urls: string[] = [ + 'javascript:alert("hi")', + 'https://localhost:8080/applications', + ] const links = ExternalLinks(urls); expect(links).toHaveLength(1); @@ -51,8 +56,16 @@ test('malicious URLs from list to be removed', () => { }); }); + test('list to be sorted', () => { - const urls: string[] = ['https://a', 'https://b', 'a|https://c', 'z|https://c', 'x|https://d', 'x|https://c']; + const urls: string[] = [ + 'https://a', + 'https://b', + 'a|https://c', + 'z|https://c', + 'x|https://d', + 'x|https://c', + ] const links = ExternalLinks(urls); // 'a|https://c', @@ -62,12 +75,12 @@ test('list to be sorted', () => { // 'https://a', // 'https://b', expect(links).toHaveLength(6); - expect(links[0].title).toEqual('a'); - expect(links[1].title).toEqual('x'); - expect(links[1].ref).toEqual('https://c'); - expect(links[2].title).toEqual('x'); - expect(links[2].ref).toEqual('https://d'); - expect(links[3].title).toEqual('z'); - expect(links[4].title).toEqual('https://a'); - expect(links[5].title).toEqual('https://b'); + expect(links[0].title).toEqual('a') + expect(links[1].title).toEqual('x') + expect(links[1].ref).toEqual('https://c') + expect(links[2].title).toEqual('x') + expect(links[2].ref).toEqual('https://d') + expect(links[3].title).toEqual('z') + expect(links[4].title).toEqual('https://a') + expect(links[5].title).toEqual('https://b') }); diff --git a/ui/src/app/applications/components/applications-list/applications-status-bar.tsx b/ui/src/app/applications/components/applications-list/applications-status-bar.tsx index d1b4f8a460a14..c20b5612d121f 100644 --- a/ui/src/app/applications/components/applications-list/applications-status-bar.tsx +++ b/ui/src/app/applications/components/applications-list/applications-status-bar.tsx @@ -53,7 +53,7 @@ export const ApplicationsStatusBar = ({applications}: ApplicationsStatusBarProps return ( - {() => ( + {ctx => ( <> {totalItems > 1 && (
    diff --git a/ui/src/app/applications/components/applications-list/applications-summary.tsx b/ui/src/app/applications/components/applications-list/applications-summary.tsx index efff821a01def..0a77350fd1127 100644 --- a/ui/src/app/applications/components/applications-list/applications-summary.tsx +++ b/ui/src/app/applications/components/applications-list/applications-summary.tsx @@ -40,7 +40,7 @@ export const ApplicationsSummary = ({applications}: {applications: models.Applic }, { title: 'CLUSTERS', - value: new Set(applications.map(app => app.spec.destination.server || app.spec.destination.name)).size + value: new Set(applications.map(app => app.spec.destination.server)).size }, { title: 'NAMESPACES', diff --git a/ui/src/app/applications/components/applications-list/applications-table.tsx b/ui/src/app/applications/components/applications-list/applications-table.tsx index 4952a0f08fb9a..a34ea5d4d2191 100644 --- a/ui/src/app/applications/components/applications-list/applications-table.tsx +++ b/ui/src/app/applications/components/applications-list/applications-table.tsx @@ -59,7 +59,7 @@ export const ApplicationsTable = (props: { applications-list__entry applications-list__entry--health-${app.status.health.status} ${selectedApp === i ? 'applications-tiles__selected' : ''}`}>
    ctx.navigation.goto(`applications/${app.metadata.namespace}/${app.metadata.name}`, {}, {event: e})}> + onClick={e => ctx.navigation.goto(`/applications/${app.metadata.namespace}/${app.metadata.name}`, {}, {event: e})}>
    @@ -140,21 +140,9 @@ export const ApplicationsTable = (props: { )} items={[ - { - title: 'Sync', - iconClassName: 'fa fa-fw fa-sync', - action: () => props.syncApplication(app.metadata.name, app.metadata.namespace) - }, - { - title: 'Refresh', - iconClassName: 'fa fa-fw fa-redo', - action: () => props.refreshApplication(app.metadata.name, app.metadata.namespace) - }, - { - title: 'Delete', - iconClassName: 'fa fa-fw fa-times-circle', - action: () => props.deleteApplication(app.metadata.name, app.metadata.namespace) - } + {title: 'Sync', action: () => props.syncApplication(app.metadata.name, app.metadata.namespace)}, + {title: 'Refresh', action: () => props.refreshApplication(app.metadata.name, app.metadata.namespace)}, + {title: 'Delete', action: () => props.deleteApplication(app.metadata.name, app.metadata.namespace)} ]} />
    diff --git a/ui/src/app/applications/components/applications-list/applications-tiles.tsx b/ui/src/app/applications/components/applications-list/applications-tiles.tsx index ce27238b87d60..3467d3b952a87 100644 --- a/ui/src/app/applications/components/applications-list/applications-tiles.tsx +++ b/ui/src/app/applications/components/applications-list/applications-tiles.tsx @@ -118,7 +118,7 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
    - ctx.navigation.goto(`applications/${app.metadata.namespace}/${app.metadata.name}`, {view: pref.appDetails.view}, {event: e}) + ctx.navigation.goto(`/applications/${app.metadata.namespace}/${app.metadata.name}`, {view: pref.appDetails.view}, {event: e}) }>
    { setValues(update); }} style={{width: '100%'}} - inputStyle={{marginBottom: '0.5em', backgroundColor: 'black', border: 'none', color: '#fff'}} + inputStyle={{marginBottom: '0.5em', backgroundColor: 'black', border: 'none'}} /> )} {((props.field ? tags : options) || []).map((opt, i) => ( diff --git a/ui/src/app/applications/components/label-selector.ts b/ui/src/app/applications/components/label-selector.ts index 7814e068052c8..86909ee0d116b 100644 --- a/ui/src/app/applications/components/label-selector.ts +++ b/ui/src/app/applications/components/label-selector.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-prototype-builtins */ type operatorFn = (labels: {[name: string]: string}, key: string, values: string[]) => boolean; const operators: {[type: string]: operatorFn} = { diff --git a/ui/src/app/applications/components/pod-logs-viewer/container-selector.tsx b/ui/src/app/applications/components/pod-logs-viewer/container-selector.tsx index bcd67ead7e9ab..5dca12dd6af6c 100644 --- a/ui/src/app/applications/components/pod-logs-viewer/container-selector.tsx +++ b/ui/src/app/applications/components/pod-logs-viewer/container-selector.tsx @@ -27,7 +27,7 @@ export const ContainerSelector = ({ }; if (containerNames.length <= 1) return <>; return ( - +

    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 D#fBI6 diff --git a/docs/assets/okta-create-oidc-app.png b/docs/assets/okta-create-oidc-app.png deleted file mode 100644 index cf0b75b0e4a21f6196799a5097b52261f876e627..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 360829 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