diff --git a/.github/workflows/ci-build.yaml b/.github/workflows/ci-build.yaml index a72f453ba513c..6a9745c5891db 100644 --- a/.github/workflows/ci-build.yaml +++ b/.github/workflows/ci-build.yaml @@ -74,7 +74,7 @@ jobs: uses: golangci/golangci-lint-action@v2 with: version: v1.38.0 - args: --timeout 5m --exclude SA5011 + args: --timeout 10m --exclude SA5011 test-go: name: Run unit tests for Go packages diff --git a/.github/workflows/image.yaml b/.github/workflows/image.yaml index 076550ac6f4a8..4a1cae9c7ce80 100644 --- a/.github/workflows/image.yaml +++ b/.github/workflows/image.yaml @@ -51,7 +51,7 @@ jobs: env: TOKEN: ${{ secrets.TOKEN }} - run: | - docker run -v $(pwd):/src -w /src --rm -t lyft/kustomizer:v3.3.0 kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }} + docker run -u $(id -u):$(id -g) -v $(pwd):/src -w /src --rm -t ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }} kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }} git config --global user.email 'ci@argoproj.com' git config --global user.name 'CI' git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ steps.image.outputs.tag }}' && git push) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index cd8525d7ba924..7d14fec19bfab 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -266,40 +266,6 @@ jobs: asset_content_type: application/octet-stream if: ${{ env.DRY_RUN != 'true' }} - # include argocd-util as part of release artifacts (argoproj/argo-cd#5174) - - name: Upload argocd-util-linux-amd64 binary to release assets - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./dist/argocd-linux-amd64 - asset_name: argocd-util-linux-amd64 - asset_content_type: application/octet-stream - if: ${{ env.DRY_RUN != 'true' }} - - - name: Upload argocd-util-darwin-amd64 binary to release assets - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./dist/argocd-darwin-amd64 - asset_name: argocd-util-darwin-amd64 - asset_content_type: application/octet-stream - if: ${{ env.DRY_RUN != 'true' }} - - - name: Upload argocd-util-windows-amd64 binary to release assets - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./dist/argocd-windows-amd64.exe - asset_name: argocd-util-windows-amd64.exe - asset_content_type: application/octet-stream - if: ${{ env.DRY_RUN != 'true' }} - - name: Update homebrew formula env: HOMEBREW_TOKEN: ${{ secrets.RELEASE_HOMEBREW_TOKEN }} diff --git a/.gitignore b/.gitignore index aed35e25f6d1e..b639ffa2c9c86 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,9 @@ .idea/ .DS_Store vendor/ -dist/ +dist/* +ui/dist/app/* +!ui/dist/app/gitkeep site/ *.iml # delve debug binaries @@ -19,5 +21,4 @@ node_modules/ cmd/argocd/argocd cmd/argocd-application-controller/argocd-application-controller cmd/argocd-repo-server/argocd-repo-server -cmd/argocd-server/argocd-server -cmd/argocd-util/argocd-util \ No newline at end of file +cmd/argocd-server/argocd-server \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d05907df7b393..fe967b2dcc137 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,100 @@ # Changelog +## v2.1.1 (2021-08-25) + +### Bug Fixes + +- fix: password reset requirements (#7071) +- fix: Custom Styles feature is broken (#7067) +- fix(ui): Add State to props passed to Extensions (#7045) +- fix: keep uid_entrypoint.sh for backward compatibility (#7047) + +## v2.1.0 (2021-08-20) + +> [Upgrade instructions](./docs/operator-manual/upgrading/2.0-2.1.md) + +### Argo CD Core + +Argo CD Core - lightweight Argo CD distribution that packages only core GitOps features and relies +on Kubernetes API/RBAC to power UI and CLI. + +### Core Features + +* The synchronization process became much much faster and requires significantly less memory. +* An additional caching that ensures that each repository's target revisions are queried only once per + reconciliation cycle. This dramatically reduces the number of Git requests. +* Improved Diffing Customizations: use JQ path expressions to exclude required fields from the diffing. +* Health assessment support for new CRDs: introduced health assessment of CRDs from trident.netapp.io, + elasticsearch.k8s.elastic.co, cluster.x-k8s.io, and minio.min.io API groups. + +### Improved Settings + +A set of changes had been implemented to simplify configuring Argo CD. + +* Simplified Repository Registration: you no longer need to modify the argocd-cm ConfigMap to register a + new Git or Helm repository. +* Enhanced Resource Customizations: the resource.customizations key has been deprecated in favor of + a separate ConfigMap key per resource. +* Reference secret values from any Kubernetes secret: starting v2.1 you can use sensitive data stored in + any Kubernetes secret to configure Argo CD. +* Simplify parametrization of Argo CD server processes: an additional optional ConfigMap argocd-cmd-params-cm + has been introduced. + +### Refreshed User Interface + +* Enhanced and more consistent filters on Applications List and Applications Details pages. +* Status bar on the Application List page. +* The redesigned search box on the Application List page and more. + +### The argocd-util CLI deprecation + +The argocd CLI and now available under argocd admin subcommand. + +## v2.0.5 (2021-07-22) + +* fix: allow argocd-notification ingress to repo-server (#6746) +* fix: argocd-server crashes due to nil pointer dereference (#6757) +* fix: WebUI failure when loading pod view 't.parentRefs is undefined' (#6490) (#6535) +* fix: prevent 'cannot read property "filter" of undefined' during nodes filtering (#6453) +* fix: download Pod Logs button not honouring argocd-server rootpath (#6548) (#6627) +* fix: Version warning banner in docs (#6682) +* fix: upgrade gitops engine to fix workflow health check + +## v2.0.4 (2021-06-22) + +* fix: typo in networkPolicy definition in manifests (#6532) +* fix: Update redis to 6.2.4 (#6475) +* fix: allows access to dex metrics from any pod (#6420) +* fix: add client side retry to prevent 'transport is closing' errors (#6402) +* fix: Update documentation Argocd app CRD health with app of apps (#6281) +* fix(ui): Crash on application pod view (#6384) +* chore: pin mkdocs version to fix docs build (#6421) +* chore: regenerate manifests using codegen (#6422) +* refactor: use RLock and RUnlock for project to improve performance (#6225) +* chore: Update Golang to v1.16.4 (#6358) + +## v2.0.3 (2021-05-27) + +### Bug Fixes + +* fix: add missing --container flag to 'argocd app logs' command (#6320) +* fix: grpc web proxy must ensure to read full header (#6319) +* fix: controller should refresh app before running sync operation (#6294) + +## v2.0.2 (2021-05-20) + +### Bug Fixes + +* fix: enable access to metrics port in embedded network policies (#6277) +* fix: display log streaming error in logs viewer (#6100) (#6273) +* fix: Don't count errored or completed neighbor pods toward resource consumption (#6259) +* fix: Enable kex algo diffie-hellman-group-exchange-sha256 for go-git ssh (#6256) +* fix: copy github app key from repocreds (#6140, #6197) +* fix(ui): UI crashes after reinstalling ArgoCD (#6218) +* fix: add network policies to restrict traffic flow between argocd components (#6156) +* fix: Revert "feat: Add health checks for kubernetes-external-secrets (#5435)" +* chore: Allow ingress traffic to argocd-server by default (#6179) + ## v2.0.1 (2021-04-15) ### Bug Fixes diff --git a/Dockerfile b/Dockerfile index 4a03ed42e6cda..4e2b56c678484 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,7 +47,6 @@ RUN groupadd -g 999 argocd && \ mkdir -p /home/argocd && \ chown argocd:0 /home/argocd && \ chmod g=u /home/argocd && \ - chmod g=u /etc/passwd && \ apt-get update && \ apt-get dist-upgrade -y && \ apt-get install -y git git-lfs python3-pip tini gpg tzdata && \ @@ -62,9 +61,9 @@ COPY --from=builder /usr/local/bin/ks /usr/local/bin/ks COPY --from=builder /usr/local/bin/helm2 /usr/local/bin/helm2 COPY --from=builder /usr/local/bin/helm /usr/local/bin/helm COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize -# script to add current (possibly arbitrary) user to /etc/passwd at runtime -# (if it's not already there, to be openshift friendly) -COPY uid_entrypoint.sh /usr/local/bin/uid_entrypoint.sh +COPY entrypoint.sh /usr/local/bin/entrypoint.sh +# keep uid_entrypoint.sh for backward compatibility +RUN ln -s /usr/local/bin/entrypoint.sh /usr/local/bin/uid_entrypoint.sh # support for mounting configuration from a configmap RUN mkdir -p /app/config/ssh && \ @@ -113,6 +112,7 @@ RUN go mod download # Perform the build COPY . . +COPY --from=argocd-ui /src/dist/app /go/src/github.com/argoproj/argo-cd/ui/dist/app RUN make argocd-all ARG BUILD_ALL_CLIS=true @@ -126,10 +126,8 @@ RUN if [ "$BUILD_ALL_CLIS" = "true" ] ; then \ #################################################################################################### FROM argocd-base COPY --from=argocd-build /go/src/github.com/argoproj/argo-cd/dist/argocd* /usr/local/bin/ -COPY --from=argocd-ui ./src/dist/app /shared/app USER root -RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-util RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-repo-server RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-application-controller diff --git a/Dockerfile.dev b/Dockerfile.dev index df7cc471d4670..671460816caa5 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -11,9 +11,4 @@ RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-repo-server RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-application-controller RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-dex -RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-util -RUN ln -s /usr/local/bin/argocd-darwin-amd64 /usr/local/bin/argocd-util-darwin-amd64 -RUN ln -s /usr/local/bin/argocd-windows-amd64.exe /usr/local/bin/argocd-util-windows-amd64.exe USER 999 - -COPY --from=argocd-ui ./src/dist/app /shared/app diff --git a/Makefile b/Makefile index ed4b22eda3252..30ab271ab739c 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ PACKAGE=github.com/argoproj/argo-cd/v2/common CURRENT_DIR=$(shell pwd) DIST_DIR=${CURRENT_DIR}/dist CLI_NAME=argocd -UTIL_CLI_NAME=argocd-util BIN_NAME=argocd HOST_OS:=$(shell go env GOOS) @@ -159,7 +158,7 @@ IMAGE_PREFIX=${IMAGE_NAMESPACE}/ endif .PHONY: all -all: cli image argocd-util +all: cli image # We have some legacy requirements for being checked out within $GOPATH. # The ensure-gopath target can be used as dependency to ensure we are running @@ -212,10 +211,6 @@ cli: test-tools-image cli-local: clean-debug CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd -.PHONY: cli-argocd -cli-argocd: - go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd - .PHONY: release-cli release-cli: clean-debug image docker create --name tmp-argocd-linux $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) @@ -224,16 +219,6 @@ release-cli: clean-debug image docker cp tmp-argocd-linux:/usr/local/bin/argocd-windows-amd64.exe ${DIST_DIR}/argocd-windows-amd64.exe docker rm tmp-argocd-linux -.PHONY: argocd-util -argocd-util: clean-debug - # Build argocd-util as a statically linked binary, so it could run within the alpine-based dex container (argoproj/argo-cd#844) - CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${UTIL_CLI_NAME} ./cmd - -# .PHONY: dev-tools-image -# dev-tools-image: -# docker build -t $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE) . -f hack/Dockerfile.dev-tools -# docker tag $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE) $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE):$(DEV_TOOLS_VERSION) - .PHONY: test-tools-image test-tools-image: docker build --build-arg UID=$(shell id -u) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile . @@ -273,6 +258,8 @@ IMAGE_TAG="dev-$(shell git describe --always --dirty)" image: docker build -t argocd-base --target argocd-base . docker build -t argocd-ui --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/' CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-darwin-amd64 ./cmd CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-windows-amd64.exe ./cmd @@ -280,9 +267,6 @@ image: 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-dex - ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-util - ln -sfn ${DIST_DIR}/argocd-darwin-amd64 ${DIST_DIR}/argocd-util-darwin-amd64 - ln -sfn ${DIST_DIR}/argocd-windows-amd64.exe ${DIST_DIR}/argocd-util-windows-amd64.exe cp Dockerfile.dev dist docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist else @@ -467,6 +451,12 @@ start-local: mod-vendor-local dep-ui-local ARGOCD_E2E_TEST=false \ goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START} +# Run goreman start with exclude option , provide exclude env variable with list of services +.PHONY: run +run: + bash ./hack/goreman-start.sh + + # Runs pre-commit validation with the virtualized toolchain .PHONY: pre-commit pre-commit: codegen build lint test diff --git a/OWNERS b/OWNERS index b80afda334c9e..d1272fb598f42 100644 --- a/OWNERS +++ b/OWNERS @@ -9,6 +9,7 @@ approvers: - jessesuen - jgwest - mayzhang2000 +- rbreeze reviewers: - dthomson25 @@ -18,3 +19,4 @@ reviewers: - reginapizza - hblixt - chetan-rns +- wanghong230 diff --git a/Procfile b/Procfile index 3f108a1686f35..0805460e71ce7 100644 --- a/Procfile +++ b/Procfile @@ -1,6 +1,6 @@ controller: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}" -api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --staticassets ui/dist/app" -dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.27.0 serve /dex.yaml" +api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} " +dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.30.0 serve /dex.yaml" redis: bash -c "if [ $ARGOCD_REDIS_LOCAL == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:6.2.4-alpine --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi" repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server go run ./cmd/main.go --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}" ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start' diff --git a/SECURITY.md b/SECURITY.md index 9e810916a19c8..0f4b1d1d877ad 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,6 +1,6 @@ # Security Policy for Argo CD -Version: **v1.1 (2020-06-29)** +Version: **v1.2 (2020-08-07)** ## Preface @@ -56,13 +56,11 @@ We will do our best to react quickly on your inquiry, and to coordinate a fix and disclosure with you. Sometimes, it might take a little longer for us to react (e.g. out of office conditions), so please bear with us in these cases. -We will publish security advisiories using the Git Hub SA feature to keep our -community well informed, and will credit you for your findings (unless you -prefer to stay anonymous, of course). +We will publish security advisiories using the +[Git Hub Security Advisories](https://github.com/argoproj/argo-cd/security/advisories) +feature to keep our community well informed, and will credit you for your +findings (unless you prefer to stay anonymous, of course). -Please report vulnerabilities by e-mail to all of the following people: +Please report vulnerabilities by e-mail to the following address: -* jfischer@redhat.com -* Jesse_Suen@intuit.com -* Alexander_Matyushentsev@intuit.com -* Edward_Lee@intuit.com +* cncf-argo-security@lists.cncf.io diff --git a/USERS.md b/USERS.md index 227797df30bbc..8f60c89aac2c9 100644 --- a/USERS.md +++ b/USERS.md @@ -16,6 +16,7 @@ Currently, the following organizations are **officially** using Argo CD: 1. [AppDirect](https://www.appdirect.com) 1. [Arctiq Inc.](https://www.arctiq.ca) 1. [ARZ Allgemeines Rechenzentrum GmbH ](https://www.arz.at/) +1. [Axual B.V.](https://axual.com) 1. [Baloise](https://www.baloise.com) 1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform) 1. [Beat](https://thebeat.co/en/) @@ -23,13 +24,14 @@ Currently, the following organizations are **officially** using Argo CD: 1. [BioBox Analytics](https://biobox.io) 1. [BMW Group](https://www.bmwgroup.com/) 1. [Camptocamp](https://camptocamp.com) +1. [Capital One](https://www.capitalone.com) 1. [CARFAX](https://www.carfax.com) 1. [Celonis](https://www.celonis.com/) 1. [Chime](https://www.chime.com) 1. [Codefresh](https://www.codefresh.io/) 1. [Codility](https://www.codility.com/) 1. [Commonbond](https://commonbond.co/) -1. [Crédit Agricole](https://www.ca-cib.com) +1. [Crédit Agricole CIB](https://www.ca-cib.com) 1. [CROZ d.o.o.](https://croz.net/) 1. [CyberAgent](https://www.cyberagent.co.jp/en/) 1. [Cybozu](https://cybozu-global.com) @@ -45,7 +47,9 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Fave](https://myfave.com) 1. [Future PLC](https://www.futureplc.com/) 1. [Garner](https://www.garnercorp.com) +1. [G DATA CyberDefense AG](https://www.gdata-software.com/) 1. [Generali Deutschland AG](https://www.generali.de/) +1. [Gitpod](https://www.gitpod.io) 1. [Glovo](https://www.glovoapp.com) 1. [GMETRI](https://gmetri.com/) 1. [Gojek](https://www.gojek.io/) @@ -59,6 +63,7 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Index Exchange](https://www.indexexchange.com/) 1. [InsideBoard](https://www.insideboard.com) 1. [Intuit](https://www.intuit.com/) +1. [Joblift](https://joblift.com/) 1. [JovianX](https://www.jovianx.com/) 1. [Karrot](https://www.daangn.com/) 1. [Kasa](https://kasa.co.kr/) @@ -71,9 +76,11 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Lytt](https://www.lytt.co/) 1. [Major League Baseball](https://mlb.com) 1. [Mambu](https://www.mambu.com/) +1. [Mattermost](https://www.mattermost.com) 1. [Max Kelsen](https://www.maxkelsen.com/) 1. [MindSpore](https://mindspore.cn) 1. [Mirantis](https://mirantis.com/) +1. [mixi Group](https://mixi.co.jp/) 1. [Moengage](https://www.moengage.com/) 1. [Money Forward](https://corp.moneyforward.com/en/) 1. [MOO Print](https://www.moo.com/) @@ -110,6 +117,7 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Speee](https://speee.jp/) 1. [Spendesk](https://spendesk.com/) 1. [Sumo Logic](https://sumologic.com/) +1. [Sutpc](http://www.sutpc.com/) 1. [Swisscom](https://www.swisscom.ch) 1. [Swissquote](https://github.com/swissquote) 1. [Syncier](https://syncier.com/) @@ -123,6 +131,7 @@ Currently, the following organizations are **officially** using Argo CD: 1. [tru.ID](https://tru.id) 1. [Twilio SendGrid](https://sendgrid.com) 1. [tZERO](https://www.tzero.com/) +1. [ungleich.ch](https://ungleich.ch/) 1. [UBIO](https://ub.io/) 1. [UFirstGroup](https://www.ufirstgroup.com/en/) 1. [Universidad Mesoamericana](https://www.umes.edu.gt/) @@ -136,6 +145,8 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Webstores](https://www.webstores.nl) 1. [Whitehat Berlin](https://whitehat.berlin) by Guido Maria Serra +Fenaroli 1. [Witick](https://witick.io/) +1. [WooliesX](https://wooliesx.com.au/) +1. [Woolworths Group](https://www.woolworthsgroup.com.au/) 1. [WSpot](https://www.wspot.com.br/) 1. [Yieldlab](https://www.yieldlab.de/) 1. [Zimpler](https://www.zimpler.com/) @@ -148,3 +159,11 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Boticario](https://www.boticario.com.br/) 1. [Beleza Na Web](https://www.belezanaweb.com.br/) 1. [MariaDB](https://mariadb.com) +1. [Lightricks](https://www.lightricks.com/) +1. [RightRev](https://rightrev.com/) +1. [MeDirect](https://medirect.com.mt/) +1. [Technacy](https://www.technacy.it/) +1. [freee](https://corp.freee.co.jp/en/company/) +1. [Youverify](https://youverify.co/) +1. [Keeeb](https://www.keeeb.com/) +1. [Faro](https://www.faro.com/) diff --git a/VERSION b/VERSION index 227cea215648b..ccbccc3dc6263 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.0 +2.2.0 diff --git a/assets/swagger.json b/assets/swagger.json index 1be636f2813c3..d264b3fc551fb 100644 --- a/assets/swagger.json +++ b/assets/swagger.json @@ -2081,6 +2081,37 @@ } } }, + "/api/v1/projects/{name}/detailed": { + "get": { + "tags": [ + "ProjectService" + ], + "summary": "GetDetailedProject returns a project that include project, global project and scoped resources by name", + "operationId": "ProjectService_GetDetailedProject", + "parameters": [ + { + "type": "string", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/projectDetailedProjectsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + } + } + }, "/api/v1/projects/{name}/events": { "get": { "tags": [ @@ -2863,6 +2894,12 @@ "description": "HTTP/HTTPS proxy to access the repository.", "name": "proxy", "in": "query" + }, + { + "type": "string", + "description": "Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity.", + "name": "project", + "in": "query" } ], "responses": { @@ -3605,6 +3642,9 @@ "oidcConfig": { "$ref": "#/definitions/clusterOIDCConfig" }, + "passwordPattern": { + "type": "string" + }, "plugins": { "type": "array", "items": { @@ -3620,9 +3660,15 @@ "statusBadgeEnabled": { "type": "boolean" }, + "trackingMethod": { + "type": "string" + }, "uiBannerContent": { "type": "string" }, + "uiBannerPermanent": { + "type": "boolean" + }, "uiBannerURL": { "type": "string" }, @@ -3674,6 +3720,32 @@ } } }, + "projectDetailedProjectsResponse": { + "type": "object", + "properties": { + "clusters": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha1Cluster" + } + }, + "globalProjects": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha1AppProject" + } + }, + "project": { + "$ref": "#/definitions/v1alpha1AppProject" + }, + "repositories": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha1Repository" + } + } + } + }, "projectEmptyResponse": { "type": "object" }, @@ -5106,6 +5178,17 @@ "type": "object", "title": "Cluster is the definition of a cluster resource", "properties": { + "annotations": { + "type": "object", + "title": "Annotations for cluster secret metadata", + "additionalProperties": { + "type": "string" + } + }, + "clusterResources": { + "description": "Indicates if cluster level resources should be managed. This setting is used only if cluster is connected in a namespaced mode.", + "type": "boolean" + }, "config": { "$ref": "#/definitions/v1alpha1ClusterConfig" }, @@ -5115,6 +5198,13 @@ "info": { "$ref": "#/definitions/v1alpha1ClusterInfo" }, + "labels": { + "type": "object", + "title": "Labels for cluster secret metadata", + "additionalProperties": { + "type": "string" + } + }, "name": { "type": "string", "title": "Name of the cluster. If omitted, will use the server address" @@ -5126,6 +5216,10 @@ "type": "string" } }, + "project": { + "type": "string", + "title": "Reference between project and cluster that allow you automatically to be added as item inside Destinations project entity" + }, "refreshRequestedAt": { "$ref": "#/definitions/v1Time" }, @@ -5842,6 +5936,10 @@ "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 allow you automatically to be added as item inside SourceRepos project entity" + }, "proxy": { "type": "string", "title": "Proxy specifies the HTTP/HTTPS proxy used to access the repo" diff --git a/cmd/argocd-application-controller/commands/argocd_application_controller.go b/cmd/argocd-application-controller/commands/argocd_application_controller.go index 90c91e0200c91..b301ca6256dce 100644 --- a/cmd/argocd-application-controller/commands/argocd_application_controller.go +++ b/cmd/argocd-application-controller/commands/argocd_application_controller.go @@ -110,10 +110,14 @@ func NewCommand() *cobra.Command { errors.CheckError(err) cache.Cache.SetClient(cacheutil.NewTwoLevelClient(cache.Cache.GetClient(), 10*time.Minute)) - settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace) + var appController *controller.ApplicationController + + settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace, settings.WithRepoOrClusterChangedHandler(func() { + appController.InvalidateProjectsCache() + })) kubectl := kubeutil.NewKubectl() clusterFilter := getClusterFilter() - appController, err := controller.NewApplicationController( + appController, err = controller.NewApplicationController( namespace, settingsMgr, kubeClient, @@ -144,16 +148,16 @@ func NewCommand() *cobra.Command { } clientConfig = cli.AddKubectlFlagsToCmd(&command) - command.Flags().Int64Var(&appResyncPeriod, "app-resync", int64(env.ParseDurationFromEnv("ARGOCD_RECONCILIATION_TIMEOUT", defaultAppResyncPeriod*time.Second, 0, math.MaxInt32).Seconds()), "Time period in seconds for application resync.") + command.Flags().Int64Var(&appResyncPeriod, "app-resync", int64(env.ParseDurationFromEnv("ARGOCD_RECONCILIATION_TIMEOUT", defaultAppResyncPeriod*time.Second, 0, math.MaxInt64).Seconds()), "Time period in seconds for application resync.") command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address.") - command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", int(env.ParseDurationFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS", 60*time.Second, 0, math.MaxInt32).Seconds()), "Repo server RPC call timeout seconds.") + command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.") command.Flags().IntVar(&statusProcessors, "status-processors", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS", 20, 0, math.MaxInt32), "Number of application status processors") command.Flags().IntVar(&operationProcessors, "operation-processors", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_OPERATION_PROCESSORS", 10, 0, math.MaxInt32), "Number of application operation processors") command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json") command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error") command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level") command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDMetrics, "Start metrics server on given port") - command.Flags().DurationVar(&metricsCacheExpiration, "metrics-cache-expiration", env.ParseDurationFromEnv("ARGOCD_APPLICATION_CONTROLLER_METRICS_CACHE_EXPIRATION", 0*time.Second, 0, math.MaxInt32), "Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)") + command.Flags().DurationVar(&metricsCacheExpiration, "metrics-cache-expiration", env.ParseDurationFromEnv("ARGOCD_APPLICATION_CONTROLLER_METRICS_CACHE_EXPIRATION", 0*time.Second, 0, math.MaxInt64), "Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)") command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS", 5, 0, math.MaxInt32), "Specifies timeout between application self heal attempts") command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", 20, "Number of allowed concurrent kubectl fork/execs. Any value less the 1 means no limit.") command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT", false), "Disable TLS on connections to repo server") diff --git a/cmd/argocd-server/commands/argocd_server.go b/cmd/argocd-server/commands/argocd_server.go index f627788fa72de..13126c0ddd95c 100644 --- a/cmd/argocd-server/commands/argocd_server.go +++ b/cmd/argocd-server/commands/argocd_server.go @@ -52,7 +52,6 @@ func NewCommand() *cobra.Command { glogLevel int clientConfig clientcmd.ClientConfig repoServerTimeoutSeconds int - staticAssetsDir string baseHRef string rootPath string repoServerAddress string @@ -64,6 +63,7 @@ func NewCommand() *cobra.Command { frameOptions string repoServerPlaintext bool repoServerStrictTLS bool + staticAssetsDir string ) var command = &cobra.Command{ Use: cliName, @@ -128,7 +128,6 @@ func NewCommand() *cobra.Command { ListenPort: listenPort, MetricsPort: metricsPort, Namespace: namespace, - StaticAssetsDir: staticAssetsDir, BaseHRef: baseHRef, RootPath: rootPath, KubeClientset: kubeclientset, @@ -141,6 +140,7 @@ func NewCommand() *cobra.Command { Cache: cache, XFrameOptions: frameOptions, RedisClient: redisClient, + StaticAssetsDir: staticAssetsDir, } stats.RegisterStackDumper() @@ -159,7 +159,7 @@ func NewCommand() *cobra.Command { clientConfig = cli.AddKubectlFlagsToCmd(command) command.Flags().BoolVar(&insecure, "insecure", env.ParseBoolFromEnv("ARGOCD_SERVER_INSECURE", false), "Run server without TLS") - command.Flags().StringVar(&staticAssetsDir, "staticassets", "", "Static assets directory path") + command.Flags().StringVar(&staticAssetsDir, "staticassets", env.StringFromEnv("ARGOCD_SERVER_STATIC_ASSETS", "/shared/app"), "Directory path that contains additional static assets") command.Flags().StringVar(&baseHRef, "basehref", env.StringFromEnv("ARGOCD_SERVER_BASEHREF", "/"), "Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from /") command.Flags().StringVar(&rootPath, "rootpath", env.StringFromEnv("ARGOCD_SERVER_ROOTPATH", ""), "Used if Argo CD is running behind reverse proxy under subpath different from /") command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json") @@ -172,7 +172,7 @@ func NewCommand() *cobra.Command { command.AddCommand(cli.NewVersionCmd(cliName)) command.Flags().IntVar(&listenPort, "port", common.DefaultPortAPIServer, "Listen on given port") command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port") - command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", int(env.ParseDurationFromEnv("ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS", 60*time.Second, 0, math.MaxInt32).Seconds()), "Repo server RPC call timeout seconds.") + command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.") command.Flags().StringVar(&frameOptions, "x-frame-options", env.StringFromEnv("ARGOCD_SERVER_X_FRAME_OPTIONS", "sameorigin"), "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".") command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_PLAINTEXT", false), "Use a plaintext client (non-TLS) to connect to repository server") command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_STRICT_TLS", false), "Perform strict validation of TLS certificates when connecting to repo server") diff --git a/cmd/argocd/commands/account.go b/cmd/argocd/commands/account.go index 3a9e2ca656a86..9a79e1ab06221 100644 --- a/cmd/argocd/commands/account.go +++ b/cmd/argocd/commands/account.go @@ -377,7 +377,7 @@ func NewAccountDeleteTokenCommand(clientOpts *argocdclient.ClientOptions) *cobra argocd account delete-token ID # Delete token of the account with the specified name -argocd account generate-token --account `, +argocd account delete-token --account ID`, Run: func(c *cobra.Command, args []string) { if len(args) != 1 { c.HelpFunc()(c, args) diff --git a/cmd/argocd-util/commands/argocd_util.go b/cmd/argocd/commands/admin/admin.go similarity index 95% rename from cmd/argocd-util/commands/argocd_util.go rename to cmd/argocd/commands/admin/admin.go index 7cc8a1ffcb5e6..6ad1dad6af998 100644 --- a/cmd/argocd-util/commands/argocd_util.go +++ b/cmd/argocd/commands/admin/admin.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "reflect" @@ -15,14 +15,11 @@ import ( cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" "github.com/argoproj/argo-cd/v2/common" - "github.com/argoproj/argo-cd/v2/util/cli" "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/settings" ) const ( - // CLIName is the name of the CLI - cliName = "argocd-util" // YamlSeparator separates sections of a YAML file yamlSeparator = "---\n" ) @@ -35,23 +32,21 @@ var ( appplicationSetResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applicationsets"} ) -// NewCommand returns a new instance of an argocd command -func NewCommand() *cobra.Command { +// NewAdminCommand returns a new instance of an argocd command +func NewAdminCommand() *cobra.Command { var ( pathOpts = clientcmd.NewDefaultPathOptions() ) var command = &cobra.Command{ - Use: cliName, - Short: "argocd-util tools used by Argo CD", - Long: "argocd-util has internal utility tools used by Argo CD", + 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) }, } - command.AddCommand(cli.NewVersionCmd(cliName)) command.AddCommand(NewClusterCommand(pathOpts)) command.AddCommand(NewProjectsCommand()) command.AddCommand(NewSettingsCommand()) @@ -59,6 +54,7 @@ func NewCommand() *cobra.Command { command.AddCommand(NewRepoCommand()) command.AddCommand(NewImportCommand()) command.AddCommand(NewExportCommand()) + command.AddCommand(NewDashboardCommand()) 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") diff --git a/cmd/argocd-util/commands/app.go b/cmd/argocd/commands/admin/app.go similarity index 85% rename from cmd/argocd-util/commands/app.go rename to cmd/argocd/commands/admin/app.go index c02243e6a87db..a44557a171e65 100644 --- a/cmd/argocd-util/commands/app.go +++ b/cmd/argocd/commands/admin/app.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "context" @@ -10,6 +10,8 @@ import ( "sort" "time" + "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/ghodss/yaml" "github.com/spf13/cobra" apiv1 "k8s.io/api/core/v1" @@ -34,6 +36,7 @@ import ( "github.com/argoproj/argo-cd/v2/util/config" "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/argo-cd/v2/util/errors" + "github.com/argoproj/argo-cd/v2/util/io" kubeutil "github.com/argoproj/argo-cd/v2/util/kube" "github.com/argoproj/argo-cd/v2/util/settings" ) @@ -62,28 +65,29 @@ func NewGenAppSpecCommand() *cobra.Command { labels []string outputFormat string annotations []string + inline bool ) var command = &cobra.Command{ Use: "generate-spec APPNAME", Short: "Generate declarative config for an application", Example: ` # Generate declarative config for a directory app - argocd-util app generate-spec guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --directory-recurse + argocd admin app generate-spec guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --directory-recurse # Generate declarative config for a Jsonnet app - argocd-util app generate-spec jsonnet-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path jsonnet-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --jsonnet-ext-str replicas=2 + argocd admin app generate-spec jsonnet-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path jsonnet-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --jsonnet-ext-str replicas=2 # Generate declarative config for a Helm app - argocd-util app generate-spec helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2 + argocd admin app generate-spec helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2 # Generate declarative config for a Helm app from a Helm repo - argocd-util app generate-spec nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc + argocd admin app generate-spec nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc # Generate declarative config for a Kustomize app - argocd-util app generate-spec kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1 + argocd admin app generate-spec 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 # Generate declarative config for a app using a custom tool: - argocd-util app generate-spec ksane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane + argocd admin app generate-spec ksane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane `, Run: func(c *cobra.Command, args []string) { app, err := cmdutil.ConstructApp(fileURL, appName, labels, annotations, args, appOpts, c.Flags()) @@ -94,9 +98,11 @@ func NewGenAppSpecCommand() *cobra.Command { os.Exit(1) } - var printResources []interface{} - printResources = append(printResources, app) - errors.CheckError(cmdutil.PrintResources(printResources, outputFormat)) + out, closer, err := getOutWriter(inline, fileURL) + errors.CheckError(err) + defer io.Close(closer) + + errors.CheckError(PrintResources(outputFormat, out, app)) }, } command.Flags().StringVar(&appName, "name", "", "A name for the app, ignored if a file is set (DEPRECATED)") @@ -104,6 +110,7 @@ func NewGenAppSpecCommand() *cobra.Command { command.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "Labels to apply to the app") 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") // Only complete files with appropriate extension. err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"}) @@ -249,7 +256,7 @@ func NewReconcileCommand() *cobra.Command { if repoServerAddress == "" { printLine("Repo server is not provided, trying to port-forward to argocd-repo-server pod.") overrides := clientcmd.ConfigOverrides{} - repoServerPort, err := kubeutil.PortForward("app.kubernetes.io/name=argocd-repo-server", 8081, namespace, &overrides) + repoServerPort, err := kubeutil.PortForward(8081, namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server") errors.CheckError(err) repoServerAddress = fmt.Sprintf("localhost:%d", repoServerPort) } @@ -324,11 +331,11 @@ func reconcileApplications( settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, namespace) argoDB := db.NewDB(namespace, settingsMgr, kubeClientset) - appInformerFactory := appinformers.NewFilteredSharedInformerFactory( + appInformerFactory := appinformers.NewSharedInformerFactoryWithOptions( appClientset, 1*time.Hour, - namespace, - func(options *v1.ListOptions) {}, + appinformers.WithNamespace(namespace), + appinformers.WithTweakListOptions(func(options *v1.ListOptions) {}), ) appInformer := appInformerFactory.Argoproj().V1alpha1().Applications().Informer() @@ -361,7 +368,7 @@ func reconcileApplications( ) appStateManager := controller.NewAppStateManager( - argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second) + argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking()) appsList, err := appClientset.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), v1.ListOptions{LabelSelector: selector}) if err != nil { @@ -403,5 +410,5 @@ func reconcileApplications( } func newLiveStateCache(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache { - return cache.NewLiveStateCache(argoDB, appInformer, settingsMgr, kubeutil.NewKubectl(), server, func(managedByApp map[string]bool, ref apiv1.ObjectReference) {}, nil) + 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-util/commands/app_test.go b/cmd/argocd/commands/admin/app_test.go similarity index 99% rename from cmd/argocd-util/commands/app_test.go rename to cmd/argocd/commands/admin/app_test.go index e14c1e4eecfb5..bddedc4aad080 100644 --- a/cmd/argocd-util/commands/app_test.go +++ b/cmd/argocd/commands/admin/app_test.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "testing" diff --git a/cmd/argocd-util/commands/backup.go b/cmd/argocd/commands/admin/backup.go similarity index 84% rename from cmd/argocd-util/commands/backup.go rename to cmd/argocd/commands/admin/backup.go index ec4885beb2162..1b3dcc99374cc 100644 --- a/cmd/argocd-util/commands/backup.go +++ b/cmd/argocd/commands/admin/backup.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "bufio" @@ -10,7 +10,7 @@ import ( "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/ghodss/yaml" - "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" apierr "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -88,7 +88,11 @@ func NewExportCommand() *cobra.Command { } applicationSets, err := acdClients.applicationSets.List(context.Background(), v1.ListOptions{}) if err != nil && !apierr.IsNotFound(err) { - errors.CheckError(err) + if apierr.IsForbidden(err) { + log.Warn(err) + } else { + errors.CheckError(err) + } } if applicationSets != nil { for _, appSet := range applicationSets.Items { @@ -176,6 +180,17 @@ func NewImportCommand() *cobra.Command { for _, proj := range projects.Items { pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "AppProject", Name: proj.GetName()}] = proj } + applicationSets, err := acdClients.applicationSets.List(context.Background(), v1.ListOptions{}) + if apierr.IsForbidden(err) || apierr.IsNotFound(err) { + log.Warnf("argoproj.io/ApplicationSet: %v\n", err) + } else { + errors.CheckError(err) + } + if applicationSets != nil { + for _, appSet := range applicationSets.Items { + pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "ApplicationSet", Name: appSet.GetName()}] = appSet + } + } // Create or replace existing object backupObjects, err := kube.SplitYAML(input) @@ -199,22 +214,39 @@ func NewImportCommand() *cobra.Command { dynClient = acdClients.applicationSets } if !exists { + isForbidden := false if !dryRun { _, err = dynClient.Create(context.Background(), bakObj, v1.CreateOptions{}) - errors.CheckError(err) + if apierr.IsForbidden(err) || apierr.IsNotFound(err) { + isForbidden = true + log.Warnf("%s/%s %s: %v", gvk.Group, gvk.Kind, bakObj.GetName(), err) + } else { + errors.CheckError(err) + } } - fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg) + if !isForbidden { + fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg) + } + } else if specsEqual(*bakObj, liveObj) { if verbose { fmt.Printf("%s/%s %s unchanged%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg) } } else { + isForbidden := false if !dryRun { newLive := updateLive(bakObj, &liveObj) _, err = dynClient.Update(context.Background(), newLive, v1.UpdateOptions{}) - errors.CheckError(err) + if apierr.IsForbidden(err) || apierr.IsNotFound(err) { + isForbidden = true + log.Warnf("%s/%s %s: %v", gvk.Group, gvk.Kind, bakObj.GetName(), err) + } else { + errors.CheckError(err) + } + } + if !isForbidden { + fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg) } - fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg) } } @@ -239,16 +271,24 @@ func NewImportCommand() *cobra.Command { } } } + case "ApplicationSet": + dynClient = acdClients.applicationSets default: - logrus.Fatalf("Unexpected kind '%s' in prune list", key.Kind) + log.Fatalf("Unexpected kind '%s' in prune list", key.Kind) } + isForbidden := false if !dryRun { err = dynClient.Delete(context.Background(), key.Name, v1.DeleteOptions{}) - if err != nil && !apierr.IsNotFound(err) { + if apierr.IsForbidden(err) || apierr.IsNotFound(err) { + isForbidden = true + log.Warnf("%s/%s %s: %v\n", key.Group, key.Kind, key.Name, err) + } else { errors.CheckError(err) } } - fmt.Printf("%s/%s %s pruned%s\n", key.Group, key.Kind, key.Name, dryRunMsg) + if !isForbidden { + fmt.Printf("%s/%s %s pruned%s\n", key.Group, key.Kind, key.Name, dryRunMsg) + } } else { fmt.Printf("%s/%s %s needs pruning\n", key.Group, key.Kind, key.Name) } @@ -304,6 +344,8 @@ func updateLive(bak, live *unstructured.Unstructured) *unstructured.Unstructured if _, ok := bak.Object["status"]; ok { newLive.Object["status"] = bak.Object["status"] } + case "ApplicationSet": + newLive.Object["spec"] = bak.Object["spec"] } return newLive } diff --git a/cmd/argocd-util/commands/cluster.go b/cmd/argocd/commands/admin/cluster.go similarity index 56% rename from cmd/argocd-util/commands/cluster.go rename to cmd/argocd/commands/admin/cluster.go index f168967609372..69b5988f167d9 100644 --- a/cmd/argocd-util/commands/cluster.go +++ b/cmd/argocd/commands/admin/cluster.go @@ -1,10 +1,12 @@ -package commands +package admin import ( "context" "fmt" "math" "os" + "sort" + "strings" "text/tabwriter" "time" @@ -22,14 +24,18 @@ import ( "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/controller/sharding" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "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" appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" "github.com/argoproj/argo-cd/v2/util/cli" "github.com/argoproj/argo-cd/v2/util/clusterauth" "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/argo-cd/v2/util/errors" + "github.com/argoproj/argo-cd/v2/util/glob" kubeutil "github.com/argoproj/argo-cd/v2/util/kube" "github.com/argoproj/argo-cd/v2/util/settings" + "github.com/argoproj/argo-cd/v2/util/text/label" ) func NewClusterCommand(pathOpts *clientcmd.PathOptions) *cobra.Command { @@ -45,16 +51,23 @@ func NewClusterCommand(pathOpts *clientcmd.PathOptions) *cobra.Command { command.AddCommand(NewGenClusterConfigCommand(pathOpts)) command.AddCommand(NewClusterStatsCommand()) command.AddCommand(NewClusterShardsCommand()) + namespacesCommand := NewClusterNamespacesCommand() + namespacesCommand.AddCommand(NewClusterEnableNamespacedMode()) + namespacesCommand.AddCommand(NewClusterDisableNamespacedMode()) + command.AddCommand(namespacesCommand) return command } type ClusterWithInfo struct { 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 + Namespaces []string } -func loadClusters(kubeClient *kubernetes.Clientset, replicas int, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int) ([]ClusterWithInfo, error) { +func loadClusters(kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int) ([]ClusterWithInfo, error) { settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, namespace) argoDB := db.NewDB(namespace, settingsMgr, kubeClient) @@ -65,7 +78,8 @@ func loadClusters(kubeClient *kubernetes.Clientset, replicas int, namespace stri var cache *appstatecache.Cache if portForwardRedis { overrides := clientcmd.ConfigOverrides{} - port, err := kubeutil.PortForward("app.kubernetes.io/name=argocd-redis-ha-haproxy", 6379, namespace, &overrides) + port, err := kubeutil.PortForward(6379, namespace, &overrides, + "app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis") if err != nil { return nil, err } @@ -78,6 +92,18 @@ func loadClusters(kubeClient *kubernetes.Clientset, replicas int, namespace stri } } + appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), v1.ListOptions{}) + if err != nil { + return nil, err + } + apps := appItems.Items + for i, app := range apps { + err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, argoDB) + if err != nil { + return nil, err + } + apps[i] = app + } clusters := make([]ClusterWithInfo, len(clustersList.Items)) batchSize := 10 batchesCount := int(math.Ceil(float64(len(clusters)) / float64(batchSize))) @@ -98,9 +124,18 @@ func loadClusters(kubeClient *kubernetes.Clientset, replicas int, namespace stri if shard != -1 && clusterShard != shard { return nil } - + nsSet := map[string]bool{} + for _, app := range apps { + if app.Spec.Destination.Server == cluster.Server { + nsSet[app.Spec.Destination.Namespace] = true + } + } + var namespaces []string + for ns := range nsSet { + namespaces = append(namespaces, ns) + } _ = cache.GetClusterInfo(cluster.Server, &cluster.Info) - clusters[batchStart+i] = ClusterWithInfo{cluster, clusterShard} + clusters[batchStart+i] = ClusterWithInfo{cluster, clusterShard, namespaces} return nil }) } @@ -135,6 +170,7 @@ func NewClusterShardsCommand() *cobra.Command { namespace, _, err := clientConfig.Namespace() errors.CheckError(err) kubeClient := kubernetes.NewForConfigOrDie(clientCfg) + appClient := versioned.NewForConfigOrDie(clientCfg) if replicas == 0 { replicas, err = getControllerReplicas(kubeClient, namespace) @@ -144,7 +180,7 @@ func NewClusterShardsCommand() *cobra.Command { return } - clusters, err := loadClusters(kubeClient, replicas, namespace, portForwardRedis, cacheSrc, shard) + clusters, err := loadClusters(kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard) errors.CheckError(err) if len(clusters) == 0 { return @@ -180,6 +216,216 @@ func printStatsSummary(clusters []ClusterWithInfo) { _ = w.Flush() } +func runClusterNamespacesCommand(clientConfig clientcmd.ClientConfig, action func(appClient *versioned.Clientset, argoDB db.ArgoDB, clusters map[string][]string) error) error { + clientCfg, err := clientConfig.ClientConfig() + if err != nil { + return err + } + namespace, _, err := clientConfig.Namespace() + if err != nil { + return err + } + + kubeClient := kubernetes.NewForConfigOrDie(clientCfg) + appClient := versioned.NewForConfigOrDie(clientCfg) + + settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, namespace) + argoDB := db.NewDB(namespace, settingsMgr, kubeClient) + clustersList, err := argoDB.ListClusters(context.Background()) + if err != nil { + return err + } + appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), v1.ListOptions{}) + if err != nil { + return err + } + apps := appItems.Items + for i, app := range apps { + err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, argoDB) + if err != nil { + return err + } + apps[i] = app + } + + clusters := map[string][]string{} + for _, cluster := range clustersList.Items { + nsSet := map[string]bool{} + for _, app := range apps { + if app.Spec.Destination.Server != cluster.Server { + continue + } + // Use namespaces of actually deployed resources, since some application use dummy target namespace + // If resources list is empty then use target namespace + if len(app.Status.Resources) != 0 { + for _, res := range app.Status.Resources { + if res.Namespace != "" { + nsSet[res.Namespace] = true + } + } + } else { + if app.Spec.Destination.Server == cluster.Server { + nsSet[app.Spec.Destination.Namespace] = true + } + } + } + var namespaces []string + for ns := range nsSet { + namespaces = append(namespaces, ns) + } + clusters[cluster.Server] = namespaces + } + return action(appClient, argoDB, clusters) +} + +func NewClusterNamespacesCommand() *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) { + log.SetLevel(log.WarnLevel) + + err := runClusterNamespacesCommand(clientConfig, func(appClient *versioned.Clientset, _ db.ArgoDB, clusters map[string][]string) error { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + _, _ = fmt.Fprintf(w, "CLUSTER\tNAMESPACES\n") + + for cluster, namespaces := range clusters { + // print shortest namespace names first + sort.Slice(namespaces, func(i, j int) bool { + return len(namespaces[j]) > len(namespaces[i]) + }) + namespacesStr := "" + if len(namespaces) > 4 { + namespacesStr = fmt.Sprintf("%s (total %d)", strings.Join(namespaces[:4], ","), len(namespaces)) + } else { + namespacesStr = strings.Join(namespaces, ",") + } + + _, _ = fmt.Fprintf(w, "%s\t%s\n", cluster, namespacesStr) + } + _ = w.Flush() + return nil + }) + errors.CheckError(err) + }, + } + clientConfig = cli.AddKubectlFlagsToCmd(&command) + return &command +} + +func NewClusterEnableNamespacedMode() *cobra.Command { + var ( + clientConfig clientcmd.ClientConfig + dryRun bool + clusterResources bool + namespacesCount int + ) + 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) { + log.SetLevel(log.WarnLevel) + + if len(args) == 0 { + cmd.HelpFunc()(cmd, args) + os.Exit(1) + } + pattern := args[0] + + errors.CheckError(runClusterNamespacesCommand(clientConfig, func(_ *versioned.Clientset, argoDB db.ArgoDB, clusters map[string][]string) error { + for server, namespaces := range clusters { + if len(namespaces) == 0 || len(namespaces) > namespacesCount || !glob.Match(pattern, server) { + continue + } + + cluster, err := argoDB.GetCluster(context.Background(), server) + if err != nil { + return err + } + cluster.Namespaces = namespaces + cluster.ClusterResources = clusterResources + fmt.Printf("Setting cluster %s namespaces to %v...", server, namespaces) + if !dryRun { + _, err = argoDB.UpdateCluster(context.Background(), cluster) + if err != nil { + return err + } + fmt.Println("done") + } else { + fmt.Println("done (dry run)") + } + + } + return nil + })) + }, + } + clientConfig = cli.AddKubectlFlagsToCmd(&command) + command.Flags().BoolVar(&dryRun, "dry-run", true, "Print what will be performed") + command.Flags().BoolVar(&clusterResources, "cluster-resources", false, "Indicates if cluster level resources should be managed.") + command.Flags().IntVar(&namespacesCount, "max-namespace-count", 0, "Max number of namespaces that cluster should managed managed namespaces is less or equal to specified count") + + return &command +} + +func NewClusterDisableNamespacedMode() *cobra.Command { + var ( + clientConfig clientcmd.ClientConfig + dryRun bool + ) + 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) { + log.SetLevel(log.WarnLevel) + + if len(args) == 0 { + cmd.HelpFunc()(cmd, args) + os.Exit(1) + } + + pattern := args[0] + + errors.CheckError(runClusterNamespacesCommand(clientConfig, func(_ *versioned.Clientset, argoDB db.ArgoDB, clusters map[string][]string) error { + for server := range clusters { + if !glob.Match(pattern, server) { + continue + } + + cluster, err := argoDB.GetCluster(context.Background(), server) + if err != nil { + return err + } + + if len(cluster.Namespaces) == 0 { + continue + } + + cluster.Namespaces = nil + fmt.Printf("Disabling namespaced mode for cluster %s...", server) + if !dryRun { + _, err = argoDB.UpdateCluster(context.Background(), cluster) + if err != nil { + return err + } + fmt.Println("done") + } else { + fmt.Println("done (dry run)") + } + + } + return nil + })) + }, + } + clientConfig = cli.AddKubectlFlagsToCmd(&command) + command.Flags().BoolVar(&dryRun, "dry-run", true, "Print what will be performed") + return &command +} + func NewClusterStatsCommand() *cobra.Command { var ( shard int @@ -200,17 +446,18 @@ func NewClusterStatsCommand() *cobra.Command { errors.CheckError(err) kubeClient := kubernetes.NewForConfigOrDie(clientCfg) + appClient := versioned.NewForConfigOrDie(clientCfg) if replicas == 0 { replicas, err = getControllerReplicas(kubeClient, namespace) errors.CheckError(err) } - clusters, err := loadClusters(kubeClient, replicas, namespace, portForwardRedis, cacheSrc, shard) + clusters, err := loadClusters(kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard) errors.CheckError(err) w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - _, _ = fmt.Fprintf(w, "SERVER\tSHARD\tCONNECTION\tAPPS COUNT\tRESOURCES COUNT\n") + _, _ = fmt.Fprintf(w, "SERVER\tSHARD\tCONNECTION\tNAMESPACES COUNT\tAPPS COUNT\tRESOURCES COUNT\n") for _, cluster := range clusters { - _, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%d\t%d\n", cluster.Server, cluster.Shard, cluster.Info.ConnectionState.Status, cluster.Info.ApplicationsCount, cluster.Info.CacheInfo.ResourcesCount) + _, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%d\t%d\t%d\n", cluster.Server, cluster.Shard, cluster.Info.ConnectionState.Status, len(cluster.Namespaces), cluster.Info.ApplicationsCount, cluster.Info.CacheInfo.ResourcesCount) } _ = w.Flush() }, @@ -223,7 +470,7 @@ func NewClusterStatsCommand() *cobra.Command { return &command } -// NewClusterConfig returns a new instance of `argocd-util kubeconfig` command +// NewClusterConfig returns a new instance of `argocd admin kubeconfig` command func NewClusterConfig() *cobra.Command { var ( clientConfig clientcmd.ClientConfig @@ -262,6 +509,8 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command bearerToken string generateToken bool outputFormat string + labels []string + annotations []string ) var command = &cobra.Command{ Use: "generate-spec CONTEXT", @@ -315,7 +564,13 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command if clusterOpts.Name != "" { contextName = clusterOpts.Name } - clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, conf, bearerToken, awsAuthConf, execProviderConf) + + labelsMap, err := label.Parse(labels) + errors.CheckError(err) + annotationsMap, err := label.Parse(annotations) + errors.CheckError(err) + + clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, bearerToken, awsAuthConf, execProviderConf, labelsMap, annotationsMap) if clusterOpts.InCluster { clst.Server = argoappv1.KubernetesInternalAPIServerAddr } @@ -335,10 +590,7 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(context.Background(), secName, v1.GetOptions{}) errors.CheckError(err) - cmdutil.ConvertSecretData(secret) - var printResources []interface{} - printResources = append(printResources, secret) - errors.CheckError(cmdutil.PrintResources(printResources, outputFormat)) + errors.CheckError(PrintResources(outputFormat, os.Stdout, secret)) }, } command.PersistentFlags().StringVar(&pathOpts.LoadingRules.ExplicitPath, pathOpts.ExplicitFileFlag, pathOpts.LoadingRules.ExplicitPath, "use a particular kubeconfig file") @@ -347,6 +599,8 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command command.Flags().StringVar(&clusterOpts.ServiceAccount, "service-account", "argocd-manager", fmt.Sprintf("System namespace service account to use for kubernetes resource management. If not set then default \"%s\" SA will be used", clusterauth.ArgoCDManagerServiceAccount)) command.Flags().StringVar(&clusterOpts.SystemNamespace, "system-namespace", common.DefaultSystemNamespace, "Use different system namespace") command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml") + 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)") cmdutil.AddClusterFlags(command, &clusterOpts) return command } diff --git a/cmd/argocd/commands/admin/dashboard.go b/cmd/argocd/commands/admin/dashboard.go new file mode 100644 index 0000000000000..9da3582453856 --- /dev/null +++ b/cmd/argocd/commands/admin/dashboard.go @@ -0,0 +1,30 @@ +package admin + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" + "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/pkg/apiclient" +) + +func NewDashboardCommand() *cobra.Command { + var ( + port int + ) + cmd := &cobra.Command{ + Use: "dashboard", + Short: "Starts Argo CD Web UI locally", + Run: func(cmd *cobra.Command, args []string) { + println(fmt.Sprintf("Argo CD UI is available at http://localhost:%d", port)) + <-context.Background().Done() + }, + } + clientOpts := &apiclient.ClientOptions{Core: true} + headless.InitCommand(cmd, clientOpts, &port) + cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port") + return cmd +} diff --git a/cmd/argocd/commands/admin/generatespec_utils.go b/cmd/argocd/commands/admin/generatespec_utils.go new file mode 100644 index 0000000000000..d5d8b7c8444c2 --- /dev/null +++ b/cmd/argocd/commands/admin/generatespec_utils.go @@ -0,0 +1,108 @@ +package admin + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "os" + + "github.com/argoproj/gitops-engine/pkg/utils/kube" + "github.com/ghodss/yaml" + v1 "k8s.io/api/core/v1" + + ioutil "github.com/argoproj/argo-cd/v2/util/io" +) + +func getOutWriter(inline bool, filePath string) (io.Writer, io.Closer, error) { + if !inline { + return os.Stdout, ioutil.NopCloser, nil + } + + if filePath == "" { + return nil, nil, errors.New("The file path must be specified using flag '--file'") + } + + err := os.Rename(filePath, fmt.Sprintf("%s.back", filePath)) + if err != nil { + return nil, nil, err + } + + fileOut, err := os.Create(filePath) + if err != nil { + return nil, nil, err + } + return fileOut, fileOut, nil +} + +// PrintResources prints a single resource in YAML or JSON format to stdout according to the output format +func PrintResources(output string, out io.Writer, resources ...interface{}) error { + for i, resource := range resources { + if secret, ok := resource.(*v1.Secret); ok { + convertSecretData(secret) + } + filteredResource, err := omitFields(resource) + if err != nil { + return err + } + resources[i] = filteredResource + } + var obj interface{} = resources + if len(resources) == 1 { + obj = resources[0] + } + + switch output { + case "json": + jsonBytes, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return err + } + + _, _ = fmt.Fprintln(out, string(jsonBytes)) + case "yaml": + yamlBytes, err := yaml.Marshal(obj) + if err != nil { + return err + } + // marshaled YAML already ends with the new line character + _, _ = fmt.Fprint(out, string(yamlBytes)) + default: + return fmt.Errorf("unknown output format: %s", output) + } + return nil +} + +// omit fields such as status, creationTimestamp and metadata.namespace in k8s objects +func omitFields(resource interface{}) (interface{}, error) { + jsonBytes, err := json.Marshal(resource) + if err != nil { + return nil, err + } + + toMap := make(map[string]interface{}) + err = json.Unmarshal(jsonBytes, &toMap) + if err != nil { + return nil, err + } + + delete(toMap, "status") + if v, ok := toMap["metadata"]; ok { + if metadata, ok := v.(map[string]interface{}); ok { + delete(metadata, "creationTimestamp") + delete(metadata, "namespace") + } + } + return toMap, nil +} + +// convertSecretData converts kubernetes secret's data to stringData +func convertSecretData(secret *v1.Secret) { + secret.Kind = kube.SecretKind + secret.APIVersion = "v1" + secret.StringData = map[string]string{} + for k, v := range secret.Data { + secret.StringData[k] = string(v) + } + secret.Data = map[string][]byte{} +} diff --git a/cmd/argocd/commands/admin/generatespec_utils_test.go b/cmd/argocd/commands/admin/generatespec_utils_test.go new file mode 100644 index 0000000000000..4352ce331b333 --- /dev/null +++ b/cmd/argocd/commands/admin/generatespec_utils_test.go @@ -0,0 +1,58 @@ +package admin + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/argoproj/argo-cd/v2/util/io" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestGetOutWriter_InlineOff(t *testing.T) { + out, closer, err := getOutWriter(false, "") + require.NoError(t, err) + defer io.Close(closer) + + assert.Equal(t, os.Stdout, out) +} + +func TestGetOutWriter_InlineOn(t *testing.T) { + tmpFile, err := ioutil.TempFile("", "") + require.NoError(t, err) + defer func() { + _ = os.Remove(tmpFile.Name()) + _ = os.Remove(fmt.Sprintf("%s.back", tmpFile.Name())) + }() + + out, closer, err := getOutWriter(true, tmpFile.Name()) + require.NoError(t, err) + defer io.Close(closer) + + assert.Equal(t, tmpFile.Name(), out.(*os.File).Name()) + _, err = os.Stat(fmt.Sprintf("%s.back", tmpFile.Name())) + assert.NoError(t, err, "Back file must be created") +} + +func TestPrintResources_Secret_YAML(t *testing.T) { + out := bytes.Buffer{} + err := PrintResources("yaml", &out, &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "my-secret"}, + Data: map[string][]byte{"my-secret-key": []byte("my-secret-data")}, + }) + assert.NoError(t, err) + + assert.Equal(t, `apiVersion: v1 +kind: Secret +metadata: + name: my-secret +stringData: + my-secret-key: my-secret-data +`, out.String()) +} diff --git a/cmd/argocd-util/commands/project.go b/cmd/argocd/commands/admin/project.go similarity index 90% rename from cmd/argocd-util/commands/project.go rename to cmd/argocd/commands/admin/project.go index e4f484c610528..e2478460a98da 100644 --- a/cmd/argocd-util/commands/project.go +++ b/cmd/argocd/commands/admin/project.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "context" @@ -13,6 +13,7 @@ import ( appclient "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/typed/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/cli" "github.com/argoproj/argo-cd/v2/util/errors" + "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/spf13/cobra" @@ -35,12 +36,13 @@ func NewProjectsCommand() *cobra.Command { return command } -// NewGenProjectConfigCommand generates declarative configuration file for given project +// NewGenProjectSpecCommand generates declarative configuration file for given project func NewGenProjectSpecCommand() *cobra.Command { var ( opts cmdutil.ProjectOpts fileURL string outputFormat string + inline bool ) var command = &cobra.Command{ Use: "generate-spec PROJECT", @@ -49,13 +51,16 @@ func NewGenProjectSpecCommand() *cobra.Command { proj, err := cmdutil.ConstructAppProj(fileURL, args, opts, c) errors.CheckError(err) - var printResources []interface{} - printResources = append(printResources, proj) - errors.CheckError(cmdutil.PrintResources(printResources, outputFormat)) + out, closer, err := getOutWriter(inline, fileURL) + errors.CheckError(err) + defer io.Close(closer) + + errors.CheckError(PrintResources(outputFormat, out, proj)) }, } command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml") command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the project") + command.Flags().BoolVarP(&inline, "inline", "i", false, "If set then generated resource is written back to the file specified in --file flag") // Only complete files with appropriate extension. err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"}) @@ -138,10 +143,10 @@ func NewUpdatePolicyRuleCommand() *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 - argocd-util projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow + argocd admin projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow # Remove policy that which manages running (action/*) from all roles which name matches *deployer* in all projects - argocd-util projects update-role-policy '*' remove override --role '*deployer*' + argocd admin projects update-role-policy '*' remove override --role '*deployer*' `, Run: func(c *cobra.Command, args []string) { if len(args) != 3 { diff --git a/cmd/argocd-util/commands/project_allowlist.go b/cmd/argocd/commands/admin/project_allowlist.go similarity index 99% rename from cmd/argocd-util/commands/project_allowlist.go rename to cmd/argocd/commands/admin/project_allowlist.go index 96d52e15573fb..2d9dd85b363fe 100644 --- a/cmd/argocd-util/commands/project_allowlist.go +++ b/cmd/argocd/commands/admin/project_allowlist.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "bufio" diff --git a/cmd/argocd-util/commands/project_allowlist_test.go b/cmd/argocd/commands/admin/project_allowlist_test.go similarity index 99% rename from cmd/argocd-util/commands/project_allowlist_test.go rename to cmd/argocd/commands/admin/project_allowlist_test.go index 5db55f0b0ebc0..dea4d29297b96 100644 --- a/cmd/argocd-util/commands/project_allowlist_test.go +++ b/cmd/argocd/commands/admin/project_allowlist_test.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "reflect" diff --git a/cmd/argocd-util/commands/project_test.go b/cmd/argocd/commands/admin/project_test.go similarity index 99% rename from cmd/argocd-util/commands/project_test.go rename to cmd/argocd/commands/admin/project_test.go index 8447b755af31f..f01ebb5c9acf0 100644 --- a/cmd/argocd-util/commands/project_test.go +++ b/cmd/argocd/commands/admin/project_test.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "context" diff --git a/cmd/argocd-util/commands/repo.go b/cmd/argocd/commands/admin/repo.go similarity index 80% rename from cmd/argocd-util/commands/repo.go rename to cmd/argocd/commands/admin/repo.go index 5b9855ef05273..d6e3ae9339e30 100644 --- a/cmd/argocd-util/commands/repo.go +++ b/cmd/argocd/commands/admin/repo.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "context" @@ -9,7 +9,6 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" apiv1 "k8s.io/api/core/v1" - apierr "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" @@ -49,25 +48,25 @@ func NewGenRepoSpecCommand() *cobra.Command { // For better readability and easier formatting var repoAddExamples = ` # Add a Git repository via SSH using a private key for authentication, ignoring the server's host key: - argocd-util repo generate-spec git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa + argocd admin repo generate-spec 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-util repo generate-spec ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa + argocd admin repo generate-spec ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa # Add a private Git repository via HTTPS using username/password and TLS client certificates: - argocd-util repo generate-spec https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key + argocd admin repo generate-spec https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key # Add a private Git repository via HTTPS using username/password without verifying the server's TLS certificate - argocd-util repo generate-spec https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification + argocd admin repo generate-spec https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification # Add a public Helm repository named 'stable' via HTTPS - argocd-util repo generate-spec https://charts.helm.sh/stable --type helm --name stable + argocd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable # Add a private Helm repository named 'stable' via HTTPS - argocd-util repo generate-spec https://charts.helm.sh/stable --type helm --name stable --username test --password test + argocd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable --username test --password test # Add a private Helm OCI-based repository named 'stable' via HTTPS - argocd-util repo generate-spec helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test + argocd admin repo generate-spec helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test ` var command = &cobra.Command{ @@ -155,21 +154,13 @@ func NewGenRepoSpecCommand() *cobra.Command { settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, ArgoCDNamespace) argoDB := db.NewDB(ArgoCDNamespace, settingsMgr, kubeClientset) - var printResources []interface{} _, err := argoDB.CreateRepository(context.Background(), &repoOpts.Repo) errors.CheckError(err) secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(context.Background(), db.RepoURLToSecretName(repoSecretPrefix, repoOpts.Repo.Repo), v1.GetOptions{}) - if err != nil { - if !apierr.IsNotFound(err) { - errors.CheckError(err) - } - } else { - cmdutil.ConvertSecretData(secret) - printResources = append(printResources, secret) - } + errors.CheckError(err) - errors.CheckError(cmdutil.PrintResources(printResources, outputFormat)) + errors.CheckError(PrintResources(outputFormat, os.Stdout, secret)) }, } command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml") diff --git a/cmd/argocd-util/commands/secrets_redactor_test.go b/cmd/argocd/commands/admin/secrets_redactor_test.go similarity index 99% rename from cmd/argocd-util/commands/secrets_redactor_test.go rename to cmd/argocd/commands/admin/secrets_redactor_test.go index b8cc43135ac33..cb1b3e78dbfea 100644 --- a/cmd/argocd-util/commands/secrets_redactor_test.go +++ b/cmd/argocd/commands/admin/secrets_redactor_test.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "testing" diff --git a/cmd/argocd-util/commands/settings.go b/cmd/argocd/commands/admin/settings.go similarity index 96% rename from cmd/argocd-util/commands/settings.go rename to cmd/argocd/commands/admin/settings.go index e6b8e276f9815..78bf8da0839d0 100644 --- a/cmd/argocd-util/commands/settings.go +++ b/cmd/argocd/commands/admin/settings.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "bytes" @@ -303,10 +303,10 @@ func NewValidateSettingsCommand(cmdCtx commandContext) *cobra.Command { Long: "Validates settings specified in 'argocd-cm' ConfigMap and 'argocd-secret' Secret", Example: ` #Validates all settings in the specified YAML file -argocd-util settings validate --argocd-cm-path ./argocd-cm.yaml +argocd admin settings validate --argocd-cm-path ./argocd-cm.yaml #Validates accounts and plugins settings in Kubernetes cluster of current kubeconfig context -argocd-util settings validate --group accounts --group plugins --load-cluster-settings`, +argocd admin settings validate --group accounts --group plugins --load-cluster-settings`, Run: func(c *cobra.Command, args []string) { settingsManager, err := cmdCtx.createSettingsManager() errors.CheckError(err) @@ -392,7 +392,7 @@ func NewResourceIgnoreDifferencesCommand(cmdCtx commandContext) *cobra.Command { Short: "Renders fields excluded from diffing", Long: "Renders ignored fields using the 'ignoreDifferences' setting specified in the 'resource.customizations' field of 'argocd-cm' ConfigMap", Example: ` -argocd-util settings resource-overrides ignore-differences ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`, +argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`, Run: func(c *cobra.Command, args []string) { if len(args) < 1 { c.HelpFunc()(c, args) @@ -436,7 +436,7 @@ func NewResourceHealthCommand(cmdCtx commandContext) *cobra.Command { Short: "Assess resource health", Long: "Assess resource health using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap", Example: ` -argocd-util settings resource-overrides health ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`, +argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`, Run: func(c *cobra.Command, args []string) { if len(args) < 1 { c.HelpFunc()(c, args) @@ -467,7 +467,7 @@ func NewResourceActionListCommand(cmdCtx commandContext) *cobra.Command { 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", Example: ` -argocd-util settings resource-overrides action list /tmp/deploy.yaml --argocd-cm-path ./argocd-cm.yaml`, +argocd admin settings resource-overrides action list /tmp/deploy.yaml --argocd-cm-path ./argocd-cm.yaml`, Run: func(c *cobra.Command, args []string) { if len(args) < 1 { c.HelpFunc()(c, args) @@ -510,7 +510,7 @@ func NewResourceActionRunCommand(cmdCtx commandContext) *cobra.Command { Short: "Executes resource action", Long: "Executes resource action using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields", Example: ` -argocd-util settings resource-overrides action run /tmp/deploy.yaml restart --argocd-cm-path ./argocd-cm.yaml`, +argocd admin settings resource-overrides action run /tmp/deploy.yaml restart --argocd-cm-path ./argocd-cm.yaml`, Run: func(c *cobra.Command, args []string) { if len(args) < 2 { c.HelpFunc()(c, args) diff --git a/cmd/argocd-util/commands/settings_rbac.go b/cmd/argocd/commands/admin/settings_rbac.go similarity index 96% rename from cmd/argocd-util/commands/settings_rbac.go rename to cmd/argocd/commands/admin/settings_rbac.go index 50113d99845f8..8aaf0e13c9a5d 100644 --- a/cmd/argocd-util/commands/settings_rbac.go +++ b/cmd/argocd/commands/admin/settings_rbac.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "context" @@ -101,19 +101,19 @@ something. Example: ` # Check whether role some:role has permissions to create an application in the # 'default' project, using a local policy.csv file -argocd-util settings rbac can some:role create application 'default/app' --policy-file policy.csv +argocd admin settings rbac can some:role create application 'default/app' --policy-file policy.csv # Policy file can also be K8s config map with data keys like argocd-rbac-cm, # i.e. 'policy.csv' and (optionally) 'policy.default' -argocd-util settings rbac can some:role create application 'default/app' --policy-file argocd-rbac-cm.yaml +argocd admin settings rbac can some:role create application 'default/app' --policy-file argocd-rbac-cm.yaml # If --policy-file is not given, the ConfigMap 'argocd-rbac-cm' from K8s is # used. You need to specify the argocd namespace, and make sure that your # current Kubernetes context is pointing to the cluster Argo CD is running in -argocd-util settings rbac can some:role create application 'default/app' --namespace argocd +argocd admin settings rbac can some:role create application 'default/app' --namespace argocd # You can override a possibly configured default role -argocd-util settings rbac can someuser create application 'default/app' --default-role role:readonly +argocd admin settings rbac can someuser create application 'default/app' --default-role role:readonly `, Run: func(c *cobra.Command, args []string) { diff --git a/cmd/argocd-util/commands/settings_rbac_test.go b/cmd/argocd/commands/admin/settings_rbac_test.go similarity index 99% rename from cmd/argocd-util/commands/settings_rbac_test.go rename to cmd/argocd/commands/admin/settings_rbac_test.go index 6cae85b89a9e7..0574f409e4c25 100644 --- a/cmd/argocd-util/commands/settings_rbac_test.go +++ b/cmd/argocd/commands/admin/settings_rbac_test.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "io/ioutil" diff --git a/cmd/argocd-util/commands/settings_test.go b/cmd/argocd/commands/admin/settings_test.go similarity index 99% rename from cmd/argocd-util/commands/settings_test.go rename to cmd/argocd/commands/admin/settings_test.go index 6145b979b8b8e..9f5da58a42725 100644 --- a/cmd/argocd-util/commands/settings_test.go +++ b/cmd/argocd/commands/admin/settings_test.go @@ -1,4 +1,4 @@ -package commands +package admin import ( "bytes" diff --git a/cmd/argocd-util/commands/testdata/rbac/argocd-rbac-cm.yaml b/cmd/argocd/commands/admin/testdata/rbac/argocd-rbac-cm.yaml similarity index 100% rename from cmd/argocd-util/commands/testdata/rbac/argocd-rbac-cm.yaml rename to cmd/argocd/commands/admin/testdata/rbac/argocd-rbac-cm.yaml diff --git a/cmd/argocd-util/commands/testdata/rbac/policy.csv b/cmd/argocd/commands/admin/testdata/rbac/policy.csv similarity index 100% rename from cmd/argocd-util/commands/testdata/rbac/policy.csv rename to cmd/argocd/commands/admin/testdata/rbac/policy.csv diff --git a/cmd/argocd-util/commands/testdata/test_clusterrole.yaml b/cmd/argocd/commands/admin/testdata/test_clusterrole.yaml similarity index 100% rename from cmd/argocd-util/commands/testdata/test_clusterrole.yaml rename to cmd/argocd/commands/admin/testdata/test_clusterrole.yaml diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index 002a48b292e7e..da601b7741e8b 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -92,6 +92,7 @@ func NewApplicationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman command.AddCommand(NewApplicationEditCommand(clientOpts)) command.AddCommand(NewApplicationPatchCommand(clientOpts)) command.AddCommand(NewApplicationPatchResourceCommand(clientOpts)) + command.AddCommand(NewApplicationDeleteResourceCommand(clientOpts)) command.AddCommand(NewApplicationResourceActionsCommand(clientOpts)) command.AddCommand(NewApplicationListResourcesCommand(clientOpts)) command.AddCommand(NewApplicationLogsCommand(clientOpts)) @@ -675,7 +676,7 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C } for _, env := range pluginEnvs { err = app.Spec.Source.Plugin.RemoveEnvEntry(env) - if err != nil { + if err == nil { updated = true } } @@ -732,8 +733,8 @@ func liveObjects(resources []*argoappv1.ResourceDiff) ([]*unstructured.Unstructu } func getLocalObjects(app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, kustomizeOptions *argoappv1.KustomizeOptions, - configManagementPlugins []*argoappv1.ConfigManagementPlugin) []*unstructured.Unstructured { - manifestStrings := getLocalObjectsString(app, local, localRepoRoot, appLabelKey, kubeVersion, kustomizeOptions, configManagementPlugins) + configManagementPlugins []*argoappv1.ConfigManagementPlugin, trackingMethod string) []*unstructured.Unstructured { + manifestStrings := getLocalObjectsString(app, local, localRepoRoot, appLabelKey, kubeVersion, kustomizeOptions, configManagementPlugins, trackingMethod) objs := make([]*unstructured.Unstructured, len(manifestStrings)) for i := range manifestStrings { obj := unstructured.Unstructured{} @@ -745,7 +746,7 @@ func getLocalObjects(app *argoappv1.Application, local, localRepoRoot, appLabelK } func getLocalObjectsString(app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, kustomizeOptions *argoappv1.KustomizeOptions, - configManagementPlugins []*argoappv1.ConfigManagementPlugin) []string { + configManagementPlugins []*argoappv1.ConfigManagementPlugin, trackingMethod string) []string { res, err := repository.GenerateManifests(local, localRepoRoot, app.Spec.Source.TargetRevision, &repoapiclient.ManifestRequest{ Repo: &argoappv1.Repository{Repo: app.Spec.Source.RepoURL}, @@ -756,6 +757,7 @@ func getLocalObjectsString(app *argoappv1.Application, local, localRepoRoot, app KustomizeOptions: kustomizeOptions, KubeVersion: kubeVersion, Plugins: configManagementPlugins, + TrackingMethod: trackingMethod, }, true) errors.CheckError(err) @@ -841,7 +843,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co defer argoio.Close(conn) cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server}) errors.CheckError(err) - localObjs := groupObjsByKey(getLocalObjects(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins), liveObjs, app.Spec.Destination.Namespace) + localObjs := groupObjsByKey(getLocalObjects(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins, argoSettings.TrackingMethod), liveObjs, app.Spec.Destination.Namespace) items = groupObjsForDiff(resources, localObjs, items, argoSettings, appName) } else if revision != "" { var unstructureds []*unstructured.Unstructured @@ -1376,7 +1378,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server}) errors.CheckError(err) argoio.Close(conn) - localObjsStrings = getLocalObjectsString(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins) + localObjsStrings = getLocalObjectsString(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins, argoSettings.TrackingMethod) } syncReq := applicationpkg.ApplicationSyncRequest{ @@ -1814,6 +1816,23 @@ func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra return command } +func findRevisionHistory(application *argoappv1.Application, historyId int64) (*argoappv1.RevisionHistory, error) { + // in case if history id not passed and need fetch previous history revision + if historyId == -1 { + l := len(application.Status.History) + if l < 2 { + return nil, fmt.Errorf("Application '%s' should have at least two successful deployments", application.ObjectMeta.Name) + } + return &application.Status.History[l-2], nil + } + for _, di := range application.Status.History { + if di.ID == historyId { + return &di, nil + } + } + return nil, fmt.Errorf("Application '%s' does not have deployment id '%d' in history\n", application.ObjectMeta.Name, historyId) +} + // NewApplicationRollbackCommand returns a new instance of an `argocd app rollback` command func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( @@ -1821,36 +1840,33 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr timeout uint ) var command = &cobra.Command{ - Use: "rollback APPNAME ID", - Short: "Rollback application to a previous deployed version by History ID", + 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) { - if len(args) != 2 { + if len(args) == 0 { c.HelpFunc()(c, args) os.Exit(1) } appName := args[0] - depID, err := strconv.Atoi(args[1]) - errors.CheckError(err) + var err error + depID := -1 + if len(args) > 1 { + depID, err = strconv.Atoi(args[1]) + errors.CheckError(err) + } acdClient := argocdclient.NewClientOrDie(clientOpts) conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) ctx := context.Background() app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName}) errors.CheckError(err) - var depInfo *argoappv1.RevisionHistory - for _, di := range app.Status.History { - if di.ID == int64(depID) { - depInfo = &di - break - } - } - if depInfo == nil { - log.Fatalf("Application '%s' does not have deployment id '%d' in history\n", app.ObjectMeta.Name, depID) - } + + depInfo, err := findRevisionHistory(app, int64(depID)) + errors.CheckError(err) _, err = appIf.Rollback(ctx, &applicationpkg.ApplicationRollbackRequest{ Name: &appName, - ID: int64(depID), + ID: depInfo.ID, Prune: prune, }) errors.CheckError(err) @@ -2188,3 +2204,59 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) return command } + +func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { + var resourceName string + var namespace string + var kind string + var group string + var force bool + var orphan bool + var all bool + command := &cobra.Command{ + Use: "delete-resource APPNAME", + Short: "Delete resource in an application", + } + + command.Flags().StringVar(&resourceName, "resource-name", "", "Name of resource") + command.Flags().StringVar(&kind, "kind", "", "Kind") + err := command.MarkFlagRequired("kind") + errors.CheckError(err) + command.Flags().StringVar(&group, "group", "", "Group") + command.Flags().StringVar(&namespace, "namespace", "", "Namespace") + 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.Run = func(c *cobra.Command, args []string) { + if len(args) != 1 { + c.HelpFunc()(c, args) + os.Exit(1) + } + appName := args[0] + + conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie() + defer argoio.Close(conn) + ctx := context.Background() + resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName}) + errors.CheckError(err) + objectsToDelete := filterResources(command, resources.Items, group, kind, namespace, resourceName, all) + for i := range objectsToDelete { + obj := objectsToDelete[i] + gvk := obj.GroupVersionKind() + _, err = appIf.DeleteResource(ctx, &applicationpkg.ApplicationResourceDeleteRequest{ + Name: &appName, + Namespace: obj.GetNamespace(), + ResourceName: obj.GetName(), + Version: gvk.Version, + Group: gvk.Group, + Kind: gvk.Kind, + Force: &force, + Orphan: &orphan, + }) + errors.CheckError(err) + log.Infof("Resource '%s' deleted", obj.GetName()) + } + } + + return command +} diff --git a/cmd/argocd/commands/app_test.go b/cmd/argocd/commands/app_test.go new file mode 100644 index 0000000000000..77aecb1373fed --- /dev/null +++ b/cmd/argocd/commands/app_test.go @@ -0,0 +1,163 @@ +package commands + +import ( + "testing" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +func TestFindRevisionHistoryWithoutPassedId(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 TestFindRevisionHistoryWithoutPassedIdAndEmptyHistoryList(t *testing.T) { + + histories := v1alpha1.RevisionHistories{} + + 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 with errors") + } + + if history != nil { + t.Fatal("History should be empty") + } + + 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}) + histories = append(histories, v1alpha1.RevisionHistory{ID: 2}) + histories = append(histories, v1alpha1.RevisionHistory{ID: 3, Revision: "123"}) + + 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, 3) + + if err != nil { + t.Fatal("Find revision history should fail without errors") + } + + if history == nil { + t.Fatal("History should be found") + } + + 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}) + histories = append(histories, v1alpha1.RevisionHistory{ID: 2}) + histories = append(histories, v1alpha1.RevisionHistory{ID: 3, Revision: "123"}) + + 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, 4) + + if err == nil { + t.Fatal("Find revision history should fail with errors") + } + + if history != nil { + t.Fatal("History should be not found") + } + + if err.Error() != "Application '' does not have deployment id '4' in history\n" { + t.Fatal("Find revision history should fail with correct error message") + } + +} diff --git a/cmd/argocd/commands/cluster.go b/cmd/argocd/commands/cluster.go index 6f05638120782..2332cb0f83563 100644 --- a/cmd/argocd/commands/cluster.go +++ b/cmd/argocd/commands/cluster.go @@ -4,13 +4,18 @@ import ( "context" "fmt" "os" + "regexp" "strings" "text/tabwriter" "github.com/mattn/go-isatty" + "k8s.io/client-go/kubernetes" + + "github.com/argoproj/argo-cd/v2/util/cli" + "github.com/argoproj/argo-cd/v2/util/text/label" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" @@ -18,7 +23,6 @@ import ( argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster" argoappv1 "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/clusterauth" "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/io" @@ -60,6 +64,8 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie var ( clusterOpts cmdutil.ClusterOptions skipConfirmation bool + labels []string + annotations []string ) var command = &cobra.Command{ Use: "add CONTEXT", @@ -122,18 +128,27 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie } errors.CheckError(err) } + + labelsMap, err := label.Parse(labels) + errors.CheckError(err) + annotationsMap, err := label.Parse(annotations) + errors.CheckError(err) + conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie() defer io.Close(conn) if clusterOpts.Name != "" { contextName = clusterOpts.Name } - clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, conf, managerBearerToken, awsAuthConf, execProviderConf) + clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, managerBearerToken, awsAuthConf, execProviderConf, labelsMap, annotationsMap) if clusterOpts.InCluster { clst.Server = argoappv1.KubernetesInternalAPIServerAddr } if clusterOpts.Shard >= 0 { clst.Shard = &clusterOpts.Shard } + if clusterOpts.Project != "" { + clst.Project = clusterOpts.Project + } clstCreateReq := clusterpkg.ClusterCreateRequest{ Cluster: clst, Upsert: clusterOpts.Upsert, @@ -148,6 +163,8 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie command.Flags().StringVar(&clusterOpts.ServiceAccount, "service-account", "", fmt.Sprintf("System namespace service account to use for kubernetes resource management. If not set then default \"%s\" SA will be created", clusterauth.ArgoCDManagerServiceAccount)) command.Flags().StringVar(&clusterOpts.SystemNamespace, "system-namespace", common.DefaultSystemNamespace, "Use different system namespace") command.Flags().BoolVarP(&skipConfirmation, "yes", "y", false, "Skip explicit confirmation") + 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)") cmdutil.AddClusterFlags(command, &clusterOpts) return command } @@ -158,9 +175,10 @@ func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command output string ) var command = &cobra.Command{ - Use: "get SERVER", - Short: "Get cluster information", - Example: `argocd cluster get https://12.34.567.89`, + Use: "get SERVER/NAME", + Short: "Get cluster information", + Example: `argocd cluster get https://12.34.567.89 +argocd cluster get in-cluster`, Run: func(c *cobra.Command, args []string) { if len(args) == 0 { c.HelpFunc()(c, args) @@ -169,8 +187,8 @@ func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie() defer io.Close(conn) clusters := make([]argoappv1.Cluster, 0) - for _, clusterName := range args { - clst, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Server: clusterName}) + for _, clusterSelector := range args { + clst, err := clusterIf.Get(context.Background(), getQueryBySelector(clusterSelector)) errors.CheckError(err) clusters = append(clusters, *clst) } @@ -257,17 +275,29 @@ func NewClusterRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm // Print table of cluster information func printClusterTable(clusters []argoappv1.Cluster) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - _, _ = fmt.Fprintf(w, "SERVER\tNAME\tVERSION\tSTATUS\tMESSAGE\n") + _, _ = fmt.Fprintf(w, "SERVER\tNAME\tVERSION\tSTATUS\tMESSAGE\tPROJECT\n") for _, c := range clusters { server := c.Server if len(c.Namespaces) > 0 { server = fmt.Sprintf("%s (%d namespaces)", c.Server, len(c.Namespaces)) } - _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", server, c.Name, c.ServerVersion, c.ConnectionState.Status, c.ConnectionState.Message) + _, _ = 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() } +// Returns cluster query for getting cluster depending on the cluster selector +func getQueryBySelector(clusterSelector string) *clusterpkg.ClusterQuery { + var query clusterpkg.ClusterQuery + isServer, err := regexp.MatchString(`^https?://`, clusterSelector) + if isServer || err != nil { + query.Server = clusterSelector + } else { + query.Name = clusterSelector + } + return &query +} + // Print list of cluster servers func printClusterServers(clusters []argoappv1.Cluster) { for _, c := range clusters { diff --git a/cmd/argocd/commands/cluster_test.go b/cmd/argocd/commands/cluster_test.go index ebed69059a8e6..b0e978ab66ed2 100644 --- a/cmd/argocd/commands/cluster_test.go +++ b/cmd/argocd/commands/cluster_test.go @@ -6,8 +6,24 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + + "github.com/stretchr/testify/assert" ) +func Test_getQueryBySelector(t *testing.T) { + query := getQueryBySelector("my-cluster") + 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, query.Server, "http://my-server") + + query = getQueryBySelector("https://my-server") + assert.Equal(t, query.Name, "") + assert.Equal(t, query.Server, "https://my-server") +} + func Test_printClusterTable(t *testing.T) { printClusterTable([]v1alpha1.Cluster{ { diff --git a/cmd/argocd/commands/headless/forward.go b/cmd/argocd/commands/headless/forward.go new file mode 100644 index 0000000000000..c5384be85bd99 --- /dev/null +++ b/cmd/argocd/commands/headless/forward.go @@ -0,0 +1,97 @@ +package headless + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/go-redis/redis/v8" + "k8s.io/client-go/tools/clientcmd" + + "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + repoapiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + "github.com/argoproj/argo-cd/v2/util/cache" + "github.com/argoproj/argo-cd/v2/util/io" + kubeutil "github.com/argoproj/argo-cd/v2/util/kube" +) + +type forwardCacheClient struct { + namespace string + init sync.Once + client cache.CacheClient + err error +} + +func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error { + c.init.Do(func() { + overrides := clientcmd.ConfigOverrides{} + redisPort, err := kubeutil.PortForward(6379, c.namespace, &overrides, + "app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis") + if err != nil { + c.err = err + return + } + + redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort)}) + c.client = cache.NewRedisCache(redisClient, time.Hour) + }) + if c.err != nil { + return c.err + } + return action(c.client) +} + +func (c *forwardCacheClient) Set(item *cache.Item) error { + return c.doLazy(func(client cache.CacheClient) error { + return client.Set(item) + }) +} + +func (c *forwardCacheClient) Get(key string, obj interface{}) error { + return c.doLazy(func(client cache.CacheClient) error { + return client.Get(key, obj) + }) +} + +func (c *forwardCacheClient) Delete(key string) error { + return c.doLazy(func(client cache.CacheClient) error { + return client.Delete(key) + }) +} + +func (c *forwardCacheClient) OnUpdated(ctx context.Context, key string, callback func() error) error { + return c.doLazy(func(client cache.CacheClient) error { + return client.OnUpdated(ctx, key, callback) + }) +} + +func (c *forwardCacheClient) NotifyUpdated(key string) error { + return c.doLazy(func(client cache.CacheClient) error { + return client.NotifyUpdated(key) + }) +} + +type forwardRepoClientset struct { + namespace string + init sync.Once + repoClientset repoapiclient.Clientset + err error +} + +func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.RepoServerServiceClient, error) { + c.init.Do(func() { + overrides := clientcmd.ConfigOverrides{} + repoServerPort, err := kubeutil.PortForward(8081, c.namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server") + if err != nil { + c.err = err + return + } + c.repoClientset = apiclient.NewRepoServerClientset(fmt.Sprintf("localhost:%d", repoServerPort), 60, apiclient.TLSConfiguration{ + DisableTLS: false, StrictValidation: false}) + }) + if c.err != nil { + return nil, nil, c.err + } + return c.repoClientset.NewRepoServerClient() +} diff --git a/cmd/argocd/commands/headless/headless.go b/cmd/argocd/commands/headless/headless.go new file mode 100644 index 0000000000000..1b033d05da068 --- /dev/null +++ b/cmd/argocd/commands/headless/headless.go @@ -0,0 +1,151 @@ +package headless + +import ( + "context" + "fmt" + "net" + "os" + "time" + + "github.com/alicebob/miniredis/v2" + "github.com/go-redis/redis/v8" + "github.com/golang/protobuf/ptypes/empty" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + + argoapi "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" + "github.com/argoproj/argo-cd/v2/server" + servercache "github.com/argoproj/argo-cd/v2/server/cache" + cacheutil "github.com/argoproj/argo-cd/v2/util/cache" + appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" + "github.com/argoproj/argo-cd/v2/util/cli" + "github.com/argoproj/argo-cd/v2/util/io" + "github.com/argoproj/argo-cd/v2/util/localconfig" +) + +func testAPI(clientOpts *argoapi.ClientOptions) error { + apiClient, err := argoapi.NewClient(clientOpts) + if err != nil { + return err + } + closer, versionClient, err := apiClient.NewVersionClient() + if err != nil { + return err + } + defer io.Close(closer) + _, err = versionClient.Version(context.Background(), &empty.Empty{}) + return err +} + +// InitCommand allows executing command in a headless mode: on the fly starts Argo CD API server and +// changes provided client options to use started API server port +func InitCommand(cmd *cobra.Command, clientOpts *argoapi.ClientOptions, port *int) *cobra.Command { + ctx, cancel := context.WithCancel(context.Background()) + flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError) + clientConfig := cli.AddKubectlFlagsToSet(flags) + // copy k8s persistent flags into argocd command flags + flags.VisitAll(func(flag *pflag.Flag) { + // skip Kubernetes server flags since argocd has it's own server flag + if flag.Name == "server" { + return + } + cmd.Flags().AddFlag(flag) + }) + cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + startInProcessAPI := clientOpts.Core + if !startInProcessAPI { + localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath) + if err != nil { + return err + } + if localCfg != nil { + configCtx, err := localCfg.ResolveContext(clientOpts.Context) + if err != nil { + return err + } + startInProcessAPI = configCtx.Server.Core + } + } + if !startInProcessAPI { + return nil + } + + // get rid of logging error handler + runtime.ErrorHandlers = runtime.ErrorHandlers[1:] + cli.SetLogLevel(log.ErrorLevel.String()) + log.SetLevel(log.ErrorLevel) + os.Setenv(v1alpha1.EnvVarFakeInClusterConfig, "true") + if port == nil || *port == 0 { + ln, err := net.Listen("tcp", "localhost:0") + if err != nil { + return err + } + port = &ln.Addr().(*net.TCPAddr).Port + io.Close(ln) + } + + restConfig, err := clientConfig.ClientConfig() + if err != nil { + return err + } + appClientset, err := appclientset.NewForConfig(restConfig) + if err != nil { + return err + } + kubeClientset, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return err + } + + namespace, _, err := clientConfig.Namespace() + if err != nil { + return err + } + + mr, err := miniredis.Run() + if err != nil { + return err + } + + appstateCache := appstatecache.NewCache(cacheutil.NewCache(&forwardCacheClient{namespace: namespace}), time.Hour) + srv := server.NewServer(ctx, server.ArgoCDServerOpts{ + 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: "localhost", + RepoClientset: &forwardRepoClientset{namespace: namespace}, + }) + + go srv.Run(ctx, *port, 0) + clientOpts.ServerAddr = fmt.Sprintf("localhost:%d", *port) + clientOpts.PlainText = true + if !cache.WaitForCacheSync(ctx.Done(), srv.Initialized) { + log.Fatal("Timed out waiting for project cache to sync") + } + tries := 5 + for i := 0; i < tries; i++ { + err = testAPI(clientOpts) + if err == nil { + break + } + time.Sleep(time.Second) + } + return err + } + cmd.PostRun = func(cmd *cobra.Command, args []string) { + cancel() + } + return cmd +} diff --git a/cmd/argocd/commands/login.go b/cmd/argocd/commands/login.go index 8aa5e314be6af..8d8f1a861edad 100644 --- a/cmd/argocd/commands/login.go +++ b/cmd/argocd/commands/login.go @@ -45,16 +45,26 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman Use: "login SERVER", Short: "Log in to Argo CD", Long: "Log in to Argo CD", + Example: `# Login to Argo CD using a username and password +argocd login cd.argoproj.io + +# Login to Argo CD using SSO +argocd login cd.argoproj.io --sso + +# Configure direct access using Kubernetes API server +argocd login cd.argoproj.io --core`, Run: func(c *cobra.Command, args []string) { var server string - if len(args) != 1 && !globalClientOpts.PortForward { + if len(args) != 1 && !globalClientOpts.PortForward && !globalClientOpts.Core { c.HelpFunc()(c, args) os.Exit(1) } if globalClientOpts.PortForward { server = "port-forward" + } else if globalClientOpts.Core { + server = "kubernetes" } else { server = args[0] tlsTestResult, err := grpc_util.TestTLS(server) @@ -86,9 +96,6 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman PortForwardNamespace: globalClientOpts.PortForwardNamespace, Headers: globalClientOpts.Headers, } - acdClient := argocdclient.NewClientOrDie(&clientOpts) - setConn, setIf := acdClient.NewSettingsClientOrDie() - defer io.Close(setConn) if ctxName == "" { ctxName = server @@ -101,28 +108,32 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman // Perform the login var tokenString string var refreshToken string - if !sso { - tokenString = passwordLogin(acdClient, username, password) - } else { - ctx := context.Background() - httpClient, err := acdClient.HTTPClient() - errors.CheckError(err) - ctx = oidc.ClientContext(ctx, httpClient) - acdSet, err := setIf.Get(ctx, &settingspkg.SettingsQuery{}) - errors.CheckError(err) - oauth2conf, provider, err := acdClient.OIDCConfig(ctx, acdSet) + if !globalClientOpts.Core { + acdClient := argocdclient.NewClientOrDie(&clientOpts) + setConn, setIf := acdClient.NewSettingsClientOrDie() + defer io.Close(setConn) + if !sso { + tokenString = passwordLogin(acdClient, username, password) + } else { + ctx := context.Background() + httpClient, err := acdClient.HTTPClient() + errors.CheckError(err) + ctx = oidc.ClientContext(ctx, httpClient) + acdSet, err := setIf.Get(ctx, &settingspkg.SettingsQuery{}) + errors.CheckError(err) + oauth2conf, provider, err := acdClient.OIDCConfig(ctx, acdSet) + errors.CheckError(err) + tokenString, refreshToken = oauth2Login(ctx, ssoPort, acdSet.GetOIDCConfig(), oauth2conf, provider) + } + parser := &jwt.Parser{ + ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()), + } + claims := jwt.MapClaims{} + _, _, err := parser.ParseUnverified(tokenString, &claims) errors.CheckError(err) - tokenString, refreshToken = oauth2Login(ctx, ssoPort, acdSet.GetOIDCConfig(), oauth2conf, provider) + fmt.Printf("'%s' logged in successfully\n", userDisplayName(claims)) } - parser := &jwt.Parser{ - ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()), - } - claims := jwt.MapClaims{} - _, _, err := parser.ParseUnverified(tokenString, &claims) - errors.CheckError(err) - - fmt.Printf("'%s' logged in successfully\n", userDisplayName(claims)) // login successful. Persist the config localCfg, err := localconfig.ReadLocalConfig(globalClientOpts.ConfigPath) errors.CheckError(err) @@ -135,6 +146,7 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman Insecure: globalClientOpts.Insecure, GRPCWeb: globalClientOpts.GRPCWeb, GRPCWebRootPath: globalClientOpts.GRPCWebRootPath, + Core: globalClientOpts.Core, }) localCfg.UpsertUser(localconfig.User{ Name: ctxName, diff --git a/cmd/argocd/commands/repo.go b/cmd/argocd/commands/repo.go index ada05d361e3ce..246b01af02081 100644 --- a/cmd/argocd/commands/repo.go +++ b/cmd/argocd/commands/repo.go @@ -182,6 +182,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { GithubAppInstallationID: repoOpts.Repo.GithubAppInstallationId, GithubAppEnterpriseBaseUrl: repoOpts.Repo.GitHubAppEnterpriseBaseURL, Proxy: repoOpts.Proxy, + Project: repoOpts.Repo.Project, } _, err := repoIf.ValidateAccess(context.Background(), &repoAccessReq) errors.CheckError(err) @@ -226,7 +227,7 @@ func NewRepoRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command // Print table of repo info func printRepoTable(repos appsv1.Repositories) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - _, _ = fmt.Fprintf(w, "TYPE\tNAME\tREPO\tINSECURE\tOCI\tLFS\tCREDS\tSTATUS\tMESSAGE\n") + _, _ = fmt.Fprintf(w, "TYPE\tNAME\tREPO\tINSECURE\tOCI\tLFS\tCREDS\tSTATUS\tMESSAGE\tPROJECT\n") for _, r := range repos { var hasCreds string if !r.HasCredentials() { @@ -238,7 +239,7 @@ func printRepoTable(repos appsv1.Repositories) { hasCreds = "true" } } - _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%v\t%v\t%s\t%s\t%s\n", r.Type, r.Name, r.Repo, r.IsInsecure(), r.EnableOCI, r.EnableLFS, hasCreds, r.ConnectionState.Status, r.ConnectionState.Message) + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%v\t%v\t%s\t%s\t%s\t%s\n", r.Type, r.Name, r.Repo, r.IsInsecure(), r.EnableOCI, r.EnableLFS, hasCreds, r.ConnectionState.Status, r.ConnectionState.Message, r.Project) } _ = w.Flush() } diff --git a/cmd/argocd/commands/repocreds.go b/cmd/argocd/commands/repocreds.go index 4e415e57ad22f..d81862f8176d2 100644 --- a/cmd/argocd/commands/repocreds.go +++ b/cmd/argocd/commands/repocreds.go @@ -60,6 +60,9 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma # Add credentials with GitHub App authentication to use for all repositories under https://ghe.example.com/repos argocd repocreds add https://ghe.example.com/repos/ --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem --github-app-enterprise-base-url https://ghe.example.com/api/v3 + + # Add credentials with helm oci registry so that these oci registry urls do not need to be added as repos individually. + argocd repocreds add localhost:5000/myrepo --enable-oci --type helm ` var command = &cobra.Command{ diff --git a/cmd/argocd/commands/root.go b/cmd/argocd/commands/root.go index 34ea23ae5207d..f5289948caf34 100644 --- a/cmd/argocd/commands/root.go +++ b/cmd/argocd/commands/root.go @@ -4,6 +4,8 @@ import ( "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" + "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/admin" + "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" "github.com/argoproj/argo-cd/v2/util/cli" @@ -38,19 +40,20 @@ func NewCommand() *cobra.Command { } command.AddCommand(NewCompletionCommand()) - command.AddCommand(NewVersionCmd(&clientOpts)) - command.AddCommand(NewClusterCommand(&clientOpts, pathOpts)) - command.AddCommand(NewApplicationCommand(&clientOpts)) + command.AddCommand(headless.InitCommand(NewVersionCmd(&clientOpts), &clientOpts, nil)) + command.AddCommand(headless.InitCommand(NewClusterCommand(&clientOpts, pathOpts), &clientOpts, nil)) + command.AddCommand(headless.InitCommand(NewApplicationCommand(&clientOpts), &clientOpts, nil)) command.AddCommand(NewLoginCommand(&clientOpts)) command.AddCommand(NewReloginCommand(&clientOpts)) - command.AddCommand(NewRepoCommand(&clientOpts)) - command.AddCommand(NewRepoCredsCommand(&clientOpts)) + command.AddCommand(headless.InitCommand(NewRepoCommand(&clientOpts), &clientOpts, nil)) + command.AddCommand(headless.InitCommand(NewRepoCredsCommand(&clientOpts), &clientOpts, nil)) command.AddCommand(NewContextCommand(&clientOpts)) - command.AddCommand(NewProjectCommand(&clientOpts)) - command.AddCommand(NewAccountCommand(&clientOpts)) + command.AddCommand(headless.InitCommand(NewProjectCommand(&clientOpts), &clientOpts, nil)) + command.AddCommand(headless.InitCommand(NewAccountCommand(&clientOpts), &clientOpts, nil)) command.AddCommand(NewLogoutCommand(&clientOpts)) - command.AddCommand(NewCertCommand(&clientOpts)) - command.AddCommand(NewGPGCommand(&clientOpts)) + command.AddCommand(headless.InitCommand(NewCertCommand(&clientOpts), &clientOpts, nil)) + command.AddCommand(headless.InitCommand(NewGPGCommand(&clientOpts), &clientOpts, nil)) + command.AddCommand(admin.NewAdminCommand()) defaultLocalConfigPath, err := localconfig.DefaultLocalConfigPath() errors.CheckError(err) @@ -70,5 +73,6 @@ func NewCommand() *cobra.Command { 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", 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") return command } diff --git a/cmd/main.go b/cmd/main.go index 842715c6c8a80..21bcacb672477 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -11,7 +11,6 @@ import ( dex "github.com/argoproj/argo-cd/v2/cmd/argocd-dex/commands" reposerver "github.com/argoproj/argo-cd/v2/cmd/argocd-repo-server/commands" apiserver "github.com/argoproj/argo-cd/v2/cmd/argocd-server/commands" - util "github.com/argoproj/argo-cd/v2/cmd/argocd-util/commands" cli "github.com/argoproj/argo-cd/v2/cmd/argocd/commands" ) @@ -29,8 +28,6 @@ func main() { switch binaryName { case "argocd", "argocd-linux-amd64", "argocd-darwin-amd64", "argocd-windows-amd64.exe": command = cli.NewCommand() - case "argocd-util", "argocd-util-linux-amd64", "argocd-util-darwin-amd64", "argocd-util-windows-amd64.exe": - command = util.NewCommand() case "argocd-server": command = apiserver.NewCommand() case "argocd-application-controller": @@ -40,21 +37,7 @@ func main() { case "argocd-dex": command = dex.NewCommand() default: - if len(os.Args[1:]) > 0 { - // trying to guess between argocd and argocd-util by matching sub command - for _, cmd := range []*cobra.Command{cli.NewCommand(), util.NewCommand()} { - if _, _, err := cmd.Find(os.Args[1:]); err == nil { - command = cmd - break - } - } - } - - if command == nil { - fmt.Printf("Unknown binary name '%s'.Use '%s' environment variable to specify required binary name "+ - "(possible values 'argocd' or 'argocd-util').\n", binaryName, binaryNameEnv) - os.Exit(1) - } + command = cli.NewCommand() } if err := command.Execute(); err != nil { diff --git a/cmd/util/cluster.go b/cmd/util/cluster.go index 4b2687cf4f4bd..71a4f79c0641f 100644 --- a/cmd/util/cluster.go +++ b/cmd/util/cluster.go @@ -55,7 +55,7 @@ func PrintKubeContexts(ca clientcmd.ConfigAccess) { } } -func NewCluster(name string, namespaces []string, conf *rest.Config, managerBearerToken string, awsAuthConf *argoappv1.AWSAuthConfig, execProviderConf *argoappv1.ExecProviderConfig) *argoappv1.Cluster { +func NewCluster(name string, namespaces []string, clusterResources bool, conf *rest.Config, managerBearerToken string, awsAuthConf *argoappv1.AWSAuthConfig, execProviderConf *argoappv1.ExecProviderConfig, labels, annotations map[string]string) *argoappv1.Cluster { tlsClientConfig := argoappv1.TLSClientConfig{ Insecure: conf.TLSClientConfig.Insecure, ServerName: conf.TLSClientConfig.ServerName, @@ -80,14 +80,17 @@ func NewCluster(name string, namespaces []string, conf *rest.Config, managerBear } clst := argoappv1.Cluster{ - Server: conf.Host, - Name: name, - Namespaces: namespaces, + Server: conf.Host, + Name: name, + Namespaces: namespaces, + ClusterResources: clusterResources, Config: argoappv1.ClusterConfig{ TLSClientConfig: tlsClientConfig, AWSAuthConfig: awsAuthConf, ExecProviderConfig: execProviderConf, }, + Labels: labels, + Annotations: annotations, } // Bearer token will preferentially be used for auth if present, @@ -108,7 +111,9 @@ type ClusterOptions struct { AwsClusterName string SystemNamespace string Namespaces []string + ClusterResources bool Name string + Project string Shard int64 ExecProviderCommand string ExecProviderArgs []string @@ -122,7 +127,9 @@ func AddClusterFlags(command *cobra.Command, opts *ClusterOptions) { 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().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") + command.Flags().StringVar(&opts.Project, "project", "", "project of the cluster") command.Flags().Int64Var(&opts.Shard, "shard", -1, "Cluster shard number; inferred from hostname if not set") command.Flags().StringVar(&opts.ExecProviderCommand, "exec-command", "", "Command to run to provide client credentials to the cluster. You may need to build a custom ArgoCD image to ensure the command is available at runtime.") command.Flags().StringArrayVar(&opts.ExecProviderArgs, "exec-command-args", nil, "Arguments to supply to the --exec-command executable") diff --git a/cmd/util/cluster_test.go b/cmd/util/cluster_test.go index 80cbbe60c2948..6afea6fa7c17c 100644 --- a/cmd/util/cluster_test.go +++ b/cmd/util/cluster_test.go @@ -11,7 +11,9 @@ import ( ) func Test_newCluster(t *testing.T) { - clusterWithData := NewCluster("test-cluster", []string{"test-namespace"}, &rest.Config{ + labels := map[string]string{"key1": "val1"} + annotations := map[string]string{"key2": "val2"} + clusterWithData := NewCluster("test-cluster", []string{"test-namespace"}, false, &rest.Config{ TLSClientConfig: rest.TLSClientConfig{ Insecure: false, ServerName: "test-endpoint.example.com", @@ -23,13 +25,15 @@ func Test_newCluster(t *testing.T) { }, "test-bearer-token", &v1alpha1.AWSAuthConfig{}, - &v1alpha1.ExecProviderConfig{}) + &v1alpha1.ExecProviderConfig{}, labels, annotations) assert.Equal(t, "test-cert-data", string(clusterWithData.Config.CertData)) assert.Equal(t, "test-key-data", string(clusterWithData.Config.KeyData)) assert.Equal(t, "", clusterWithData.Config.BearerToken) + assert.Equal(t, labels, clusterWithData.Labels) + assert.Equal(t, annotations, clusterWithData.Annotations) - clusterWithFiles := NewCluster("test-cluster", []string{"test-namespace"}, &rest.Config{ + clusterWithFiles := NewCluster("test-cluster", []string{"test-namespace"}, false, &rest.Config{ TLSClientConfig: rest.TLSClientConfig{ Insecure: false, ServerName: "test-endpoint.example.com", @@ -41,13 +45,15 @@ func Test_newCluster(t *testing.T) { }, "test-bearer-token", &v1alpha1.AWSAuthConfig{}, - &v1alpha1.ExecProviderConfig{}) + &v1alpha1.ExecProviderConfig{}, labels, nil) 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) - clusterWithBearerToken := NewCluster("test-cluster", []string{"test-namespace"}, &rest.Config{ + clusterWithBearerToken := NewCluster("test-cluster", []string{"test-namespace"}, false, &rest.Config{ TLSClientConfig: rest.TLSClientConfig{ Insecure: false, ServerName: "test-endpoint.example.com", @@ -57,7 +63,9 @@ func Test_newCluster(t *testing.T) { }, "test-bearer-token", &v1alpha1.AWSAuthConfig{}, - &v1alpha1.ExecProviderConfig{}) + &v1alpha1.ExecProviderConfig{}, nil, nil) assert.Equal(t, "test-bearer-token", clusterWithBearerToken.Config.BearerToken) + assert.Nil(t, clusterWithBearerToken.Labels) + assert.Nil(t, clusterWithBearerToken.Annotations) } diff --git a/cmd/util/common.go b/cmd/util/common.go index 1210b0ad9c1ec..7c7b629ab4c98 100644 --- a/cmd/util/common.go +++ b/cmd/util/common.go @@ -1,85 +1,6 @@ package util -import ( - "encoding/json" - "fmt" - - "github.com/ghodss/yaml" - v1 "k8s.io/api/core/v1" - - "github.com/argoproj/gitops-engine/pkg/utils/kube" -) - var ( LogFormat string LogLevel string ) - -// PrintResource prints a single resource in YAML or JSON format to stdout according to the output format -func PrintResources(resources []interface{}, output string) error { - for i, resource := range resources { - filteredResource, err := omitFields(resource) - if err != nil { - return err - } - resources[i] = filteredResource - } - var obj interface{} = resources - if len(resources) == 1 { - obj = resources[0] - } - - switch output { - case "json": - jsonBytes, err := json.MarshalIndent(obj, "", " ") - if err != nil { - return err - } - - fmt.Println(string(jsonBytes)) - case "yaml": - yamlBytes, err := yaml.Marshal(obj) - if err != nil { - return err - } - // marshaled YAML already ends with the new line character - fmt.Print(string(yamlBytes)) - default: - return fmt.Errorf("unknown output format: %s", output) - } - return nil -} - -// omit fields such as status, creationTimestamp and metadata.namespace in k8s objects -func omitFields(resource interface{}) (interface{}, error) { - jsonBytes, err := json.Marshal(resource) - if err != nil { - return nil, err - } - - toMap := make(map[string]interface{}) - err = json.Unmarshal([]byte(string(jsonBytes)), &toMap) - if err != nil { - return nil, err - } - - delete(toMap, "status") - if v, ok := toMap["metadata"]; ok { - if metadata, ok := v.(map[string]interface{}); ok { - delete(metadata, "creationTimestamp") - delete(metadata, "namespace") - } - } - return toMap, nil -} - -// ConvertSecretData converts kubernetes secret's data to stringData -func ConvertSecretData(secret *v1.Secret) { - secret.Kind = kube.SecretKind - secret.APIVersion = "v1" - secret.StringData = map[string]string{} - for k, v := range secret.Data { - secret.StringData[k] = string(v) - } - secret.Data = map[string][]byte{} -} diff --git a/cmd/util/repo.go b/cmd/util/repo.go index 776e162a879cb..138891c369bfc 100644 --- a/cmd/util/repo.go +++ b/cmd/util/repo.go @@ -27,6 +27,7 @@ type RepoOptions struct { func AddRepoFlags(command *cobra.Command, opts *RepoOptions) { command.Flags().StringVar(&opts.Repo.Type, "type", common.DefaultRepoType, "type of the repository, \"git\" or \"helm\"") command.Flags().StringVar(&opts.Repo.Name, "name", "", "name of the repository, mandatory for repositories of type helm") + command.Flags().StringVar(&opts.Repo.Project, "project", "", "project of the repository") command.Flags().StringVar(&opts.Repo.Username, "username", "", "username to the repository") command.Flags().StringVar(&opts.Repo.Password, "password", "", "password to the repository") command.Flags().StringVar(&opts.SshPrivateKeyPath, "ssh-private-key-path", "", "path to the private ssh key (e.g. ~/.ssh/id_rsa)") diff --git a/common/common.go b/common/common.go index 8cfa93a65326b..248771760d916 100644 --- a/common/common.go +++ b/common/common.go @@ -70,6 +70,9 @@ const ( ChangePasswordSSOTokenMaxAge = time.Minute * 5 // GithubAppCredsExpirationDuration is the default time used to cache the GitHub app credentials GithubAppCredsExpirationDuration = time.Minute * 60 + + // PasswordPatten is the default password patten + PasswordPatten = `^.{8,32}$` ) // Dex related constants @@ -110,6 +113,9 @@ const ( // LabelValueSecretTypeRepoCreds indicates a secret type of repository credentials LabelValueSecretTypeRepoCreds = "repo-creds" + // The Argo CD application name is used as the instance name + AnnotationKeyAppInstance = "argocd.argoproj.io/tracking-id" + // AnnotationCompareOptions is a comma-separated list of options for comparison AnnotationCompareOptions = "argocd.argoproj.io/compare-options" diff --git a/controller/appcontroller.go b/controller/appcontroller.go index eb253619d507a..7d1bd9e04c4f3 100644 --- a/controller/appcontroller.go +++ b/controller/appcontroller.go @@ -112,6 +112,7 @@ type ApplicationController struct { metricsServer *metrics.MetricsServer kubectlSemaphore *semaphore.Weighted clusterFilter func(cluster *appv1.Cluster) bool + projByNameCache sync.Map } // NewApplicationController creates new instance of ApplicationController. @@ -151,6 +152,7 @@ func NewApplicationController( settingsMgr: settingsMgr, selfHealTimeout: selfHealTimeout, clusterFilter: clusterFilter, + projByNameCache: sync.Map{}, } if kubectlParallelismLimit > 0 { ctrl.kubectlSemaphore = semaphore.NewWeighted(kubectlParallelismLimit) @@ -163,16 +165,19 @@ func NewApplicationController( AddFunc: func(obj interface{}) { if key, err := cache.MetaNamespaceKeyFunc(obj); err == nil { ctrl.projectRefreshQueue.Add(key) + ctrl.InvalidateProjectsCache() } }, UpdateFunc: func(old, new interface{}) { if key, err := cache.MetaNamespaceKeyFunc(new); err == nil { ctrl.projectRefreshQueue.Add(key) + ctrl.InvalidateProjectsCache() } }, DeleteFunc: func(obj interface{}) { if key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj); err == nil { ctrl.projectRefreshQueue.Add(key) + ctrl.InvalidateProjectsCache() } }, }) @@ -190,8 +195,8 @@ func NewApplicationController( return nil, err } } - stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settingsMgr, kubectl, ctrl.metricsServer, ctrl.handleObjectUpdated, clusterFilter) - appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout) + stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settingsMgr, kubectl, ctrl.metricsServer, ctrl.handleObjectUpdated, clusterFilter, argo.NewResourceTracking()) + appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking()) ctrl.appInformer = appInformer ctrl.appLister = appLister ctrl.projInformer = projInformer @@ -201,6 +206,13 @@ func NewApplicationController( return &ctrl, nil } +func (ctrl *ApplicationController) InvalidateProjectsCache() { + ctrl.projByNameCache.Range(func(key, _ interface{}) bool { + ctrl.projByNameCache.Delete(key) + return true + }) +} + func (ctrl *ApplicationController) GetMetricsServer() *metrics.MetricsServer { return ctrl.metricsServer } @@ -230,8 +242,35 @@ func isSelfReferencedApp(app *appv1.Application, ref v1.ObjectReference) bool { gvk.Kind == application.ApplicationKind } +func (ctrl *ApplicationController) newAppProjCache(name string) *appProjCache { + return &appProjCache{name: name, ctrl: ctrl} +} + +type appProjCache struct { + name string + ctrl *ApplicationController + + lock sync.Mutex + appProj *appv1.AppProject +} + +func (projCache *appProjCache) GetAppProject(ctx context.Context) (*appv1.AppProject, error) { + projCache.lock.Lock() + defer projCache.lock.Unlock() + if projCache.appProj != nil { + return projCache.appProj, nil + } + proj, err := argo.GetAppProjectByName(projCache.name, applisters.NewAppProjectLister(projCache.ctrl.projInformer.GetIndexer()), projCache.ctrl.namespace, projCache.ctrl.settingsMgr, projCache.ctrl.db, ctx) + if err != nil { + return nil, err + } + projCache.appProj = proj + return projCache.appProj, nil +} + func (ctrl *ApplicationController) getAppProj(app *appv1.Application) (*appv1.AppProject, error) { - return argo.GetAppProject(&app.Spec, applisters.NewAppProjectLister(ctrl.projInformer.GetIndexer()), ctrl.namespace, ctrl.settingsMgr) + projCache, _ := ctrl.projByNameCache.LoadOrStore(app.Spec.GetProject(), ctrl.newAppProjCache(app.Spec.GetProject())) + return projCache.(*appProjCache).GetAppProject(context.TODO()) } func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]bool, ref v1.ObjectReference) { @@ -316,7 +355,7 @@ func isKnownOrphanedResourceExclusion(key kube.ResourceKey, proj *appv1.AppProje func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managedResources []*appv1.ResourceDiff) (*appv1.ApplicationTree, error) { nodes := make([]appv1.ResourceNode, 0) - proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(ctrl.projInformer.GetIndexer()), ctrl.namespace, ctrl.settingsMgr) + proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(ctrl.projInformer.GetIndexer()), ctrl.namespace, ctrl.settingsMgr, ctrl.db, context.TODO()) if err != nil { return nil, err } diff --git a/controller/cache/cache.go b/controller/cache/cache.go index 1650d163fb8fd..c17c1199acb7f 100644 --- a/controller/cache/cache.go +++ b/controller/cache/cache.go @@ -105,7 +105,8 @@ func NewLiveStateCache( kubectl kube.Kubectl, metricsServer *metrics.MetricsServer, onObjectUpdated ObjectUpdatedHandler, - clusterFilter func(cluster *appv1.Cluster) bool) LiveStateCache { + clusterFilter func(cluster *appv1.Cluster) bool, + resourceTracking argo.ResourceTracking) LiveStateCache { return &liveStateCache{ appInformer: appInformer, @@ -116,8 +117,9 @@ func NewLiveStateCache( settingsMgr: settingsMgr, metricsServer: metricsServer, // The default limit of 50 is chosen based on experiments. - listSemaphore: semaphore.NewWeighted(50), - clusterFilter: clusterFilter, + listSemaphore: semaphore.NewWeighted(50), + clusterFilter: clusterFilter, + resourceTracking: resourceTracking, } } @@ -127,13 +129,14 @@ type cacheSettings struct { } type liveStateCache struct { - db db.ArgoDB - appInformer cache.SharedIndexInformer - onObjectUpdated ObjectUpdatedHandler - kubectl kube.Kubectl - settingsMgr *settings.SettingsManager - metricsServer *metrics.MetricsServer - clusterFilter func(cluster *appv1.Cluster) bool + db db.ArgoDB + appInformer cache.SharedIndexInformer + onObjectUpdated ObjectUpdatedHandler + kubectl kube.Kubectl + settingsMgr *settings.SettingsManager + metricsServer *metrics.MetricsServer + clusterFilter func(cluster *appv1.Cluster) bool + resourceTracking argo.ResourceTracking // listSemaphore is used to limit the number of concurrent memory consuming operations on the // k8s list queries results across all clusters to avoid memory spikes during cache initialization. @@ -285,16 +288,19 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e return nil, fmt.Errorf("controller is configured to ignore cluster %s", cluster.Server) } + trackingMethod := argo.GetTrackingMethod(c.settingsMgr) clusterCache = clustercache.NewClusterCache(cluster.RESTConfig(), clustercache.SetListSemaphore(c.listSemaphore), clustercache.SetResyncTimeout(K8SClusterResyncDuration), clustercache.SetSettings(cacheSettings.clusterSettings), clustercache.SetNamespaces(cluster.Namespaces), + clustercache.SetClusterResources(cluster.ClusterResources), clustercache.SetPopulateResourceInfoHandler(func(un *unstructured.Unstructured, isRoot bool) (interface{}, bool) { res := &ResourceInfo{} populateNodeInfo(un, res) res.Health, _ = health.GetResourceHealth(un, cacheSettings.clusterSettings.ResourceHealthOverride) - appName := kube.GetAppInstanceLabel(un, cacheSettings.appInstanceLabelKey) + + appName := c.resourceTracking.GetAppName(un, cacheSettings.appInstanceLabelKey, trackingMethod) if isRoot && appName != "" { res.AppName = appName } @@ -544,6 +550,9 @@ func (c *liveStateCache) handleModEvent(oldCluster *appv1.Cluster, newCluster *a if !reflect.DeepEqual(oldCluster.Namespaces, newCluster.Namespaces) { updateSettings = append(updateSettings, clustercache.SetNamespaces(newCluster.Namespaces)) } + if !reflect.DeepEqual(oldCluster.ClusterResources, newCluster.ClusterResources) { + updateSettings = append(updateSettings, clustercache.SetClusterResources(newCluster.ClusterResources)) + } forceInvalidate := false if newCluster.RefreshRequestedAt != nil && cluster.GetClusterInfo().LastCacheSyncTime != nil && diff --git a/controller/cache/info.go b/controller/cache/info.go index 4be138b374513..ab1357ee0e89b 100644 --- a/controller/cache/info.go +++ b/controller/cache/info.go @@ -1,9 +1,12 @@ package cache import ( + "errors" "fmt" "strings" + "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/argoproj/gitops-engine/pkg/utils/text" v1 "k8s.io/api/core/v1" @@ -86,16 +89,36 @@ func populateServiceInfo(un *unstructured.Unstructured, res *ResourceInfo) { res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetLabels: targetLabels, Ingress: ingress} } +func getServiceName(backend map[string]interface{}, gvk schema.GroupVersionKind) (string, error) { + switch gvk.Group { + case "extensions": + return fmt.Sprintf("%s", backend["serviceName"]), nil + case "networking.k8s.io": + switch gvk.Version { + case "v1beta1": + return fmt.Sprintf("%s", backend["serviceName"]), nil + case "v1": + if service, ok, err := unstructured.NestedMap(backend, "service"); ok && err == nil { + return fmt.Sprintf("%s", service["name"]), nil + } + } + } + return "", errors.New("unable to resolve string") +} + func populateIngressInfo(un *unstructured.Unstructured, res *ResourceInfo) { ingress := getIngress(un) targetsMap := make(map[v1alpha1.ResourceRef]bool) + gvk := un.GroupVersionKind() if backend, ok, err := unstructured.NestedMap(un.Object, "spec", "backend"); ok && err == nil { - targetsMap[v1alpha1.ResourceRef{ - Group: "", - Kind: kube.ServiceKind, - Namespace: un.GetNamespace(), - Name: fmt.Sprintf("%s", backend["serviceName"]), - }] = true + if serviceName, err := getServiceName(backend, gvk); err == nil { + targetsMap[v1alpha1.ResourceRef{ + Group: "", + Kind: kube.ServiceKind, + Namespace: un.GetNamespace(), + Name: serviceName, + }] = true + } } urlsSet := make(map[string]bool) if rules, ok, err := unstructured.NestedSlice(un.Object, "spec", "rules"); ok && err == nil { @@ -123,13 +146,15 @@ func populateIngressInfo(un *unstructured.Unstructured, res *ResourceInfo) { continue } - if serviceName, ok, err := unstructured.NestedString(path, "backend", "serviceName"); ok && err == nil { - targetsMap[v1alpha1.ResourceRef{ - Group: "", - Kind: kube.ServiceKind, - Namespace: un.GetNamespace(), - Name: serviceName, - }] = true + if backend, ok, err := unstructured.NestedMap(path, "backend"); ok && err == nil { + if serviceName, err := getServiceName(backend, gvk); err == nil { + targetsMap[v1alpha1.ResourceRef{ + Group: "", + Kind: kube.ServiceKind, + Namespace: un.GetNamespace(), + Name: serviceName, + }] = true + } } if host == nil || host == "" { @@ -146,6 +171,17 @@ func populateIngressInfo(un *unstructured.Unstructured, res *ResourceInfo) { tlshost := tlsline["host"] if tlshost == host { stringPort = "https" + continue + } + if hosts := tlsline["hosts"]; hosts != nil { + tlshosts, ok := tlsline["hosts"].(map[string]interface{}) + if ok { + for j := range tlshosts { + if tlshosts[j] == host { + stringPort = "https" + } + } + } } } } diff --git a/controller/cache/info_test.go b/controller/cache/info_test.go index 9dcb2b08f04e6..dbbfee2a77579 100644 --- a/controller/cache/info_test.go +++ b/controller/cache/info_test.go @@ -133,6 +133,43 @@ var ( ingress: - ip: 107.178.210.11`) + testIngressNetworkingV1 = strToUnstructured(` + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: helm-guestbook + namespace: default + uid: "4" + spec: + backend: + service: + name: not-found-service + port: + number: 443 + rules: + - host: helm-guestbook.com + http: + paths: + - backend: + service: + name: helm-guestbook + port: + number: 443 + path: / + - backend: + service: + name: helm-guestbook + port: + name: https + path: / + tls: + - host: helm-guestbook.com + secretName: my-tls-secret + status: + loadBalancer: + ingress: + - ip: 107.178.210.11`) + testIstioVirtualService = strToUnstructured(` apiVersion: networking.istio.io/v1alpha3 kind: VirtualService @@ -255,27 +292,35 @@ func TestGetIstioVirtualServiceInfo(t *testing.T) { } func TestGetIngressInfo(t *testing.T) { - info := &ResourceInfo{} - populateNodeInfo(testIngress, info) - assert.Equal(t, 0, len(info.Info)) - sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool { - return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0 - }) - assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ - Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}}, - TargetRefs: []v1alpha1.ResourceRef{{ - Namespace: "default", - Group: "", - Kind: kube.ServiceKind, - Name: "not-found-service", - }, { - Namespace: "default", - Group: "", - Kind: kube.ServiceKind, - Name: "helm-guestbook", - }}, - ExternalURLs: []string{"https://helm-guestbook.com/"}, - }, info.NetworkingInfo) + var tests = []struct { + Ingress *unstructured.Unstructured + }{ + {testIngress}, + {testIngressNetworkingV1}, + } + for _, tc := range tests { + info := &ResourceInfo{} + populateNodeInfo(tc.Ingress, info) + assert.Equal(t, 0, len(info.Info)) + sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool { + return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0 + }) + assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ + Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}}, + TargetRefs: []v1alpha1.ResourceRef{{ + Namespace: "default", + Group: "", + Kind: kube.ServiceKind, + Name: "not-found-service", + }, { + Namespace: "default", + Group: "", + Kind: kube.ServiceKind, + Name: "helm-guestbook", + }}, + ExternalURLs: []string{"https://helm-guestbook.com/"}, + }, info.NetworkingInfo) + } } func TestGetIngressInfoWildCardPath(t *testing.T) { @@ -396,7 +441,7 @@ func TestGetIngressInfoNoHost(t *testing.T) { } func TestExternalUrlWithSubPath(t *testing.T) { ingress := strToUnstructured(` - apiVersion: extensions/v1beta1 + apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: helm-guestbook @@ -424,7 +469,7 @@ func TestExternalUrlWithSubPath(t *testing.T) { } func TestExternalUrlWithMultipleSubPaths(t *testing.T) { ingress := strToUnstructured(` - apiVersion: extensions/v1beta1 + apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: helm-guestbook @@ -463,7 +508,7 @@ func TestExternalUrlWithMultipleSubPaths(t *testing.T) { } func TestExternalUrlWithNoSubPath(t *testing.T) { ingress := strToUnstructured(` - apiVersion: extensions/v1beta1 + apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: helm-guestbook diff --git a/controller/metrics/clustercollector.go b/controller/metrics/clustercollector.go index f5b5b45b69a22..bc40a7bb60707 100644 --- a/controller/metrics/clustercollector.go +++ b/controller/metrics/clustercollector.go @@ -41,6 +41,12 @@ var ( descClusterDefaultLabels, nil, ) + descClusterConnectionStatus = prometheus.NewDesc( + "argocd_cluster_connection_status", + "The k8s cluster current connection status.", + append(descClusterDefaultLabels, "k8s_version"), + nil, + ) ) type HasClustersInfo interface { @@ -77,9 +83,11 @@ func (c *clusterCollector) Describe(ch chan<- *prometheus.Desc) { ch <- descClusterCacheResources ch <- descClusterAPIs ch <- descClusterCacheAgeSeconds + ch <- descClusterConnectionStatus } func (c *clusterCollector) Collect(ch chan<- prometheus.Metric) { + now := time.Now() for _, c := range c.info { defaultValues := []string{c.Server} @@ -91,5 +99,6 @@ func (c *clusterCollector) Collect(ch chan<- prometheus.Metric) { cacheAgeSeconds = int(now.Sub(*c.LastCacheSyncTime).Seconds()) } ch <- prometheus.MustNewConstMetric(descClusterCacheAgeSeconds, prometheus.GaugeValue, float64(cacheAgeSeconds), defaultValues...) + ch <- prometheus.MustNewConstMetric(descClusterConnectionStatus, prometheus.GaugeValue, boolFloat64(c.SyncError != nil), append(defaultValues, c.K8SVersion)...) } } diff --git a/controller/metrics/metrics_test.go b/controller/metrics/metrics_test.go index f526f8098fa2f..3a34f30aa4065 100644 --- a/controller/metrics/metrics_test.go +++ b/controller/metrics/metrics_test.go @@ -138,7 +138,7 @@ func newFakeLister(fakeAppYAMLs ...string) (context.CancelFunc, applister.Applic fakeApps = append(fakeApps, a) } appClientset := appclientset.NewSimpleClientset(fakeApps...) - factory := appinformer.NewFilteredSharedInformerFactory(appClientset, 0, "argocd", func(options *metav1.ListOptions) {}) + factory := appinformer.NewSharedInformerFactoryWithOptions(appClientset, 0, appinformer.WithNamespace("argocd"), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) appInformer := factory.Argoproj().V1alpha1().Applications().Informer() go appInformer.Run(ctx.Done()) if !cache.WaitForCacheSync(ctx.Done(), appInformer.HasSynced) { diff --git a/controller/state.go b/controller/state.go index a6819a88afb55..a5c121d484897 100644 --- a/controller/state.go +++ b/controller/state.go @@ -98,6 +98,7 @@ type appStateManager struct { cache *appstatecache.Cache namespace string statusRefreshTimeout time.Duration + resourceTracking argo.ResourceTracking } func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1.ApplicationSource, appLabelKey, revision string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, *apiclient.ManifestResponse, error) { @@ -148,6 +149,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1 if err != nil { return nil, nil, err } + kustomizeOptions, err := kustomizeSettings.GetOptions(app.Spec.Source) if err != nil { return nil, nil, err @@ -174,6 +176,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1 ApiVersions: argo.APIGroupsToVersions(apiGroups), VerifySignature: verifySignature, HelmRepoCreds: permittedHelmCredentials, + TrackingMethod: string(argo.GetTrackingMethod(m.settingsMgr)), }) if err != nil { return nil, nil, err @@ -460,9 +463,11 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap } } + trackingMethod := argo.GetTrackingMethod(m.settingsMgr) + for _, liveObj := range liveObjByKey { if liveObj != nil { - appInstanceName := kubeutil.GetAppInstanceLabel(liveObj, appLabelKey) + appInstanceName := m.resourceTracking.GetAppName(liveObj, appLabelKey, trackingMethod) if appInstanceName != "" && appInstanceName != app.Name { conditions = append(conditions, v1alpha1.ApplicationCondition{ Type: v1alpha1.ApplicationConditionSharedResourceWarning, @@ -681,6 +686,7 @@ func NewAppStateManager( metricsServer *metrics.MetricsServer, cache *appstatecache.Cache, statusRefreshTimeout time.Duration, + resourceTracking argo.ResourceTracking, ) AppStateManager { return &appStateManager{ liveStateCache: liveStateCache, @@ -694,5 +700,6 @@ func NewAppStateManager( projInformer: projInformer, metricsServer: metricsServer, statusRefreshTimeout: statusRefreshTimeout, + resourceTracking: resourceTracking, } } diff --git a/controller/sync.go b/controller/sync.go index 1d839d094384c..5f5ceac74d0d7 100644 --- a/controller/sync.go +++ b/controller/sync.go @@ -87,7 +87,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha revision = syncOp.Revision } - proj, err := argo.GetAppProject(&app.Spec, listersv1alpha1.NewAppProjectLister(m.projInformer.GetIndexer()), m.namespace, m.settingsMgr) + proj, err := argo.GetAppProject(&app.Spec, listersv1alpha1.NewAppProjectLister(m.projInformer.GetIndexer()), m.namespace, m.settingsMgr, m.db, context.TODO()) if err != nil { state.Phase = common.OperationError state.Message = fmt.Sprintf("Failed to load application project: %v", err) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index dd68b9251bd85..0cef196dca5a1 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -1 +1 @@ -Please refer to [the Contribution Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/contributing/) +Please refer to [the Contribution Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/code-contributions/) diff --git a/docs/assets/azure-enterprise-claims.png b/docs/assets/azure-enterprise-claims.png old mode 100755 new mode 100644 diff --git a/docs/assets/azure-enterprise-saml-urls.png b/docs/assets/azure-enterprise-saml-urls.png old mode 100755 new mode 100644 diff --git a/docs/assets/azure-enterprise-users.png b/docs/assets/azure-enterprise-users.png old mode 100755 new mode 100644 diff --git a/docs/assets/banner.png b/docs/assets/banner.png new file mode 100644 index 0000000000000..522199e1d8ee8 Binary files /dev/null and b/docs/assets/banner.png differ diff --git a/docs/assets/google-admin-oidc-uris.png b/docs/assets/google-admin-oidc-uris.png new file mode 100644 index 0000000000000..0fcedcc2f3f08 Binary files /dev/null and b/docs/assets/google-admin-oidc-uris.png differ diff --git a/docs/assets/google-groups-membership.png b/docs/assets/google-groups-membership.png new file mode 100644 index 0000000000000..98aa661d76699 Binary files /dev/null and b/docs/assets/google-groups-membership.png differ diff --git a/docs/developer-guide/code-contributions.md b/docs/developer-guide/code-contributions.md new file mode 100644 index 0000000000000..f09a3ae55e0e2 --- /dev/null +++ b/docs/developer-guide/code-contributions.md @@ -0,0 +1,112 @@ +# Submitting code contributions to Argo CD + +## Preface + +The Argo CD project continuously grows, both in terms of features and community size. It gets adopted by more and more organisations which entrust Argo CD to handle their critical production workloads. Thus, we need to take great care with any changes that affect compatibility, performance, scalability, stability and security of Argo CD. For this reason, every new feature or larger enhancement must be properly designed and discussed before it gets accepted into the code base. + +We do welcome and encourage everyone to participate in the Argo CD project, but please understand that we can't accept each and every contribution from the community, for various reasons. + +If you want to submit code for a great new feature or enhancement, we kindly ask you to take a look at the +enhancement process outlined below before you start to write code or submit a PR. This will ensure that your idea is well aligned with the project's strategy and technical requirements, and it will help greatly in getting your code merged into our code base. + +Before submitting code for a new feature (and also, to some extent, for more complex bug fixes) please +[raise an Enhancement Proposal or Bug Issue](https://github.com/argoproj/argo-cd/issues/new/choose) +first. + +Each enhancement proposal needs to go through our +[triage process](#triage-process) +before we accept code contributions. To facilitate triage and to provide transparency, we use +[this GitHub project](https://github.com/orgs/argoproj/projects/18) to keep track of this process' outcome. + +_Please_ do not spend too much time on larger features or refactorings before the corresponding enhancement has been triaged. This may save everyone some amount of frustration and time, as the enhancement proposal might be rejected, and the code would never get merged. However, sometimes it's helpful to have some PoC code along with a proposal. + +We will do our best to triage incoming enhancement proposals quickly, with one of the following outcomes: + +* Accepted +* Rejected +* Proposal requires a design document to be further discussed + +Depending on how many enhancement proposals we receive at given times, it may take some time until we can look at yours. + +Also, please make sure you have read our +[Toolchain Guide](toolchain-guide.md) +to understand our toolchain and our continuous integration processes. It contains some invaluable information to get started with the complex code base that makes up Argo CD. + +## Quick start + +If you want a quick start contributing to Argo CD, take a look at issues that are labeled with +[help wanted](https://github.com/argoproj/argo-cd/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) +or +[good first issue](https://github.com/argoproj/argo-cd/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). + +These are issues that were already triaged and accepted. + +If the issue is already attached to next +[version milestone](https://github.com/argoproj/argo-cd/milestones), +we have decided to also dedicate some of our time on reviews to PRs received for these issues. + +We encourage our community to pick up issues that are labeled in this way *and* are attached to the next version's milestone, with a promise for them to get a proper review with the clear intention for the incoming PRs to get merged. + +## Triage process + +### Overview + +Our triage process for enhancements proposals ensures that we take a look at all incoming enhancements to determine whether we will accept code submissions to implement them. + +The process works as follows: + +* New Enhancement Proposals raised on our GitHub issue tracker are moved to the _Incoming_ column of the project's board. These are the proposals that are in the queue for triage. +* The _Active_ column holds the issues that are currently being triaged, or will be triaged next. +* The _Accepted_ column holds the issues that have been triaged and are considered good to be implemented (e.g. the project agreed that the feature would be great to have) +* The _Declined_ column holds the issues that were rejected during triage. The issue will be updated with information about why the proposal has been rejected. +* The _Needs discussion_ column holds the issues that were found to require additional information, or even a design document, during triage. + +### Triage cadence + +Triage of enhancement proposals is performed transparently, offline using issue comments and online in our weekly contributor's meeting. _Everyone_ is invited to participate in triaging, the process is not limited to participation only by maintainers. + +Usually, we will triage enhancement proposals in a First-In-First-Out order, which mean that oldest proposals will be triaged first. + +We aim to triage at least 10 proposals a week. Depending on our available time, we may be triaging a higher or lower number of proposals in any given week. + +## Proposal states + +### Accepted proposals + +When a proposal is considered _Accepted_, it was decided that this enhancement would be valuable to the community at large and fits into the overall strategic roadmap of the project. + +Implementation of the issue may be started, either by the proposal's creator or another community member (including maintainers of the project). + +The issue should be refined enough by now to contain any concerns and guidelines to be taken into consideration during implementation. + +### Declined proposals + +We don't decline proposals lightly, and we will do our best to give a proper reasoning why we think that the proposal does not fit with the future of the project. Reasons for declining proposals may be - amongst others - that the change would be breaking for many, or that it does not meet the strategic direction of the project. Usually, discussion will be facilitated with the enhancement's creator before declining a proposal. + +Once a proposal is in _Declined_ state it's unlikely that we will accept code contributions for its implementation. + +### Proposals that need discussion + +Sometimes, we can't completely understand a proposal from its GitHub issue and require more information on the original intent or on more details about the implementation. If we are confronted with such an issue during the triage, we move this issue to the _Needs discussion_ column to indicate that we expect the issue's creator to supply more information on their idea. We may ask you to provide this information, either by adding that information to the issue itself or by joining one of our +[regular contributor's meeting](#regular-contributor-meeting) +to discuss the proposal with us. + +Also, issues that we find to require a more formal design document will be moved to this column. + +## Design documents + +For some enhancement proposals (especially those that will change behavior of Argo CD substantially, are attached with some caveats or where upgrade/downgrade paths are not clear), a more formal design document will be required in order to fully discuss and understand the enhancement in the broader community. This requirement is usually determined during triage. If you submitted an enhancement proposal, we may ask you to provide this more formal write down, along with some concerns or topics that need to be adressed. + +Design documents are usually submitted as PR and use [this template](https://github.com/argoproj/argo-cd/blob/master/docs/proposals/001-proposal-template.md) as a guide what kind of information we're looking for. Discussion will take place in the review process. When a design document gets merged, we consider it as approved and code can be written and submitted to implement this specific design. + +## Regular contributor meeting + +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 4pm UTC (9am Pacific, 12pm Eastern, 6pm Central European, 9:30pm 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 about it. \ No newline at end of file diff --git a/docs/developer-guide/contributing.md b/docs/developer-guide/contributing.md index 18cebfa76d778..d7524c8323f51 100644 --- a/docs/developer-guide/contributing.md +++ b/docs/developer-guide/contributing.md @@ -1,327 +1,2 @@ -# Contribution guide - -## Preface - -We want to make contributing to ArgoCD as simple and smooth as possible. - -This guide shall help you in setting up your build & test environment, so that you can start developing and testing bug fixes and feature enhancements without having to make too much effort in setting up a local toolchain. - -If you want to submit a PR, please read this document carefully, as it contains important information guiding you through our PR quality gates. - -As is the case with the development process, this document is under constant change. If you notice any error, or if you think this document is out-of-date, or if you think it is missing something: Feel free to submit a PR or submit a bug to our GitHub issue tracker. - -If you need guidance with submitting a PR, or have any other questions regarding development of ArgoCD, do not hesitate to [join our Slack](https://argoproj.github.io/community/join-slack) and get in touch with us in the `#argo-dev` channel! - -## Before you start - -You will need at least the following things in your toolchain in order to develop and test ArgoCD locally: - -* A Kubernetes cluster. You won't need a fully blown multi-master, multi-node cluster, but you will need something like K3S, Minikube or microk8s. You will also need a working Kubernetes client (`kubectl`) configuration in your development environment. The configuration must reside in `~/.kube/config` and the API server URL must point to the IP address of your local machine (or VM), and **not** to `localhost` or `127.0.0.1` if you are using the virtualized development toolchain (see below) - -* You will also need a working Docker runtime environment, to be able to build and run images. -The Docker version must be fairly recent, and support multi-stage builds. You should not work as root. Make your local user a member of the `docker` group to be able to control the Docker service on your machine. - -* Obviously, you will need a `git` client for pulling source code and pushing back your changes. - -* Last but not least, you will need a Go SDK and related tools (such as GNU `make`) installed and working on your development environment. The minimum required Go version for building and testing ArgoCD is **v1.16**. - -* We will assume that your Go workspace is at `~/go`. - -!!! note - **Attention minikube users**: By default, minikube will create Kubernetes client configuration that uses authentication data from files. This is incompatible with the virtualized toolchain. So if you intend to use the virtualized toolchain, you have to embed this authentication data into the client configuration. To do so, start minikube using `minikube start --embed-certs`. Please also note that minikube using the Docker driver is currently not supported with the virtualized toolchain, because the Docker driver exposes the API server on 127.0.0.1 hard-coded. If in doubt, run `make verify-kube-connect` to find out. - -## Submitting PRs - -When you submit a PR against ArgoCD's GitHub repository, a couple of CI checks will be run automatically to ensure your changes will build fine and meet certain quality standards. Your contribution needs to pass those checks in order to be merged into the repository. - -In general, it might be beneficial to only submit a PR for an existing issue. Especially for larger changes, an Enhancement Proposal should exist before. - -!!!note - - Please make sure that you always create PRs from a branch that is up-to-date with the latest changes from ArgoCD's master branch. Depending on how long it takes for the maintainers to review and merge your PR, it might be necessary to pull in latest changes into your branch again. - -Please understand that we, as an Open Source project, have limited capacities for reviewing and merging PRs to ArgoCD. We will do our best to review your PR and give you feedback as soon as possible, but please bear with us if it takes a little longer as expected. - -The following read will help you to submit a PR that meets the standards of our CI tests: - -### Title of the PR - -Please use a meaningful and concise title for your PR. This will help us to pick PRs for review quickly, and the PR title will also end up in the Changelog. - -We use the [Semantic PR title checker](https://github.com/zeke/semantic-pull-requests) to categorize your PR into one of the following categories: - -* `fix` - Your PR contains one or more code bug fixes -* `feat` - Your PR contains a new feature -* `docs` - Your PR improves the documentation -* `chore` - Your PR improves any internals of ArgoCD, such as the build process, unit tests, etc - -Please prefix the title of your PR with one of the valid categories. For example, if you chose the title your PR `Add documentation for GitHub SSO integration`, please use `docs: Add documentation for GitHub SSO integration` instead. - -### Contributor License Agreement - -Every contributor to ArgoCD must have signed the current Contributor License Agreement (CLA). You only have to sign the CLA when you are a first time contributor, or when the agreement has changed since your last time signing it. The main purpose of the CLA is to ensure that you hold the required rights for your contribution. The CLA signing is an automated process. - -You can read the current version of the CLA [here](https://cla-assistant.io/argoproj/argo-cd). - -### PR template checklist - -Upon opening a PR, the details will contain a checklist from a template. Please read the checklist, and tick those marks that apply to you. - -### Automated builds & tests - -After you have submitted your PR, and whenever you push new commits to that branch, GitHub will run a number of Continuous Integration checks against your code. It will execute the following actions, and each of them has to pass: - -* Build the Go code (`make build`) -* Generate API glue code and manifests (`make codegen`) -* Run a Go linter on the code (`make lint`) -* Run the unit tests (`make test`) -* Run the End-to-End tests (`make test-e2e`) -* Build and lint the UI code (`make lint-ui`) -* Build the `argocd` CLI (`make cli`) - -If any of these tests in the CI pipeline fail, it means that some of your contribution is considered faulty (or a test might be flaky, see below). - -### Code test coverage - -We use [CodeCov](https://codecov.io) in our CI pipeline to check for test coverage, and once you submit your PR, it will run and report on the coverage difference as a comment within your PR. If the difference is too high in the negative, i.e. your submission introduced a significant drop in code coverage, the CI check will fail. - -Whenever you develop a new feature or submit a bug fix, please also write appropriate unit tests for it. If you write a completely new module, please aim for at least 80% of coverage. -If you want to see how much coverage just a specific module (i.e. your new one) has, you can set the `TEST_MODULE` to the (fully qualified) name of that module with `make test`, i.e.: - -```bash - make test TEST_MODULE=github.com/argoproj/argo-cd/server/cache -... -ok github.com/argoproj/argo-cd/server/cache 0.029s coverage: 89.3% of statements -``` - -## Local vs Virtualized toolchain - -ArgoCD provides a fully virtualized development and testing toolchain using Docker images. It is recommended to use those images, as they provide the same runtime environment as the final product and it is much easier to keep up-to-date with changes to the toolchain and dependencies. But as using Docker comes with a slight performance penalty, you might want to setup a local toolchain. - -Most relevant targets for the build & test cycles in the `Makefile` provide two variants, one of them suffixed with `-local`. For example, `make test` will run unit tests in the Docker container, `make test-local` will run it natively on your local system. - -If you are going to use the virtualized toolchain, please bear in mind the following things: - -* Your Kubernetes API server must listen on the interface of your local machine or VM, and not on `127.0.0.1` only. -* Your Kubernetes client configuration (`~/.kube/config`) must not use an API URL that points to `localhost` or `127.0.0.1`. - -You can test whether the virtualized toolchain has access to your Kubernetes cluster by running `make verify-kube-connect` (*after* you have setup your development environment, as described below), which will run `kubectl version` inside the Docker container used for running all tests. - -The Docker container for the virtualized toolchain will use the following local mounts from your workstation, and possibly modify its contents: - -* `~/go/src` - Your Go workspace's source directory (modifications expected) -* `~/.cache/go-build` - Your Go build cache (modifications expected) -* `~/.kube` - Your Kubernetes client configuration (no modifications) -* `/tmp` - Your system's temp directory (modifications expected) - -## Setting up your development environment - -The following steps are required no matter whether you chose to use a virtualized or a local toolchain. - -### Clone the ArgoCD repository from your personal fork on GitHub - -* `mkdir -p ~/go/src/github.com/argoproj` -* `cd ~/go/src/github.com/argoproj` -* `git clone https://github.com/yourghuser/argo-cd` -* `cd argo-cd` - -### Optional: Setup an additional Git remote - -While everyone has their own Git workflow, the author of this document recommends to create a remote called `upstream` in your local copy pointing to the original ArgoCD repository. This way, you can easily keep your local branches up-to-date by merging in latest changes from the ArgoCD repository, i.e. by doing a `git pull upstream master` in your locally checked out branch. To create the remote, run `git remote add upstream https://github.com/argoproj/argo-cd` - -### Install the must-have requirements - -Make sure you fulfill the pre-requisites above and run some preliminary tests. Neither of them should report an error. - -* Run `kubectl version` -* Run `docker version` -* Run `go version` - -### Build (or pull) the required Docker image - -Build the required Docker image by running `make test-tools-image` or pull the latest version by issuing `docker pull argoproj/argocd-test-tools`. - -The `Dockerfile` used to build these images can be found at `test/container/Dockerfile`. - -### Test connection from build container to your K8s cluster - -Run `make verify-kube-connect`, it should execute without error. - -If you receive an error similar to the following: - -``` -The connection to the server 127.0.0.1:6443 was refused - did you specify the right host or port? -make: *** [Makefile:386: verify-kube-connect] Error 1 -``` - -you should edit your `~/.kube/config` and modify the `server` option to point to your correct K8s API (as described above). - -### Using k3d - -[k3d](https://github.com/rancher/k3d) is a lightweight wrapper to run [k3s](https://github.com/rancher/k3s), a minimal Kubernetes distribution, in docker. Because it's running in a docker container, you're dealing with docker's internal networking rules when using k3d. A typical Kubernetes cluster running on your local machine is part of the same network that you're on so you can access it using **kubectl**. However, a Kubernetes cluster running within a docker container (in this case, the one launched by make) cannot access 0.0.0.0 from inside the container itself, when 0.0.0.0 is a network resource outside the container itself (and/or the container's network). This is the cost of a fully self-contained, disposable Kubernetes cluster. The following steps should help with a successful `make verify-kube-connect` execution. - -1. Find your host IP by executing `ifconfig` on Mac/Linux and `ipconfig` on Windows. For most users, the following command works to find the IP address. - - * For Mac: - - ``` - IP=`ifconfig en0 | grep inet | grep -v inet6 | awk '{print $2}'` - echo $IP - ``` - - * For Linux: - - ``` - IP=`ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2}'` - echo $IP - ``` - - Keep in mind that this IP is dynamically assigned by the router so if your router restarts for any reason, your IP might change. - -2. Edit your ~/.kube/config and replace 0.0.0.0 with the above IP address. - -3. Execute a `kubectl version` to make sure you can still connect to the Kubernetes API server via this new IP. Run `make verify-kube-connect` and check if it works. - -4. Finally, so that you don't have to keep updating your kube-config whenever you spin up a new k3d cluster, add `--api-port $IP:6550` to your **k3d cluster create** command, where $IP is the value from step 1. An example command is provided here: - -``` -k3d cluster create my-cluster --wait --k3s-server-arg '--disable=traefik' --api-port $IP:6550 -p 443:443@loadbalancer -``` - -## The development cycle - -When you have developed and possibly manually tested the code you want to contribute, you should ensure that everything will build correctly. Commit your changes to the local copy of your Git branch and perform the following steps: - -### Pull in all build dependencies - -As build dependencies change over time, you have to synchronize your development environment with the current specification. In order to pull in all required dependencies, issue: - -* `make dep-ui` - -ArgoCD recently migrated to Go modules. Usually, dependencies will be downloaded on build time, but the Makefile provides two targets to download and vendor all dependencies: - -* `make mod-download` will download all required Go modules and -* `make mod-vendor` will vendor those dependencies into the ArgoCD source tree - -### Generate API glue code and other assets - -ArgoCD relies on Google's [Protocol Buffers](https://developers.google.com/protocol-buffers) for its API, and this makes heavy use of auto-generated glue code and stubs. Whenever you touched parts of the API code, you must re-generate the auto generated code. - -* Run `make codegen`, this might take a while -* Check if something has changed by running `git status` or `git diff` -* Commit any possible changes to your local Git branch, an appropriate commit message would be `Changes from codegen`, for example. - -!!!note - There are a few non-obvious assets that are auto-generated. You should not change the autogenerated assets, as they will be overwritten by a subsequent run of `make codegen`. Instead, change their source files. Prominent examples of non-obvious auto-generated code are `swagger.json` or the installation manifest YAMLs. - -### Build your code and run unit tests - -After the code glue has been generated, your code should build and the unit tests should run without any errors. Execute the following statements: - -* `make build` -* `make test` - -These steps are non-modifying, so there's no need to check for changes afterwards. - -### Lint your code base - -In order to keep a consistent code style in our source tree, your code must be well-formed in accordance to some widely accepted rules, which are applied by a Linter. - -The Linter might make some automatic changes to your code, such as indentation fixes. Some other errors reported by the Linter have to be fixed manually. - -* Run `make lint` and observe any errors reported by the Linter -* Fix any of the errors reported and commit to your local branch -* Finally, after the Linter reports no errors anymore, run `git status` or `git diff` to check for any changes made automatically by Lint -* If there were automatic changes, commit them to your local branch - -If you touched UI code, you should also run the Yarn linter on it: - -* Run `make lint-ui` -* Fix any of the errors reported by it - -## Contributing to Argo CD UI - -Argo CD, along with Argo Workflows, uses shared React components from [Argo UI](https://github.com/argoproj/argo-ui). Examples of some of these components include buttons, containers, form controls, -and others. Although you can make changes to these files and run them locally, in order to have these changes added to the Argo CD repo, you will need to follow these steps. - -1. Fork and clone the [Argo UI repository](https://github.com/argoproj/argo-ui). - -2. `cd` into your `argo-ui` directory, and then run `yarn install`. - -3. Make your file changes. - -4. Run `yarn start` to start a [storybook](https://storybook.js.org/) dev server and view the components in your browser. Make sure all your changes work as expected. - -5. Use [yarn link](https://classic.yarnpkg.com/en/docs/cli/link/) to link Argo UI package to your Argo CD repository. (Commands below assume that `argo-ui` and `argo-cd` are both located within the same parent folder) - - * `cd argo-ui` - * `yarn link` - * `cd ../argo-cd/ui` - * `yarn link argo-ui` - - Once `argo-ui` package has been successfully linked, test out changes in your local development environment. - -6. Commit changes and open a PR to [Argo UI](https://github.com/argoproj/argo-ui). - -7. Once your PR has been merged in Argo UI, `cd` into your `argo-cd` folder and run `yarn add https://github.com/argoproj/argo-ui.git`. This will update the commit SHA in the `ui/yarn.lock` file to use the lastest master commit for argo-ui. - -8. Submit changes to `ui/yarn.lock`in a PR to Argo CD. - -## Setting up a local toolchain - -For development, you can either use the fully virtualized toolchain provided as Docker images, or you can set up the toolchain on your local development machine. Due to the dynamic nature of requirements, you might want to stay with the virtualized environment. - -### Install required dependencies and build-tools - -!!!note - The installations instructions are valid for Linux hosts only. Mac instructions will follow shortly. - -For installing the tools required to build and test ArgoCD on your local system, we provide convenient installer scripts. By default, they will install binaries to `/usr/local/bin` on your system, which might require `root` privileges. - -You can change the target location by setting the `BIN` environment before running the installer scripts. For example, you can install the binaries into `~/go/bin` (which should then be the first component in your `PATH` environment, i.e. `export PATH=~/go/bin:$PATH`): - -```shell -make BIN=~/go/bin install-tools-local -``` - -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): - -* Git LFS plugin -* GnuPG version 2 - -### Install Go dependencies - -You need to pull in all required Go dependencies. To do so, run - -* `make mod-download-local` -* `make mod-vendor-local` - -### Test your build toolchain - -The first thing you can do whether your build toolchain is setup correctly is by generating the glue code for the API and after that, run a normal build: - -* `make codegen-local` -* `make build-local` - -This should return without any error. - -### Run unit-tests - -The next thing is to make sure that unit tests are running correctly on your system. These will require that all dependencies, such as Helm, Kustomize, Git, GnuPG, etc are correctly installed and fully functioning: - -* `make test-local` - -### Run end-to-end tests - -The final step is running the End-to-End testsuite, which makes sure that your Kubernetes dependencies are working properly. This will involve starting all of the ArgoCD components locally on your computer. The end-to-end tests consists of two parts: a server component, and a client component. - -* First, start the End-to-End server: `make start-e2e-local`. This will spawn a number of processes and services on your system. -* When all components have started, run `make test-e2e-local` to run the end-to-end tests against your local services. - -For more information about End-to-End tests, refer to the [End-to-End test documentation](test-e2e.md). - - -## Enhancement proposals - -If you are proposing a major feature, change in design or process refactor, please help define how it would look like with a new enhancement proposal as described in the enhancement proposal [template](/docs/proposals/001-proposal-template.md). - +The contents of this document have been moved to the +[Toolchain guide](toolchain-guide.md) \ No newline at end of file diff --git a/docs/developer-guide/dependencies.md b/docs/developer-guide/dependencies.md index a6cee142e5f29..410fd1241b1b2 100644 --- a/docs/developer-guide/dependencies.md +++ b/docs/developer-guide/dependencies.md @@ -45,7 +45,7 @@ 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`, and then, + - 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. diff --git a/docs/developer-guide/release-process-and-cadence.md b/docs/developer-guide/release-process-and-cadence.md index 62c7e4a4874c1..fd654e1a59d99 100644 --- a/docs/developer-guide/release-process-and-cadence.md +++ b/docs/developer-guide/release-process-and-cadence.md @@ -42,7 +42,8 @@ the maintainer should label it with a `verified` label. The releasing procedure is described in [releasing](./releasing.md) document. Before closing the release milestone following should be verified: -- [ ] All merged PRs have the `verified` label +- [ ] All merged PRs and verified (verify and remove `needs-verification` label): +- [ ] Triage issues reported by `yarn audit` and ensure there are no exploitable security issues. - [ ] Roadmap is updated based one current release changes - [ ] Next release milestone is created - [ ] Upcoming release milestone is updated \ No newline at end of file diff --git a/docs/developer-guide/running-locally.md b/docs/developer-guide/running-locally.md index 0ea809f8bd999..92a85dcb6e2f5 100644 --- a/docs/developer-guide/running-locally.md +++ b/docs/developer-guide/running-locally.md @@ -4,9 +4,9 @@ During development, it might be viable to run ArgoCD outside of a Kubernetes cluster. This will greatly speed up development, as you don't have to constantly build, push and install new ArgoCD Docker images with your latest changes. -You will still need a working Kubernetes cluster, as described in the [Contribution Guide](contributing.md), where ArgoCD will store all of its resources. +You will still need a working Kubernetes cluster, as described in the [Toolchain Guide](toolchain-guide.md), where ArgoCD will store all of its resources and configuration. -If you followed the [Contribution Guide](contributing.md) in setting up your toolchain, you can run ArgoCD locally with these simple steps: +If you followed the [Toolchain Guide](toolchain-guide.md) in setting up your toolchain, you can run ArgoCD locally with these simple steps: ### Install ArgoCD resources to your cluster diff --git a/docs/developer-guide/toolchain-guide.md b/docs/developer-guide/toolchain-guide.md new file mode 100644 index 0000000000000..041cb556512b2 --- /dev/null +++ b/docs/developer-guide/toolchain-guide.md @@ -0,0 +1,327 @@ +# Development toolchain + +## Preface + +!!!note "Before you start" + The Argo CD project continuously grows, both in terms of features and community size. It gets adopted by more and more organisations which entrust Argo CD to handle their critical production workloads. Thus, we need to take great care with any changes that affect compatibility, performance, scalability, stability and security of Argo CD. For this reason, every new feature or larger enhancement must be properly designed and discussed before it gets accepted into the code base. + + We do welcome and encourage everyone to participate in the Argo CD project, but please understand that we can't accept each and every contribution from the community, for various reasons. If you want to submit code for a great new feature or enhancement, we kindly ask you to take a look at the + [code contribution guide](code-contributions.md#) before you start to write code or submit a PR. + +We want to make contributing to Argo CD as simple and smooth as possible. + +This guide shall help you in setting up your build & test environment, so that you can start developing and testing bug fixes and feature enhancements without having to make too much effort in setting up a local toolchain. + +If you want to submit a PR, please read this document carefully, as it contains important information guiding you through our PR quality gates. + +As is the case with the development process, this document is under constant change. If you notice any error, or if you think this document is out-of-date, or if you think it is missing something: Feel free to submit a PR or submit a bug to our GitHub issue tracker. + +If you need guidance with submitting a PR, or have any other questions regarding development of Argo CD, do not hesitate to [join our Slack](https://argoproj.github.io/community/join-slack) and get in touch with us in the `#argo-contributors` channel! + +## Before you start + +You will need at least the following things in your toolchain in order to develop and test Argo CD locally: + +* A Kubernetes cluster. You won't need a fully blown multi-master, multi-node cluster, but you will need something like K3S, Minikube or microk8s. You will also need a working Kubernetes client (`kubectl`) configuration in your development environment. The configuration must reside in `~/.kube/config` and the API server URL must point to the IP address of your local machine (or VM), and **not** to `localhost` or `127.0.0.1` if you are using the virtualized development toolchain (see below) + +* You will also need a working Docker runtime environment, to be able to build and run images. +The Docker version must be fairly recent, and support multi-stage builds. You should not work as root. Make your local user a member of the `docker` group to be able to control the Docker service on your machine. + +* Obviously, you will need a `git` client for pulling source code and pushing back your changes. + +* Last but not least, you will need a Go SDK and related tools (such as GNU `make`) installed and working on your development environment. The minimum required Go version for building and testing Argo CD is **v1.16**. + +* We will assume that your Go workspace is at `~/go`. + +!!! note + **Attention minikube users**: By default, minikube will create Kubernetes client configuration that uses authentication data from files. This is incompatible with the virtualized toolchain. So if you intend to use the virtualized toolchain, you have to embed this authentication data into the client configuration. To do so, start minikube using `minikube start --embed-certs`. Please also note that minikube using the Docker driver is currently not supported with the virtualized toolchain, because the Docker driver exposes the API server on 127.0.0.1 hard-coded. If in doubt, run `make verify-kube-connect` to find out. + +## Submitting PRs + +### Continuous Integration process + +When you submit a PR against Argo CD's GitHub repository, a couple of CI checks will be run automatically to ensure your changes will build fine and meet certain quality standards. Your contribution needs to pass those checks in order to be merged into the repository. + +!!!note + + Please make sure that you always create PRs from a branch that is up-to-date with the latest changes from Argo CD's master branch. Depending on how long it takes for the maintainers to review and merge your PR, it might be necessary to pull in latest changes into your branch again. + +Please understand that we, as an Open Source project, have limited capacities for reviewing and merging PRs to Argo CD. We will do our best to review your PR and give you feedback as soon as possible, but please bear with us if it takes a little longer as expected. + +The following read will help you to submit a PR that meets the standards of our CI tests: + +### Title of the PR + +Please use a meaningful and concise title for your PR. This will help us to pick PRs for review quickly, and the PR title will also end up in the Changelog. + +We use the [Semantic PR title checker](https://github.com/zeke/semantic-pull-requests) to categorize your PR into one of the following categories: + +* `fix` - Your PR contains one or more code bug fixes +* `feat` - Your PR contains a new feature +* `docs` - Your PR improves the documentation +* `chore` - Your PR improves any internals of Argo CD, such as the build process, unit tests, etc + +Please prefix the title of your PR with one of the valid categories. For example, if you chose the title your PR `Add documentation for GitHub SSO integration`, please use `docs: Add documentation for GitHub SSO integration` instead. + +### Contributor License Agreement + +Every contributor to Argo CD must have signed the current Contributor License Agreement (CLA). You only have to sign the CLA when you are a first time contributor, or when the agreement has changed since your last time signing it. The main purpose of the CLA is to ensure that you hold the required rights for your contribution. The CLA signing is an automated process. + +You can read the current version of the CLA [here](https://cla-assistant.io/argoproj/argo-cd). + +### PR template checklist + +Upon opening a PR, the details will contain a checklist from a template. Please read the checklist, and tick those marks that apply to you. + +### Automated builds & tests + +After you have submitted your PR, and whenever you push new commits to that branch, GitHub will run a number of Continuous Integration checks against your code. It will execute the following actions, and each of them has to pass: + +* Build the Go code (`make build`) +* Generate API glue code and manifests (`make codegen`) +* Run a Go linter on the code (`make lint`) +* Run the unit tests (`make test`) +* Run the End-to-End tests (`make test-e2e`) +* Build and lint the UI code (`make lint-ui`) +* Build the `argocd` CLI (`make cli`) + +If any of these tests in the CI pipeline fail, it means that some of your contribution is considered faulty (or a test might be flaky, see below). + +### Code test coverage + +We use [CodeCov](https://codecov.io) in our CI pipeline to check for test coverage, and once you submit your PR, it will run and report on the coverage difference as a comment within your PR. If the difference is too high in the negative, i.e. your submission introduced a significant drop in code coverage, the CI check will fail. + +Whenever you develop a new feature or submit a bug fix, please also write appropriate unit tests for it. If you write a completely new module, please aim for at least 80% of coverage. +If you want to see how much coverage just a specific module (i.e. your new one) has, you can set the `TEST_MODULE` to the (fully qualified) name of that module with `make test`, i.e.: + +```bash + make test TEST_MODULE=github.com/argoproj/argo-cd/server/cache +... +ok github.com/argoproj/argo-cd/server/cache 0.029s coverage: 89.3% of statements +``` + +## Local vs Virtualized toolchain + +Argo CD provides a fully virtualized development and testing toolchain using Docker images. It is recommended to use those images, as they provide the same runtime environment as the final product and it is much easier to keep up-to-date with changes to the toolchain and dependencies. But as using Docker comes with a slight performance penalty, you might want to setup a local toolchain. + +Most relevant targets for the build & test cycles in the `Makefile` provide two variants, one of them suffixed with `-local`. For example, `make test` will run unit tests in the Docker container, `make test-local` will run it natively on your local system. + +If you are going to use the virtualized toolchain, please bear in mind the following things: + +* Your Kubernetes API server must listen on the interface of your local machine or VM, and not on `127.0.0.1` only. +* Your Kubernetes client configuration (`~/.kube/config`) must not use an API URL that points to `localhost` or `127.0.0.1`. + +You can test whether the virtualized toolchain has access to your Kubernetes cluster by running `make verify-kube-connect` (*after* you have setup your development environment, as described below), which will run `kubectl version` inside the Docker container used for running all tests. + +The Docker container for the virtualized toolchain will use the following local mounts from your workstation, and possibly modify its contents: + +* `~/go/src` - Your Go workspace's source directory (modifications expected) +* `~/.cache/go-build` - Your Go build cache (modifications expected) +* `~/.kube` - Your Kubernetes client configuration (no modifications) +* `/tmp` - Your system's temp directory (modifications expected) + +## Setting up your development environment + +The following steps are required no matter whether you chose to use a virtualized or a local toolchain. + +### Clone the Argo CD repository from your personal fork on GitHub + +* `mkdir -p ~/go/src/github.com/argoproj` +* `cd ~/go/src/github.com/argoproj` +* `git clone https://github.com/yourghuser/argo-cd` +* `cd argo-cd` + +### Optional: Setup an additional Git remote + +While everyone has their own Git workflow, the author of this document recommends to create a remote called `upstream` in your local copy pointing to the original Argo CD repository. This way, you can easily keep your local branches up-to-date by merging in latest changes from the Argo CD repository, i.e. by doing a `git pull upstream master` in your locally checked out branch. To create the remote, run `git remote add upstream https://github.com/argoproj/argo-cd` + +### Install the must-have requirements + +Make sure you fulfill the pre-requisites above and run some preliminary tests. Neither of them should report an error. + +* Run `kubectl version` +* Run `docker version` +* Run `go version` + +### Build (or pull) the required Docker image + +Build the required Docker image by running `make test-tools-image` or pull the latest version by issuing `docker pull argoproj/argocd-test-tools`. + +The `Dockerfile` used to build these images can be found at `test/container/Dockerfile`. + +### Test connection from build container to your K8s cluster + +Run `make verify-kube-connect`, it should execute without error. + +If you receive an error similar to the following: + +``` +The connection to the server 127.0.0.1:6443 was refused - did you specify the right host or port? +make: *** [Makefile:386: verify-kube-connect] Error 1 +``` + +you should edit your `~/.kube/config` and modify the `server` option to point to your correct K8s API (as described above). + +### Using k3d + +[k3d](https://github.com/rancher/k3d) is a lightweight wrapper to run [k3s](https://github.com/rancher/k3s), a minimal Kubernetes distribution, in docker. Because it's running in a docker container, you're dealing with docker's internal networking rules when using k3d. A typical Kubernetes cluster running on your local machine is part of the same network that you're on so you can access it using **kubectl**. However, a Kubernetes cluster running within a docker container (in this case, the one launched by make) cannot access 0.0.0.0 from inside the container itself, when 0.0.0.0 is a network resource outside the container itself (and/or the container's network). This is the cost of a fully self-contained, disposable Kubernetes cluster. The following steps should help with a successful `make verify-kube-connect` execution. + +1. Find your host IP by executing `ifconfig` on Mac/Linux and `ipconfig` on Windows. For most users, the following command works to find the IP address. + + * For Mac: + + ``` + IP=`ifconfig en0 | grep inet | grep -v inet6 | awk '{print $2}'` + echo $IP + ``` + + * For Linux: + + ``` + IP=`ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2}'` + echo $IP + ``` + + Keep in mind that this IP is dynamically assigned by the router so if your router restarts for any reason, your IP might change. + +2. Edit your ~/.kube/config and replace 0.0.0.0 with the above IP address. + +3. Execute a `kubectl version` to make sure you can still connect to the Kubernetes API server via this new IP. Run `make verify-kube-connect` and check if it works. + +4. Finally, so that you don't have to keep updating your kube-config whenever you spin up a new k3d cluster, add `--api-port $IP:6550` to your **k3d cluster create** command, where $IP is the value from step 1. An example command is provided here: + +``` +k3d cluster create my-cluster --wait --k3s-server-arg '--disable=traefik' --api-port $IP:6550 -p 443:443@loadbalancer +``` + +## The development cycle + +When you have developed and possibly manually tested the code you want to contribute, you should ensure that everything will build correctly. Commit your changes to the local copy of your Git branch and perform the following steps: + +### Pull in all build dependencies + +As build dependencies change over time, you have to synchronize your development environment with the current specification. In order to pull in all required dependencies, issue: + +* `make dep-ui` + +Argo CD recently migrated to Go modules. Usually, dependencies will be downloaded on build time, but the Makefile provides two targets to download and vendor all dependencies: + +* `make mod-download` will download all required Go modules and +* `make mod-vendor` will vendor those dependencies into the Argo CD source tree + +### Generate API glue code and other assets + +Argo CD relies on Google's [Protocol Buffers](https://developers.google.com/protocol-buffers) for its API, and this makes heavy use of auto-generated glue code and stubs. Whenever you touched parts of the API code, you must re-generate the auto generated code. + +* Run `make codegen`, this might take a while +* Check if something has changed by running `git status` or `git diff` +* Commit any possible changes to your local Git branch, an appropriate commit message would be `Changes from codegen`, for example. + +!!!note + There are a few non-obvious assets that are auto-generated. You should not change the autogenerated assets, as they will be overwritten by a subsequent run of `make codegen`. Instead, change their source files. Prominent examples of non-obvious auto-generated code are `swagger.json` or the installation manifest YAMLs. + +### Build your code and run unit tests + +After the code glue has been generated, your code should build and the unit tests should run without any errors. Execute the following statements: + +* `make build` +* `make test` + +These steps are non-modifying, so there's no need to check for changes afterwards. + +### Lint your code base + +In order to keep a consistent code style in our source tree, your code must be well-formed in accordance to some widely accepted rules, which are applied by a Linter. + +The Linter might make some automatic changes to your code, such as indentation fixes. Some other errors reported by the Linter have to be fixed manually. + +* Run `make lint` and observe any errors reported by the Linter +* Fix any of the errors reported and commit to your local branch +* Finally, after the Linter reports no errors anymore, run `git status` or `git diff` to check for any changes made automatically by Lint +* If there were automatic changes, commit them to your local branch + +If you touched UI code, you should also run the Yarn linter on it: + +* Run `make lint-ui` +* Fix any of the errors reported by it + +## Contributing to Argo CD UI + +Argo CD, along with Argo Workflows, uses shared React components from [Argo UI](https://github.com/argoproj/argo-ui). Examples of some of these components include buttons, containers, form controls, +and others. Although you can make changes to these files and run them locally, in order to have these changes added to the Argo CD repo, you will need to follow these steps. + +1. Fork and clone the [Argo UI repository](https://github.com/argoproj/argo-ui). + +2. `cd` into your `argo-ui` directory, and then run `yarn install`. + +3. Make your file changes. + +4. Run `yarn start` to start a [storybook](https://storybook.js.org/) dev server and view the components in your browser. Make sure all your changes work as expected. + +5. Use [yarn link](https://classic.yarnpkg.com/en/docs/cli/link/) to link Argo UI package to your Argo CD repository. (Commands below assume that `argo-ui` and `argo-cd` are both located within the same parent folder) + + * `cd argo-ui` + * `yarn link` + * `cd ../argo-cd/ui` + * `yarn link argo-ui` + + Once `argo-ui` package has been successfully linked, test out changes in your local development environment. + +6. Commit changes and open a PR to [Argo UI](https://github.com/argoproj/argo-ui). + +7. Once your PR has been merged in Argo UI, `cd` into your `argo-cd` folder and run `yarn add https://github.com/argoproj/argo-ui.git`. This will update the commit SHA in the `ui/yarn.lock` file to use the lastest master commit for argo-ui. + +8. Submit changes to `ui/yarn.lock`in a PR to Argo CD. + +## Setting up a local toolchain + +For development, you can either use the fully virtualized toolchain provided as Docker images, or you can set up the toolchain on your local development machine. Due to the dynamic nature of requirements, you might want to stay with the virtualized environment. + +### Install required dependencies and build-tools + +!!!note + The installations instructions are valid for Linux hosts only. Mac instructions will follow shortly. + +For installing the tools required to build and test Argo CD on your local system, we provide convenient installer scripts. By default, they will install binaries to `/usr/local/bin` on your system, which might require `root` privileges. + +You can change the target location by setting the `BIN` environment before running the installer scripts. For example, you can install the binaries into `~/go/bin` (which should then be the first component in your `PATH` environment, i.e. `export PATH=~/go/bin:$PATH`): + +```shell +make BIN=~/go/bin install-tools-local +``` + +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): + +* Git LFS plugin +* GnuPG version 2 + +### Install Go dependencies + +You need to pull in all required Go dependencies. To do so, run + +* `make mod-download-local` +* `make mod-vendor-local` + +### Test your build toolchain + +The first thing you can do whether your build toolchain is setup correctly is by generating the glue code for the API and after that, run a normal build: + +* `make codegen-local` +* `make build-local` + +This should return without any error. + +### Run unit-tests + +The next thing is to make sure that unit tests are running correctly on your system. These will require that all dependencies, such as Helm, Kustomize, Git, GnuPG, etc are correctly installed and fully functioning: + +* `make test-local` + +### Run end-to-end tests + +The final step is running the End-to-End testsuite, which makes sure that your Kubernetes dependencies are working properly. This will involve starting all of the Argo CD components locally on your computer. The end-to-end tests consists of two parts: a server component, and a client component. + +* First, start the End-to-End server: `make start-e2e-local`. This will spawn a number of processes and services on your system. +* When all components have started, run `make test-e2e-local` to run the end-to-end tests against your local services. + +For more information about End-to-End tests, refer to the [End-to-End test documentation](test-e2e.md). diff --git a/docs/faq.md b/docs/faq.md index 280fda904d46a..efb0f09069e2c 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -98,7 +98,7 @@ Use the following steps to reconstruct configured cluster config and connect to ```bash kubectl exec -it bash # ssh into any argocd server pod -argocd-util cluster kubeconfig https:// /tmp/config --namespace argocd # generate your cluster config +argocd admin cluster kubeconfig https:// /tmp/config --namespace argocd # generate your cluster config KUBECONFIG=/tmp/config kubectl get pods # test connection manually ``` diff --git a/docs/getting_started.md b/docs/getting_started.md index ae64f14874cc0..ae531f3785ead 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -21,12 +21,14 @@ 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 installing Argo CD into a different namespace then make sure to update the namespace reference. -!!! note - If you are not interested in UI, SSO, multi-cluster management and just want to pull changes into the cluster then you can disable - authentication using `--disable-auth` flag and access Argo CD via CLI using `--port-forward` or `--port-forward-namespace` flags - and proceed to step [#6](#6-create-an-application-from-a-git-repository): - - `kubectl patch deploy argocd-server -n argocd -p '[{"op": "add", "path": "/spec/template/spec/containers/0/command/-", "value": "--disable-auth"}]' --type json` +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 +``` + +Use `argocd login --core` to [configure](./user-guide/commands/argocd_login.md) CLI access and skip steps 3-5. ## 2. Download Argo CD CLI @@ -91,6 +93,9 @@ Using the username `admin` and the password from above, login to Argo CD's IP or argocd login ``` +!!! note + The CLI environment must be able to communicate with the Argo CD controller. If it isn't directly accessible as described above in step 3, you can tell the CLI to access it using port forwarding through one of these mechanisms: 1) add `--port-forward-namespace argocd` flag to every CLI command; or 2) set `ARGOCD_OPTS` environment variable: `export ARGOCD_OPTS='--port-forward-namespace argocd'`. + Change the password using the command: ```bash @@ -129,10 +134,11 @@ An example repository containing a guestbook application is available at ### Creating Apps Via CLI -!!! note - You can access Argo CD using port forwarding: add `--port-forward-namespace argocd` flag to every CLI command or set `ARGOCD_OPTS` environment variable: `export ARGOCD_OPTS='--port-forward-namespace argocd'`: +Create the example guestbook application with the following command: - `argocd app create guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-server https://kubernetes.default.svc --dest-namespace default` +```bash +argocd app create guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-server https://kubernetes.default.svc --dest-namespace default` +``` ### Creating Apps Via UI @@ -150,7 +156,7 @@ Connect the [https://github.com/argoproj/argocd-example-apps.git](https://github ![connect repo](assets/connect-repo.png) -For **Destination**, set cluster to `in-cluster` and namespace to `default`: +For **Destination**, set cluster URL to `https://kubernetes.default.svc` (or `in-cluster` for cluster name) and namespace to `default`: ![destination](assets/destination.png) diff --git a/docs/operator-manual/argocd-cm.yaml b/docs/operator-manual/argocd-cm.yaml index 142ed59c7d72d..393eca7788ba3 100644 --- a/docs/operator-manual/argocd-cm.yaml +++ b/docs/operator-manual/argocd-cm.yaml @@ -18,6 +18,9 @@ data: # Specifies token expiration duration users.session.duration: "24h" + # Specifies regex expression for password + passwordPattern: "^.{8,32}$" + # Enables google analytics tracking is specified ga.trackingid: "UA-12345-1" # Unless set to 'false' then user ids are hashed before sending to google analytics @@ -221,6 +224,12 @@ data: # Optional link for banner. If set, the entire banner text will become a link. # You can have bannercontent without a bannerurl, but not the other way around. ui.bannerurl: "https://argoproj.github.io" + # Uncomment to make the banner not show the close buttons, thereby making the banner permanent. + # Because it is permanent, only one line of text is available to not take up too much real estate in the UI, + # so it is recommended that the length of the bannercontent text is kept reasonably short. Note that you can + # 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" # Application reconciliation timeout is the max amount of time required to discover if a new manifests version got # published to the repository. Reconciliation by timeout is disabled if timeout is set to 0. Three minutes by default. diff --git a/docs/operator-manual/argocd-cmd-params-cm.yaml b/docs/operator-manual/argocd-cmd-params-cm.yaml index c6cdeea4645cb..1cd6023803481 100644 --- a/docs/operator-manual/argocd-cmd-params-cm.yaml +++ b/docs/operator-manual/argocd-cmd-params-cm.yaml @@ -48,6 +48,8 @@ data: server.basehref: "/" # Used if Argo CD is running behind reverse proxy under subpath different from / server.rootpath: "/" + # Directory path that contains additional static assets + server.staticassets: "/shared/app" # Set the logging format. One of: text|json (default "text") server.log.format: "text" diff --git a/docs/operator-manual/custom-styles.md b/docs/operator-manual/custom-styles.md index c06fdae624e02..d7063a0224b10 100644 --- a/docs/operator-manual/custom-styles.md +++ b/docs/operator-manual/custom-styles.md @@ -88,11 +88,31 @@ spec: name: styles ``` -Note that the CSS file should be mounted within a subdirectory of the existing "/shared/app" directory +Note that the CSS file should be mounted within a subdirectory of the "/shared/app" directory (e.g. "/shared/app/custom"). Otherwise, the file will likely fail to be imported by the browser with an -"incorrect MIME type" error. +"incorrect MIME type" error. The subdirectory can be changed using `server.staticassets` key of the +[argocd-cmd-params-cm.yaml](./argocd-cmd-params-cm.yaml) ConfigMap. ## Developing Style Overlays The styles specified in the injected CSS file should be specific to components and classes defined in [argo-ui](https://github.com/argoproj/argo-ui). It is recommended to test out the styles you wish to apply first by making use of your browser's built-in developer tools. For a more full-featured -experience, you may wish to build a separate project using the [Argo CD UI dev server](https://webpack.js.org/configuration/dev-server/). \ No newline at end of file +experience, you may wish to build a separate project using the [Argo CD UI dev server](https://webpack.js.org/configuration/dev-server/). + +## Banners + +Argo CD can optionally display a banner that can be used to notify your users of upcoming maintenance and operational changes. This feature can be enabled by specifying the banner message using the `ui.bannercontent` field in the `argocd-cm` ConfigMap and Argo CD will display this message at the top of every UI page. You can optionally add a link to this message by setting `ui.bannerurl`. + +### argocd-cm +```yaml +--- +apiVersion: v1 +kind: ConfigMap +metadata: + ... + name: argocd-cm +data: + ui.bannercontent: "Banner message linked to a URL" + ui.bannerurl: "www.bannerlink.com" +``` + +![banner with link](../assets/banner.png) diff --git a/docs/operator-manual/custom_tools.md b/docs/operator-manual/custom_tools.md index 9eaace6ec03b8..060c08d01a8f7 100644 --- a/docs/operator-manual/custom_tools.md +++ b/docs/operator-manual/custom_tools.md @@ -69,5 +69,5 @@ RUN apt-get update && \ chmod +x /usr/local/bin/sops # Switch back to non-root user -USER argocd +USER 999 ``` diff --git a/docs/operator-manual/declarative-setup.md b/docs/operator-manual/declarative-setup.md index fa7e92eb67c29..61a195b334f3a 100644 --- a/docs/operator-manual/declarative-setup.md +++ b/docs/operator-manual/declarative-setup.md @@ -171,6 +171,9 @@ Repository details are stored in secrets. To configure a repo, create a secret w 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). +!!!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 descibed here: https://github.com/bitnami-labs/sealed-secrets#sealedsecrets-as-templates-for-secrets + Example for HTTPS: ```yaml @@ -182,6 +185,7 @@ metadata: labels: argocd.argoproj.io/secret-type: repository stringData: + type: git url: https://github.com/argoproj/private-repo password: my-password username: my-username @@ -198,6 +202,7 @@ metadata: labels: argocd.argoproj.io/secret-type: repository stringData: + type: git url: git@github.com:argoproj/my-private-repository sshPrivateKey: | -----BEGIN OPENSSH PRIVATE KEY----- @@ -215,6 +220,7 @@ metadata: labels: argocd.argoproj.io/secret-type: repository stringData: + type: git repo: https://github.com/argoproj/my-private-repository githubAppID: 1 githubAppInstallationID: 2 @@ -231,6 +237,7 @@ metadata: labels: argocd.argoproj.io/secret-type: repository stringData: + type: git repo: https://ghe.example.com/argoproj/my-private-repository githubAppID: 1 githubAppInstallationID: 2 @@ -257,6 +264,7 @@ metadata: labels: argocd.argoproj.io/secret-type: repository stringData: + type: git url: https://github.com/argoproj/private-repo --- apiVersion: v1 @@ -267,6 +275,7 @@ metadata: labels: argocd.argoproj.io/secret-type: repository stringData: + type: git url: https://github.com/argoproj/other-private-repo --- apiVersion: v1 @@ -277,6 +286,7 @@ metadata: labels: argocd.argoproj.io/secret-type: repo-creds stringData: + type: git url: https://github.com/argoproj password: my-password username: my-username @@ -416,6 +426,7 @@ metadata: labels: argocd.argoproj.io/secret-type: repository stringData: + type: git url: https://github.com/argoproj/private-repo proxy: https://proxy-server-url:8888 password: my-password diff --git a/docs/operator-manual/disaster_recovery.md b/docs/operator-manual/disaster_recovery.md index b6a17885b2345..6bb52847d978a 100644 --- a/docs/operator-manual/disaster_recovery.md +++ b/docs/operator-manual/disaster_recovery.md @@ -1,6 +1,6 @@ # Disaster Recovery -You can use `argocd-util` to import and export all Argo CD data. +You can use `argocd admin` to import and export all Argo CD data. Make sure you have `~/.kube/config` pointing to your Argo CD cluster. @@ -15,14 +15,14 @@ export VERSION=v1.0.1 Export to a backup: ```bash -docker run -v ~/.kube:/home/argocd/.kube --rm argoproj/argocd:$VERSION argocd-util export > backup.yaml +docker run -v ~/.kube:/home/argocd/.kube --rm argoproj/argocd:$VERSION argocd admin export > backup.yaml ``` Import from a backup: ```bash -docker run -i -v ~/.kube:/home/argocd/.kube --rm argoproj/argocd:$VERSION argocd-util import - < backup.yaml +docker run -i -v ~/.kube:/home/argocd/.kube --rm argoproj/argocd:$VERSION argocd admin import - < backup.yaml ``` !!! note - If you are running Argo CD on a namespace different than default remember to pass the namespace parameter (-n ). 'argocd-util export' will not fail if you run it in the wrong namespace. + If you are running Argo CD on a namespace different than default remember to pass the namespace parameter (-n ). 'argocd admin export' will not fail if you run it in the wrong namespace. diff --git a/docs/operator-manual/health.md b/docs/operator-manual/health.md index fcc4e34730856..2129dfa8e1893 100644 --- a/docs/operator-manual/health.md +++ b/docs/operator-manual/health.md @@ -36,21 +36,19 @@ metadata: app.kubernetes.io/name: argocd-cm app.kubernetes.io/part-of: argocd data: - 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 + 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 end - return hs + end + end + return hs ``` ## Custom Health Checks diff --git a/docs/operator-manual/ingress.md b/docs/operator-manual/ingress.md index a4a893e7a7c17..6c6d5527132e0 100644 --- a/docs/operator-manual/ingress.md +++ b/docs/operator-manual/ingress.md @@ -79,7 +79,7 @@ Since Contour Ingress supports only a single protocol per Ingress object, define Internal HTTP/HTTPS Ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: argocd-server-http @@ -102,7 +102,7 @@ spec: Internal gRPC Ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: argocd-server-grpc @@ -124,7 +124,7 @@ spec: External HTTPS SSO Callback Ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: argocd-server-external-callback-http @@ -159,8 +159,6 @@ spec: - name: argocd-server command: - /argocd-server - - --staticassets - - /shared/app - --repo-server - argocd-repo-server:8081 - --insecure @@ -180,7 +178,7 @@ In order to expose the Argo CD API server with a single ingress rule and hostnam must be used to passthrough TLS connections and terminate TLS at the Argo CD API server. ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: argocd-server-ingress @@ -246,7 +244,7 @@ way would be to define two Ingress objects. One for HTTP/HTTPS, and the other fo HTTP/HTTPS Ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: argocd-server-http-ingress @@ -271,7 +269,7 @@ spec: gRPC Ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: argocd-server-grpc-ingress @@ -304,8 +302,6 @@ spec: - name: argocd-server command: - argocd-server - - --staticassets - - /shared/app - --repo-server - argocd-repo-server:8081 - --insecure @@ -383,7 +379,7 @@ spec: Once we create this service, we can configure the Ingress to conditionally route all `application/grpc` traffic to the new HTTP2 backend, using the `alb.ingress.kubernetes.io/conditions` annotation, as seen below. Note: The value after the . in the condition annotation _must_ be the same name as the service that you want traffic to route to - and will be applied on any path with a matching serviceName. ```yaml - apiVersion: networking.k8s.io/v1 # Use extensions/v1beta1 for Kubernetes 1.18 and older + apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -412,6 +408,162 @@ Once we create this service, we can configure the Ingress to conditionally route - argocd.argoproj.io ``` +## Google Cloud load balancers with Kubernetes Ingress + +You can make use of the integration of GKE with Google Cloud to deploy Load Balancers using just Kubernetes objects. + +For this we will need these five objects: +- A Service +- A BackendConfig +- A FrontendConfig +- A secret with your SSL certificate +- An Ingress for GKE + +If you need detail for all the options available for these Google integrations, you can check the [Google docs on configuring Ingress features](https://cloud.google.com/kubernetes-engine/docs/how-to/ingress-features) + +### Disable internal TLS + +First, to avoid internal redirection loops from HTTP to HTTPS, the API server should be run with TLS disabled. Edit the argocd-server deployment to add the --insecure flag to the argocd-server command. For this you can edit your resource live with `kubectl -n argocd edit deployments.apps argocd-server` or use a kustomize patch before installing Argo CD. + +The container command should change from: +```yaml + containers: + - command: + - argocd-server + - --staticassets + - /shared/app +``` + +To: +```yaml + containers: + - command: + - argocd-server + - --insecure + - --staticassets + - /shared/app +``` + +### Creating a service + +Now you need an externally accesible service. This is practically the same as the internal service Argo CD has, but as a NodePort and 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: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: argocd-server-external + namespace: argocd + annotations: + cloud.google.com/neg: '{"ingress": true}' + cloud.google.com/backend-config: '{"ports": {"http":"argocd-backend-config"}}' +spec: + type: NodePort + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app.kubernetes.io/name: argocd-server +``` + +### Creating a BackendConfig + +See that previous service referencing a backend config called `argo-backend-config`? So lets deploy it using this yaml: + +```yaml +apiVersion: cloud.google.com/v1 +kind: BackendConfig +metadata: + name: argocd-backend-config + namespace: argocd +spec: + healthCheck: + checkIntervalSec: 30 + timeoutSec: 5 + healthyThreshold: 1 + unhealthyThreshold: 2 + type: HTTP + requestPath: /healthz + port: 8080 +``` + +It uses the same health check as the pods. + +### Creating a FrontendConfig + +Now we can deploy a frontend config with an HTTP to HTTPS redirect: + +```yaml +apiVersion: networking.gke.io/v1beta1 +kind: FrontendConfig +metadata: + name: argocd-frontend-config + namespace: argocd +spec: + redirectToHttps: + enabled: true +``` + +--- +!!! note + + The next two steps (the certificate secret and the Ingress) are described supposing that you manage the certificate yourself, and you have the certificate and key files for it. In the case that your certificate is Google-managed, fix the next two steps using the [guide to use a Google-managed SSL certificate](https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs#creating_an_ingress_with_a_google-managed_certificate). + +--- + +### Creating a certificate secret + +We need now to create a secret with the SSL certificate we want in our load balancer. It's as easy as executing this command on the path you have your certificate keys stored: + +``` +kubectl -n argocd create secret tls secret-yourdomain-com \ + --cert cert-file.crt --key key-file.key +``` + +### Creating an Ingress + +And finally, to top it all, our Ingress. Note the reference to our frontend config, the service, and to the certificate secret: +```yaml +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: argocd + namespace: argocd + annotations: + networking.gke.io/v1beta1.FrontendConfig: argocd-frontend-config +spec: + tls: + - secretName: secret-yourdomain-com + rules: + - host: argocd.yourdomain.com + http: + paths: + - path: /* + backend: + serviceName: argocd-server-external + servicePort: http +``` +--- +!!! warning "Deprecation Warning" + + Note that, according to this [deprecation guide](https://kubernetes.io/docs/reference/using-api/deprecation-guide/#ingress-v122), if you're using Kubernetes 1.22+, instead of `networking.k8s.io/v1beta1`, you should use `networking.k8s.io/v1`. + +--- + +As you may know already, it can take some minutes to deploy the load balancer and become ready to accept connections. Once it's ready, get the public IP address for your Load Balancer, go to your DNS server (Google or third party) and point your domain or subdomain (i.e. argocd.yourdomain.com) to that IP address. + +You can get that IP address describing the Ingress object like this: + +``` +kubectl -n argocd describe ingresses argocd | grep Address +``` + +Once the DNS change is propagated, you're ready to use Argo with your Google Cloud Load Balancer + ## Authenticating through multiple layers of authenticating reverse proxies ArgoCD endpoints may be protected by one or more reverse proxies layers, in that case, you can provide additional headers through the `argocd` CLI `--header` parameter to authenticate through those layers. @@ -433,8 +585,6 @@ spec: containers: - command: - /argocd-server - - --staticassets - - /shared/app - --repo-server - argocd-repo-server:8081 - --rootpath @@ -487,8 +637,6 @@ spec: containers: - command: - /argocd-server - - --staticassets - - /shared/app - --repo-server - argocd-repo-server:8081 - --basehref diff --git a/docs/operator-manual/installation.md b/docs/operator-manual/installation.md new file mode 100644 index 0000000000000..6cb657e309460 --- /dev/null +++ b/docs/operator-manual/installation.md @@ -0,0 +1,83 @@ +# Installation + +Argo CD has two type of installations: multi-tenant and core. + +## Multi-Tenant + +The multi-tenant installation is the most common way to install Argo CD. This type of installation is typically used to service multiple application developer teams +in the organization and maintained by a platform team. + +The end-users can access Argo CD via the API server using the Web UI or `argocd` CLI. The `argocd` CLI has to be configured using `argocd login ` command +(learn more [here](../user-guide/commands/argocd_login.md)). + +Two types of installation manifests are provided: + +### Non High Availability: + +Not recommended for production use. This type of installation is typically used during evaluation period for demonstrations and testing. + +* [install.yaml](https://github.com/argoproj/argo-cd/blob/master/manifests/install.yaml) - Standard Argo CD installation with cluster-admin access. Use this + manifest set if you plan to use Argo CD to deploy applications in the same cluster that Argo CD runs + in (i.e. kubernetes.svc.default). It will still be able to deploy to external clusters with inputted + credentials. + +* [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 + on inputted cluster credentials. An example of using this set of manifests is if you run several + Argo CD instances for different teams, where each instance will be deploying applications to + external clusters. It will still be possible to deploy to the same cluster (kubernetes.svc.default) + with inputted credentials (i.e. `argocd cluster add --in-cluster --namespace `). + + > Note: Argo CD CRDs are not included into [namespace-install.yaml](https://github.com/argoproj/argo-cd/blob/master/manifests/namespace-install.yaml). + > and have to be installed separately. The CRD manifests are located in the [manifests/crds](https://github.com/argoproj/argo-cd/blob/master/manifests/crds) directory. + > Use the following command to install them: + > ```bash + > kubectl apply -k https://github.com/argoproj/argo-cd/manifests/crds\?ref\=stable + > ``` + +### High Availability: + +High Availability installation is recommended for production use. This bundle includes the same components but tuned for high availability and resiliency. + +* [ha/install.yaml](https://github.com/argoproj/argo-cd/blob/master/manifests/ha/install.yaml) - the same as install.yaml but with multiple replicas for + supported components. + +* [ha/namespace-install.yaml](https://github.com/argoproj/argo-cd/blob/master/manifests/ha/namespace-install.yaml) - the same as namespace-install.yaml but + with multiple replicas for supported components. + +## Core + +The core installation is most suitable for cluster administrators who indepently use Argo CD and don't need multi-tenancy features. This installation +includes fewer components and is easier to setup. The bundle does not include the API server or UI, and installs the lightweight (non-HA) version of each component. + +The end-users need Kubernetes access to manage Argo CD. The `argocd` CLI has to be configured using the following commands: + +```bash +kubectl config set-context --current --namespace=argocd # change current kube context to argocd namespace +argocd login --core +``` + +The Web UI is also available and can be started using the `argocd admin dashboard` command. + +Installation manifests are available at [core-install.yaml](https://github.com/argoproj/argo-cd/blob/master/manifests/core-install.yaml). + +## Kustomize + +The Argo CD manifests can also be installed using Kustomize. It is recommended to include the manifest as a remote resource and apply additional customizations +using Kustomize patches. + + +```yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: argocd +resources: +- https://raw.githubusercontent.com/argoproj/argo-cd/v2.0.4/manifests/ha/install.yaml +``` + +## Helm + +The Argo CD can be installed using [Helm](https://helm.sh/). The Helm chart is currently community maintained and available at +[argo-helm/charts/argo-cd](https://github.com/argoproj/argo-helm/tree/master/charts/argo-cd). diff --git a/docs/operator-manual/metrics.md b/docs/operator-manual/metrics.md index 9cc3075a2e9b6..e8d0d7dd9001f 100644 --- a/docs/operator-manual/metrics.md +++ b/docs/operator-manual/metrics.md @@ -65,7 +65,7 @@ metadata: spec: selector: matchLabels: - app.kubernetes.io/name: argocd-repo-server-metrics + app.kubernetes.io/name: argocd-repo-server endpoints: - port: metrics ``` diff --git a/docs/operator-manual/rbac.md b/docs/operator-manual/rbac.md index f818ed98b4137..b6a18f810b00d 100644 --- a/docs/operator-manual/rbac.md +++ b/docs/operator-manual/rbac.md @@ -84,27 +84,27 @@ The anonymous users get default role permissions specified by `policy.default` i ## Validating and testing your RBAC policies If you want to ensure that your RBAC policies are working as expected, you can -use the `argocd-util settings rbac` command to validate them. This tool allows you to +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-util settings rbac validate` command. +implementation, you can use the `argocd admin settings rbac validate` command. ### Validating a policy To validate a policy stored in a local text file: ```shell -argocd-util settings rbac validate --policy-file somepolicy.csv +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-util settings rbac validate --policy-file argocd-rbac-cm.yaml +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`, @@ -112,17 +112,17 @@ ensure that your current context in `~/.kube/config` is pointing to your Argo CD cluster and give appropriate namespace: ```shell -argocd-util settings rbac validate --namespace argocd +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-util settings rbac can` command. Its general syntax is +use the `argocd admin settings rbac can` command. Its general syntax is ```shell -argocd-util settings rbac can SOMEROLE ACTION RESOURCE SUBRESOURCE [flags] +argocd admin settings rbac can SOMEROLE ACTION RESOURCE SUBRESOURCE [flags] ``` Given the example from the above ConfigMap, which defines the role @@ -130,13 +130,13 @@ Given the example from the above ConfigMap, which defines the role you can test whether that role can do something like follows: ```shell -$ argocd-util settings rbac can role:org-admin get applications --policy-file argocd-rbac-cm.yaml +$ argocd admin settings rbac can role:org-admin get applications --policy-file argocd-rbac-cm.yaml Yes -$ argocd-util settings rbac can role:org-admin get clusters --policy-file argocd-rbac-cm.yaml +$ argocd admin settings rbac can role:org-admin get clusters --policy-file argocd-rbac-cm.yaml Yes -$ argocd-util settings rbac can role:org-admin create clusters 'somecluster' --policy-file argocd-rbac-cm.yaml +$ argocd admin settings rbac can role:org-admin create clusters 'somecluster' --policy-file argocd-rbac-cm.yaml No -$ argocd-util settings rbac can role:org-admin create applications 'someproj/someapp' --policy-file argocd-rbac-cm.yaml +$ argocd admin settings rbac can role:org-admin create applications 'someproj/someapp' --policy-file argocd-rbac-cm.yaml Yes ``` @@ -148,19 +148,19 @@ You can test against the role: ```shell # Plain policy, without a default role defined -$ argocd-util settings rbac can role:staging-db-admins get applications --policy-file policy.csv +$ argocd admin settings rbac can role:staging-db-admins get applications --policy-file policy.csv No -$ argocd-util settings rbac can role:staging-db-admins get applications 'staging-db-admins/*' --policy-file policy.csv +$ argocd admin settings rbac can role:staging-db-admins get applications 'staging-db-admins/*' --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-util settings rbac can role:staging-db-admins get applications --policy-file policy.csv --default-role role:readonly +$ argocd admin settings rbac can role:staging-db-admins get applications --policy-file policy.csv --default-role role:readonly Yes ``` Or against the group defined: ```shell -$ argocd-util settings rbac can db-admins get applications 'staging-db-admins/*' --policy-file policy.csv +$ argocd admin settings rbac can db-admins get applications 'staging-db-admins/*' --policy-file policy.csv Yes ``` diff --git a/docs/operator-manual/secret-management.md b/docs/operator-manual/secret-management.md index fcd0b3443b9d4..954e6bd0ff983 100644 --- a/docs/operator-manual/secret-management.md +++ b/docs/operator-manual/secret-management.md @@ -4,10 +4,10 @@ Argo CD is un-opinionated about how secrets are managed. There's many ways to do * [Bitnami Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets) * [GoDaddy Kubernetes External Secrets](https://github.com/godaddy/kubernetes-external-secrets) -* [External Secrets Operator](https://github.com/ContainerSolutions/externalsecret-operator) +* [External Secrets Operator](https://github.com/external-secrets/external-secrets) * [Hashicorp Vault](https://www.vaultproject.io) * [Banzai Cloud Bank-Vaults](https://github.com/banzaicloud/bank-vaults) -* [Helm Secrets](https://github.com/futuresimple/helm-secrets) +* [Helm Secrets](https://github.com/jkroepke/helm-secrets) * [Kustomize secret generator plugins](https://github.com/kubernetes-sigs/kustomize/blob/fd7a353df6cece4629b8e8ad56b71e30636f38fc/examples/kvSourceGoPlugin.md#secret-values-from-anywhere) * [aws-secret-operator](https://github.com/mumoshu/aws-secret-operator) * [KSOPS](https://github.com/viaduct-ai/kustomize-sops#argo-cd-integration) diff --git a/docs/operator-manual/security.md b/docs/operator-manual/security.md index cdea22223e713..04d236ad22ff6 100644 --- a/docs/operator-manual/security.md +++ b/docs/operator-manual/security.md @@ -11,7 +11,8 @@ Authentication to Argo CD API server is performed exclusively using [JSON Web To in one of the following ways: 1. For the local `admin` user, a username/password is exchanged for a JWT using the `/api/v1/session` - endpoint. This token is signed & issued by the Argo CD API server itself, and has no expiration. + endpoint. This token is signed & issued by the Argo CD API server itself and it expires after 24 hours + (this token used not to expire, see [CVE-2021-26921](https://github.com/argoproj/argo-cd/security/advisories/GHSA-9h6w-j7w4-jr52)). When the admin password is updated, all existing admin JWT tokens are immediately revoked. The password is stored as a bcrypt hash in the [`argocd-secret`](https://github.com/argoproj/argo-cd/blob/master/manifests/base/config/argocd-secret.yaml) Secret. @@ -37,6 +38,7 @@ permits access to the API request. All network communication is performed over TLS including service-to-service communication between the three components (argocd-server, argocd-repo-server, argocd-application-controller). The Argo CD API server can enforce the use of TLS 1.2 using the flag: `--tlsminversion 1.2`. +Communication with Redis is performed over plain HTTP by default. TLS can be setup with command line arguments. ## Sensitive Information @@ -153,3 +155,12 @@ Payloads from webhook events are considered untrusted. Argo CD only examines the the involved applications of the webhook event (e.g. which repo was modified), then refreshes the related application for reconciliation. This refresh is the same refresh which occurs regularly at three minute intervals, just fast-tracked by the webhook event. + +## Logging + +Argo CD logs payloads of most API requests except request that are considered sensitive, such as +`/cluster.ClusterService/Create`, `/session.SessionService/Create` etc. The full list of method +can be found in [server/server.go](https://github.com/argoproj/argo-cd/blob/abba8dddce8cd897ba23320e3715690f465b4a95/server/server.go#L516). + +Argo CD does not log IP addresses of clients requesting API endpoints, since the API server is typically behind a proxy. Instead, it is recommended +to configure IP addresses logging in the proxy server that sits in front of the API server. \ No newline at end of file diff --git a/docs/operator-manual/server-commands/argocd-application-controller.md b/docs/operator-manual/server-commands/argocd-application-controller.md index 02a4cab25deb3..6a21f76848175 100644 --- a/docs/operator-manual/server-commands/argocd-application-controller.md +++ b/docs/operator-manual/server-commands/argocd-application-controller.md @@ -36,6 +36,11 @@ argocd-application-controller [flags] --operation-processors int Number of application operation processors (default 10) --password string Password for basic authentication to the API server --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-insecure-skip-tls-verify Skip Redis server certificate validation. + --redis-use-tls Use TLS when connecting to Redis. --redisdb int Redis database. --repo-server string Repo server address. (default "argocd-repo-server:8081") --repo-server-plaintext Disable TLS on connections to repo server diff --git a/docs/operator-manual/server-commands/argocd-repo-server.md b/docs/operator-manual/server-commands/argocd-repo-server.md index 15b381d55a334..a72b89d46b5a4 100644 --- a/docs/operator-manual/server-commands/argocd-repo-server.md +++ b/docs/operator-manual/server-commands/argocd-repo-server.md @@ -22,6 +22,11 @@ argocd-repo-server [flags] --parallelismlimit int Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit. --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. + --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-insecure-skip-tls-verify Skip Redis server certificate validation. + --redis-use-tls Use TLS when connecting to Redis. --redisdb int Redis database. --repo-cache-expiration duration Cache expiration for repo state, incl. app lists, app details, manifest generation, revision meta-data (default 24h0m0s) --revision-cache-expiration duration Cache expiration for cached revision (default 3m0s) diff --git a/docs/operator-manual/server-commands/argocd-server.md b/docs/operator-manual/server-commands/argocd-server.md index ac294d1705bbf..d7b4bdfb759b6 100644 --- a/docs/operator-manual/server-commands/argocd-server.md +++ b/docs/operator-manual/server-commands/argocd-server.md @@ -41,6 +41,11 @@ argocd-server [flags] --password string Password for basic authentication to the API server --port int Listen on given port (default 8080) --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-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 @@ -51,7 +56,7 @@ argocd-server [flags] --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 Static assets directory path + --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") diff --git a/docs/operator-manual/server-commands/argocd-util.md b/docs/operator-manual/server-commands/argocd-util.md deleted file mode 100644 index 9a4e9d8e0e732..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util.md +++ /dev/null @@ -1,31 +0,0 @@ -## argocd-util - -argocd-util tools used by Argo CD - -### Synopsis - -argocd-util has internal utility tools used by Argo CD - -``` -argocd-util [flags] -``` - -### Options - -``` - -h, --help help for argocd-util - --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") -``` - -### SEE ALSO - -* [argocd-util app](argocd-util_app.md) - Manage applications configuration -* [argocd-util cluster](argocd-util_cluster.md) - Manage clusters configuration -* [argocd-util export](argocd-util_export.md) - Export all Argo CD data to stdout (default) or a file -* [argocd-util import](argocd-util_import.md) - Import Argo CD data from stdin (specify `-') or a file -* [argocd-util proj](argocd-util_proj.md) - Manage projects configuration -* [argocd-util repo](argocd-util_repo.md) - Manage repositories configuration -* [argocd-util settings](argocd-util_settings.md) - Provides set of commands for settings validation and troubleshooting -* [argocd-util version](argocd-util_version.md) - Print version information - diff --git a/docs/operator-manual/server-commands/argocd-util_app.md b/docs/operator-manual/server-commands/argocd-util_app.md deleted file mode 100644 index a26c078cd7aa7..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_app.md +++ /dev/null @@ -1,21 +0,0 @@ -## argocd-util app - -Manage applications configuration - -``` -argocd-util app [flags] -``` - -### Options - -``` - -h, --help help for app -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD -* [argocd-util app diff-reconcile-results](argocd-util_app_diff-reconcile-results.md) - Compare results of two reconciliations and print diff. -* [argocd-util app generate-spec](argocd-util_app_generate-spec.md) - Generate declarative config for an application -* [argocd-util app get-reconcile-results](argocd-util_app_get-reconcile-results.md) - Reconcile all applications and stores reconciliation summary in the specified file. - diff --git a/docs/operator-manual/server-commands/argocd-util_app_diff-reconcile-results.md b/docs/operator-manual/server-commands/argocd-util_app_diff-reconcile-results.md deleted file mode 100644 index 105fe09fe5693..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_app_diff-reconcile-results.md +++ /dev/null @@ -1,18 +0,0 @@ -## argocd-util app diff-reconcile-results - -Compare results of two reconciliations and print diff. - -``` -argocd-util app diff-reconcile-results PATH1 PATH2 [flags] -``` - -### Options - -``` - -h, --help help for diff-reconcile-results -``` - -### SEE ALSO - -* [argocd-util app](argocd-util_app.md) - Manage applications configuration - diff --git a/docs/operator-manual/server-commands/argocd-util_apps.md b/docs/operator-manual/server-commands/argocd-util_apps.md deleted file mode 100644 index 92a27b27860be..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_apps.md +++ /dev/null @@ -1,20 +0,0 @@ -## argocd-util apps - -Utility commands operate on ArgoCD applications - -``` -argocd-util apps [flags] -``` - -### Options - -``` - -h, --help help for apps -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD -* [argocd-util apps diff-reconcile-results](argocd-util_apps_diff-reconcile-results.md) - Compare results of two reconciliations and print diff. -* [argocd-util apps get-reconcile-results](argocd-util_apps_get-reconcile-results.md) - Reconcile all applications and stores reconciliation summary in the specified file. - diff --git a/docs/operator-manual/server-commands/argocd-util_apps_diff-reconcile-results.md b/docs/operator-manual/server-commands/argocd-util_apps_diff-reconcile-results.md deleted file mode 100644 index 290a25190b7a4..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_apps_diff-reconcile-results.md +++ /dev/null @@ -1,18 +0,0 @@ -## argocd-util apps diff-reconcile-results - -Compare results of two reconciliations and print diff. - -``` -argocd-util apps diff-reconcile-results PATH1 PATH2 [flags] -``` - -### Options - -``` - -h, --help help for diff-reconcile-results -``` - -### SEE ALSO - -* [argocd-util apps](argocd-util_apps.md) - Utility commands operate on ArgoCD applications - diff --git a/docs/operator-manual/server-commands/argocd-util_apps_get-reconcile-results.md b/docs/operator-manual/server-commands/argocd-util_apps_get-reconcile-results.md deleted file mode 100644 index f8aaac48a5fd5..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_apps_get-reconcile-results.md +++ /dev/null @@ -1,39 +0,0 @@ -## argocd-util apps get-reconcile-results - -Reconcile all applications and stores reconciliation summary in the specified file. - -``` -argocd-util apps get-reconcile-results PATH [flags] -``` - -### Options - -``` - --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. - --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 - -h, --help help for get-reconcile-results - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to a kube config. Only required if out-of-cluster - --l string Label selector - -n, --namespace string If present, the namespace scope for this CLI request - --o string Output format (yaml|json) (default "yaml") - --password string Password for basic authentication to the API server - --refresh If set to true then recalculates apps reconciliation - --repo-server string Repo server address. - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util apps](argocd-util_apps.md) - Utility commands operate on ArgoCD applications - diff --git a/docs/operator-manual/server-commands/argocd-util_cluster.md b/docs/operator-manual/server-commands/argocd-util_cluster.md deleted file mode 100644 index 2d39ebae9e1f2..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_cluster.md +++ /dev/null @@ -1,22 +0,0 @@ -## argocd-util cluster - -Manage clusters configuration - -``` -argocd-util cluster [flags] -``` - -### Options - -``` - -h, --help help for cluster -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD -* [argocd-util cluster generate-spec](argocd-util_cluster_generate-spec.md) - Generate declarative config for a cluster -* [argocd-util cluster kubeconfig](argocd-util_cluster_kubeconfig.md) - Generates kubeconfig for the specified cluster -* [argocd-util cluster shards](argocd-util_cluster_shards.md) - Print information about each controller shard and portion of Kubernetes resources it is responsible for. -* [argocd-util cluster stats](argocd-util_cluster_stats.md) - Prints information cluster statistics and inferred shard number - diff --git a/docs/operator-manual/server-commands/argocd-util_cluster_generate-spec.md b/docs/operator-manual/server-commands/argocd-util_cluster_generate-spec.md deleted file mode 100644 index 77c7d70ebfb2c..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_cluster_generate-spec.md +++ /dev/null @@ -1,35 +0,0 @@ -## argocd-util cluster generate-spec - -Generate declarative config for a cluster - -``` -argocd-util cluster generate-spec CONTEXT [flags] -``` - -### Options - -``` - --aws-cluster-name string AWS Cluster name if set then aws cli eks token command will be used to access cluster - --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 - --exec-command string Command to run to provide client credentials to the cluster. You may need to build a custom ArgoCD image to ensure the command is available at runtime. - --exec-command-api-version string Preferred input version of the ExecInfo for the --exec-command executable - --exec-command-args stringArray Arguments to supply to the --exec-command executable - --exec-command-env stringToString Environment vars to set when running the --exec-command executable (default []) - --exec-command-install-hint string Text shown to the user when the --exec-command executable doesn't seem to be present - --generate-bearer-token Generate authentication token that should be used to access K8S API server - -h, --help help for generate-spec - --in-cluster Indicates Argo CD resides inside this cluster and should connect using the internal k8s hostname (kubernetes.default.svc) - --kubeconfig string use a particular kubeconfig file - --name string Overwrite the cluster name - --namespace stringArray List of namespaces which are allowed to manage - -o, --output string Output format. One of: json|yaml (default "yaml") - --service-account string System namespace service account to use for kubernetes resource management. If not set then default "argocd-manager" SA will be used (default "argocd-manager") - --shard int Cluster shard number; inferred from hostname if not set (default -1) - --system-namespace string Use different system namespace (default "kube-system") -``` - -### SEE ALSO - -* [argocd-util cluster](argocd-util_cluster.md) - Manage clusters configuration - diff --git a/docs/operator-manual/server-commands/argocd-util_cluster_kubeconfig.md b/docs/operator-manual/server-commands/argocd-util_cluster_kubeconfig.md deleted file mode 100644 index 302be65813470..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_cluster_kubeconfig.md +++ /dev/null @@ -1,35 +0,0 @@ -## argocd-util cluster kubeconfig - -Generates kubeconfig for the specified cluster - -``` -argocd-util cluster kubeconfig CLUSTER_URL OUTPUT_PATH [flags] -``` - -### Options - -``` - --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. - --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 - -h, --help help for kubeconfig - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to a kube config. Only required if out-of-cluster - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util cluster](argocd-util_cluster.md) - Manage clusters configuration - diff --git a/docs/operator-manual/server-commands/argocd-util_config.md b/docs/operator-manual/server-commands/argocd-util_config.md deleted file mode 100644 index b7e711501dcaa..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_config.md +++ /dev/null @@ -1,22 +0,0 @@ -## argocd-util config - -Generate declarative configuration files - -``` -argocd-util config [flags] -``` - -### Options - -``` - -h, --help help for config -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD -* [argocd-util config app](argocd-util_config_app.md) - Generate declarative config for an application -* [argocd-util config cluster](argocd-util_config_cluster.md) - Generate declarative config for a cluster -* [argocd-util config proj](argocd-util_config_proj.md) - Generate declarative config for a project -* [argocd-util config repo](argocd-util_config_repo.md) - Generate declarative config for a repo - diff --git a/docs/operator-manual/server-commands/argocd-util_config_app.md b/docs/operator-manual/server-commands/argocd-util_config_app.md deleted file mode 100644 index 1908198c9a612..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_config_app.md +++ /dev/null @@ -1,89 +0,0 @@ -## argocd-util config app - -Generate declarative config for an application - -``` -argocd-util config app APPNAME [flags] -``` - -### Examples - -``` - - # Generate declarative config for a directory app - argocd-util config app guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --directory-recurse - - # Generate declarative config for a Jsonnet app - argocd-util config app jsonnet-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path jsonnet-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --jsonnet-ext-str replicas=2 - - # Generate declarative config for a Helm app - argocd-util config app helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2 - - # Generate declarative config for a Helm app from a Helm repo - argocd-util config app nginx-ingress --repo https://kubernetes-charts.storage.googleapis.com --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc - - # Generate declarative config for a Kustomize app - argocd-util config app 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 - - # Generate declarative config for a app using a custom tool: - argocd-util config app ksane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane - -``` - -### Options - -``` - --allow-empty Set allow zero live resources when sync is automated - --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 (overrides the namespace specified in the ksonnet app.yaml) - --dest-server string K8s cluster URL (e.g. https://kubernetes.default.svc) - --directory-exclude string Set glob expression used to exclude files from application source path - --directory-include string Set glob expression used to include files from application source path - --directory-recurse Recurse directory - --env string Application environment to monitor - --helm-chart string Helm Chart name - --helm-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-version string Helm version - -h, --help help for app - --jsonnet-ext-var-code stringArray Jsonnet ext var - --jsonnet-ext-var-str stringArray Jsonnet string ext var - --jsonnet-libs stringArray Additional jsonnet libs (prefixed by repoRoot) - --jsonnet-tla-code stringArray Jsonnet top level code arguments - --jsonnet-tla-str stringArray Jsonnet top level string arguments - --kustomize-common-annotation stringArray Set common labels in Kustomize - --kustomize-common-label stringArray Set common labels in Kustomize - --kustomize-image stringArray Kustomize images (e.g. --kustomize-image node:8.15.0 --kustomize-image mysql=mariadb,alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d) - --kustomize-version string Kustomize version - -l, --label stringArray Labels to apply to the app - --name string A name for the app, ignored if a file is set (DEPRECATED) - --nameprefix string Kustomize nameprefix - --namesuffix string Kustomize namesuffix - -o, --output string Output format. One of: json|yaml (default "yaml") - -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 - --release-name string Helm release-name - --repo string Repository URL, ignored if a file is set - --retry-backoff-duration duration Retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h) (default 5s) - --retry-backoff-factor int Factor multiplies the base duration after each failed retry (default 2) - --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 The tracking source branch, tag, commit or Helm chart version the application will sync to - --revision-history-limit int How many items to keep in revision history (default 10) - --self-heal Set self healing when sync is automated - --sync-option Prune=false Add or remove a sync option, e.g add Prune=false. Remove using `!` prefix, e.g. `!Prune=false` - --sync-policy string Set the sync policy (one of: none, automated (aliases of automated: auto, automatic)) - --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 -``` - -### SEE ALSO - -* [argocd-util config](argocd-util_config.md) - Generate declarative configuration files - diff --git a/docs/operator-manual/server-commands/argocd-util_config_cluster.md b/docs/operator-manual/server-commands/argocd-util_config_cluster.md deleted file mode 100644 index 8bd02356d6402..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_config_cluster.md +++ /dev/null @@ -1,32 +0,0 @@ -## argocd-util config cluster - -Generate declarative config for a cluster - -``` -argocd-util config cluster CONTEXT [flags] -``` - -### Options - -``` - --aws-cluster-name string AWS Cluster name if set then aws cli eks token command will be used to access cluster - --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 - --exec-command string Command to run to provide client credentials to the cluster. You may need to build a custom ArgoCD image to ensure the command is available at runtime. - --exec-command-api-version string Preferred input version of the ExecInfo for the --exec-command executable - --exec-command-args stringArray Arguments to supply to the --exec-command executable - --exec-command-env stringToString Environment vars to set when running the --exec-command executable (default []) - --exec-command-install-hint string Text shown to the user when the --exec-command executable doesn't seem to be present - -h, --help help for cluster - --in-cluster Indicates Argo CD resides inside this cluster and should connect using the internal k8s hostname (kubernetes.default.svc) - --kubeconfig string use a particular kubeconfig file - --name string Overwrite the cluster name - --namespace stringArray List of namespaces which are allowed to manage - -o, --output string Output format. One of: json|yaml (default "yaml") - --shard int Cluster shard number; inferred from hostname if not set (default -1) -``` - -### SEE ALSO - -* [argocd-util config](argocd-util_config.md) - Generate declarative configuration files - diff --git a/docs/operator-manual/server-commands/argocd-util_config_proj.md b/docs/operator-manual/server-commands/argocd-util_config_proj.md deleted file mode 100644 index b43963cc877d2..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_config_proj.md +++ /dev/null @@ -1,25 +0,0 @@ -## argocd-util config proj - -Generate declarative config for a project - -``` -argocd-util config proj PROJECT [flags] -``` - -### Options - -``` - --description string Project description - -d, --dest stringArray Permitted destination server and namespace (e.g. https://192.168.99.100:8443,default) - -h, --help help for proj - --orphaned-resources Enables orphaned resources monitoring - --orphaned-resources-warn Specifies if applications should have a warning condition when orphaned resources detected - -o, --output string Output format. One of: json|yaml (default "yaml") - --signature-keys strings GnuPG public key IDs for commit signature verification - -s, --src stringArray Permitted source repository URL -``` - -### SEE ALSO - -* [argocd-util config](argocd-util_config.md) - Generate declarative configuration files - diff --git a/docs/operator-manual/server-commands/argocd-util_config_repo.md b/docs/operator-manual/server-commands/argocd-util_config_repo.md deleted file mode 100644 index 80fec43bf7a7d..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_config_repo.md +++ /dev/null @@ -1,61 +0,0 @@ -## argocd-util config repo - -Generate declarative config for a repo - -``` -argocd-util config repo REPOURL [flags] -``` - -### Examples - -``` - - # Add a Git repository via SSH using a private key for authentication, ignoring the server's host key: - argocd-util config repo 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-util config repo ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa - - # Add a private Git repository via HTTPS using username/password and TLS client certificates: - argocd-util config repo https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key - - # Add a private Git repository via HTTPS using username/password without verifying the server's TLS certificate - argocd-util config repo https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification - - # Add a public Helm repository named 'stable' via HTTPS - argocd-util config repo https://kubernetes-charts.storage.googleapis.com --type helm --name stable - - # Add a private Helm repository named 'stable' via HTTPS - argocd-util config repo https://kubernetes-charts.storage.googleapis.com --type helm --name stable --username test --password test - - # Add a private Helm OCI-based repository named 'stable' via HTTPS - argocd-util config repo helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test - -``` - -### Options - -``` - --enable-lfs enable git-lfs (Large File Support) on this repository - --enable-oci enable helm-oci (Helm OCI-Based Repository) - --github-app-enterprise-base-url string base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3 - --github-app-id int id of the GitHub Application - --github-app-installation-id int installation id of the GitHub Application - --github-app-private-key-path string private key of the GitHub Application - -h, --help help for repo - --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 - -o, --output string Output format. One of: json|yaml (default "yaml") - --password string password to the repository - --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) - --type string type of the repository, "git" or "helm" (default "git") - --username string username to the repository -``` - -### SEE ALSO - -* [argocd-util config](argocd-util_config.md) - Generate declarative configuration files - diff --git a/docs/operator-manual/server-commands/argocd-util_gendexcfg.md b/docs/operator-manual/server-commands/argocd-util_gendexcfg.md deleted file mode 100644 index 4f3f0c5a78082..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_gendexcfg.md +++ /dev/null @@ -1,36 +0,0 @@ -## argocd-util gendexcfg - -Generates a dex config from Argo CD settings - -``` -argocd-util gendexcfg [flags] -``` - -### Options - -``` - --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. - --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 - -h, --help help for gendexcfg - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --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 - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD - diff --git a/docs/operator-manual/server-commands/argocd-util_kubeconfig.md b/docs/operator-manual/server-commands/argocd-util_kubeconfig.md deleted file mode 100644 index 70426e22876cb..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_kubeconfig.md +++ /dev/null @@ -1,35 +0,0 @@ -## argocd-util kubeconfig - -Generates kubeconfig for the specified cluster - -``` -argocd-util kubeconfig CLUSTER_URL OUTPUT_PATH [flags] -``` - -### Options - -``` - --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. - --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 - -h, --help help for kubeconfig - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to a kube config. Only required if out-of-cluster - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD - diff --git a/docs/operator-manual/server-commands/argocd-util_proj.md b/docs/operator-manual/server-commands/argocd-util_proj.md deleted file mode 100644 index 0cef263287e10..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_proj.md +++ /dev/null @@ -1,21 +0,0 @@ -## argocd-util proj - -Manage projects configuration - -``` -argocd-util proj [flags] -``` - -### Options - -``` - -h, --help help for proj -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD -* [argocd-util proj generate-allow-list](argocd-util_proj_generate-allow-list.md) - Generates project allow list from the specified clusterRole file -* [argocd-util proj generate-spec](argocd-util_proj_generate-spec.md) - Generate declarative config for a project -* [argocd-util proj update-role-policy](argocd-util_proj_update-role-policy.md) - Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions. - diff --git a/docs/operator-manual/server-commands/argocd-util_proj_generate-allow-list.md b/docs/operator-manual/server-commands/argocd-util_proj_generate-allow-list.md deleted file mode 100644 index 75e77b2740eac..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_proj_generate-allow-list.md +++ /dev/null @@ -1,36 +0,0 @@ -## argocd-util proj generate-allow-list - -Generates project allow list from the specified clusterRole file - -``` -argocd-util proj generate-allow-list CLUSTERROLE_PATH PROJ_NAME [flags] -``` - -### Options - -``` - --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. - --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 - -h, --help help for generate-allow-list - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to a kube config. Only required if out-of-cluster - -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 - --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 -``` - -### SEE ALSO - -* [argocd-util proj](argocd-util_proj.md) - Manage projects configuration - diff --git a/docs/operator-manual/server-commands/argocd-util_proj_generate-spec.md b/docs/operator-manual/server-commands/argocd-util_proj_generate-spec.md deleted file mode 100644 index 74407a69d8ab8..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_proj_generate-spec.md +++ /dev/null @@ -1,30 +0,0 @@ -## argocd-util proj generate-spec - -Generate declarative config for a project - -``` -argocd-util proj generate-spec PROJECT [flags] -``` - -### Options - -``` - --allow-cluster-resource stringArray List of allowed cluster level resources - --allow-namespaced-resource stringArray List of allowed namespaced resources - --deny-cluster-resource stringArray List of denied cluster level resources - --deny-namespaced-resource stringArray List of denied namespaced resources - --description string Project description - -d, --dest stringArray Permitted destination server and namespace (e.g. https://192.168.99.100:8443,default) - -f, --file string Filename or URL to Kubernetes manifests for the project - -h, --help help for generate-spec - --orphaned-resources Enables orphaned resources monitoring - --orphaned-resources-warn Specifies if applications should have a warning condition when orphaned resources detected - -o, --output string Output format. One of: json|yaml (default "yaml") - --signature-keys strings GnuPG public key IDs for commit signature verification - -s, --src stringArray Permitted source repository URL -``` - -### SEE ALSO - -* [argocd-util proj](argocd-util_proj.md) - Manage projects configuration - diff --git a/docs/operator-manual/server-commands/argocd-util_projects.md b/docs/operator-manual/server-commands/argocd-util_projects.md deleted file mode 100644 index 5aa2d214f2894..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_projects.md +++ /dev/null @@ -1,20 +0,0 @@ -## argocd-util projects - -Utility commands operate on ArgoCD Projects - -``` -argocd-util projects [flags] -``` - -### Options - -``` - -h, --help help for projects -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD -* [argocd-util projects generate-allow-list](argocd-util_projects_generate-allow-list.md) - Generates project allow list from the specified clusterRole file -* [argocd-util projects update-role-policy](argocd-util_projects_update-role-policy.md) - Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions. - diff --git a/docs/operator-manual/server-commands/argocd-util_projects_update-role-policy.md b/docs/operator-manual/server-commands/argocd-util_projects_update-role-policy.md deleted file mode 100644 index 346034bc541de..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_projects_update-role-policy.md +++ /dev/null @@ -1,51 +0,0 @@ -## argocd-util projects update-role-policy - -Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions. - -``` -argocd-util projects update-role-policy PROJECT_GLOB MODIFICATION ACTION [flags] -``` - -### Examples - -``` - # Add policy that allows executing any action (action/*) to roles which name matches to *deployer* in all projects - argocd-util projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow - - # Remove policy that which manages running (action/*) from all roles which name matches *deployer* in all projects - argocd-util projects update-role-policy '*' remove override --role '*deployer*' - -``` - -### Options - -``` - --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. - --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 - --dry-run Dry run (default true) - -h, --help help for update-role-policy - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --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 - --permission string Action permission - --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") - --resource string Resource e.g. 'applications' - --role string Role name pattern e.g. '*deployer*' (default "*") - --scope string Resource scope e.g. '*' - --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 -``` - -### SEE ALSO - -* [argocd-util projects](argocd-util_projects.md) - Utility commands operate on ArgoCD Projects - diff --git a/docs/operator-manual/server-commands/argocd-util_repo.md b/docs/operator-manual/server-commands/argocd-util_repo.md deleted file mode 100644 index 5010cf024a18d..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_repo.md +++ /dev/null @@ -1,19 +0,0 @@ -## argocd-util repo - -Manage repositories configuration - -``` -argocd-util repo [flags] -``` - -### Options - -``` - -h, --help help for repo -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD -* [argocd-util repo generate-spec](argocd-util_repo_generate-spec.md) - Generate declarative config for a repo - diff --git a/docs/operator-manual/server-commands/argocd-util_repo_generate-spec.md b/docs/operator-manual/server-commands/argocd-util_repo_generate-spec.md deleted file mode 100644 index 166a57e080646..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_repo_generate-spec.md +++ /dev/null @@ -1,62 +0,0 @@ -## argocd-util repo generate-spec - -Generate declarative config for a repo - -``` -argocd-util repo generate-spec REPOURL [flags] -``` - -### Examples - -``` - - # Add a Git repository via SSH using a private key for authentication, ignoring the server's host key: - argocd-util repo generate-spec 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-util repo generate-spec ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa - - # Add a private Git repository via HTTPS using username/password and TLS client certificates: - argocd-util repo generate-spec https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key - - # Add a private Git repository via HTTPS using username/password without verifying the server's TLS certificate - argocd-util repo generate-spec https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification - - # Add a public Helm repository named 'stable' via HTTPS - argocd-util repo generate-spec https://charts.helm.sh/stable --type helm --name stable - - # Add a private Helm repository named 'stable' via HTTPS - argocd-util repo generate-spec https://charts.helm.sh/stable --type helm --name stable --username test --password test - - # Add a private Helm OCI-based repository named 'stable' via HTTPS - argocd-util repo generate-spec helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test - -``` - -### Options - -``` - --enable-lfs enable git-lfs (Large File Support) on this repository - --enable-oci enable helm-oci (Helm OCI-Based Repository) - --github-app-enterprise-base-url string base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3 - --github-app-id int id of the GitHub Application - --github-app-installation-id int installation id of the GitHub Application - --github-app-private-key-path string private key of the GitHub Application - -h, --help help for generate-spec - --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 - -o, --output string Output format. One of: json|yaml (default "yaml") - --password string password to the repository - --proxy string use proxy to access repository - --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) - --type string type of the repository, "git" or "helm" (default "git") - --username string username to the repository -``` - -### SEE ALSO - -* [argocd-util repo](argocd-util_repo.md) - Manage repositories configuration - diff --git a/docs/operator-manual/server-commands/argocd-util_rundex.md b/docs/operator-manual/server-commands/argocd-util_rundex.md deleted file mode 100644 index 069cf329e23f8..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_rundex.md +++ /dev/null @@ -1,35 +0,0 @@ -## argocd-util rundex - -Runs dex generating a config using settings from the Argo CD configmap and secret - -``` -argocd-util rundex [flags] -``` - -### Options - -``` - --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. - --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 - -h, --help help for rundex - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --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 - --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 -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD - diff --git a/docs/operator-manual/server-commands/argocd-util_settings.md b/docs/operator-manual/server-commands/argocd-util_settings.md deleted file mode 100644 index 728f52ccd3527..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_settings.md +++ /dev/null @@ -1,41 +0,0 @@ -## argocd-util settings - -Provides set of commands for settings validation and troubleshooting - -``` -argocd-util settings [flags] -``` - -### Options - -``` - --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --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 - -h, --help help for settings - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to a kube config. Only required if out-of-cluster - --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD -* [argocd-util settings rbac](argocd-util_settings_rbac.md) - Validate and test RBAC configuration -* [argocd-util settings resource-overrides](argocd-util_settings_resource-overrides.md) - Troubleshoot resource overrides -* [argocd-util settings validate](argocd-util_settings_validate.md) - Validate settings - diff --git a/docs/operator-manual/server-commands/argocd-util_settings_rbac.md b/docs/operator-manual/server-commands/argocd-util_settings_rbac.md deleted file mode 100644 index 768f75dd39364..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_settings_rbac.md +++ /dev/null @@ -1,45 +0,0 @@ -## argocd-util settings rbac - -Validate and test RBAC configuration - -``` -argocd-util settings rbac [flags] -``` - -### Options - -``` - -h, --help help for rbac -``` - -### Options inherited from parent commands - -``` - --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --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 - --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 - --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util settings](argocd-util_settings.md) - Provides set of commands for settings validation and troubleshooting -* [argocd-util settings rbac can](argocd-util_settings_rbac_can.md) - Check RBAC permissions for a role or subject -* [argocd-util settings rbac validate](argocd-util_settings_rbac_validate.md) - Validate RBAC policy - diff --git a/docs/operator-manual/server-commands/argocd-util_settings_rbac_can.md b/docs/operator-manual/server-commands/argocd-util_settings_rbac_can.md deleted file mode 100644 index 24a5c2d2277f7..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_settings_rbac_can.md +++ /dev/null @@ -1,78 +0,0 @@ -## argocd-util settings rbac can - -Check RBAC permissions for a role or subject - -### Synopsis - - -Check whether a given role or subject has appropriate RBAC permissions to do -something. - - -``` -argocd-util settings rbac can ROLE/SUBJECT ACTION RESOURCE [SUB-RESOURCE] [flags] -``` - -### Examples - -``` - -# Check whether role some:role has permissions to create an application in the -# 'default' project, using a local policy.csv file -argocd-util settings rbac can some:role create application 'default/app' --policy-file policy.csv - -# Policy file can also be K8s config map with data keys like argocd-rbac-cm, -# i.e. 'policy.csv' and (optionally) 'policy.default' -argocd-util settings rbac can some:role create application 'default/app' --policy-file argocd-rbac-cm.yaml - -# If --policy-file is not given, the ConfigMap 'argocd-rbac-cm' from K8s is -# used. You need to specify the argocd namespace, and make sure that your -# current Kubernetes context is pointing to the cluster Argo CD is running in -argocd-util settings rbac can some:role create application 'default/app' --namespace argocd - -# You can override a possibly configured default role -argocd-util settings rbac can someuser create application 'default/app' --default-role role:readonly - - -``` - -### Options - -``` - --default-role string name of the default role to use - -h, --help help for can - --policy-file string path to the policy file to use - -q, --quiet quiet mode - do not print results to stdout - --strict whether to perform strict check on action and resource names (default true) - --use-builtin-policy whether to also use builtin-policy (default true) -``` - -### Options inherited from parent commands - -``` - --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --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 - --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 - --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util settings rbac](argocd-util_settings_rbac.md) - Validate and test RBAC configuration - diff --git a/docs/operator-manual/server-commands/argocd-util_settings_rbac_validate.md b/docs/operator-manual/server-commands/argocd-util_settings_rbac_validate.md deleted file mode 100644 index 81ae11d44face..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_settings_rbac_validate.md +++ /dev/null @@ -1,51 +0,0 @@ -## argocd-util settings rbac validate - -Validate RBAC policy - -### Synopsis - - -Validates an RBAC policy for being syntactically correct. The policy must be -a local file, and in either CSV or K8s ConfigMap format. - - -``` -argocd-util settings rbac validate --policy-file=POLICYFILE [flags] -``` - -### Options - -``` - -h, --help help for validate - --policy-file string path to the policy file to use -``` - -### Options inherited from parent commands - -``` - --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --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 - --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 - --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util settings rbac](argocd-util_settings_rbac.md) - Validate and test RBAC configuration - diff --git a/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides.md b/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides.md deleted file mode 100644 index 9baf7d5e88a26..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides.md +++ /dev/null @@ -1,47 +0,0 @@ -## argocd-util settings resource-overrides - -Troubleshoot resource overrides - -``` -argocd-util settings resource-overrides [flags] -``` - -### Options - -``` - -h, --help help for resource-overrides -``` - -### Options inherited from parent commands - -``` - --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --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 - --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 - --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util settings](argocd-util_settings.md) - Provides set of commands for settings validation and troubleshooting -* [argocd-util settings resource-overrides health](argocd-util_settings_resource-overrides_health.md) - Assess resource health -* [argocd-util settings resource-overrides ignore-differences](argocd-util_settings_resource-overrides_ignore-differences.md) - Renders fields excluded from diffing -* [argocd-util settings resource-overrides list-actions](argocd-util_settings_resource-overrides_list-actions.md) - List available resource actions -* [argocd-util settings resource-overrides run-action](argocd-util_settings_resource-overrides_run-action.md) - Executes resource action - diff --git a/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_health.md b/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_health.md deleted file mode 100644 index 815bf6d406cc3..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_health.md +++ /dev/null @@ -1,54 +0,0 @@ -## argocd-util settings resource-overrides health - -Assess resource health - -### Synopsis - -Assess resource health using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap - -``` -argocd-util settings resource-overrides health RESOURCE_YAML_PATH [flags] -``` - -### Examples - -``` - -argocd-util settings resource-overrides health ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml -``` - -### Options - -``` - -h, --help help for health -``` - -### Options inherited from parent commands - -``` - --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --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 - --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 - --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util settings resource-overrides](argocd-util_settings_resource-overrides.md) - Troubleshoot resource overrides - diff --git a/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_ignore-differences.md b/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_ignore-differences.md deleted file mode 100644 index c66be6208dca4..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_ignore-differences.md +++ /dev/null @@ -1,54 +0,0 @@ -## argocd-util settings resource-overrides ignore-differences - -Renders fields excluded from diffing - -### Synopsis - -Renders ignored fields using the 'ignoreDifferences' setting specified in the 'resource.customizations' field of 'argocd-cm' ConfigMap - -``` -argocd-util settings resource-overrides ignore-differences RESOURCE_YAML_PATH [flags] -``` - -### Examples - -``` - -argocd-util settings resource-overrides ignore-differences ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml -``` - -### Options - -``` - -h, --help help for ignore-differences -``` - -### Options inherited from parent commands - -``` - --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --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 - --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 - --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util settings resource-overrides](argocd-util_settings_resource-overrides.md) - Troubleshoot resource overrides - diff --git a/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_list-actions.md b/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_list-actions.md deleted file mode 100644 index 804a74b788368..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_list-actions.md +++ /dev/null @@ -1,54 +0,0 @@ -## argocd-util settings resource-overrides list-actions - -List available resource actions - -### Synopsis - -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 - -``` -argocd-util settings resource-overrides list-actions RESOURCE_YAML_PATH [flags] -``` - -### Examples - -``` - -argocd-util settings resource-overrides action list /tmp/deploy.yaml --argocd-cm-path ./argocd-cm.yaml -``` - -### Options - -``` - -h, --help help for list-actions -``` - -### Options inherited from parent commands - -``` - --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --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 - --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 - --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util settings resource-overrides](argocd-util_settings_resource-overrides.md) - Troubleshoot resource overrides - diff --git a/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_run-action.md b/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_run-action.md deleted file mode 100644 index f4992e79dfd9e..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_settings_resource-overrides_run-action.md +++ /dev/null @@ -1,54 +0,0 @@ -## argocd-util settings resource-overrides run-action - -Executes resource action - -### Synopsis - -Executes resource action using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields - -``` -argocd-util settings resource-overrides run-action RESOURCE_YAML_PATH ACTION [flags] -``` - -### Examples - -``` - -argocd-util settings resource-overrides action run /tmp/deploy.yaml restart --argocd-cm-path ./argocd-cm.yaml -``` - -### Options - -``` - -h, --help help for run-action -``` - -### Options inherited from parent commands - -``` - --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --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 - --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 - --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util settings resource-overrides](argocd-util_settings_resource-overrides.md) - Troubleshoot resource overrides - diff --git a/docs/operator-manual/server-commands/argocd-util_settings_validate.md b/docs/operator-manual/server-commands/argocd-util_settings_validate.md deleted file mode 100644 index 855c3098b2887..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_settings_validate.md +++ /dev/null @@ -1,59 +0,0 @@ -## argocd-util settings validate - -Validate settings - -### Synopsis - -Validates settings specified in 'argocd-cm' ConfigMap and 'argocd-secret' Secret - -``` -argocd-util settings validate [flags] -``` - -### Examples - -``` - -#Validates all settings in the specified YAML file -argocd-util settings validate --argocd-cm-path ./argocd-cm.yaml - -#Validates accounts and plugins settings in Kubernetes cluster of current kubeconfig context -argocd-util settings validate --group accounts --group plugins --load-cluster-settings -``` - -### Options - -``` - --group stringArray Optional list of setting groups that have to be validated ( one of: accounts, general, kustomize, plugins, repositories, resource-overrides) - -h, --help help for validate -``` - -### Options inherited from parent commands - -``` - --argocd-cm-path string Path to local argocd-cm.yaml file - --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --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 - --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 - --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server -``` - -### SEE ALSO - -* [argocd-util settings](argocd-util_settings.md) - Provides set of commands for settings validation and troubleshooting - diff --git a/docs/operator-manual/server-commands/argocd-util_version.md b/docs/operator-manual/server-commands/argocd-util_version.md deleted file mode 100644 index cf2325ad03c25..0000000000000 --- a/docs/operator-manual/server-commands/argocd-util_version.md +++ /dev/null @@ -1,19 +0,0 @@ -## argocd-util version - -Print version information - -``` -argocd-util version [flags] -``` - -### Options - -``` - -h, --help help for version - --short print just the version number -``` - -### SEE ALSO - -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD - diff --git a/docs/operator-manual/troubleshooting.md b/docs/operator-manual/troubleshooting.md index 6057cb4d366cc..884045410b0b8 100644 --- a/docs/operator-manual/troubleshooting.md +++ b/docs/operator-manual/troubleshooting.md @@ -1,29 +1,15 @@ # Troubleshooting Tools -The document describes how to use `argocd-tool` binary to simplify Argo CD settings customizations and troubleshot +The document describes how to use `argocd admin` subcommands to simplify Argo CD settings customizations and troubleshot connectivity issues. ## Settings Argo CD provides multiple ways to customize system behavior and has a lot of settings. It might be dangerous to modify -settings on Argo CD used in production by multiple users. Before applying settings you can use `argocd-util` binary to -make sure that settings are valid and Argo CD is working as expected. The `argocd-util` binary is available in `argocd` -image and might be used using docker. -You can download the latest `argocd-util` binary from [the latest release page of this repository](https://github.com/argoproj/argo-cd/releases/latest), which will include the `argocd-util` CLI. -Example: +settings on Argo CD used in production by multiple users. Before applying settings you can use `argocd admin` subcommands to +make sure that settings are valid and Argo CD is working as expected. -```bash -docker run --rm -it -w /src -v $(pwd):/src argoproj/argocd: \ - argocd-util settings validate --argocd-cm-path ./argocd-cm.yaml -``` - -If you are using Linux you can extract `argocd-util` binary from docker image: - -```bash -docker run --rm -it -w /src -v $(pwd):/src argocd cp /usr/local/bin/argocd-util ./argocd-util -``` - -The `argocd-util settings validate` command performs basic settings validation and print short summary +The `argocd admin settings validate` command performs basic settings validation and print short summary of each settings group. **Diffing Customization** @@ -31,11 +17,10 @@ of each settings group. [Diffing customization](../user-guide/diffing.md) allows excluding some resource fields from diffing process. The diffing customizations are configured in `resource.customizations` field of `argocd-cm` ConfigMap. -The following `argocd-util` command prints information about fields excluded from diffing in the specified ConfigMap. +The following `argocd admin` command prints information about fields excluded from diffing in the specified ConfigMap. ```bash -docker run --rm -it -w /src -v $(pwd):/src argoproj/argocd: \ - argocd-util settings resource-overrides ignore-differences ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml +argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml ``` **Health Assessment** @@ -44,35 +29,32 @@ Argo CD provides built-in [health assessment](./health.md) for several kubernete customized by writing your own health checks in [Lua](https://www.lua.org/). The health checks are configured in the `resource.customizations` field of `argocd-cm` ConfigMap. -The following `argocd-util` command assess resource health using Lua script configured in the specified ConfigMap. +The following `argocd admin` command assess resource health using Lua script configured in the specified ConfigMap. ```bash -docker run --rm -it -w /src -v $(pwd):/src argoproj/argocd: \ - argocd-util settings resource-overrides health ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml +argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml ``` **Resource Actions** Resource actions allows configuring named Lua script which performs resource modification. -The following `argocd-util` command executes action using Lua script configured in the specified ConfigMap and prints +The following `argocd admin` command executes action using Lua script configured in the specified ConfigMap and prints applied modifications. ```bash -docker run --rm -it -w /src -v $(pwd):/src argoproj/argocd: \ - argocd-util settings resource-overrides run-action /tmp/deploy.yaml restart --argocd-cm-path /private/tmp/argocd-cm.yaml +argocd admin settings resource-overrides run-action /tmp/deploy.yaml restart --argocd-cm-path /private/tmp/argocd-cm.yaml ``` -The following `argocd-util` command lists actions available for a given resource using Lua script configured in the specified ConfigMap. +The following `argocd admin` command lists actions available for a given resource using Lua script configured in the specified ConfigMap. ```bash -docker run --rm -it -w /src -v $(pwd):/src argoproj/argocd: \ - argocd-util settings resource-overrides list-actions /tmp/deploy.yaml --argocd-cm-path /private/tmp/argocd-cm.yaml +argocd admin settings resource-overrides list-actions /tmp/deploy.yaml --argocd-cm-path /private/tmp/argocd-cm.yaml ``` ## Cluster credentials -The `argocd-util cluster kubeconfig` is useful if you manually created Secret with cluster credentials and trying need to +The `argocd admin cluster kubeconfig` is useful if you manually created Secret with cluster credentials and trying need to troubleshoot connectivity issues. In this case, it is suggested to use the following steps: 1 SSH into [argocd-application-controller] pod. @@ -82,10 +64,10 @@ kubectl exec -n argocd -it \ $(kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-application-controller -o jsonpath='{.items[0].metadata.name}') bash ``` -2 Use `argocd-util cluster kubeconfig` command to export kubeconfig file from the configured Secret: +2 Use `argocd admin cluster kubeconfig` command to export kubeconfig file from the configured Secret: ``` -argocd-util cluster kubeconfig https:// /tmp/kubeconfig --namespace argocd +argocd admin cluster kubeconfig https:// /tmp/kubeconfig --namespace argocd ``` 3 Use `kubectl` to get more details about connection issues, fix them and apply changes back to secret: diff --git a/docs/operator-manual/upgrading/2.0-2.1.md b/docs/operator-manual/upgrading/2.0-2.1.md index a8f2f7024fdb5..d5ea6f795a07d 100644 --- a/docs/operator-manual/upgrading/2.0-2.1.md +++ b/docs/operator-manual/upgrading/2.0-2.1.md @@ -2,7 +2,7 @@ ## Upgraded Kustomize Version -Note that bundled Kustomize has been upgraded to v4.1.2. Some of the flags are changed in Kustomize V4. +Note that bundled Kustomize has been upgraded to v4.2.0. Some of the flags are changed in Kustomize V4. For example flag name `load_restrictor` is changed in Kustomize v4+. It is changed from `--load_restrictor=none` to `--load-restrictor LoadRestrictionsNone`. ## Replacing `--app-resync` flag with `timeout.reconciliation` setting @@ -19,4 +19,27 @@ From here on you can follow the [regular upgrade process](./overview.md). The configuration of repositories and repository credential templates via the `argocd-cm` has been deprecated. Repositories and repository credentials are now discovered via Secrets that are labeled with `argocd.argoproj.io/secret-type=repository` or `argocd.argoproj.io/secret-type=repo-creds` respectively. See the examples in [argocd-repositories.yaml](../argocd-repositories.yaml) -and [argocd-repo-creds.yaml](../argocd-repo-creds.yaml). \ No newline at end of file +and [argocd-repo-creds.yaml](../argocd-repo-creds.yaml). + +## The `argocd-util` CLI commands merged into `argocd admin` + +The `argocd-util` CLI commands are available under `argocd admin` and the `argocd-util` binary is no longer available. + +## Replace runtime system user while [BYOI](https://argoproj.github.io/argo-cd/operator-manual/custom_tools/#byoi-build-your-own-image) + +Runtime system user should to be changed from `argocd` to `999`, as shown below. + +```dockerfile +FROM argoproj/argocd:latest + +# Switch to root for the ability to perform install +USER root + +# Something custom here +RUN apt-get update + +# Switch back to non-root user + +# deprecated: USER argocd +USER 999 +``` diff --git a/docs/operator-manual/upgrading/overview.md b/docs/operator-manual/upgrading/overview.md index 2e4fd464b36c0..61beb5dcd453b 100644 --- a/docs/operator-manual/upgrading/overview.md +++ b/docs/operator-manual/upgrading/overview.md @@ -37,6 +37,8 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/ +* [v2.0 to v2.1](./2.0-2.1.md) +* [v1.8 to v2.0](./1.8-2.0.md) * [v1.7 to v1.8](./1.7-1.8.md) * [v1.6 to v1.7](./1.6-1.7.md) * [v1.5 to v1.6](./1.5-1.6.md) diff --git a/docs/operator-manual/user-management/google.md b/docs/operator-manual/user-management/google.md index 9f319ab10776f..9ee62ab5839bc 100644 --- a/docs/operator-manual/user-management/google.md +++ b/docs/operator-manual/user-management/google.md @@ -1,6 +1,13 @@ # Google -* [G Suite SAML App Auth using Dex](#g-suite-saml-app-auth-using-dex) +There are three different ways to integrate Argo CD login with your Google Workspace users. Generally the OpenID Connect (_oidc_) method would be the recommended way of doing this integration (and easier, as well...), but depending on your needs, you may choose a different option. + +* [OpenID Connect using Dex](#openid-connect-using-dex) + This is the recommended login method if you don't need information about the groups the user's belongs to. Google doesn't expose the `groups` claim via _oidc_, so you won't be able to use Google Groups membership information for RBAC. +* [SAML App Auth using Dex](#saml-app-auth-using-dex) + Dex [recommends avoiding this method](https://dexidp.io/docs/connectors/saml/#warning). Also, you won't get Google Groups membership information through this method. +* [OpenID Connect plus Google Groups using Dex](#openid-connect-plus-google-groups-using-dex) + This is the recommended method if you need to user Google Groups membership in your RBAC configuration. Once you've set up one of the above integrations, be sure to edit `argo-rbac-cm` to configure permissions (as in the example below). See [RBAC Configurations](../rbac.md) for more detailed scenarios. @@ -14,10 +21,66 @@ data: policy.default: role:readonly ``` -## G Suite SAML App Auth using Dex +## OpenID Connect using Dex + +### Configure your OAuth consent screen + +If you've never configured this, you'll be redirected straight to this if you try to create an OAuth Client ID + +1. Go to your [OAuth Consent](https://console.cloud.google.com/apis/credentials/consent) configuration. If you still haven't created one, select `Internal` or `External` and click `Create` +2. Go and [edit your OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent/edit) Verify you're in the correct project! +3. Configure a name for your login app and a user support email address +4. The app logo and filling the information links is not mandatory, but it's a nice touch for the login page +5. In "Authorized domains" add the domains who are allowed to log in to ArgoCD (e.g. if you add `example.com`, all Google Workspace users with an `@example.com` address will be able to log in) +6. Save to continue to the "Scopes" section +7. Click on "Add or remove scopes" and add the `.../auth/userinfo.profile` and the `openid` scopes +8. Save, review the summary of your changes and finish + +### Configure a new OAuth Client ID + +1. Go to your [Google API Credentials](https://console.cloud.google.com/apis/credentials) console, and make sure you're in the correct project. +2. Click on "+Create Credentials"/"OAuth Client ID" +3. Select "Web Application" in the Application Type drop down menu, and enter an identifying name for your app (e.g. `Argo CD`) +4. Fill "Authorized JavaScript origins" with your Argo CD URL, e.g. `https://argocd.example.com` +5. Fill "Authorized redirect URIs" with your Argo CD URL plus `/api/dex/callback`, e.g. `https://argocd.example.com/api/dex/callback` + + ![](../../assets/google-admin-oidc-uris.png) + +6. Click "Create" and save your "Client ID" and your "Client Secret" for later + +### Configure Argo to use OpenID Connect + +Edit `argo-cm` and add the following dex.config to the data section, replacing `clientID` and `clientSecret` with the values you saved before: + +```yaml +data: + url: https://argocd.example.com + dex.config: | + connectors: + - config: + issuer: https://accounts.google.com + clientID: XXXXXXXXXXXXX.apps.googleusercontent.com + clientSecret: XXXXXXXXXXXXX + type: oidc + id: google + name: Google +``` + +### References + +- [Dex oidc connector docs](https://dexidp.io/docs/connectors/oidc/) + +## SAML App Auth using Dex ### Configure a new SAML App +--- +!!! warning "Deprecation Warning" + + Note that, according to [Dex documentation](https://dexidp.io/docs/connectors/saml/#warning), SAML is considered unsafe and they are planning to deprecate that module. + +--- + 1. In the [Google admin console](https://admin.google.com), open the left-side menu and select `Apps` > `SAML Apps` ![Google Admin Apps Menu](../../assets/google-admin-saml-apps-menu.png "Google Admin menu with the Apps / SAML Apps path selected") @@ -76,3 +139,90 @@ data: - [Dex SAML connector docs](https://dexidp.io/docs/connectors/saml/) - [Google's SAML error messages](https://support.google.com/a/answer/6301076?hl=en) + +## OpenID Connect plus Google Groups using Dex + +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: +- An oidc client ID and secret + Same as when you're configuring an [OpenID connection](#openid-connect-using-dex), this authenticates your users +- A Google service account + This is used to connect to the Google Directory API and pull information about your user's group membership + +Also, you'll need the email address for an admin user on this domain. Dex will impersonate that user identity to fetch user information from the API. + +### Configure OpenID Connect + +Go through the same steps as in [OpenID Connect using Dex](#openid-connect-using-dex), except for configuring `argocd-cm`. We'll do that later. + +### Set up Directory API access + +1. Follow [Google instructions to create a service account with Domain-Wide Delegation](https://developers.google.com/admin-sdk/directory/v1/guides/delegation) + - When assigning API scopes to the service account assign **only** the `https://www.googleapis.com/auth/admin.directory.group.readonly` scope and nothing else. If you assign any other scopes, you won't be able to fetch information from the API + - Create the credentials in JSON format and store them in a safe place, we'll need them later +2. Enable the [Admin SDK](https://console.developers.google.com/apis/library/admin.googleapis.com/) + +### Configure Dex + +1. Create a secret with the contents of the previous json file encoded in base64, like this + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: argocd-google-groups-json + namespace: argocd +data: + googleAuth.json: JSON_FILE_BASE64_ENCODED +``` + +2. Edit your `argocd-dex-server` deployment to mount that secret as a file + - Add a volume mount in `/spec/template/spec/containers/0/volumeMounts/` like this: + ```yaml + volumeMounts: + - mountPath: /shared + name: static-files + - mountPath: /tmp + name: dexconfig + - mountPath: /tmp/oidc + name: google-json + readOnly: true + ``` + Be aware of editing the running container and not the init container! + - Add a volume in `/spec/template/spec/volumes/` like this: + ```yaml + volumes: + - emptyDir: {} + name: static-files + - emptyDir: {} + name: dexconfig + - name: google-json + secret: + defaultMode: 420 + secretName: argocd-google-groups-json + ``` + +3. Edit `argo-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: + ```yaml + 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 + type: google + id: google + name: Google + ``` + +4. Restart your `argocd-dex-server` deployment to be sure it's using the latest configuration +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 + +### References + +- [Dex Google connector docs](https://dexidp.io/docs/connectors/google/) \ No newline at end of file diff --git a/docs/operator-manual/user-management/index.md b/docs/operator-manual/user-management/index.md index 8ed40e7871cb1..f3415be786659 100644 --- a/docs/operator-manual/user-management/index.md +++ b/docs/operator-manual/user-management/index.md @@ -77,6 +77,7 @@ argocd account get --account * Set user password ```bash +# if you are managing users as the admin user, should be the current admin password. argocd account update-password \ --account \ --current-password \ @@ -114,7 +115,8 @@ There are two ways that SSO can be configured: * [Bundled Dex OIDC provider](#dex) - use this option if your current provider does not support OIDC (e.g. SAML, LDAP) or if you wish to leverage any of Dex's connector features (e.g. the ability to map GitHub - organizations and teams to OIDC groups claims). + organizations and teams to OIDC groups claims). Dex also supports OIDC directly and can fetch user + information from the identity provider when the groups cannot be included in the IDToken. * [Existing OIDC provider](#existing-oidc-provider) - use this if you already have an OIDC provider which you are using (e.g. [Okta](okta.md), [OneLogin](onelogin.md), [Auth0](auth0.md), [Microsoft](microsoft.md), [Keycloak](keycloak.md), @@ -196,6 +198,91 @@ NOTES: Argo CD will automatically use the correct `redirectURI` for any OAuth2 connectors, to match the correct external callback URL (e.g. `https://argocd.example.com/api/dex/callback`) +## OIDC Configuration with DEX + +Dex can be used for OIDC authentication instead of ArgoCD directly. This provides a separate set of +features such as fetching information from the `UserInfo` endpoint and +[federated tokens](https://dexidp.io/docs/custom-scopes-claims-clients/#cross-client-trust-and-authorized-party) + +### Configuration: +* In the `argocd-cm` ConfigMap add the `OIDC` connector to the `connectors` sub field inside `dex.config`. +See Dex's [OIDC connect documentation](https://dexidp.io/docs/connectors/oidc/) to see what other +configuration options might be useful. We're going to be using a minimal configuration here. +* The issuer URL should be where Dex talks to the OIDC provider. There would normally be a +`.well-known/openid-configuration` under this URL which has information about what the provider supports. +e.g. https://accounts.google.com/.well-known/openid-configuration + + +```yaml +data: + url: "https://argocd.example.com" + dex.config: | + connectors: + # OIDC + - type: OIDC + id: oidc + name: OIDC + issuer: https://example-OIDC-provider.com + clientID: aaaabbbbccccddddeee + clientSecret: $dex.oidc.clientSecret +``` + +### Requesting additional ID token claims + +By default Dex only retrieves the profile and email scopes. In order to retrieve more more claims you +can add them under the `scopes` entry in the Dex configuration. To enable group claims through Dex, +`insecureEnableGroups` also needs to enabled. Group information is currently only refreshed at authentication +time and support to refresh group information more dynamically can be tracked here: [dexidp/dex#1065](https://github.com/dexidp/dex/issues/1065). + +```yaml +data: + url: "https://argocd.example.com" + dex.config: | + connectors: + # OIDC + - type: OIDC + id: oidc + name: OIDC + issuer: https://example-OIDC-provider.com + clientID: aaaabbbbccccddddeee + clientSecret: $dex.oidc.clientSecret + insecureEnableGroups: true + scopes: + - profile + - email + - groups +``` + +!!! warning + Because group information is only refreshed at authentication time just adding or removing an account from a group will not change a user's membership until they reauthenticate. Depending on your organization's needs this could be a security risk and could be mitigated by changing the authentication token's lifetime. + +### Retrieving claims that are not in the token + +When an Idp does not or cannot support certain claims in an IDToken they can be retrieved separately using +the UserInfo endpoint. Dex supports this functionality using the `getUserInfo` endpoint. One of the most +common claims that is not supported in the IDToken is the `groups` claim and both `getUserInfo` and `insecureEnableGroups` +must be set to true. + +```yaml +data: + url: "https://argocd.example.com" + dex.config: | + connectors: + # OIDC + - type: OIDC + id: oidc + name: OIDC + issuer: https://example-OIDC-provider.com + clientID: aaaabbbbccccddddeee + clientSecret: $dex.oidc.clientSecret + insecureEnableGroups: true + scopes: + - profile + - email + - groups + getUserInfo: true +``` + ## Existing OIDC Provider To configure Argo CD to delegate authenticate to your existing OIDC provider, add the OAuth2 diff --git a/docs/operator-manual/user-management/keycloak.md b/docs/operator-manual/user-management/keycloak.md index 84c6bc1d0ea85..694598bc7a1b8 100644 --- a/docs/operator-manual/user-management/keycloak.md +++ b/docs/operator-manual/user-management/keycloak.md @@ -57,17 +57,19 @@ Let's start by storing the client secret you generated earlier in the argocd sec 1. First you'll need to encode the client secret in base64: `$ echo -n '83083958-8ec6-47b0-a411-a8c55381fbd2' | base64` 2. Then you can edit the secret and add the base64 value to a new key called _oidc.keycloak.clientSecret_ using `$ kubectl edit secret argocd-secret`. - Your Secret should look something like this: - ```yaml - apiVersion: v1 - kind: Secret - metadata: - name: argocd-secret - data: - ... - oidc.keycloak.clientSecret: ODMwODM5NTgtOGVjNi00N2IwLWE0MTEtYThjNTUzODFmYmQy - ... - ``` + +Your Secret should look something like this: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: argocd-secret +data: + ... + oidc.keycloak.clientSecret: ODMwODM5NTgtOGVjNi00N2IwLWE0MTEtYThjNTUzODFmYmQy + ... +``` Now we can configure the config map and add the oidc configuration to enable our keycloak authentication. You can use `$ kubectl edit configmap argocd-cm`. diff --git a/docs/proposals/002-ui-extensions.md b/docs/proposals/002-ui-extensions.md index 979bd3bee6616..8fa02d25fd11c 100644 --- a/docs/proposals/002-ui-extensions.md +++ b/docs/proposals/002-ui-extensions.md @@ -72,7 +72,7 @@ It is proposed that a git repository be used to contain the javascript code, as In the most simplest form, an Argo CD extension could simply be a pointer to a git repository at a revision: -```yaml= +```yaml kind: ArgoCDExtension metadata: name: argo-rollouts diff --git a/docs/proposals/application-name-identifier.md b/docs/proposals/application-name-identifier.md new file mode 100644 index 0000000000000..3d425e9432dbc --- /dev/null +++ b/docs/proposals/application-name-identifier.md @@ -0,0 +1,273 @@ +--- +title: Change the way application resources are identified +authors: + - "@jannfis" +sponsors: + - TBD +reviewers: + - TBD +approvers: + - TBD + +creation-date: 2021-06-07 +last-updated: 2021-06-07 +--- + +# Change the way application resources are identified + +This is a proposal to introduce the tracking method settings that allows using +an annotation as the application identifier instead of the application instance label. +This will allow application names longer than 63 characters and solve issues caused by +copying `app.kubernetes.io/instance` label. As an additional goal, we propose to introduce an +installation ID that will allow multiple Argo CD instances to manage resources +on the same cluster. + + +## Summary + +Argo CD identifies resources it manages by setting the _application instance +label_ to the name of the managing `Application` on all resources that are +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 applicaton resources are identified and allows switching to +using the annotation instead of `app.kubernetes.io/instance` label. + +## Motivation + +The main motivation behind this change is to solve the following known issues: + +* The Kubernetes label value cannot be longer than 63 characters. In large scale + installations, in order to build up an easy to understand and + well-formed naming schemes for applications managed by Argo CD, people often + hit the 63 character limit and need to define the naming scheme around this + unnecessary limit. + +* Popular off-the-shelf Helm charts often add the `app.kubernetes.io/instance` label + to the generated resource manifests. This label confuses Argo CD and makes it think the + resource is managed by the application. + +* Kubernetes operators often create additional resources without creating owner reference + and copy the `app.kubernetes.io/instance` label from the application resource. This is + also confusing Argo CD and makes it think the resource is managed by the application. + +An additional motivation - while we're at touching at application instance +label - is to improve the way how multiple Argo CD instances could manage +applications on the same cluster, without requiring the user to actually +perform instance specific configuration. + +### Goals + +* Allow application names of more than 63 characters + +* Prevent confusion caused by copied/generated `app.kubernetes.io/instance` label + +* Keep having a human-readable way to identify resources that belong to a + given Argo CD application + +* As a stretch-goal, allow multiple Argo CD instances to manage resources on + the same cluster without the need for configuring application instance label + key (usually `app.kubernetes.io/instance`) + +### Non-Goals + +* Change the default name of the application instance label + +## Proposal + +We propose introducing a new setting `trackingMethod` that allows to control +how application resources are identified. The `trackingMethod` setting takes +one of the following values: + +* `label` (default) - Argo CD keep using the `app.kubernetes.io/instance` label. +* `annotation+label` - Argo CD keep adding `app.kubernetes.io/instance` but only + for informational purposes: label is not used for tracking, value is truncated if + longer than 63 characters. The `app.kubernetes.io/instance` annotation is used + to track application resources. +* `annotation` - Argo CD uses the `app.kubernetes.io/instance` annotation to track + application resources. + +The `app.kubernetes.io/instance` attribute values includes the application name, +resources identifier it is applied to, and optionally the Argo CD installation ID: + +The application name allows to identify the application that manages the resource. The +resource identifier prevents confusion if an operation copies the +`app.kubernetes.io/instance` annotation to another resource. Finally optional +installation ID allows separate two Argo CD instances that manages resources in the same cluster. + +The `trackingMethod` setting should be available at the system level and the application level to +allow the smooth transition from the old `app.kubernetes.io/instance` label to the new tracking method. +Using the app leverl settings users will be able to first switch applications one by one to the new tracking method +and prepare for the migration. Next system level setting can be changed to `annotation` or `annotation+label` +and not-migrated applications can be configured to use `labels` using application level setting. + + +### Use cases + +Add a list of detailed use cases this enhancement intends to take care of. + +#### Use case 1: Allow for more than 63 characters in application name + +As a user, I would like to be able to give my applications names with arbitrary +length, because I want to include identifiers like target regions and possibly +availability zones, the environment and possibly other identifiers (e.g. a team +name) in the application names. The current restriction of 63 characters is not +sufficient for my naming requirements. + +#### Use case 2: Allow for retrieving all resources using Kubernetes + +As an administrator, I want to enable my users to use more than 63 characters +in their application names, but I still want to be able to retrieve all of the +resources managed by that particular application using Kubernetes mechanisms, +e.g. a label selector as in the following example: + +``` +kubectl get deployments -l app.kubernetes.io/instance= --all-namespaces +``` + +#### Use case 3: Multiple Argo CD instances managing apps on same cluster + +I also want to be able to see which application and Argo CD instance is the +one in charge of a given resource. + +### Implementation Details/Notes/Constraints [optional] + +#### Include resource identifies in the `app.kubernetes.io/instance` annotation + +The `app.kubernetes.io/instance` annotation might be accidently added or copied +same as label. To prevent Argo CD confusion the annotation value should include +the identifier of the resource annotation was applied to. The resource identifier +includes the group, kind, namespace and name of the resource. It is proposed to use `;` +to separate identifier from the application name. + +```yaml +annotations: + app.kubernetes.io/instance: ;/// +``` + +Example: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment + namespace: default + annotations: + app.kubernetes.io/instance: my-application;apps/Deployment/default/my-deployment +``` + +#### Allow multiple Argo CD instances manage applications on same cluster + +As of today, to allow two or more Argo CD instances with a similar set of +permissions (e.g. cluster-wide read access to resources) manage applications +on the same cluster, users would have to configure the _application instance +label key_ in the Argo CD configuration to a unique value. Otherwise, if an +application with the same name exists in two different Argo CD installations, +both would claim ownership of the resources of that application. + +We do see the need for preventing such scenarios out-of-the-box in Argo CD. +For this, we do suggest the introduction of an _installation ID_ in the +form of a standard _GUID_. + +This GUID would be generated once by Argo CD upon startup, and is persisted in +the Argo CD configuration, e.g. by storing it as `installationID` in the +`argocd-cm` ConfigMap. The GUID of the installation would need to be encoded +in some way in the resources managed by that Argo CD instance. + +We suggest using a dedicated annotation to store the GUID and modify Argo CD so that it matches _both_, the app +instance key and the GUID to determine whether a resource is managed by +this Argo CD instance. Given above mentioned GUID, this may look like the +following on a resource: + + ```yaml + apiVersion: v1 + Kind: Secret + metadata: + name: some-secret + namespace: some-namespace + annotations: + app.kubernetes.io/instance: my-application;/Secret/some-namespace/some-secret + argo-cd.argoproj.io/installation-id: 61199294-412c-4e78-a237-3ebba6784fcd + ``` + +The user should be able to opt-out of this feature by setting the `installationID` to an empty string. + +### Security Considerations + +We think this change will not have a direct impact on the security of Argo CD +or the applications it manages. + +### Risks and Mitigations + +The proposal assumes that user can keep adding `app.kubernetes.io/instance` label +to be able to retrieve resources using `kubectl get -l app.kubernetes.io/instance=` command. +However, Argo CD is going to truncate the value of the label if it is longer than 63 characters. There is +a small possibility that there are several applications with the same first 63 characters in the name. This +should be clearly stated in documentation. + +### Upgrade / Downgrade Strategy + +Upgrading to a version that implements this proposal should be seamless, as +previously injected labels will not be removed and additional annotations will +be applied to the resource. E.g. consider following resource in Git, that will +be synced as part of an application named `some-application`. In Git, the +resource looks like follows: + +```yaml +apiVersion: v1 +Kind: Secret +metadata: + name: some-secret + namespace: some-namespace +``` + +When synced with the current incarnation of Argo CD, Argo CD would inject the +application instance label and once the resource is applied in the cluster, it +would look like follows: + +```yaml +apiVersion: v1 +Kind: Secret +metadata: + name: some-secret + namespace: some-namespace + labels: + app.kubernetes.io/instance: some-application +``` + +Once Argo CD is updated to a version implementing this proposal, the resource +would be rewritten to look like the following: + +```yaml +apiVersion: v1 +Kind: Secret +metadata: + name: some-secret + namespace: some-namespace + labels: + app.kubernetes.io/instance: some-application + annotations: + app.kubernetes.io/instance: my-application;/Secret/some-namespace/some-secret + argo-cd.argoproj.io/installation-id: 61199294-412c-4e78-a237-3ebba6784fcd +``` + +On a rollback to a previous Argo CD version, this change would be reverted +and the resource would look like the first shown example above. + +## Drawbacks + +We do see some drawbacks to this implementation: + +* This change would trigger a re-sync of each and every managed resource, which + may result in unexpected heavy load on Argo CD and the cluster at upgrade + time. The workaround is an ability to opt-out of this as a default and enable it + on application basis. + +## Alternatives + +* Enabling application names longer than 63 characters could also be done + by using the hashed value of the application name and additional metadata as a label. + The disadvantage of this approach is that hash value is not human friendly. In particular, + it is difficult to retrieve application manifests using `kubectl get -l app.kubernetes.io/instance=`. \ No newline at end of file diff --git a/docs/proposals/headless-argocd.md b/docs/proposals/headless-argocd.md new file mode 100644 index 0000000000000..51bee5ce85bcb --- /dev/null +++ b/docs/proposals/headless-argocd.md @@ -0,0 +1,132 @@ +--- +title: Neat-enhancement-idea +authors: +- "@alexmt" + sponsors: +- TBD + reviewers: +- "@jessesuen" +- TBD + approvers: +- "@jessesuen" +- TBD + +creation-date: 2020-05-01 +last-updated: 2020-05-01 +--- + +# Neat Enhancement Idea + +Support "disabling" multi-tenancy features by introducing Headless Argo CD. + +## Summary + +There are two main group of GitOps users: + +* Application developers - engineers who leverages Kubernetes to run applications. +* Cluster administrators - engineers who manage and support Kubernetes clusters for the organization. + +Argo CD is a perfect fit for application developers thanks to its multi-tenancy features. Instead of running a separate Argo CD instance for +each team, it is possible to run on the instance and leverage features like SSO, RBAC, and Web user interface. However, this is not the case +for cluster administrators. Administrators prefer to rely on Kubernetes RBAC and view SSO and Argo CD RBAC as an obstacle and security threat. +SSO, RBAC, and UI/API are totally optional and can be disabled but it requires additional configuration and learning. + +## Motivation + +It is proposed to introduce officially supported **Headless Argo CD** that encapsulates changes required to disable multi-tenancy features +and provide a seamless experience for cluster administrators (or any other user who don't need multi-tenancy). + +### Goals + +The goals of "Headless Argo CD" are: + +#### Provide an easy way to deploy Argo CD without API/UI + +The end-user should be able to install required components using a single `kubectl apply` command without following any additional instructions. + +#### Provide an easy way to use and manage Headless Argo CD + +The `Headless Argo CD` should provide a simple way to view and manage Argo CD applications using CLI/UI. The access control should be enforced by +Kubernetes RBAC only. + +#### Easy transition from Headless to non-Headless Argo CD + +It is a common case when the Argo CD adopter wants to start small and then expand Argo CD to the whole organization. It should be easy +to "upgrade" headless to full Argo CD installation. + +### Non-Goals + +#### Not modified Argo CD + +The `Headless Argo CD` is not modified Argo CD. It is Argo CD distribution that missing UI/API and CLI that provides commands for Argo CD admin. + +#### Not deprecating existing operational methods + +The `Headless Argo CD` is not intended to deprecate any of the existing operational methods. + +## Proposal + +#### Headless Installation Manifests + +In order to simplify installation of Argo CD without API we need introduce `headless/install.yaml` in [manifests](../../manifests) directory. +The installation manifests should include only non HA controller, repo-server, Redis components, and RBAC. + +#### Headless CLI + +Without the API server, users won't be able to take advantage of Argo CD UI and `argocd` CLI so the user experience won't be complete. To fill that gap +we need to change the `argocd` CLI that and support talking directly to Kubernetes without requiring Argo CD API Server. The [argo-cd#6361](https://github.com/argoproj/argo-cd/pull/6361) +demonstrates required changes: + +* Adds `--headless` flag to `argocd` commands +* If the `--headless` flag is set to true then pre-run function that starts "local" Argo CD API server and points CLI to locally running instance +* Finally on-demand port-forwards to Redis and repo server. + +The user should be able to store `--headless` flag in config in order to avoid specifying the flag for every command. It is proposed to use `argocd login --headless` to generate +"headless" config. + +#### Local UI + +In addition to exposing CLI commands the PR introduces `argocd admin dashboard` command. The new command starts API server locally and exposes Argo CD UI locally. +In order to make this possible the static assets have been embedded into Argo CD binary. + +### Merge Argo CD Util + +The potential users of "headless" mode will benefit from `argocd-util` commands. The experience won't be smooth since they will need to switch back and forth +between `argocd` and `argocd-util`. Given that we still have not finalized how users are supposed to get `argocd-util` binary (https://github.com/argoproj/argo-cd/issues/5307) +it is proposed to deprecate `argocd-util` and merge in into `argocd` CLI under admin subcommand: + +``` +argocd admin app generate-spec guestbook --repo https://github.com/argoproj/argocd-example-apps +``` + +### Use cases + +Add a list of detailed use cases this enhancement intends to take care of. + +## Use case 1: + +As an Argo CD administrator, I would like to manage cluster resources using Argo CD without exposing API/UI outside of the cluster. + +## Use case 2: + +As an Argo CD administrator, I would like to use Argo CD CLI commands and user interface to manage Argo CD applications/settings using only `kubeconf` file and without Argo CD API access. + +### Security Considerations + +The Headless CLI/UI disables built-in Argo CD authentication and relies only on Kubernetes RBAC. So if the user will be able to make the same change using Headless CLI as using kubectl. + +### Risks and Mitigations + +TBD + +### Upgrade / Downgrade Strategy + +Switching to and from Argo CD Headless does not modify any persistent data or settings. So upgrade/downgrade should be seamless by just applying the right manifest file. + +## Drawbacks + +* Embedding static resources into the binary increases it's size by ~20 mb. The image size is the same. + +## Alternatives + +* Re-invent GitOps Agent CLI experience and don't re-use Argo CD. \ No newline at end of file diff --git a/docs/roadmap.md b/docs/roadmap.md index 2a41a09c251eb..a94db45f0e5cf 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,27 +1,27 @@ # Roadmap - [Roadmap](#roadmap) - - [v2.1](#v21) + - [v2.2](#v22) - [Config Management Tools Integrations (proposal)](#config-management-tools-integrations-proposal) - [Argo CD Extensions (proposal)](#argo-cd-extensions-proposal) - [Project scoped repository and clusters (proposal)](#project-scoped-repository-and-clusters-proposal) - - [Headless Argo CD (aka GitOps Agent) (proposal)](#headless-argo-cd-aka-gitops-agent-proposal) - - [v2.2 and beyond](#v22-and-beyond) + - [Core Argo CD (proposal)](#core-argo-cd-aka-gitops-agent-proposal) + - [v2.3 and beyond](#v23-and-beyond) - [Application Details Page Usability](#application-details-page-usability) - [Cluster Management User Interface](#cluster-management-user-interface) - [GitOps Engine Enhancements](#gitops-engine-enhancements) - [Completed](#completed) - - [Core Functionality Bug Fixes](#core-functionality-bug-fixes) - - [Performance](#performance) - - [ApplicationSet](#applicationset) - - [Large Applications support](#large-applications-support) - - [Serviceability](#serviceability) - - [Argo CD Notifications](#argo-cd-notifications) - - [Automated Registry Monitoring](#automated-registry-monitoring) - - [Projects Enhancements](#projects-enhancements) + - [✅ Core Functionality Bug Fixes](#-core-functionality-bug-fixes) + - [✅ Performance](#-performance) + - [✅ ApplicationSet](#-applicationset) + - [✅ Large Applications support](#-large-applications-support) + - [✅ Serviceability](#-serviceability) + - [✅ Argo CD Notifications](#-argo-cd-notifications) + - [✅ Automated Registry Monitoring](#-automated-registry-monitoring) + - [✅ Projects Enhancements](#-projects-enhancements) -## v2.1 +## v2.2 ### Config Management Tools Integrations ([proposal](https://github.com/argoproj/argo-cd/pull/5927)) @@ -42,12 +42,7 @@ via Git repository. The feature streamlines the process of adding repositories and clusters to the project and makes it self-service. Instead of asking an administrator to change Argo CD settings end users can perform the change independently. -### Headless Argo CD (aka GitOps Agent) ([proposal](https://github.com/argoproj/argo-cd/pull/6385)) - -Headless Argo CD allows to installation and use of lightweight Argo CD that includes only the backend without exposing the API or UI. -The Headless Argo CD provides a better experience to users who need only core Argo CD features and don't want to deal with multi-tenancy features. - -## v2.2 and beyond +## v2.3 and beyond ### Application Details Page Usability @@ -73,8 +68,12 @@ A lot of Argo CD features are still not available in GitOps engine. The followin ## Completed +### ✅ Core Argo CD ([proposal](https://github.com/argoproj/argo-cd/pull/6385)) + +Core Argo CD allows to installation and use of lightweight Argo CD that includes only the backend without exposing the API or UI. +The Core Argo CD provides a better experience to users who need only core Argo CD features and don't want to deal with multi-tenancy features. -### Core Functionality Bug Fixes +### ✅ Core Functionality Bug Fixes The core GitOps features still have several known bugs and limitations. The full list is available in [v1.9 milestone]( https://github.com/argoproj/argo-cd/issues?q=is%3Aopen+is%3Aissue+label%3Abug+milestone%3A%22v1.9%22+label%3Acomponent%3Acore) @@ -83,7 +82,7 @@ The most notable issues: * [Argo CD synchronization lasts incredibly long](https://github.com/argoproj/argo-cd/issues/3663) -### Performance +### ✅ Performance * 2000+ Applications support. The user interface becomes notably slower if one Argo CD instance manages more than 1 thousand applications. A set of optimizations is required to fix that issue. @@ -93,7 +92,7 @@ Currently Argo CD controller is unable to handle that many clusters. The solutio * Mono Repository support. Argo CD is not optimized for mono repositories with a large number of applications. With 50+ applications in the same repository, manifest generation performance drops significantly. The repository server optimization is required to improve it. -### ApplicationSet +### ✅ ApplicationSet Argo CD Applications allow splitting the cluster configuration into logic groups that are managed independently. However, the set of applications is a configuration that should be managed declaratively as well. The app-of-apps pattern solves this problem but still has some challenges such as @@ -101,30 +100,30 @@ maintenance overhead, security, and lack of some additional features. [ApplicationSet](https://github.com/argoproj-labs/applicationset) project provides a better solution for managing applications across multiple environments. -### Large Applications support +### ✅ Large Applications support The application details page is not suitable to visualize applications that include a large number of resources (hundreds of resources). The page has to be reworked to improve user experience. -### Serviceability +### ✅ Serviceability To make Argo CD successful we need to build tools that enable Argo CD administrators to handle scalability and performance issues in a self-service model. That includes more metrics, out of the box alerts and a cluster management user interface. -### Argo CD Notifications +### ✅ Argo CD Notifications [Argo CD Notifications](https://github.com/argoproj-labs/argocd-notifications) provides the ability to notify users about Argo CD Application changes as well as implement integrations such as update GitHub commit status, trigger Jenkins job, set Grafana label, etc. -### Automated Registry Monitoring +### ✅ Automated Registry Monitoring [Argo CD Image Updater](https://github.com/argoproj-labs/argocd-image-updater) provides an ability to monitor Docker registries and automatically update image versions in the deployment repository. See [https://github.com/argoproj/argo-cd/issues/1648](https://github.com/argoproj/argo-cd/issues/1648). -### Projects Enhancements +### ✅ Projects Enhancements Argo CD projects accumulated a lot of debt: diff --git a/docs/security_considerations.md b/docs/security_considerations.md index 8ce2c49e59664..936cb135545ab 100644 --- a/docs/security_considerations.md +++ b/docs/security_considerations.md @@ -25,7 +25,7 @@ no fix yet. |2020-04-08|[CVE-2020-11576](https://nvd.nist.gov/vuln/detail/CVE-2020-11576)|User Enumeration|Medium|v1.5.0|v1.5.1| |2020-04-08|[CVE-2020-8826](https://nvd.nist.gov/vuln/detail/CVE-2020-8826)|Session-fixation|High|all|n/a| |2020-04-08|[CVE-2020-8827](https://nvd.nist.gov/vuln/detail/CVE-2020-8827)|Insufficient anti-automation/anti-brute force|High|all <= 1.5.3|v1.5.3| -|2020-04-08|[CVE-2020-8828](https://nvd.nist.gov/vuln/detail/CVE-2020-8828)|Insecure default administrative password|High|all|n/a| +|2020-04-08|[CVE-2020-8828](https://nvd.nist.gov/vuln/detail/CVE-2020-8828)|Insecure default administrative password|High|all <= 1.8.0|1.8.0| |2020-04-08|[CVE-2018-21034](https://nvd.nist.gov/vuln/detail/CVE-2018-21034)|Sensitive Information Disclosure|Medium|all <= v1.5.0|v1.5.0| ## Known Issues And Workarounds @@ -105,7 +105,7 @@ Upgrade to ArgoCD v1.5.1 or higher. As a workaround, disable local users and use |Risk|Reported by|Fix version|Workaround| |----|-----------|-----------|----------| -|High|[Matt Hamilton](https://github.com/Eriner) of [https://soluble.ai](https://soluble.ai)|n/a|Yes| +|High|[Matt Hamilton](https://github.com/Eriner) of [https://soluble.ai](https://soluble.ai)|1.8.0|Yes| **Details:** diff --git a/docs/user-guide/commands/argocd.md b/docs/user-guide/commands/argocd.md index 5f520853c4cc9..0c6bf881f2c5a 100644 --- a/docs/user-guide/commands/argocd.md +++ b/docs/user-guide/commands/argocd.md @@ -13,6 +13,7 @@ argocd [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) @@ -31,6 +32,7 @@ argocd [flags] ### SEE ALSO * [argocd account](argocd_account.md) - Manage account settings +* [argocd admin](argocd_admin.md) - Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access * [argocd app](argocd_app.md) - Manage applications * [argocd cert](argocd_cert.md) - Manage repository certificates and SSH known hosts entries * [argocd cluster](argocd_cluster.md) - Manage cluster credentials diff --git a/docs/user-guide/commands/argocd_account.md b/docs/user-guide/commands/argocd_account.md index 26b30904f92e7..ea837a841fdc5 100644 --- a/docs/user-guide/commands/argocd_account.md +++ b/docs/user-guide/commands/argocd_account.md @@ -9,7 +9,23 @@ argocd account [flags] ### Options ``` - -h, --help help for account + --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. + --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 + -h, --help help for account + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 @@ -19,6 +35,7 @@ argocd account [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_account_can-i.md b/docs/user-guide/commands/argocd_account_can-i.md index 396e4f23e730b..e8bd52c3c3272 100644 --- a/docs/user-guide/commands/argocd_account_can-i.md +++ b/docs/user-guide/commands/argocd_account_can-i.md @@ -37,6 +37,7 @@ Resources: [clusters projects applications repositories certificates] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_account_delete-token.md b/docs/user-guide/commands/argocd_account_delete-token.md index f879d719b802a..90d64a4ac780c 100644 --- a/docs/user-guide/commands/argocd_account_delete-token.md +++ b/docs/user-guide/commands/argocd_account_delete-token.md @@ -13,7 +13,7 @@ argocd account delete-token [flags] argocd account delete-token ID # Delete token of the account with the specified name -argocd account generate-token --account +argocd account delete-token --account ID ``` ### Options @@ -30,6 +30,7 @@ argocd account generate-token --account --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_account_generate-token.md b/docs/user-guide/commands/argocd_account_generate-token.md index 6a8e3d975f74f..b00108bc838eb 100644 --- a/docs/user-guide/commands/argocd_account_generate-token.md +++ b/docs/user-guide/commands/argocd_account_generate-token.md @@ -32,6 +32,7 @@ argocd account generate-token --account --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 061c3ccf620b1..af1071e374492 100644 --- a/docs/user-guide/commands/argocd_account_get-user-info.md +++ b/docs/user-guide/commands/argocd_account_get-user-info.md @@ -20,6 +20,7 @@ argocd account get-user-info [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_account_get.md b/docs/user-guide/commands/argocd_account_get.md index 1a6a310a2bf18..e1dcc3d279e72 100644 --- a/docs/user-guide/commands/argocd_account_get.md +++ b/docs/user-guide/commands/argocd_account_get.md @@ -31,6 +31,7 @@ argocd account get --account --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_account_list.md b/docs/user-guide/commands/argocd_account_list.md index 6216b57011233..53ca139151c51 100644 --- a/docs/user-guide/commands/argocd_account_list.md +++ b/docs/user-guide/commands/argocd_account_list.md @@ -26,6 +26,7 @@ argocd account list --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_account_update-password.md b/docs/user-guide/commands/argocd_account_update-password.md index 0083fd2828133..bd32405c91753 100644 --- a/docs/user-guide/commands/argocd_account_update-password.md +++ b/docs/user-guide/commands/argocd_account_update-password.md @@ -22,6 +22,7 @@ argocd account update-password [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_admin.md b/docs/user-guide/commands/argocd_admin.md new file mode 100644 index 0000000000000..3e70ef202a219 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin.md @@ -0,0 +1,48 @@ +## argocd admin + +Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access + +``` +argocd admin [flags] +``` + +### Options + +``` + -h, --help help for admin +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd](argocd.md) - argocd controls a Argo CD server +* [argocd admin app](argocd_admin_app.md) - Manage applications configuration +* [argocd admin cluster](argocd_admin_cluster.md) - Manage clusters configuration +* [argocd admin dashboard](argocd_admin_dashboard.md) - Starts Argo CD Web UI locally +* [argocd admin export](argocd_admin_export.md) - Export all Argo CD data to stdout (default) or a file +* [argocd admin import](argocd_admin_import.md) - Import Argo CD data from stdin (specify `-') or a file +* [argocd admin proj](argocd_admin_proj.md) - Manage projects configuration +* [argocd admin repo](argocd_admin_repo.md) - Manage repositories configuration +* [argocd admin settings](argocd_admin_settings.md) - Provides set of commands for settings validation and troubleshooting + diff --git a/docs/user-guide/commands/argocd_admin_app.md b/docs/user-guide/commands/argocd_admin_app.md new file mode 100644 index 0000000000000..e8ccb35bc2543 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_app.md @@ -0,0 +1,43 @@ +## argocd admin app + +Manage applications configuration + +``` +argocd admin app [flags] +``` + +### Options + +``` + -h, --help help for app +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin](argocd_admin.md) - Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access +* [argocd admin app diff-reconcile-results](argocd_admin_app_diff-reconcile-results.md) - Compare results of two reconciliations and print diff. +* [argocd admin app generate-spec](argocd_admin_app_generate-spec.md) - Generate declarative config for an application +* [argocd admin app get-reconcile-results](argocd_admin_app_get-reconcile-results.md) - Reconcile all applications and stores reconciliation summary in the specified file. + 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 new file mode 100644 index 0000000000000..887a1b2ce01d7 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_app_diff-reconcile-results.md @@ -0,0 +1,40 @@ +## argocd admin app diff-reconcile-results + +Compare results of two reconciliations and print diff. + +``` +argocd admin app diff-reconcile-results PATH1 PATH2 [flags] +``` + +### Options + +``` + -h, --help help for diff-reconcile-results +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin app](argocd_admin_app.md) - Manage applications configuration + diff --git a/docs/operator-manual/server-commands/argocd-util_app_generate-spec.md b/docs/user-guide/commands/argocd_admin_app_generate-spec.md similarity index 63% rename from docs/operator-manual/server-commands/argocd-util_app_generate-spec.md rename to docs/user-guide/commands/argocd_admin_app_generate-spec.md index c1d3cf8ffbfd2..b8a35a693f4ff 100644 --- a/docs/operator-manual/server-commands/argocd-util_app_generate-spec.md +++ b/docs/user-guide/commands/argocd_admin_app_generate-spec.md @@ -1,9 +1,9 @@ -## argocd-util app generate-spec +## argocd admin app generate-spec Generate declarative config for an application ``` -argocd-util app generate-spec APPNAME [flags] +argocd admin app generate-spec APPNAME [flags] ``` ### Examples @@ -11,22 +11,22 @@ argocd-util app generate-spec APPNAME [flags] ``` # Generate declarative config for a directory app - argocd-util app generate-spec guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --directory-recurse + argocd admin app generate-spec guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --directory-recurse # Generate declarative config for a Jsonnet app - argocd-util app generate-spec jsonnet-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path jsonnet-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --jsonnet-ext-str replicas=2 + argocd admin app generate-spec jsonnet-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path jsonnet-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --jsonnet-ext-str replicas=2 # Generate declarative config for a Helm app - argocd-util app generate-spec helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2 + argocd admin app generate-spec helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2 # Generate declarative config for a Helm app from a Helm repo - argocd-util app generate-spec nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc + argocd admin app generate-spec nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc # Generate declarative config for a Kustomize app - argocd-util app generate-spec kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1 + argocd admin app generate-spec 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 # Generate declarative config for a app using a custom tool: - argocd-util app generate-spec ksane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane + argocd admin app generate-spec ksane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane ``` @@ -51,6 +51,7 @@ argocd-util app generate-spec APPNAME [flags] --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-version string Helm version -h, --help help for generate-spec + -i, --inline If set then generated resource is written back to the file specified in --file flag --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) @@ -87,7 +88,29 @@ argocd-util app generate-spec APPNAME [flags] --values-literal-file string Filename or URL to import as a literal Helm values block ``` +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + ### SEE ALSO -* [argocd-util app](argocd-util_app.md) - Manage applications configuration +* [argocd admin app](argocd_admin_app.md) - Manage applications configuration diff --git a/docs/operator-manual/server-commands/argocd-util_app_get-reconcile-results.md b/docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md similarity index 53% rename from docs/operator-manual/server-commands/argocd-util_app_get-reconcile-results.md rename to docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md index 286b2a7adda8b..fb3f0878f8950 100644 --- a/docs/operator-manual/server-commands/argocd-util_app_get-reconcile-results.md +++ b/docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md @@ -1,9 +1,9 @@ -## argocd-util app get-reconcile-results +## argocd admin app get-reconcile-results Reconcile all applications and stores reconciliation summary in the specified file. ``` -argocd-util app get-reconcile-results PATH [flags] +argocd admin app get-reconcile-results PATH [flags] ``` ### Options @@ -26,14 +26,35 @@ argocd-util app get-reconcile-results PATH [flags] --refresh If set to true then recalculates apps reconciliation --repo-server string Repo server address. --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server --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 + +``` + --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/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + ### SEE ALSO -* [argocd-util app](argocd-util_app.md) - Manage applications configuration +* [argocd admin app](argocd_admin_app.md) - Manage applications configuration diff --git a/docs/user-guide/commands/argocd_admin_cluster.md b/docs/user-guide/commands/argocd_admin_cluster.md new file mode 100644 index 0000000000000..7e75059641420 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_cluster.md @@ -0,0 +1,45 @@ +## argocd admin cluster + +Manage clusters configuration + +``` +argocd admin cluster [flags] +``` + +### Options + +``` + -h, --help help for cluster +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin](argocd_admin.md) - Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access +* [argocd admin cluster generate-spec](argocd_admin_cluster_generate-spec.md) - Generate declarative config for a cluster +* [argocd admin cluster kubeconfig](argocd_admin_cluster_kubeconfig.md) - Generates kubeconfig for the specified cluster +* [argocd admin cluster namespaces](argocd_admin_cluster_namespaces.md) - Print information namespaces which Argo CD manages in each cluster. +* [argocd admin cluster shards](argocd_admin_cluster_shards.md) - Print information about each controller shard and portion of Kubernetes resources it is responsible for. +* [argocd admin cluster stats](argocd_admin_cluster_stats.md) - Prints information cluster statistics and inferred shard number + diff --git a/docs/user-guide/commands/argocd_admin_cluster_generate-spec.md b/docs/user-guide/commands/argocd_admin_cluster_generate-spec.md new file mode 100644 index 0000000000000..d7735a5ea6d19 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_cluster_generate-spec.md @@ -0,0 +1,61 @@ +## argocd admin cluster generate-spec + +Generate declarative config for a cluster + +``` +argocd admin cluster generate-spec CONTEXT [flags] +``` + +### Options + +``` + --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-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-resources Indicates if cluster level resources should be managed. The setting is used only if list of managed namespaces is not empty. + --exec-command string Command to run to provide client credentials to the cluster. You may need to build a custom ArgoCD image to ensure the command is available at runtime. + --exec-command-api-version string Preferred input version of the ExecInfo for the --exec-command executable + --exec-command-args stringArray Arguments to supply to the --exec-command executable + --exec-command-env stringToString Environment vars to set when running the --exec-command executable (default []) + --exec-command-install-hint string Text shown to the user when the --exec-command executable doesn't seem to be present + --generate-bearer-token Generate authentication token that should be used to access K8S API server + -h, --help help for generate-spec + --in-cluster Indicates Argo CD resides inside this cluster and should connect using the internal k8s hostname (kubernetes.default.svc) + --kubeconfig string use a particular kubeconfig file + --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 + -o, --output string Output format. One of: json|yaml (default "yaml") + --project string project of the cluster + --service-account string System namespace service account to use for kubernetes resource management. If not set then default "argocd-manager" SA will be used (default "argocd-manager") + --shard int Cluster shard number; inferred from hostname if not set (default -1) + --system-namespace string Use different system namespace (default "kube-system") +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin cluster](argocd_admin_cluster.md) - Manage clusters configuration + diff --git a/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md b/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md new file mode 100644 index 0000000000000..bc6163ecc617f --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md @@ -0,0 +1,56 @@ +## argocd admin cluster kubeconfig + +Generates kubeconfig for the specified cluster + +``` +argocd admin cluster kubeconfig CLUSTER_URL OUTPUT_PATH [flags] +``` + +### Options + +``` + --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. + --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 + -h, --help help for kubeconfig + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 + +``` + --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/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin cluster](argocd_admin_cluster.md) - Manage clusters configuration + diff --git a/docs/user-guide/commands/argocd_admin_cluster_namespaces.md b/docs/user-guide/commands/argocd_admin_cluster_namespaces.md new file mode 100644 index 0000000000000..aa9588d04d165 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_cluster_namespaces.md @@ -0,0 +1,58 @@ +## argocd admin cluster namespaces + +Print information namespaces which Argo CD manages in each cluster. + +``` +argocd admin cluster namespaces [flags] +``` + +### Options + +``` + --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. + --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 + -h, --help help for namespaces + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 + +``` + --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/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin cluster](argocd_admin_cluster.md) - Manage clusters configuration +* [argocd admin cluster namespaces disable-namespaced-mode](argocd_admin_cluster_namespaces_disable-namespaced-mode.md) - Disable namespaced mode for clusters which name matches to the specified pattern. +* [argocd admin cluster namespaces enable-namespaced-mode](argocd_admin_cluster_namespaces_enable-namespaced-mode.md) - Enable namespaced mode for clusters which name matches to the specified pattern. + 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 new file mode 100644 index 0000000000000..7a6d1f4b1484c --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_cluster_namespaces_disable-namespaced-mode.md @@ -0,0 +1,57 @@ +## argocd admin cluster namespaces disable-namespaced-mode + +Disable namespaced mode for clusters which name matches to the specified pattern. + +``` +argocd admin cluster namespaces disable-namespaced-mode PATTERN [flags] +``` + +### Options + +``` + --dry-run Print what will be performed (default true) + -h, --help help for disable-namespaced-mode +``` + +### Options inherited from parent commands + +``` + --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. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --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") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin cluster namespaces](argocd_admin_cluster_namespaces.md) - Print information namespaces which Argo CD manages in each cluster. + 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 new file mode 100644 index 0000000000000..b726e86075dea --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_cluster_namespaces_enable-namespaced-mode.md @@ -0,0 +1,59 @@ +## argocd admin cluster namespaces enable-namespaced-mode + +Enable namespaced mode for clusters which name matches to the specified pattern. + +``` +argocd admin cluster namespaces enable-namespaced-mode PATTERN [flags] +``` + +### Options + +``` + --cluster-resources Indicates if cluster level resources should be managed. + --dry-run Print what will be performed (default true) + -h, --help help for enable-namespaced-mode + --max-namespace-count int Max number of namespaces that cluster should managed managed namespaces is less or equal to specified count +``` + +### Options inherited from parent commands + +``` + --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. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --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") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin cluster namespaces](argocd_admin_cluster_namespaces.md) - Print information namespaces which Argo CD manages in each cluster. + diff --git a/docs/operator-manual/server-commands/argocd-util_cluster_shards.md b/docs/user-guide/commands/argocd_admin_cluster_shards.md similarity index 54% rename from docs/operator-manual/server-commands/argocd-util_cluster_shards.md rename to docs/user-guide/commands/argocd_admin_cluster_shards.md index ffe8e7c3bb292..7be827370654a 100644 --- a/docs/operator-manual/server-commands/argocd-util_cluster_shards.md +++ b/docs/user-guide/commands/argocd_admin_cluster_shards.md @@ -1,9 +1,9 @@ -## argocd-util cluster shards +## argocd admin cluster shards Print information about each controller shard and portion of Kubernetes resources it is responsible for. ``` -argocd-util cluster shards [flags] +argocd admin cluster shards [flags] ``` ### Options @@ -25,12 +25,16 @@ argocd-util cluster shards [flags] --password string Password for basic authentication to the API server --port-forward-redis Automatically port-forward ha proxy redis from current namespace? (default true) --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-insecure-skip-tls-verify Skip Redis server certificate validation. + --redis-use-tls Use TLS when connecting to Redis. --redisdb int Redis database. --replicas int Application controller replicas count. Inferred from number of running controller pods if not specified --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") --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 --shard int Cluster shard filter (default -1) --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 @@ -38,7 +42,29 @@ argocd-util cluster shards [flags] --username string Username for basic authentication to the API server ``` +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + ### SEE ALSO -* [argocd-util cluster](argocd-util_cluster.md) - Manage clusters configuration +* [argocd admin cluster](argocd_admin_cluster.md) - Manage clusters configuration diff --git a/docs/operator-manual/server-commands/argocd-util_cluster_stats.md b/docs/user-guide/commands/argocd_admin_cluster_stats.md similarity index 54% rename from docs/operator-manual/server-commands/argocd-util_cluster_stats.md rename to docs/user-guide/commands/argocd_admin_cluster_stats.md index 2271a7576a420..2321837040429 100644 --- a/docs/operator-manual/server-commands/argocd-util_cluster_stats.md +++ b/docs/user-guide/commands/argocd_admin_cluster_stats.md @@ -1,9 +1,9 @@ -## argocd-util cluster stats +## argocd admin cluster stats Prints information cluster statistics and inferred shard number ``` -argocd-util cluster stats [flags] +argocd admin cluster stats [flags] ``` ### Options @@ -25,12 +25,16 @@ argocd-util cluster stats [flags] --password string Password for basic authentication to the API server --port-forward-redis Automatically port-forward ha proxy redis from current namespace? (default true) --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-insecure-skip-tls-verify Skip Redis server certificate validation. + --redis-use-tls Use TLS when connecting to Redis. --redisdb int Redis database. --replicas int Application controller replicas count. Inferred from number of running controller pods if not specified --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") --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 --shard int Cluster shard filter (default -1) --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 @@ -38,7 +42,29 @@ argocd-util cluster stats [flags] --username string Username for basic authentication to the API server ``` +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + ### SEE ALSO -* [argocd-util cluster](argocd-util_cluster.md) - Manage clusters configuration +* [argocd admin cluster](argocd_admin_cluster.md) - Manage clusters configuration diff --git a/docs/user-guide/commands/argocd_admin_dashboard.md b/docs/user-guide/commands/argocd_admin_dashboard.md new file mode 100644 index 0000000000000..d7760b2289990 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_dashboard.md @@ -0,0 +1,57 @@ +## argocd admin dashboard + +Starts Argo CD Web UI locally + +``` +argocd admin dashboard [flags] +``` + +### Options + +``` + --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. + --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 + -h, --help help for dashboard + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --port int Listen on given port (default 8080) + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --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 + +``` + --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/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin](argocd_admin.md) - Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access + diff --git a/docs/operator-manual/server-commands/argocd-util_export.md b/docs/user-guide/commands/argocd_admin_export.md similarity index 50% rename from docs/operator-manual/server-commands/argocd-util_export.md rename to docs/user-guide/commands/argocd_admin_export.md index b52a1fc048a76..2ee86732f0111 100644 --- a/docs/operator-manual/server-commands/argocd-util_export.md +++ b/docs/user-guide/commands/argocd_admin_export.md @@ -1,9 +1,9 @@ -## argocd-util export +## argocd admin export Export all Argo CD data to stdout (default) or a file ``` -argocd-util export [flags] +argocd admin export [flags] ``` ### Options @@ -23,14 +23,35 @@ argocd-util export [flags] -o, --out string Output to the specified file instead of stdout (default "-") --password string Password for basic authentication to the API server --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server --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 + +``` + --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/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + ### SEE ALSO -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD +* [argocd admin](argocd_admin.md) - Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access diff --git a/docs/operator-manual/server-commands/argocd-util_import.md b/docs/user-guide/commands/argocd_admin_import.md similarity index 52% rename from docs/operator-manual/server-commands/argocd-util_import.md rename to docs/user-guide/commands/argocd_admin_import.md index 7174769b491e4..4b2e06693c4c5 100644 --- a/docs/operator-manual/server-commands/argocd-util_import.md +++ b/docs/user-guide/commands/argocd_admin_import.md @@ -1,9 +1,9 @@ -## argocd-util import +## argocd admin import Import Argo CD data from stdin (specify `-') or a file ``` -argocd-util import SOURCE [flags] +argocd admin import SOURCE [flags] ``` ### Options @@ -24,7 +24,6 @@ argocd-util import SOURCE [flags] --password string Password for basic authentication to the API server --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 --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 @@ -32,7 +31,29 @@ argocd-util import SOURCE [flags] --verbose Verbose output (versus only changed output) ``` +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + ### SEE ALSO -* [argocd-util](argocd-util.md) - argocd-util tools used by Argo CD +* [argocd admin](argocd_admin.md) - Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access diff --git a/docs/user-guide/commands/argocd_admin_proj.md b/docs/user-guide/commands/argocd_admin_proj.md new file mode 100644 index 0000000000000..1a414fded3d1b --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_proj.md @@ -0,0 +1,43 @@ +## argocd admin proj + +Manage projects configuration + +``` +argocd admin proj [flags] +``` + +### Options + +``` + -h, --help help for proj +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin](argocd_admin.md) - Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access +* [argocd admin proj generate-allow-list](argocd_admin_proj_generate-allow-list.md) - Generates project allow list from the specified clusterRole file +* [argocd admin proj generate-spec](argocd_admin_proj_generate-spec.md) - Generate declarative config for a project +* [argocd admin proj update-role-policy](argocd_admin_proj_update-role-policy.md) - Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions. + diff --git a/docs/operator-manual/server-commands/argocd-util_projects_generate-allow-list.md b/docs/user-guide/commands/argocd_admin_proj_generate-allow-list.md similarity index 50% rename from docs/operator-manual/server-commands/argocd-util_projects_generate-allow-list.md rename to docs/user-guide/commands/argocd_admin_proj_generate-allow-list.md index f78512bd7a71f..cefe7787cf135 100644 --- a/docs/operator-manual/server-commands/argocd-util_projects_generate-allow-list.md +++ b/docs/user-guide/commands/argocd_admin_proj_generate-allow-list.md @@ -1,9 +1,9 @@ -## argocd-util projects generate-allow-list +## argocd admin proj generate-allow-list Generates project allow list from the specified clusterRole file ``` -argocd-util projects generate-allow-list CLUSTERROLE_PATH PROJ_NAME [flags] +argocd admin proj generate-allow-list CLUSTERROLE_PATH PROJ_NAME [flags] ``` ### Options @@ -23,14 +23,35 @@ argocd-util projects generate-allow-list CLUSTERROLE_PATH PROJ_NAME [flags] -o, --out string Output to the specified file instead of stdout (default "-") --password string Password for basic authentication to the API server --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server --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 + +``` + --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/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + ### SEE ALSO -* [argocd-util projects](argocd-util_projects.md) - Utility commands operate on ArgoCD Projects +* [argocd admin proj](argocd_admin_proj.md) - Manage projects configuration diff --git a/docs/user-guide/commands/argocd_admin_proj_generate-spec.md b/docs/user-guide/commands/argocd_admin_proj_generate-spec.md new file mode 100644 index 0000000000000..2876a965df162 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_proj_generate-spec.md @@ -0,0 +1,53 @@ +## argocd admin proj generate-spec + +Generate declarative config for a project + +``` +argocd admin proj generate-spec PROJECT [flags] +``` + +### Options + +``` + --allow-cluster-resource stringArray List of allowed cluster level resources + --allow-namespaced-resource stringArray List of allowed namespaced resources + --deny-cluster-resource stringArray List of denied cluster level resources + --deny-namespaced-resource stringArray List of denied namespaced resources + --description string Project description + -d, --dest stringArray Permitted destination server and namespace (e.g. https://192.168.99.100:8443,default) + -f, --file string Filename or URL to Kubernetes manifests for the project + -h, --help help for generate-spec + -i, --inline If set then generated resource is written back to the file specified in --file flag + --orphaned-resources Enables orphaned resources monitoring + --orphaned-resources-warn Specifies if applications should have a warning condition when orphaned resources detected + -o, --output string Output format. One of: json|yaml (default "yaml") + --signature-keys strings GnuPG public key IDs for commit signature verification + -s, --src stringArray Permitted source repository URL +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin proj](argocd_admin_proj.md) - Manage projects configuration + diff --git a/docs/operator-manual/server-commands/argocd-util_proj_update-role-policy.md b/docs/user-guide/commands/argocd_admin_proj_update-role-policy.md similarity index 54% rename from docs/operator-manual/server-commands/argocd-util_proj_update-role-policy.md rename to docs/user-guide/commands/argocd_admin_proj_update-role-policy.md index 56506b7927f3f..9df8693e9ac32 100644 --- a/docs/operator-manual/server-commands/argocd-util_proj_update-role-policy.md +++ b/docs/user-guide/commands/argocd_admin_proj_update-role-policy.md @@ -1,19 +1,19 @@ -## argocd-util proj update-role-policy +## argocd admin proj update-role-policy Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions. ``` -argocd-util proj update-role-policy PROJECT_GLOB MODIFICATION ACTION [flags] +argocd admin proj update-role-policy PROJECT_GLOB MODIFICATION ACTION [flags] ``` ### Examples ``` # Add policy that allows executing any action (action/*) to roles which name matches to *deployer* in all projects - argocd-util projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow + argocd admin projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow # Remove policy that which manages running (action/*) from all roles which name matches *deployer* in all projects - argocd-util projects update-role-policy '*' remove override --role '*deployer*' + argocd admin projects update-role-policy '*' remove override --role '*deployer*' ``` @@ -38,14 +38,35 @@ argocd-util proj update-role-policy PROJECT_GLOB MODIFICATION ACTION [flags] --resource string Resource e.g. 'applications' --role string Role name pattern e.g. '*deployer*' (default "*") --scope string Resource scope e.g. '*' - --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 + +``` + --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/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + ### SEE ALSO -* [argocd-util proj](argocd-util_proj.md) - Manage projects configuration +* [argocd admin proj](argocd_admin_proj.md) - Manage projects configuration diff --git a/docs/user-guide/commands/argocd_admin_repo.md b/docs/user-guide/commands/argocd_admin_repo.md new file mode 100644 index 0000000000000..75251bb0771de --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_repo.md @@ -0,0 +1,41 @@ +## argocd admin repo + +Manage repositories configuration + +``` +argocd admin repo [flags] +``` + +### Options + +``` + -h, --help help for repo +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin](argocd_admin.md) - Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access +* [argocd admin repo generate-spec](argocd_admin_repo_generate-spec.md) - Generate declarative config for a repo + diff --git a/docs/user-guide/commands/argocd_admin_repo_generate-spec.md b/docs/user-guide/commands/argocd_admin_repo_generate-spec.md new file mode 100644 index 0000000000000..1cd7f0776515f --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_repo_generate-spec.md @@ -0,0 +1,85 @@ +## argocd admin repo generate-spec + +Generate declarative config for a repo + +``` +argocd admin repo generate-spec REPOURL [flags] +``` + +### Examples + +``` + + # 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 + + # Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here + argocd admin repo generate-spec ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa + + # Add a private Git repository via HTTPS using username/password and TLS client certificates: + argocd admin repo generate-spec https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key + + # Add a private Git repository via HTTPS using username/password without verifying the server's TLS certificate + argocd admin repo generate-spec https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification + + # Add a public Helm repository named 'stable' via HTTPS + argocd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable + + # Add a private Helm repository named 'stable' via HTTPS + argocd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable --username test --password test + + # Add a private Helm OCI-based repository named 'stable' via HTTPS + argocd admin repo generate-spec helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test + +``` + +### Options + +``` + --enable-lfs enable git-lfs (Large File Support) on this repository + --enable-oci enable helm-oci (Helm OCI-Based Repository) + --github-app-enterprise-base-url string base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3 + --github-app-id int id of the GitHub Application + --github-app-installation-id int installation id of the GitHub Application + --github-app-private-key-path string private key of the GitHub Application + -h, --help help for generate-spec + --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 + -o, --output string Output format. One of: json|yaml (default "yaml") + --password string password to the repository + --project string project of the repository + --proxy string use proxy to access repository + --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) + --type string type of the repository, "git" or "helm" (default "git") + --username string username to the repository +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin repo](argocd_admin_repo.md) - Manage repositories configuration + diff --git a/docs/user-guide/commands/argocd_admin_settings.md b/docs/user-guide/commands/argocd_admin_settings.md new file mode 100644 index 0000000000000..b4bebd240957a --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings.md @@ -0,0 +1,62 @@ +## argocd admin settings + +Provides set of commands for settings validation and troubleshooting + +``` +argocd admin settings [flags] +``` + +### Options + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --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 + -h, --help help for settings + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 + +``` + --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/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd admin](argocd_admin.md) - Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access +* [argocd admin settings rbac](argocd_admin_settings_rbac.md) - Validate and test RBAC configuration +* [argocd admin settings resource-overrides](argocd_admin_settings_resource-overrides.md) - Troubleshoot resource overrides +* [argocd admin settings validate](argocd_admin_settings_validate.md) - Validate settings + diff --git a/docs/user-guide/commands/argocd_admin_settings_rbac.md b/docs/user-guide/commands/argocd_admin_settings_rbac.md new file mode 100644 index 0000000000000..353637e187641 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings_rbac.md @@ -0,0 +1,61 @@ +## argocd admin settings rbac + +Validate and test RBAC configuration + +``` +argocd admin settings rbac [flags] +``` + +### Options + +``` + -h, --help help for rbac +``` + +### Options inherited from parent commands + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin settings](argocd_admin_settings.md) - Provides set of commands for settings validation and troubleshooting +* [argocd admin settings rbac can](argocd_admin_settings_rbac_can.md) - Check RBAC permissions for a role or subject +* [argocd admin settings rbac validate](argocd_admin_settings_rbac_validate.md) - Validate RBAC policy + diff --git a/docs/user-guide/commands/argocd_admin_settings_rbac_can.md b/docs/user-guide/commands/argocd_admin_settings_rbac_can.md new file mode 100644 index 0000000000000..b9dd22dc94dad --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings_rbac_can.md @@ -0,0 +1,94 @@ +## argocd admin settings rbac can + +Check RBAC permissions for a role or subject + +### Synopsis + + +Check whether a given role or subject has appropriate RBAC permissions to do +something. + + +``` +argocd admin settings rbac can ROLE/SUBJECT ACTION RESOURCE [SUB-RESOURCE] [flags] +``` + +### Examples + +``` + +# Check whether role some:role has permissions to create an application in the +# 'default' project, using a local policy.csv file +argocd admin settings rbac can some:role create application 'default/app' --policy-file policy.csv + +# Policy file can also be K8s config map with data keys like argocd-rbac-cm, +# i.e. 'policy.csv' and (optionally) 'policy.default' +argocd admin settings rbac can some:role create application 'default/app' --policy-file argocd-rbac-cm.yaml + +# If --policy-file is not given, the ConfigMap 'argocd-rbac-cm' from K8s is +# used. You need to specify the argocd namespace, and make sure that your +# current Kubernetes context is pointing to the cluster Argo CD is running in +argocd admin settings rbac can some:role create application 'default/app' --namespace argocd + +# You can override a possibly configured default role +argocd admin settings rbac can someuser create application 'default/app' --default-role role:readonly + + +``` + +### Options + +``` + --default-role string name of the default role to use + -h, --help help for can + --policy-file string path to the policy file to use + -q, --quiet quiet mode - do not print results to stdout + --strict whether to perform strict check on action and resource names (default true) + --use-builtin-policy whether to also use builtin-policy (default true) +``` + +### Options inherited from parent commands + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin settings rbac](argocd_admin_settings_rbac.md) - Validate and test RBAC configuration + diff --git a/docs/user-guide/commands/argocd_admin_settings_rbac_validate.md b/docs/user-guide/commands/argocd_admin_settings_rbac_validate.md new file mode 100644 index 0000000000000..f15a1bbc94657 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings_rbac_validate.md @@ -0,0 +1,67 @@ +## argocd admin settings rbac validate + +Validate RBAC policy + +### Synopsis + + +Validates an RBAC policy for being syntactically correct. The policy must be +a local file, and in either CSV or K8s ConfigMap format. + + +``` +argocd admin settings rbac validate --policy-file=POLICYFILE [flags] +``` + +### Options + +``` + -h, --help help for validate + --policy-file string path to the policy file to use +``` + +### Options inherited from parent commands + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin settings rbac](argocd_admin_settings_rbac.md) - Validate and test RBAC configuration + diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides.md new file mode 100644 index 0000000000000..4ab2b14407d09 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides.md @@ -0,0 +1,63 @@ +## argocd admin settings resource-overrides + +Troubleshoot resource overrides + +``` +argocd admin settings resource-overrides [flags] +``` + +### Options + +``` + -h, --help help for resource-overrides +``` + +### Options inherited from parent commands + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin settings](argocd_admin_settings.md) - Provides set of commands for settings validation and troubleshooting +* [argocd admin settings resource-overrides health](argocd_admin_settings_resource-overrides_health.md) - Assess resource health +* [argocd admin settings resource-overrides ignore-differences](argocd_admin_settings_resource-overrides_ignore-differences.md) - Renders fields excluded from diffing +* [argocd admin settings resource-overrides list-actions](argocd_admin_settings_resource-overrides_list-actions.md) - List available resource actions +* [argocd admin settings resource-overrides run-action](argocd_admin_settings_resource-overrides_run-action.md) - Executes resource action + diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_health.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_health.md new file mode 100644 index 0000000000000..6f33c8f7ae5cc --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_health.md @@ -0,0 +1,70 @@ +## argocd admin settings resource-overrides health + +Assess resource health + +### Synopsis + +Assess resource health using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap + +``` +argocd admin settings resource-overrides health RESOURCE_YAML_PATH [flags] +``` + +### Examples + +``` + +argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml +``` + +### Options + +``` + -h, --help help for health +``` + +### Options inherited from parent commands + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin settings resource-overrides](argocd_admin_settings_resource-overrides.md) - Troubleshoot resource overrides + diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-differences.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-differences.md new file mode 100644 index 0000000000000..694e81ff9198d --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-differences.md @@ -0,0 +1,70 @@ +## argocd admin settings resource-overrides ignore-differences + +Renders fields excluded from diffing + +### Synopsis + +Renders ignored fields using the 'ignoreDifferences' setting specified in the 'resource.customizations' field of 'argocd-cm' ConfigMap + +``` +argocd admin settings resource-overrides ignore-differences RESOURCE_YAML_PATH [flags] +``` + +### Examples + +``` + +argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml +``` + +### Options + +``` + -h, --help help for ignore-differences +``` + +### Options inherited from parent commands + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin settings resource-overrides](argocd_admin_settings_resource-overrides.md) - Troubleshoot resource overrides + diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_list-actions.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_list-actions.md new file mode 100644 index 0000000000000..2556eae3e4d7c --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_list-actions.md @@ -0,0 +1,70 @@ +## argocd admin settings resource-overrides list-actions + +List available resource actions + +### Synopsis + +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 + +``` +argocd admin settings resource-overrides list-actions RESOURCE_YAML_PATH [flags] +``` + +### Examples + +``` + +argocd admin settings resource-overrides action list /tmp/deploy.yaml --argocd-cm-path ./argocd-cm.yaml +``` + +### Options + +``` + -h, --help help for list-actions +``` + +### Options inherited from parent commands + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin settings resource-overrides](argocd_admin_settings_resource-overrides.md) - Troubleshoot resource overrides + diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_run-action.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_run-action.md new file mode 100644 index 0000000000000..7563ddf8fbc35 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_run-action.md @@ -0,0 +1,70 @@ +## argocd admin settings resource-overrides run-action + +Executes resource action + +### Synopsis + +Executes resource action using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields + +``` +argocd admin settings resource-overrides run-action RESOURCE_YAML_PATH ACTION [flags] +``` + +### Examples + +``` + +argocd admin settings resource-overrides action run /tmp/deploy.yaml restart --argocd-cm-path ./argocd-cm.yaml +``` + +### Options + +``` + -h, --help help for run-action +``` + +### Options inherited from parent commands + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin settings resource-overrides](argocd_admin_settings_resource-overrides.md) - Troubleshoot resource overrides + diff --git a/docs/user-guide/commands/argocd_admin_settings_validate.md b/docs/user-guide/commands/argocd_admin_settings_validate.md new file mode 100644 index 0000000000000..61db807478717 --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings_validate.md @@ -0,0 +1,75 @@ +## argocd admin settings validate + +Validate settings + +### Synopsis + +Validates settings specified in 'argocd-cm' ConfigMap and 'argocd-secret' Secret + +``` +argocd admin settings validate [flags] +``` + +### Examples + +``` + +#Validates all settings in the specified YAML file +argocd admin settings validate --argocd-cm-path ./argocd-cm.yaml + +#Validates accounts and plugins settings in Kubernetes cluster of current kubeconfig context +argocd admin settings validate --group accounts --group plugins --load-cluster-settings +``` + +### Options + +``` + --group stringArray Optional list of setting groups that have to be validated ( one of: accounts, general, kustomize, plugins, repositories, resource-overrides) + -h, --help help for validate +``` + +### Options inherited from parent commands + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --context string The name of the kubeconfig context to use + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin settings](argocd_admin_settings.md) - Provides set of commands for settings validation and troubleshooting + diff --git a/docs/user-guide/commands/argocd_app.md b/docs/user-guide/commands/argocd_app.md index 17dc3fac88557..4a5c968320b71 100644 --- a/docs/user-guide/commands/argocd_app.md +++ b/docs/user-guide/commands/argocd_app.md @@ -22,7 +22,23 @@ argocd app [flags] ### Options ``` - -h, --help help for app + --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. + --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 + -h, --help help for app + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 @@ -32,6 +48,7 @@ argocd app [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) @@ -52,6 +69,7 @@ argocd app [flags] * [argocd app actions](argocd_app_actions.md) - Manage Resource actions * [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 * [argocd app diff](argocd_app_diff.md) - Perform a diff against the target and live state. * [argocd app edit](argocd_app_edit.md) - Edit application * [argocd app get](argocd_app_get.md) - Get application details @@ -62,7 +80,7 @@ argocd app [flags] * [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 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 +* [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 * [argocd app sync](argocd_app_sync.md) - Sync an application to its target state * [argocd app terminate-op](argocd_app_terminate-op.md) - Terminate running operation of an application diff --git a/docs/user-guide/commands/argocd_app_actions.md b/docs/user-guide/commands/argocd_app_actions.md index 5cb30ff0021d1..c2f556a82fe9e 100644 --- a/docs/user-guide/commands/argocd_app_actions.md +++ b/docs/user-guide/commands/argocd_app_actions.md @@ -19,6 +19,7 @@ argocd app actions [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_actions_list.md b/docs/user-guide/commands/argocd_app_actions_list.md index abbd51bd912df..c4fb92fff101d 100644 --- a/docs/user-guide/commands/argocd_app_actions_list.md +++ b/docs/user-guide/commands/argocd_app_actions_list.md @@ -24,6 +24,7 @@ argocd app actions list APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_actions_run.md b/docs/user-guide/commands/argocd_app_actions_run.md index 0222b103a9654..2d158939ea31e 100644 --- a/docs/user-guide/commands/argocd_app_actions_run.md +++ b/docs/user-guide/commands/argocd_app_actions_run.md @@ -24,6 +24,7 @@ argocd app actions run APPNAME ACTION [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_create.md b/docs/user-guide/commands/argocd_app_create.md index f27dd00747921..3c329accd99bc 100644 --- a/docs/user-guide/commands/argocd_app_create.md +++ b/docs/user-guide/commands/argocd_app_create.md @@ -94,6 +94,7 @@ argocd app create APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_delete-resource.md b/docs/user-guide/commands/argocd_app_delete-resource.md new file mode 100644 index 0000000000000..61990c0d30d9f --- /dev/null +++ b/docs/user-guide/commands/argocd_app_delete-resource.md @@ -0,0 +1,47 @@ +## argocd app delete-resource + +Delete resource in an application + +``` +argocd app delete-resource APPNAME [flags] +``` + +### Options + +``` + --all Indicates whether to patch multiple matching of resources + --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 force delete the resource + --resource-name string Name of resource +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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 + --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 + --server string Argo CD server address + --server-crt string Server certificate file +``` + +### SEE ALSO + +* [argocd app](argocd_app.md) - Manage applications + diff --git a/docs/user-guide/commands/argocd_app_delete.md b/docs/user-guide/commands/argocd_app_delete.md index 808e63557a0cb..8a7f77d11d81e 100644 --- a/docs/user-guide/commands/argocd_app_delete.md +++ b/docs/user-guide/commands/argocd_app_delete.md @@ -22,6 +22,7 @@ argocd app delete APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_diff.md b/docs/user-guide/commands/argocd_app_diff.md index 16ea0eb6260b4..7029c29b29e9d 100644 --- a/docs/user-guide/commands/argocd_app_diff.md +++ b/docs/user-guide/commands/argocd_app_diff.md @@ -31,6 +31,7 @@ argocd app diff APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_edit.md b/docs/user-guide/commands/argocd_app_edit.md index 2b9e721c48ce7..7c1a5a6820148 100644 --- a/docs/user-guide/commands/argocd_app_edit.md +++ b/docs/user-guide/commands/argocd_app_edit.md @@ -19,6 +19,7 @@ argocd app edit APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_get.md b/docs/user-guide/commands/argocd_app_get.md index 1773ba6a39163..74aa9f2ebdd64 100644 --- a/docs/user-guide/commands/argocd_app_get.md +++ b/docs/user-guide/commands/argocd_app_get.md @@ -24,6 +24,7 @@ argocd app get APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_history.md b/docs/user-guide/commands/argocd_app_history.md index 0dd9ab7f4973c..0f90315d6f3b7 100644 --- a/docs/user-guide/commands/argocd_app_history.md +++ b/docs/user-guide/commands/argocd_app_history.md @@ -20,6 +20,7 @@ argocd app history APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_list.md b/docs/user-guide/commands/argocd_app_list.md index 7dd30e3d10aba..bf0829557a047 100644 --- a/docs/user-guide/commands/argocd_app_list.md +++ b/docs/user-guide/commands/argocd_app_list.md @@ -33,6 +33,7 @@ argocd app list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_logs.md b/docs/user-guide/commands/argocd_app_logs.md index 11e8900f7c99c..0ee66d229d822 100644 --- a/docs/user-guide/commands/argocd_app_logs.md +++ b/docs/user-guide/commands/argocd_app_logs.md @@ -29,6 +29,7 @@ argocd app logs APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_manifests.md b/docs/user-guide/commands/argocd_app_manifests.md index e57d00ec80a78..c207b3a136926 100644 --- a/docs/user-guide/commands/argocd_app_manifests.md +++ b/docs/user-guide/commands/argocd_app_manifests.md @@ -21,6 +21,7 @@ argocd app manifests APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_patch-resource.md b/docs/user-guide/commands/argocd_app_patch-resource.md index 03af5af0aabbf..37fc0088824cd 100644 --- a/docs/user-guide/commands/argocd_app_patch-resource.md +++ b/docs/user-guide/commands/argocd_app_patch-resource.md @@ -26,6 +26,7 @@ argocd app patch-resource APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_patch.md b/docs/user-guide/commands/argocd_app_patch.md index 0b975fd702008..b942ad88a7627 100644 --- a/docs/user-guide/commands/argocd_app_patch.md +++ b/docs/user-guide/commands/argocd_app_patch.md @@ -30,6 +30,7 @@ argocd app patch APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_resources.md b/docs/user-guide/commands/argocd_app_resources.md index 935beb4204b5d..c2f0b1127c28e 100644 --- a/docs/user-guide/commands/argocd_app_resources.md +++ b/docs/user-guide/commands/argocd_app_resources.md @@ -20,6 +20,7 @@ argocd app resources APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_rollback.md b/docs/user-guide/commands/argocd_app_rollback.md index de0e100218774..a3f4b0dcc7658 100644 --- a/docs/user-guide/commands/argocd_app_rollback.md +++ b/docs/user-guide/commands/argocd_app_rollback.md @@ -1,9 +1,9 @@ ## argocd app rollback -Rollback application to a previous deployed version by History ID +Rollback application to a previous deployed version by History ID, omitted will Rollback to the previous version ``` -argocd app rollback APPNAME ID [flags] +argocd app rollback APPNAME [ID] [flags] ``` ### Options @@ -21,6 +21,7 @@ argocd app rollback APPNAME ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_set.md b/docs/user-guide/commands/argocd_app_set.md index 177ad2d5cf628..be4a2665ce5a5 100644 --- a/docs/user-guide/commands/argocd_app_set.md +++ b/docs/user-guide/commands/argocd_app_set.md @@ -65,6 +65,7 @@ argocd app set APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_sync.md b/docs/user-guide/commands/argocd_app_sync.md index 08aae1d685b4a..b1450018a508c 100644 --- a/docs/user-guide/commands/argocd_app_sync.md +++ b/docs/user-guide/commands/argocd_app_sync.md @@ -56,6 +56,7 @@ argocd app sync [APPNAME... | -l selector] [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_terminate-op.md b/docs/user-guide/commands/argocd_app_terminate-op.md index a1fbb98e1798a..26476986b0697 100644 --- a/docs/user-guide/commands/argocd_app_terminate-op.md +++ b/docs/user-guide/commands/argocd_app_terminate-op.md @@ -19,6 +19,7 @@ argocd app terminate-op APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_unset.md b/docs/user-guide/commands/argocd_app_unset.md index 678f19c328d2d..38fc11947169b 100644 --- a/docs/user-guide/commands/argocd_app_unset.md +++ b/docs/user-guide/commands/argocd_app_unset.md @@ -40,6 +40,7 @@ argocd app unset APPNAME parameters [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_app_wait.md b/docs/user-guide/commands/argocd_app_wait.md index e9e2950e15872..ba98e6e1b6ecd 100644 --- a/docs/user-guide/commands/argocd_app_wait.md +++ b/docs/user-guide/commands/argocd_app_wait.md @@ -39,6 +39,7 @@ argocd app wait [APPNAME.. | -l selector] [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cert.md b/docs/user-guide/commands/argocd_cert.md index 075a453049dd5..80fa88e006256 100644 --- a/docs/user-guide/commands/argocd_cert.md +++ b/docs/user-guide/commands/argocd_cert.md @@ -32,7 +32,23 @@ argocd cert [flags] ### Options ``` - -h, --help help for cert + --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. + --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 + -h, --help help for cert + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 @@ -42,6 +58,7 @@ argocd cert [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cert_add-ssh.md b/docs/user-guide/commands/argocd_cert_add-ssh.md index 339443114f8c5..f547a2c3cbd3c 100644 --- a/docs/user-guide/commands/argocd_cert_add-ssh.md +++ b/docs/user-guide/commands/argocd_cert_add-ssh.md @@ -22,6 +22,7 @@ argocd cert add-ssh --batch [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cert_add-tls.md b/docs/user-guide/commands/argocd_cert_add-tls.md index 2c81da22b1196..b697ad01b479c 100644 --- a/docs/user-guide/commands/argocd_cert_add-tls.md +++ b/docs/user-guide/commands/argocd_cert_add-tls.md @@ -21,6 +21,7 @@ argocd cert add-tls SERVERNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cert_list.md b/docs/user-guide/commands/argocd_cert_list.md index ad387e30e5808..cedf81838ae66 100644 --- a/docs/user-guide/commands/argocd_cert_list.md +++ b/docs/user-guide/commands/argocd_cert_list.md @@ -23,6 +23,7 @@ argocd cert list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cert_rm.md b/docs/user-guide/commands/argocd_cert_rm.md index a987b941edd59..d6b94dc439fae 100644 --- a/docs/user-guide/commands/argocd_cert_rm.md +++ b/docs/user-guide/commands/argocd_cert_rm.md @@ -21,6 +21,7 @@ argocd cert rm REPOSERVER [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cluster.md b/docs/user-guide/commands/argocd_cluster.md index 7f31d1d413a0f..4094fd0cbe379 100644 --- a/docs/user-guide/commands/argocd_cluster.md +++ b/docs/user-guide/commands/argocd_cluster.md @@ -26,7 +26,23 @@ argocd cluster [flags] ### Options ``` - -h, --help help for cluster + --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. + --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 + -h, --help help for cluster + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 @@ -36,6 +52,7 @@ argocd cluster [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cluster_add.md b/docs/user-guide/commands/argocd_cluster_add.md index 0497449e3c702..f6c724310a6e8 100644 --- a/docs/user-guide/commands/argocd_cluster_add.md +++ b/docs/user-guide/commands/argocd_cluster_add.md @@ -9,8 +9,10 @@ argocd cluster add CONTEXT [flags] ### Options ``` + --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-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-resources Indicates if cluster level resources should be managed. The setting is used only if list of managed namespaces is not empty. --exec-command string Command to run to provide client credentials to the cluster. You may need to build a custom ArgoCD image to ensure the command is available at runtime. --exec-command-api-version string Preferred input version of the ExecInfo for the --exec-command executable --exec-command-args stringArray Arguments to supply to the --exec-command executable @@ -19,8 +21,10 @@ argocd cluster add CONTEXT [flags] -h, --help help for add --in-cluster Indicates Argo CD resides inside this cluster and should connect using the internal k8s hostname (kubernetes.default.svc) --kubeconfig string use a particular kubeconfig file + --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 + --project string project of the cluster --service-account string System namespace service account to use for kubernetes resource management. If not set then default "argocd-manager" SA will be created --shard int Cluster shard number; inferred from hostname if not set (default -1) --system-namespace string Use different system namespace (default "kube-system") @@ -35,6 +39,7 @@ argocd cluster add CONTEXT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cluster_get.md b/docs/user-guide/commands/argocd_cluster_get.md index 68648e5321785..16efd684b8530 100644 --- a/docs/user-guide/commands/argocd_cluster_get.md +++ b/docs/user-guide/commands/argocd_cluster_get.md @@ -3,13 +3,14 @@ Get cluster information ``` -argocd cluster get SERVER [flags] +argocd cluster get SERVER/NAME [flags] ``` ### Examples ``` argocd cluster get https://12.34.567.89 +argocd cluster get in-cluster ``` ### Options @@ -26,6 +27,7 @@ argocd cluster get https://12.34.567.89 --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cluster_list.md b/docs/user-guide/commands/argocd_cluster_list.md index 1441c9f3b2960..e2e5a6d3f45a3 100644 --- a/docs/user-guide/commands/argocd_cluster_list.md +++ b/docs/user-guide/commands/argocd_cluster_list.md @@ -20,6 +20,7 @@ argocd cluster list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cluster_rm.md b/docs/user-guide/commands/argocd_cluster_rm.md index 5fb8387d7e513..d5f392df7a406 100644 --- a/docs/user-guide/commands/argocd_cluster_rm.md +++ b/docs/user-guide/commands/argocd_cluster_rm.md @@ -25,6 +25,7 @@ argocd cluster rm https://12.34.567.89 --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_cluster_rotate-auth.md b/docs/user-guide/commands/argocd_cluster_rotate-auth.md index e102b7ae439bc..25008948b4d3f 100644 --- a/docs/user-guide/commands/argocd_cluster_rotate-auth.md +++ b/docs/user-guide/commands/argocd_cluster_rotate-auth.md @@ -25,6 +25,7 @@ argocd cluster rotate-auth https://12.34.567.89 --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_completion.md b/docs/user-guide/commands/argocd_completion.md index 2758d1dbf8f20..1a6e0f6de7248 100644 --- a/docs/user-guide/commands/argocd_completion.md +++ b/docs/user-guide/commands/argocd_completion.md @@ -32,6 +32,7 @@ argocd completion SHELL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_context.md b/docs/user-guide/commands/argocd_context.md index 34aca27044b86..30933e0bb9849 100644 --- a/docs/user-guide/commands/argocd_context.md +++ b/docs/user-guide/commands/argocd_context.md @@ -20,6 +20,7 @@ argocd context [CONTEXT] [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_gpg.md b/docs/user-guide/commands/argocd_gpg.md index 800db6c49b4d7..b4c8d1bfd694f 100644 --- a/docs/user-guide/commands/argocd_gpg.md +++ b/docs/user-guide/commands/argocd_gpg.md @@ -9,7 +9,23 @@ argocd gpg [flags] ### Options ``` - -h, --help help for gpg + --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. + --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 + -h, --help help for gpg + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 @@ -19,6 +35,7 @@ argocd gpg [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_gpg_add.md b/docs/user-guide/commands/argocd_gpg_add.md index 9f0a4cfe8e2a1..bfcec5aa6b0f9 100644 --- a/docs/user-guide/commands/argocd_gpg_add.md +++ b/docs/user-guide/commands/argocd_gpg_add.md @@ -20,6 +20,7 @@ argocd gpg add [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_gpg_get.md b/docs/user-guide/commands/argocd_gpg_get.md index 855666bca4f11..37ef516d4ff14 100644 --- a/docs/user-guide/commands/argocd_gpg_get.md +++ b/docs/user-guide/commands/argocd_gpg_get.md @@ -20,6 +20,7 @@ argocd gpg get KEYID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_gpg_list.md b/docs/user-guide/commands/argocd_gpg_list.md index bec370d61efa1..51748d7f53a13 100644 --- a/docs/user-guide/commands/argocd_gpg_list.md +++ b/docs/user-guide/commands/argocd_gpg_list.md @@ -20,6 +20,7 @@ argocd gpg list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_gpg_rm.md b/docs/user-guide/commands/argocd_gpg_rm.md index 2e7c4fa3fa9b6..1d54f5b87a240 100644 --- a/docs/user-guide/commands/argocd_gpg_rm.md +++ b/docs/user-guide/commands/argocd_gpg_rm.md @@ -19,6 +19,7 @@ argocd gpg rm KEYID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_login.md b/docs/user-guide/commands/argocd_login.md index 1327c605e6329..5fcf9946fc452 100644 --- a/docs/user-guide/commands/argocd_login.md +++ b/docs/user-guide/commands/argocd_login.md @@ -10,6 +10,19 @@ Log in to Argo CD argocd login SERVER [flags] ``` +### Examples + +``` +# Login to Argo CD using a username and password +argocd login cd.argoproj.io + +# Login to Argo CD using SSO +argocd login cd.argoproj.io --sso + +# Configure direct access using Kubernetes API server +argocd login cd.argoproj.io --core +``` + ### Options ``` @@ -28,6 +41,7 @@ argocd login SERVER [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_logout.md b/docs/user-guide/commands/argocd_logout.md index 551e51754111b..3dc5119d3dc05 100644 --- a/docs/user-guide/commands/argocd_logout.md +++ b/docs/user-guide/commands/argocd_logout.md @@ -23,6 +23,7 @@ argocd logout CONTEXT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj.md b/docs/user-guide/commands/argocd_proj.md index 12b69f7c55c3d..0df0fc2a122a4 100644 --- a/docs/user-guide/commands/argocd_proj.md +++ b/docs/user-guide/commands/argocd_proj.md @@ -9,7 +9,23 @@ argocd proj [flags] ### Options ``` - -h, --help help for proj + --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. + --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 + -h, --help help for proj + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 @@ -19,6 +35,7 @@ argocd proj [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_add-destination.md b/docs/user-guide/commands/argocd_proj_add-destination.md index 2871bd207208f..e66530ee5dddf 100644 --- a/docs/user-guide/commands/argocd_proj_add-destination.md +++ b/docs/user-guide/commands/argocd_proj_add-destination.md @@ -19,6 +19,7 @@ argocd proj add-destination PROJECT SERVER NAMESPACE [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 716f23c169ac8..16e4f82b0f5e5 100644 --- a/docs/user-guide/commands/argocd_proj_add-orphaned-ignore.md +++ b/docs/user-guide/commands/argocd_proj_add-orphaned-ignore.md @@ -20,6 +20,7 @@ argocd proj add-orphaned-ignore PROJECT GROUP KIND [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 28ecb0bd4b766..3344d2b947ee0 100644 --- a/docs/user-guide/commands/argocd_proj_add-signature-key.md +++ b/docs/user-guide/commands/argocd_proj_add-signature-key.md @@ -19,6 +19,7 @@ argocd proj add-signature-key PROJECT KEY-ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_add-source.md b/docs/user-guide/commands/argocd_proj_add-source.md index ffed41a5f1386..6dacef481e07e 100644 --- a/docs/user-guide/commands/argocd_proj_add-source.md +++ b/docs/user-guide/commands/argocd_proj_add-source.md @@ -19,6 +19,7 @@ argocd proj add-source PROJECT URL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 7cf6990b79c43..7b3286ecb2ef6 100644 --- a/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md +++ b/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md @@ -20,6 +20,7 @@ argocd proj allow-cluster-resource PROJECT GROUP KIND [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 1bef140bcd61b..e1faa64826421 100644 --- a/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md +++ b/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md @@ -20,6 +20,7 @@ argocd proj allow-namespace-resource PROJECT GROUP KIND [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_create.md b/docs/user-guide/commands/argocd_proj_create.md index 4a3f104b9970f..636ad87e16dba 100644 --- a/docs/user-guide/commands/argocd_proj_create.md +++ b/docs/user-guide/commands/argocd_proj_create.md @@ -31,6 +31,7 @@ argocd proj create PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_delete.md b/docs/user-guide/commands/argocd_proj_delete.md index 875a6b0c4ae9e..514c149b6a893 100644 --- a/docs/user-guide/commands/argocd_proj_delete.md +++ b/docs/user-guide/commands/argocd_proj_delete.md @@ -19,6 +19,7 @@ argocd proj delete PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 abe0cdd340bf2..6e281ddbbf7ad 100644 --- a/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md +++ b/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md @@ -20,6 +20,7 @@ argocd proj deny-cluster-resource PROJECT GROUP KIND [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 6a9c2d5ceb292..314c3b8a93e7c 100644 --- a/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md +++ b/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md @@ -20,6 +20,7 @@ argocd proj deny-namespace-resource PROJECT GROUP KIND [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_edit.md b/docs/user-guide/commands/argocd_proj_edit.md index 5822307d5909b..2707af1c4eedb 100644 --- a/docs/user-guide/commands/argocd_proj_edit.md +++ b/docs/user-guide/commands/argocd_proj_edit.md @@ -19,6 +19,7 @@ argocd proj edit PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_get.md b/docs/user-guide/commands/argocd_proj_get.md index 654430ad396f9..91f9a8d795e34 100644 --- a/docs/user-guide/commands/argocd_proj_get.md +++ b/docs/user-guide/commands/argocd_proj_get.md @@ -20,6 +20,7 @@ argocd proj get PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_list.md b/docs/user-guide/commands/argocd_proj_list.md index fe59a37066765..c678635684628 100644 --- a/docs/user-guide/commands/argocd_proj_list.md +++ b/docs/user-guide/commands/argocd_proj_list.md @@ -20,6 +20,7 @@ argocd proj list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_remove-destination.md b/docs/user-guide/commands/argocd_proj_remove-destination.md index 9c7fd23a30c42..1bcc83abb2db6 100644 --- a/docs/user-guide/commands/argocd_proj_remove-destination.md +++ b/docs/user-guide/commands/argocd_proj_remove-destination.md @@ -19,6 +19,7 @@ argocd proj remove-destination PROJECT SERVER NAMESPACE [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 32207a5672efb..cd7e4b52b7951 100644 --- a/docs/user-guide/commands/argocd_proj_remove-orphaned-ignore.md +++ b/docs/user-guide/commands/argocd_proj_remove-orphaned-ignore.md @@ -20,6 +20,7 @@ argocd proj remove-orphaned-ignore PROJECT GROUP KIND NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 c344aa1eb5ce2..61bc7a0fcc72d 100644 --- a/docs/user-guide/commands/argocd_proj_remove-signature-key.md +++ b/docs/user-guide/commands/argocd_proj_remove-signature-key.md @@ -19,6 +19,7 @@ argocd proj remove-signature-key PROJECT KEY-ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_remove-source.md b/docs/user-guide/commands/argocd_proj_remove-source.md index 074aee9168e2b..93d7b2b4c2c11 100644 --- a/docs/user-guide/commands/argocd_proj_remove-source.md +++ b/docs/user-guide/commands/argocd_proj_remove-source.md @@ -19,6 +19,7 @@ argocd proj remove-source PROJECT URL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_role.md b/docs/user-guide/commands/argocd_proj_role.md index 6611b24628ebc..9f8ec9854f047 100644 --- a/docs/user-guide/commands/argocd_proj_role.md +++ b/docs/user-guide/commands/argocd_proj_role.md @@ -19,6 +19,7 @@ argocd proj role [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 1b273f6a50c9b..ba35936732018 100644 --- a/docs/user-guide/commands/argocd_proj_role_add-group.md +++ b/docs/user-guide/commands/argocd_proj_role_add-group.md @@ -19,6 +19,7 @@ argocd proj role add-group PROJECT ROLE-NAME GROUP-CLAIM [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 630f14d83fee9..82da1176c236f 100644 --- a/docs/user-guide/commands/argocd_proj_role_add-policy.md +++ b/docs/user-guide/commands/argocd_proj_role_add-policy.md @@ -22,6 +22,7 @@ argocd proj role add-policy PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 727517d548cb3..a0a9704d9cf0f 100644 --- a/docs/user-guide/commands/argocd_proj_role_create-token.md +++ b/docs/user-guide/commands/argocd_proj_role_create-token.md @@ -22,6 +22,7 @@ argocd proj role create-token PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_role_create.md b/docs/user-guide/commands/argocd_proj_role_create.md index 44e3c4be67638..61539301e6588 100644 --- a/docs/user-guide/commands/argocd_proj_role_create.md +++ b/docs/user-guide/commands/argocd_proj_role_create.md @@ -20,6 +20,7 @@ argocd proj role create PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 982ff3cf58c1b..a67b57391f50f 100644 --- a/docs/user-guide/commands/argocd_proj_role_delete-token.md +++ b/docs/user-guide/commands/argocd_proj_role_delete-token.md @@ -19,6 +19,7 @@ argocd proj role delete-token PROJECT ROLE-NAME ISSUED-AT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_role_delete.md b/docs/user-guide/commands/argocd_proj_role_delete.md index 3e98f66682163..d642b5b4b8d1d 100644 --- a/docs/user-guide/commands/argocd_proj_role_delete.md +++ b/docs/user-guide/commands/argocd_proj_role_delete.md @@ -19,6 +19,7 @@ argocd proj role delete PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_role_get.md b/docs/user-guide/commands/argocd_proj_role_get.md index dd01c397e20ff..f8dc3f9367482 100644 --- a/docs/user-guide/commands/argocd_proj_role_get.md +++ b/docs/user-guide/commands/argocd_proj_role_get.md @@ -19,6 +19,7 @@ argocd proj role get PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 dc1f124c4e6ca..043ebd3c71b4e 100644 --- a/docs/user-guide/commands/argocd_proj_role_list-tokens.md +++ b/docs/user-guide/commands/argocd_proj_role_list-tokens.md @@ -20,6 +20,7 @@ argocd proj role list-tokens PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_role_list.md b/docs/user-guide/commands/argocd_proj_role_list.md index 102bee7cd6f98..194c46a4fae9a 100644 --- a/docs/user-guide/commands/argocd_proj_role_list.md +++ b/docs/user-guide/commands/argocd_proj_role_list.md @@ -20,6 +20,7 @@ argocd proj role list PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 e2ed21e31cd13..f0cc407ff9332 100644 --- a/docs/user-guide/commands/argocd_proj_role_remove-group.md +++ b/docs/user-guide/commands/argocd_proj_role_remove-group.md @@ -19,6 +19,7 @@ argocd proj role remove-group PROJECT ROLE-NAME GROUP-CLAIM [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 614651afea2c2..3cefc34aa693b 100644 --- a/docs/user-guide/commands/argocd_proj_role_remove-policy.md +++ b/docs/user-guide/commands/argocd_proj_role_remove-policy.md @@ -22,6 +22,7 @@ argocd proj role remove-policy PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_set.md b/docs/user-guide/commands/argocd_proj_set.md index 35185c0c74bbd..cc45bcfdcd147 100644 --- a/docs/user-guide/commands/argocd_proj_set.md +++ b/docs/user-guide/commands/argocd_proj_set.md @@ -29,6 +29,7 @@ argocd proj set PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_windows.md b/docs/user-guide/commands/argocd_proj_windows.md index 2a4b6524bf831..0453c5a01bec2 100644 --- a/docs/user-guide/commands/argocd_proj_windows.md +++ b/docs/user-guide/commands/argocd_proj_windows.md @@ -19,6 +19,7 @@ argocd proj windows [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_windows_add.md b/docs/user-guide/commands/argocd_proj_windows_add.md index af84a2f0ec914..08454e979139e 100644 --- a/docs/user-guide/commands/argocd_proj_windows_add.md +++ b/docs/user-guide/commands/argocd_proj_windows_add.md @@ -26,6 +26,7 @@ argocd proj windows add PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_windows_delete.md b/docs/user-guide/commands/argocd_proj_windows_delete.md index 860ebacf2a7d5..0d8dc42d13baa 100644 --- a/docs/user-guide/commands/argocd_proj_windows_delete.md +++ b/docs/user-guide/commands/argocd_proj_windows_delete.md @@ -19,6 +19,7 @@ argocd proj windows delete PROJECT ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 5c2d253bb66a0..b02640c6b2aff 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 @@ -23,6 +23,7 @@ argocd proj windows disable-manual-sync PROJECT ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) 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 4ce939f15d83f..78158312cec64 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 @@ -23,6 +23,7 @@ argocd proj windows enable-manual-sync PROJECT ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_windows_list.md b/docs/user-guide/commands/argocd_proj_windows_list.md index 1b83370a04b33..12004e6572159 100644 --- a/docs/user-guide/commands/argocd_proj_windows_list.md +++ b/docs/user-guide/commands/argocd_proj_windows_list.md @@ -20,6 +20,7 @@ argocd proj windows list PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_proj_windows_update.md b/docs/user-guide/commands/argocd_proj_windows_update.md index b3ec2ebd50e65..f8bbd398e2868 100644 --- a/docs/user-guide/commands/argocd_proj_windows_update.md +++ b/docs/user-guide/commands/argocd_proj_windows_update.md @@ -28,6 +28,7 @@ argocd proj windows update PROJECT ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_relogin.md b/docs/user-guide/commands/argocd_relogin.md index 6b2141f8173d0..3a42d4cd48374 100644 --- a/docs/user-guide/commands/argocd_relogin.md +++ b/docs/user-guide/commands/argocd_relogin.md @@ -25,6 +25,7 @@ argocd relogin [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_repo.md b/docs/user-guide/commands/argocd_repo.md index 8bb9defab71f7..68f6ef629f20e 100644 --- a/docs/user-guide/commands/argocd_repo.md +++ b/docs/user-guide/commands/argocd_repo.md @@ -9,7 +9,23 @@ argocd repo [flags] ### Options ``` - -h, --help help for repo + --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. + --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 + -h, --help help for repo + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 @@ -19,6 +35,7 @@ argocd repo [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_repo_add.md b/docs/user-guide/commands/argocd_repo_add.md index 6ad72661fc90b..864842d034340 100644 --- a/docs/user-guide/commands/argocd_repo_add.md +++ b/docs/user-guide/commands/argocd_repo_add.md @@ -52,6 +52,7 @@ argocd repo add REPOURL [flags] --insecure-skip-server-verification disables server certificate and host key checks --name string name of the repository, mandatory for repositories of type helm --password string password to the repository + --project string project of the repository --proxy string use proxy to access repository --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) @@ -68,6 +69,7 @@ argocd repo add REPOURL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_repo_get.md b/docs/user-guide/commands/argocd_repo_get.md index 962fa0a5c4d5f..44eddacc10daa 100644 --- a/docs/user-guide/commands/argocd_repo_get.md +++ b/docs/user-guide/commands/argocd_repo_get.md @@ -21,6 +21,7 @@ argocd repo get [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_repo_list.md b/docs/user-guide/commands/argocd_repo_list.md index 2adadea6bff2c..cc53b8bfc5a02 100644 --- a/docs/user-guide/commands/argocd_repo_list.md +++ b/docs/user-guide/commands/argocd_repo_list.md @@ -21,6 +21,7 @@ argocd repo list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_repo_rm.md b/docs/user-guide/commands/argocd_repo_rm.md index 2c5891ddcabec..77d7ada653ec0 100644 --- a/docs/user-guide/commands/argocd_repo_rm.md +++ b/docs/user-guide/commands/argocd_repo_rm.md @@ -19,6 +19,7 @@ argocd repo rm REPO [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_repocreds.md b/docs/user-guide/commands/argocd_repocreds.md index d4e0d54387a4d..d18a3f3cc9f76 100644 --- a/docs/user-guide/commands/argocd_repocreds.md +++ b/docs/user-guide/commands/argocd_repocreds.md @@ -9,7 +9,23 @@ argocd repocreds [flags] ### Options ``` - -h, --help help for repocreds + --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. + --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 + -h, --help help for repocreds + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --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 @@ -19,6 +35,7 @@ argocd repocreds [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_repocreds_add.md b/docs/user-guide/commands/argocd_repocreds_add.md index c4b6a5d623573..4902fa94a3046 100644 --- a/docs/user-guide/commands/argocd_repocreds_add.md +++ b/docs/user-guide/commands/argocd_repocreds_add.md @@ -21,6 +21,9 @@ argocd repocreds add REPOURL [flags] # Add credentials with GitHub App authentication to use for all repositories under https://ghe.example.com/repos argocd repocreds add https://ghe.example.com/repos/ --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem --github-app-enterprise-base-url https://ghe.example.com/api/v3 + # Add credentials with helm oci registry so that these oci registry urls do not need to be added as repos individually. + argocd repocreds add localhost:5000/myrepo --enable-oci --type helm + ``` ### Options @@ -48,6 +51,7 @@ argocd repocreds add REPOURL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_repocreds_list.md b/docs/user-guide/commands/argocd_repocreds_list.md index e538dab9d6471..4eb0d781c2c99 100644 --- a/docs/user-guide/commands/argocd_repocreds_list.md +++ b/docs/user-guide/commands/argocd_repocreds_list.md @@ -20,6 +20,7 @@ argocd repocreds list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_repocreds_rm.md b/docs/user-guide/commands/argocd_repocreds_rm.md index f017bf33fcb8f..2784c13642581 100644 --- a/docs/user-guide/commands/argocd_repocreds_rm.md +++ b/docs/user-guide/commands/argocd_repocreds_rm.md @@ -19,6 +19,7 @@ argocd repocreds rm CREDSURL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/commands/argocd_version.md b/docs/user-guide/commands/argocd_version.md index c8debc2f3100d..74b44bbe945ba 100644 --- a/docs/user-guide/commands/argocd_version.md +++ b/docs/user-guide/commands/argocd_version.md @@ -26,10 +26,26 @@ argocd version [flags] ### Options ``` - --client client version only (no server required) - -h, --help help for version - -o, --output string Output format. One of: json|yaml|wide|short (default "wide") - --short print just the version number + --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. + --certificate-authority string Path to a cert file for the certificate authority + --client client version only (no server required) + --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 + -h, --help help for version + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + -n, --namespace string If present, the namespace scope for this CLI request + -o, --output string Output format. One of: json|yaml|wide|short (default "wide") + --password string Password for basic authentication to the API server + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --short print just the version number + --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 @@ -39,6 +55,7 @@ argocd version [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.argocd/config") + --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) diff --git a/docs/user-guide/diffing.md b/docs/user-guide/diffing.md index e5c3620dec813..4e650131d56f8 100644 --- a/docs/user-guide/diffing.md +++ b/docs/user-guide/diffing.md @@ -61,11 +61,9 @@ of a `MutatingWebhookConfiguration` webhooks: ```yaml data: - resource.customizations: | - admissionregistration.k8s.io/MutatingWebhookConfiguration: - ignoreDifferences: | - jqPathExpressions: - - '.webhooks[]?.clientConfig.caBundle' + resource.customizations.ignoreDifferences.admissionregistration.k8s.io_MutatingWebhookConfiguration: | + jqPathExpressions: + - '.webhooks[]?.clientConfig.caBundle' ``` The `status` field of `CustomResourceDefinitions` is often stored in Git/Helm manifest and should be ignored during diffing. The `ignoreResourceStatusField` setting simplifies @@ -119,11 +117,9 @@ metadata: app.kubernetes.io/name: argocd-cm app.kubernetes.io/part-of: argocd data: - resource.customizations: | - argoproj.io/Rollout: - knownTypeFields: - - field: spec.template.spec - type: core/v1/PodSpec + resource.customizations.knownTypeFields.argoproj.io_Rollout: | + - field: spec.template.spec + type: core/v1/PodSpec ``` The list of supported Kubernetes types is available in [diffing_known_types.txt](https://raw.githubusercontent.com/argoproj/argo-cd/master/util/argo/normalizers/diffing_known_types.txt) diff --git a/docs/user-guide/helm.md b/docs/user-guide/helm.md index ff3abe9892fe7..e7660c6d0d394 100644 --- a/docs/user-guide/helm.md +++ b/docs/user-guide/helm.md @@ -1,5 +1,28 @@ # Helm +## Declarative + +You can install Helm charts through the UI, or in the declarative GitOps way. Here is an example: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: sealed-secrets + namespace: argocd +spec: + project: default + source: + chart: sealed-secrets + repoURL: https://bitnami-labs.github.io/sealed-secrets + targetRevision: 1.16.1 + helm: + releaseName: sealed-secrets + destination: + server: https://kubernetes.default.svc + namespace: kubeseal +``` + ## Values Files Helm has the ability to use a different, or even multiple "values.yaml" files to derive its diff --git a/docs/user-guide/kustomize.md b/docs/user-guide/kustomize.md index 77cc64358cd2a..537ea2764b163 100644 --- a/docs/user-guide/kustomize.md +++ b/docs/user-guide/kustomize.md @@ -5,9 +5,9 @@ The following configuration options are available for Kustomize: * `namePrefix` is a prefix appended to resources for Kustomize apps * `nameSuffix` is a suffix appended to resources for Kustomize apps * `images` is a list of Kustomize image overrides -* `commonLabels` is a string map of an additional labels -* `commonAnnotations` is a string map of an additional annotations - +* `commonLabels` is a string map of additional labels +* `commonAnnotations` is a string map of additional annotations + To use Kustomize with an overlay, point your path to the overlay. !!! tip @@ -15,7 +15,7 @@ To use Kustomize with an overlay, point your path to the overlay. ## Private Remote Bases -If you have remote bases that are either (a) HTTPS and need username/password (b) SSH and need SSH private key, then they'll inherit that from the app's repo. +If you have remote bases that are either (a) HTTPS and need username/password (b) SSH and need SSH private key, then they'll inherit that from the app's repo. This will work if the remote bases uses the same credentials/private key. It will not work if they use different ones. For security reasons your app only ever knows about its own repo (not other team's or users repos), and so you won't be able to access other private repos, even if Argo CD knows about them. @@ -35,14 +35,14 @@ metadata: app.kubernetes.io/name: argocd-cm app.kubernetes.io/part-of: argocd data: - kustomize.buildOptions: --load_restrictor none + kustomize.buildOptions: --load_restrictor LoadRestrictionsNone kustomize.buildOptions.v3.9.1: --output /tmp ``` ## Custom Kustomize versions Argo CD supports using multiple kustomize versions simultaneously and specifies required version per application. To add additional versions make sure required versions are [bundled](../operator-manual/custom_tools.md) and then -use `kustomize.path.` fields of `argocd-cm` ConfigMap to register bundled additional versions. +use `kustomize.path.` fields of `argocd-cm` ConfigMap to register bundled additional versions. ```yaml apiVersion: v1 @@ -58,7 +58,7 @@ data: kustomize.path.v3.5.4: /custom-tools/kustomize_3_5_4 ``` -Once a new version is configured you can reference it in Application spec as following: +Once a new version is configured you can reference it in an Application spec as follows: ```yaml apiVersion: argoproj.io/v1alpha1 @@ -75,7 +75,7 @@ spec: version: v3.5.4 ``` -Additionally application kustomize version can be configured using Parameters tab of Application Details page or using following CLI command: +Additionally, the application kustomize version can be configured using the Parameters tab of the Application Details page, or using the following CLI command: ``` argocd app set --kustomize-version v3.5.4 diff --git a/docs/user-guide/private-repositories.md b/docs/user-guide/private-repositories.md index ef9aada5fc6dd..8fc1edd978b99 100644 --- a/docs/user-guide/private-repositories.md +++ b/docs/user-guide/private-repositories.md @@ -8,7 +8,7 @@ ## Credentials -If application manifests are located in private repository then repository credentials have to be configured. Argo CD supports both HTTP and SSH Git credentials. +If application manifests are located in private repository then repository credentials have to be configured. Argo CD supports both HTTPS and SSH Git credentials. ### HTTPS Username And Password Credential @@ -39,7 +39,7 @@ or UI: > earlier than v1.2 1. Navigate to `Settings/Repositories` -1. Click `Connect Repo` button and enter HTTP credentials +1. Click `Connect Repo` button and enter HTTPS credentials ![connect repo](../assets/connect-repo.png) diff --git a/docs/user-guide/projects.md b/docs/user-guide/projects.md index ad9615728127b..b88ef9e4a1bf4 100644 --- a/docs/user-guide/projects.md +++ b/docs/user-guide/projects.md @@ -211,3 +211,55 @@ kind: ConfigMap Valid operators you can use are: In, NotIn, Exists, DoesNotExist. Gt, and Lt. projectName: `proj-global-test` should be replaced with your own global project name. + +## Project scoped Repositories and Clusters + +Normally, an ArgoCD admin creates a project and decides in advance which clusters and Git repositories +it defines. However, this creates a problem in scenarios where a developer wants to add a repository or cluster +after the initial creation of the project. This forces the developer to contact their ArgoCD admin again to update the project definition. + +It is possible to offer a self-service process for developers so that they can add a repository and/or cluster in a project on their own even after the initial creation of the project. + +For this purpose ArgoCD supports project-scoped repositories and clusters. + +To begin the process, ArgoCD 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: + +``` +p, proj:my-project:admin, repositories, create, my-project/*, allow +p, proj:my-project:admin, repositories, delete, my-project/*, allow +p, proj:my-project:admin, repositories, update, my-project/*, allow +``` + +This provides extra flexibility so that admins can have stricter rules. e.g.: + +``` +p, proj:my-project:admin, repositories, update, my-project/"https://github.my-company.com/*", allow +``` + +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``` + +For the declarative setup both repositories and clusters are stored as Kubernetes Secrets, and so a new field is used to denote that this resource is project scoped: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: argocd-example-apps + labels: + argocd.argoproj.io/secret-type: repository +type: Opaque +stringData: + project: my-project1 # Project scoped + name: argocd-example-apps + url: https://github.com/argoproj/argocd-example-apps.git + username: **** + password: **** +``` + +All the examples above talk about Git repositories, but the same principles apply to clusters as well. diff --git a/docs/user-guide/sync-options.md b/docs/user-guide/sync-options.md index 5831268a372b2..7fdbda4eb4fc9 100644 --- a/docs/user-guide/sync-options.md +++ b/docs/user-guide/sync-options.md @@ -126,3 +126,10 @@ syncOptions: ``` If the `Replace=true` sync option is set the Argo CD will use `kubectl replace` or `kubectl create` command to apply changes. + +This can also be configured at individual resource level. +```yaml +metadata: + annotations: + argocd.argoproj.io/sync-options: Replace=true +``` diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000000000..88515e217e4be --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# If we're started as PID 1, we should wrap command execution through tini to +# prevent leakage of orphaned processes ("zombies"). +if test "$$" = "1"; then + exec tini -- $@ +else + exec "$@" +fi diff --git a/go.mod b/go.mod index 22cd46edbb39a..97df0318b62ae 100644 --- a/go.mod +++ b/go.mod @@ -7,10 +7,10 @@ require ( github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d github.com/alicebob/miniredis v2.5.0+incompatible github.com/alicebob/miniredis/v2 v2.14.2 - github.com/argoproj/gitops-engine v0.3.1-0.20210630192200-2c97a96cab1b - github.com/argoproj/pkg v0.9.1-0.20210512035321-be5ba22dca5b + github.com/argoproj/gitops-engine v0.4.1-0.20210901235433-33f542da003c + github.com/argoproj/pkg v0.9.1 github.com/bombsimon/logrusr v1.0.0 - github.com/bradleyfalzon/ghinstallation v1.1.1 + github.com/bradleyfalzon/ghinstallation/v2 v2.0.2 github.com/casbin/casbin v1.9.1 github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect github.com/coreos/go-oidc v2.1.0+incompatible @@ -31,7 +31,7 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.4.3 github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/go-cmp v0.5.4 + github.com/google/go-cmp v0.5.6 github.com/google/go-jsonnet v0.17.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.1.2 @@ -51,6 +51,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/pquerna/cachecontrol v0.0.0-20180306154005-525d0eb5f91d // indirect github.com/prometheus/client_golang v1.7.1 + github.com/r3labs/diff v1.1.0 github.com/robfig/cron v1.1.0 github.com/rs/cors v1.6.0 // indirect github.com/sirupsen/logrus v1.7.0 diff --git a/go.sum b/go.sum index cc10d67e43967..5fb34adc9c0d4 100644 --- a/go.sum +++ b/go.sum @@ -96,10 +96,10 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/argoproj/gitops-engine v0.3.1-0.20210630192200-2c97a96cab1b h1:VkznBAx+KwLn6FLZHpgk1i8oQqwrcGe9nWYXTJSpICc= -github.com/argoproj/gitops-engine v0.3.1-0.20210630192200-2c97a96cab1b/go.mod h1:EdFe8qIOqsmbyxRhtIydU4BUeyZ4VTsY6R3XVQhU9LA= -github.com/argoproj/pkg v0.9.1-0.20210512035321-be5ba22dca5b h1:qtlM7ioAFP40LPN7A5ZqquVmAtv08LLSZTcCNYUQx8s= -github.com/argoproj/pkg v0.9.1-0.20210512035321-be5ba22dca5b/go.mod h1:ra+bQPmbVAoEL+gYSKesuigt4m49i3Qa3mE/xQcjCiA= +github.com/argoproj/gitops-engine v0.4.1-0.20210901235433-33f542da003c h1:Phz5KatrryMHhaupXjhO9seeEWAJfpXcCssW7gQKvBA= +github.com/argoproj/gitops-engine v0.4.1-0.20210901235433-33f542da003c/go.mod h1:EdFe8qIOqsmbyxRhtIydU4BUeyZ4VTsY6R3XVQhU9LA= +github.com/argoproj/pkg v0.9.1 h1:osfOS3QkzfRf+W43lbCZb0o0bzrBweQhL+U3rgEg+5M= +github.com/argoproj/pkg v0.9.1/go.mod h1:ra+bQPmbVAoEL+gYSKesuigt4m49i3Qa3mE/xQcjCiA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -125,8 +125,8 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bombsimon/logrusr v1.0.0 h1:CTCkURYAt5nhCCnKH9eLShYayj2/8Kn/4Qg3QfiU+Ro= github.com/bombsimon/logrusr v1.0.0/go.mod h1:Jq0nHtvxabKE5EMwAAdgTaz7dfWE8C4i11NOltxGQpc= -github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I= -github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug= +github.com/bradleyfalzon/ghinstallation/v2 v2.0.2 h1:VdhctVU4Kag+Yo5iuvEvFx4HNpLEI99Cm41UnE7y1WE= +github.com/bradleyfalzon/ghinstallation/v2 v2.0.2/go.mod h1:GhRUp70E+QFvNemlFd4unyHZ8ryBiMQkJm6KgdilpUo= github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E= github.com/casbin/casbin v1.9.1 h1:ucjbS5zTrmSLtH4XogqOG920Poe6QatdXtz1FEbApeM= github.com/casbin/casbin v1.9.1/go.mod h1:z8uPsfBJGUsnkagrt3G8QvjgTKFMBJ32UP8HpZllfog= @@ -372,10 +372,11 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts= -github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github/v38 v38.0.0 h1:l/BalRp6dmFh/SFbl32RrlaVvbByhxpy+/LY0sv9isM= +github.com/google/go-github/v38 v38.0.0/go.mod h1:cStvrz/7nFr0FoENgG6GLbp53WaelXucT+BBz/3VKx4= github.com/google/go-jsonnet v0.17.0 h1:/9NIEfhK1NQRKl3sP2536b2+x5HnZMdql7x3yK/l8JY= github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= @@ -667,6 +668,8 @@ github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULU github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quobyte/api v0.1.8/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI= +github.com/r3labs/diff v1.1.0 h1:V53xhrbTHrWFWq3gI4b94AjgEJOerO1+1l0xyHOBi8M= +github.com/r3labs/diff v1.1.0/go.mod h1:7WjXasNzi0vJetRcB/RqNl5dlIsmXcTTLmF5IoH6Xig= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY= github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= diff --git a/hack/generate-proto.sh b/hack/generate-proto.sh index 5019924cd46f6..b5529cbc4746d 100755 --- a/hack/generate-proto.sh +++ b/hack/generate-proto.sh @@ -125,7 +125,7 @@ clean_swagger() { } echo "If additional types are added, the number of expected collisions may need to be increased" -EXPECTED_COLLISION_COUNT=55 +EXPECTED_COLLISION_COUNT=64 collect_swagger server ${EXPECTED_COLLISION_COUNT} clean_swagger server clean_swagger reposerver diff --git a/hack/goreman-start.sh b/hack/goreman-start.sh new file mode 100644 index 0000000000000..b5886084c7dff --- /dev/null +++ b/hack/goreman-start.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +declare -a services=("controller" "api-server" "redis" "repo-server" "ui") + +EXCLUDE=$exclude + +declare -a servicesToRun=() + +if [ "$EXCLUDE" != "" ]; then + # Parse services list by ',' character + servicesToExclude=($(echo "$EXCLUDE" | tr ',' '\n')) + + # Find subset of items from services array that not include servicesToExclude items + for element in "${services[@]}" + do + found=false + for excludedSvc in "${servicesToExclude[@]}" + do + if [[ "$excludedSvc" == "$element" ]]; then + found=true + fi + done + if [[ "$found" == false ]]; then + servicesToRun+=($element) + fi + done +fi + +command="goreman start " + +for element in "${servicesToRun[@]}" +do + command+=$element + command+=" " +done + +eval $command \ No newline at end of file diff --git a/hack/installers/checksums/kustomize_4.1.2_linux_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_4.1.2_linux_amd64.tar.gz.sha256 deleted file mode 100644 index 6dbb40eee4875..0000000000000 --- a/hack/installers/checksums/kustomize_4.1.2_linux_amd64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -4efb7d0dadba7fab5191c680fcb342c2b6f252f230019cf9cffd5e4b0cad1d12 kustomize_4.1.2_linux_amd64.tar.gz \ No newline at end of file diff --git a/hack/installers/checksums/kustomize_4.1.2_linux_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_4.1.2_linux_arm64.tar.gz.sha256 deleted file mode 100644 index 74a175d4cd7fa..0000000000000 --- a/hack/installers/checksums/kustomize_4.1.2_linux_arm64.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -d4b0ababb06d7208b439c48c5dadf979433f062ee7131203f6a94ce1159c9d5e kustomize_4.1.2_linux_arm64.tar.gz \ No newline at end of file diff --git a/hack/installers/checksums/kustomize_4.2.0_linux_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_4.2.0_linux_amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..d86b13c0f405f --- /dev/null +++ b/hack/installers/checksums/kustomize_4.2.0_linux_amd64.tar.gz.sha256 @@ -0,0 +1 @@ +220dd03dcda8e45dc50e4e42b2d71882cbc4c05e0ed863513e67930ecad939eb kustomize_4.2.0_linux_amd64.tar.gz \ No newline at end of file diff --git a/hack/installers/checksums/kustomize_4.2.0_linux_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_4.2.0_linux_arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..de3d17680853f --- /dev/null +++ b/hack/installers/checksums/kustomize_4.2.0_linux_arm64.tar.gz.sha256 @@ -0,0 +1 @@ +33f2cf3b5db64c09560c187224e9d29452fde2b7f00f85941604fc75d9769e4a kustomize_4.2.0_linux_arm64.tar.gz \ No newline at end of file diff --git a/hack/installers/install-codegen-tools.sh b/hack/installers/install-codegen-tools.sh index 58fc68821d020..46bcec2be651e 100755 --- a/hack/installers/install-codegen-tools.sh +++ b/hack/installers/install-codegen-tools.sh @@ -1,4 +1,4 @@ #!/bin/bash set -eux -o pipefail -KUSTOMIZE_VERSION=4.1.2 "$(dirname $0)/../install.sh" helm2-linux jq-linux kustomize-linux protoc-linux swagger-linux +KUSTOMIZE_VERSION=4.2.0 "$(dirname $0)/../install.sh" helm2-linux jq-linux kustomize-linux protoc-linux swagger-linux diff --git a/hack/installers/install-helm2-linux.sh b/hack/installers/install-helm2-linux.sh index 89e1737bc2f50..5b09dc37ff5cf 100755 --- a/hack/installers/install-helm2-linux.sh +++ b/hack/installers/install-helm2-linux.sh @@ -5,7 +5,7 @@ set -eux -o pipefail export TARGET_FILE=helm-v${helm2_version}-linux-${ARCHITECTURE}.tar.gz -[ -e ${DOWNLOADS}/${TARGET_FILE} ] || curl -sLf --retry 3 -o ${DOWNLOADS}/${TARGET_FILE} https://storage.googleapis.com/kubernetes-helm/helm-v${helm2_version}-linux-$ARCHITECTURE.tar.gz +[ -e ${DOWNLOADS}/${TARGET_FILE} ] || curl -sLf --retry 3 -o ${DOWNLOADS}/${TARGET_FILE} https://get.helm.sh/helm-v${helm2_version}-linux-$ARCHITECTURE.tar.gz $(dirname $0)/compare-chksum.sh mkdir -p /tmp/helm2 && tar -C /tmp/helm2 -xf $DOWNLOADS/${TARGET_FILE} sudo install -m 0755 /tmp/helm2/linux-$ARCHITECTURE/helm $BIN/helm2 diff --git a/hack/tool-versions.sh b/hack/tool-versions.sh index 1b54ddc3774db..068bb11f27222 100644 --- a/hack/tool-versions.sh +++ b/hack/tool-versions.sh @@ -14,6 +14,6 @@ jq_version=1.6 ksonnet_version=0.13.1 kubectl_version=1.17.8 kubectx_version=0.6.3 -kustomize4_version=4.1.2 +kustomize4_version=4.2.0 protoc_version=3.7.1 swagger_version=0.19.0 diff --git a/hack/update-manifests.sh b/hack/update-manifests.sh index 4e53fbd242448..b2fafb3d54ee7 100755 --- a/hack/update-manifests.sh +++ b/hack/update-manifests.sh @@ -31,6 +31,7 @@ $KUSTOMIZE version cd ${SRCROOT}/manifests/base && $KUSTOMIZE edit set image quay.io/argoproj/argocd=${IMAGE_NAMESPACE}/argocd:${IMAGE_TAG} cd ${SRCROOT}/manifests/ha/base && $KUSTOMIZE edit set image quay.io/argoproj/argocd=${IMAGE_NAMESPACE}/argocd:${IMAGE_TAG} +cd ${SRCROOT}/manifests/core-install && $KUSTOMIZE edit set image quay.io/argoproj/argocd=${IMAGE_NAMESPACE}/argocd:${IMAGE_TAG} echo "${AUTOGENMSG}" > "${SRCROOT}/manifests/install.yaml" $KUSTOMIZE build "${SRCROOT}/manifests/cluster-install" >> "${SRCROOT}/manifests/install.yaml" @@ -44,3 +45,5 @@ $KUSTOMIZE build "${SRCROOT}/manifests/ha/cluster-install" >> "${SRCROOT}/manife echo "${AUTOGENMSG}" > "${SRCROOT}/manifests/ha/namespace-install.yaml" $KUSTOMIZE build "${SRCROOT}/manifests/ha/namespace-install" >> "${SRCROOT}/manifests/ha/namespace-install.yaml" +echo "${AUTOGENMSG}" > "${SRCROOT}/manifests/core-install.yaml" +$KUSTOMIZE build "${SRCROOT}/manifests/core-install" >> "${SRCROOT}/manifests/core-install.yaml" \ 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 0ecb3d9e781fd..84b87d8f2b493 100644 --- a/manifests/base/dex/argocd-dex-server-deployment.yaml +++ b/manifests/base/dex/argocd-dex-server-deployment.yaml @@ -28,7 +28,7 @@ spec: name: dexconfig containers: - name: dex - image: ghcr.io/dexidp/dex:v2.27.0 + image: ghcr.io/dexidp/dex:v2.30.0 imagePullPolicy: Always command: [/shared/argocd-dex, rundex] securityContext: diff --git a/manifests/base/repo-server/argocd-repo-server-deployment.yaml b/manifests/base/repo-server/argocd-repo-server-deployment.yaml index ec1a7e675c29d..79d8982245fa7 100644 --- a/manifests/base/repo-server/argocd-repo-server-deployment.yaml +++ b/manifests/base/repo-server/argocd-repo-server-deployment.yaml @@ -21,7 +21,7 @@ spec: image: quay.io/argoproj/argocd:latest imagePullPolicy: Always command: - - uid_entrypoint.sh + - entrypoint.sh - argocd-repo-server - --redis - $(ARGOCD_REDIS_SERVICE):6379 @@ -98,6 +98,12 @@ spec: name: argocd-cmd-params-cm key: reposerver.default.cache.expiration 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 ports: - containerPort: 8081 - containerPort: 8084 @@ -134,6 +140,8 @@ spec: mountPath: /app/config/reposerver/tls - name: tmp mountPath: /tmp + - mountPath: /helm-working-dir + name: helm-working-dir volumes: - name: ssh-known-hosts configMap: @@ -148,6 +156,8 @@ spec: emptyDir: {} - name: tmp emptyDir: {} + - name: helm-working-dir + emptyDir: {} - name: argocd-repo-server-tls secret: secretName: argocd-repo-server-tls diff --git a/manifests/base/server/argocd-server-deployment.yaml b/manifests/base/server/argocd-server-deployment.yaml index b02dc1bd25123..586ab2e1fbaa4 100644 --- a/manifests/base/server/argocd-server-deployment.yaml +++ b/manifests/base/server/argocd-server-deployment.yaml @@ -20,10 +20,7 @@ spec: - name: argocd-server image: quay.io/argoproj/argocd:latest imagePullPolicy: Always - command: - - argocd-server - - --staticassets - - /shared/app + command: [argocd-server] env: - name: ARGOCD_SERVER_INSECURE valueFrom: @@ -139,6 +136,12 @@ spec: 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: diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml new file mode 100644 index 0000000000000..8fa27175a712b --- /dev/null +++ b/manifests/core-install.yaml @@ -0,0 +1,3302 @@ +# This is an auto-generated file. DO NOT EDIT +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + app.kubernetes.io/name: applications.argoproj.io + app.kubernetes.io/part-of: argocd + name: applications.argoproj.io +spec: + group: argoproj.io + names: + kind: Application + listKind: ApplicationList + plural: applications + shortNames: + - app + - apps + singular: application + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.sync.status + name: Sync Status + type: string + - jsonPath: .status.health.status + name: Health Status + type: string + - jsonPath: .status.sync.revision + name: Revision + 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' + 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' + type: string + metadata: + type: object + operation: + description: Operation contains information about a requested or running + operation + properties: + info: + description: Info is a list of informational items for this operation + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + 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 + retry: + description: Retry controls the strategy to apply if a sync fails + properties: + backoff: + description: Backoff controls how to backoff on subsequent retries + of failed syncs + properties: + duration: + description: Duration is the amount to back off. Default unit + is seconds, but could also be a duration (e.g. "2m", "1h") + type: string + factor: + description: Factor is a factor to multiply the base duration + after each failed retry + format: int64 + type: integer + maxDuration: + description: MaxDuration is the maximum amount of time allowed + for the backoff strategy + type: string + type: object + limit: + description: Limit is the maximum number of attempts for retrying + a failed sync. If set to 0, no retries will be performed. + format: int64 + type: integer + type: object + sync: + description: Sync contains parameters for the operation + properties: + dryRun: + description: DryRun specifies to perform a `kubectl apply --dry-run` + without actually performing the sync + type: boolean + manifests: + description: Manifests is an optional field that overrides sync + source with a local directory for development + items: + type: string + type: array + prune: + description: Prune specifies to delete resources from the cluster + that are no longer tracked in git + type: boolean + resources: + description: Resources describes which resources shall be part + of the sync + items: + description: SyncOperationResource contains resources to sync. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + 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. + type: string + 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 + properties: + chart: + description: Chart is a Helm chart name, and must be specified + for applications sourced from a Helm repo. + type: string + directory: + description: Directory holds path/directory specific options + properties: + exclude: + description: Exclude contains a glob pattern to match + paths against that should be explicitly excluded from + being used during manifest generation + type: string + include: + description: Include contains a glob pattern to match + paths against that should be explicitly included during + manifest generation + type: string + jsonnet: + description: Jsonnet holds options specific to Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet External + Variables + items: + description: JsonnetVar represents a variable to + be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level Arguments + items: + description: JsonnetVar represents a variable to + be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan a directory + recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters to the + helm template + items: + description: HelmFileParameter is a file parameter that's + passed to helm template during manifest generation + properties: + name: + description: Name is the name of the Helm parameter + type: string + path: + description: Path is the path to the file containing + the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters which + are passed to the helm template command upon manifest + generation + items: + description: HelmParameter is a parameter that's passed + to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether to tell + Helm to interpret booleans and numbers as strings + type: boolean + name: + description: Name is the name of the Helm parameter + type: string + value: + description: Value is the value for the Helm parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name to use. + If omitted it will use the application name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value files + to use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be passed + to helm template, typically defined as a block + type: string + version: + description: Version is the Helm version to use for templating + (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application environment + name + type: string + parameters: + description: Parameters are a list of ksonnet component + parameter override values + items: + description: KsonnetParameter is a ksonnet component + parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional + annotations to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional labels + to add to rendered manifests + type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies whether + to force applying common annotations to resources for + Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether to force + applying common labels to resources for Kustomize apps + type: boolean + images: + description: Images is a list of Kustomize image override + specifications + items: + description: KustomizeImage represents a Kustomize image + definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to resources + for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to resources + for Kustomize apps + type: string + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git repository, + and is only valid for applications sourced from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management + plugin specific options + properties: + env: + description: Env is a list of environment variable entries + items: + description: EnvEntry represents an entry in the application's + environment + properties: + name: + description: Name is the name of the variable, usually + expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository (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. + type: string + required: + - repoURL + type: object + syncOptions: + description: SyncOptions provide per-sync sync-options, e.g. Validate=false + items: + type: string + type: array + syncStrategy: + description: SyncStrategy describes how to perform the sync + properties: + apply: + description: Apply will perform a `kubectl apply` 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. + type: boolean + type: object + hook: + description: Hook will submit any referenced resources 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. + type: boolean + type: object + type: object + type: object + type: object + spec: + description: ApplicationSpec represents desired application state. Contains + link to repository with application definition and additional parameters + link definition revision. + properties: + destination: + description: Destination is a reference to the target Kubernetes server + and namespace + properties: + name: + description: Name is an alternate way of specifying the target + cluster by its symbolic name + 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 + type: string + server: + description: Server specifies the URL of the target cluster and + must be set to the Kubernetes control plane API + type: string + type: object + ignoreDifferences: + description: IgnoreDifferences is a list of resources and their fields + which should be ignored during comparison + items: + description: ResourceIgnoreDifferences contains resource filter + and list of json paths which should be ignored during comparison + with live state. + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + description: Info contains a list of information (URLs, email addresses, + and plain text) that relates to the application + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + 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. + 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. + format: int64 + type: integer + source: + description: Source is a reference to the location of the application's + manifests or chart + properties: + chart: + description: Chart is a Helm chart name, and must be specified + for applications sourced from a Helm repo. + type: string + directory: + description: Directory holds path/directory specific options + properties: + exclude: + description: Exclude contains a glob pattern to match paths + against that should be explicitly excluded from being used + during manifest generation + type: string + include: + description: Include contains a glob pattern to match paths + against that should be explicitly included during manifest + generation + type: string + jsonnet: + description: Jsonnet holds options specific to Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet External Variables + items: + description: JsonnetVar represents a variable to be + passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level Arguments + items: + description: JsonnetVar represents a variable to be + passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan a directory + recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters to the helm + template + items: + description: HelmFileParameter is a file parameter that's + passed to helm template during manifest generation + properties: + name: + description: Name is the name of the Helm parameter + type: string + path: + description: Path is the path to the file containing + the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters which + are passed to the helm template command upon manifest generation + items: + description: HelmParameter is a parameter that's passed + to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether to tell + Helm to interpret booleans and numbers as strings + type: boolean + name: + description: Name is the name of the Helm parameter + type: string + value: + description: Value is the value for the Helm parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name to use. + If omitted it will use the application name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value files to + use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be passed to + helm template, typically defined as a block + type: string + version: + description: Version is the Helm version to use for templating + (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application environment + name + type: string + parameters: + description: Parameters are a list of ksonnet component parameter + override values + items: + description: KsonnetParameter is a ksonnet component parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional annotations + to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional labels to + add to rendered manifests + type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies whether to force + applying common annotations to resources for Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether to force + applying common labels to resources for Kustomize apps + type: boolean + images: + description: Images is a list of Kustomize image override + specifications + items: + description: KustomizeImage represents a Kustomize image + definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to resources + for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to resources + for Kustomize apps + type: string + version: + description: Version controls which version of Kustomize to + use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git repository, + and is only valid for applications sourced from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management plugin + specific options + properties: + env: + description: Env is a list of environment variable entries + items: + description: EnvEntry represents an entry in the application's + environment + properties: + name: + description: Name is the name of the variable, usually + expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository (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. + type: string + required: + - repoURL + type: object + syncPolicy: + description: SyncPolicy controls when and how a sync will be performed + properties: + automated: + description: Automated will keep an application synced to the + target revision + properties: + allowEmpty: + description: 'AllowEmpty allows apps have zero live resources + (default: false)' + type: boolean + prune: + description: 'Prune specifies whether to delete resources + from the cluster that are not found in the sources anymore + as part of automated sync (default: false)' + type: boolean + selfHeal: + description: 'SelfHeal specifes whether to revert resources + back to their desired state upon modification in the cluster + (default: false)' + type: boolean + type: object + retry: + description: Retry controls failed sync retry behavior + properties: + backoff: + description: Backoff controls how to backoff on subsequent + retries of failed syncs + properties: + duration: + description: Duration is the amount to back off. Default + unit is seconds, but could also be a duration (e.g. + "2m", "1h") + type: string + factor: + description: Factor is a factor to multiply the base duration + after each failed retry + format: int64 + type: integer + maxDuration: + description: MaxDuration is the maximum amount of time + allowed for the backoff strategy + type: string + type: object + limit: + description: Limit is the maximum number of attempts for retrying + a failed sync. If set to 0, no retries will be performed. + format: int64 + type: integer + type: object + syncOptions: + description: Options allow you to specify whole app sync-options + items: + type: string + type: array + type: object + required: + - destination + - project + - source + type: object + status: + description: ApplicationStatus contains status information for the application + properties: + conditions: + description: Conditions is a list of currently observed application + conditions + items: + description: ApplicationCondition contains details about an application + condition, which is usally an error or warning + properties: + lastTransitionTime: + description: LastTransitionTime is the time the condition was + last observed + format: date-time + type: string + message: + description: Message contains human-readable message indicating + details about condition + type: string + type: + description: Type is an application condition type + type: string + required: + - message + - type + type: object + type: array + health: + description: Health contains information about the application's current + health status + properties: + message: + description: Message is a human-readable informational message + describing the health status + type: string + status: + description: Status holds the status code of the application or + resource + type: string + type: object + history: + description: History contains information about the application's + sync history + items: + description: RevisionHistory contains history information about + a previous sync + properties: + deployStartedAt: + description: DeployStartedAt holds the time the sync operation + started + format: date-time + type: string + deployedAt: + description: DeployedAt holds the time the sync operation completed + format: date-time + type: string + id: + description: ID is an auto incrementing identifier of the RevisionHistory + format: int64 + type: integer + revision: + description: Revision holds the revision the sync was performed + against + type: string + source: + description: Source is a reference to the application source + used for the sync operation + properties: + chart: + description: Chart is a Helm chart name, and must be specified + for applications sourced from a Helm repo. + type: string + directory: + description: Directory holds path/directory specific options + properties: + exclude: + description: Exclude contains a glob pattern to match + paths against that should be explicitly excluded from + being used during manifest generation + type: string + include: + description: Include contains a glob pattern to match + paths against that should be explicitly included during + manifest generation + type: string + jsonnet: + description: Jsonnet holds options specific to Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet External + Variables + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level + Arguments + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan a directory + recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters to the + helm template + items: + description: HelmFileParameter is a file parameter + that's passed to helm template during manifest generation + properties: + name: + description: Name is the name of the Helm parameter + type: string + path: + description: Path is the path to the file containing + the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters + which are passed to the helm template command upon + manifest generation + items: + description: HelmParameter is a parameter that's passed + to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether to + tell Helm to interpret booleans and numbers + as strings + type: boolean + name: + description: Name is the name of the Helm parameter + type: string + value: + description: Value is the value for the Helm parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name to + use. If omitted it will use the application name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value files + to use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be passed + to helm template, typically defined as a block + type: string + version: + description: Version is the Helm version to use for + templating (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application environment + name + type: string + parameters: + description: Parameters are a list of ksonnet component + parameter override values + items: + description: KsonnetParameter is a ksonnet component + parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional + annotations to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional labels + to add to rendered manifests + type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies whether + to force applying common annotations to resources + for Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether to + force applying common labels to resources for Kustomize + apps + type: boolean + images: + description: Images is a list of Kustomize image override + specifications + items: + description: KustomizeImage represents a Kustomize + image definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to resources + for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to resources + for Kustomize apps + type: string + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git repository, + and is only valid for applications sourced from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management + plugin specific options + properties: + env: + description: Env is a list of environment variable entries + items: + description: EnvEntry represents an entry in the application's + environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository (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. + type: string + required: + - repoURL + type: object + required: + - deployedAt + - id + - revision + 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' + format: date-time + type: string + operationState: + description: OperationState contains information about any ongoing + operations, such as a sync + properties: + finishedAt: + description: FinishedAt contains time of operation completion + format: date-time + type: string + message: + description: Message holds any pertinent messages when attempting + to perform operation (typically errors). + type: string + operation: + description: Operation is the original requested operation + properties: + info: + description: Info is a list of informational items for this + operation + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + 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 + retry: + description: Retry controls the strategy to apply if a sync + fails + properties: + backoff: + description: Backoff controls how to backoff on subsequent + retries of failed syncs + properties: + duration: + description: Duration is the amount to back off. Default + unit is seconds, but could also be a duration (e.g. + "2m", "1h") + type: string + factor: + description: Factor is a factor to multiply the base + duration after each failed retry + format: int64 + type: integer + maxDuration: + description: MaxDuration is the maximum amount of + time allowed for the backoff strategy + type: string + type: object + limit: + description: Limit is the maximum number of attempts for + retrying a failed sync. If set to 0, no retries will + be performed. + format: int64 + type: integer + type: object + sync: + description: Sync contains parameters for the operation + properties: + dryRun: + description: DryRun specifies to perform a `kubectl apply + --dry-run` without actually performing the sync + type: boolean + manifests: + description: Manifests is an optional field that overrides + sync source with a local directory for development + items: + type: string + type: array + prune: + description: Prune specifies to delete resources from + the cluster that are no longer tracked in git + type: boolean + resources: + description: Resources describes which resources shall + be part of the sync + items: + description: SyncOperationResource contains resources + to sync. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + 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. + type: string + 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 + properties: + chart: + description: Chart is a Helm chart name, and must + be specified for applications sourced from a Helm + repo. + type: string + directory: + description: Directory holds path/directory specific + options + properties: + exclude: + description: Exclude contains a glob pattern to + match paths against that should be explicitly + excluded from being used during manifest generation + type: string + include: + description: Include contains a glob pattern to + match paths against that should be explicitly + included during manifest generation + type: string + jsonnet: + description: Jsonnet holds options specific to + Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet + External Variables + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest + generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level + Arguments + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest + generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan + a directory recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters + to the helm template + items: + description: HelmFileParameter is a file parameter + that's passed to helm template during manifest + generation + properties: + name: + description: Name is the name of the Helm + parameter + type: string + path: + description: Path is the path to the file + containing the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters + which are passed to the helm template command + upon manifest generation + items: + description: HelmParameter is a parameter that's + passed to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether + to tell Helm to interpret booleans and + numbers as strings + type: boolean + name: + description: Name is the name of the Helm + parameter + type: string + value: + description: Value is the value for the + Helm parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name + to use. If omitted it will use the application + name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value + files to use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be + passed to helm template, typically defined as + a block + type: string + version: + description: Version is the Helm version to use + for templating (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application + environment name + type: string + parameters: + description: Parameters are a list of ksonnet + component parameter override values + items: + description: KsonnetParameter is a ksonnet component + parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional + annotations to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional + labels to add to rendered manifests + type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies + whether to force applying common annotations + to resources for Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether + to force applying common labels to resources + for Kustomize apps + type: boolean + images: + description: Images is a list of Kustomize image + override specifications + items: + description: KustomizeImage represents a Kustomize + image definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to + resources for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to + resources for Kustomize apps + type: string + version: + description: Version controls which version of + Kustomize to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git + repository, and is only valid for applications sourced + from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management + plugin specific options + properties: + env: + description: Env is a list of environment variable + entries + items: + description: EnvEntry represents an entry in + the application's environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository + (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. + type: string + required: + - repoURL + type: object + syncOptions: + description: SyncOptions provide per-sync sync-options, + e.g. Validate=false + items: + type: string + type: array + syncStrategy: + description: SyncStrategy describes how to perform the + sync + properties: + apply: + description: Apply will perform a `kubectl apply` + 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. + type: boolean + type: object + hook: + description: Hook will submit any referenced resources + 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. + type: boolean + type: object + type: object + type: object + type: object + phase: + description: Phase is the current phase of the operation + type: string + retryCount: + description: RetryCount contains time of operation retries + format: int64 + type: integer + startedAt: + description: StartedAt contains time of operation start + format: date-time + type: string + syncResult: + description: SyncResult is the result of a Sync operation + properties: + resources: + description: Resources contains a list of sync result items + for each individual resource in a sync operation + items: + description: ResourceResult holds the operation result details + of a specific resource + properties: + group: + 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. + type: string + hookType: + description: HookType specifies the type of the hook. + Empty for non-hook resources + type: string + kind: + description: Kind specifies the API kind of the resource + type: string + message: + description: Message contains an informational or error + message for the last sync OR operation + type: string + name: + description: Name specifies the name of the resource + type: string + namespace: + description: Namespace specifies the target namespace + of the resource + type: string + status: + description: Status holds the final result of the sync. + Will be empty if the resources is yet to be applied/pruned + and is always zero-value for hooks + type: string + syncPhase: + description: SyncPhase indicates the particular phase + of the sync that this result was acquired in + type: string + version: + description: Version specifies the API version of the + resource + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + revision: + description: Revision holds the revision this sync operation + was performed to + type: string + source: + description: Source records the application source information + of the sync, used for comparing auto-sync + properties: + chart: + description: Chart is a Helm chart name, and must be specified + for applications sourced from a Helm repo. + type: string + directory: + description: Directory holds path/directory specific options + properties: + exclude: + description: Exclude contains a glob pattern to match + paths against that should be explicitly excluded + from being used during manifest generation + type: string + include: + description: Include contains a glob pattern to match + paths against that should be explicitly included + during manifest generation + type: string + jsonnet: + description: Jsonnet holds options specific to Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet External + Variables + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level + Arguments + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan a directory + recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters to + the helm template + items: + description: HelmFileParameter is a file parameter + that's passed to helm template during manifest + generation + properties: + name: + description: Name is the name of the Helm parameter + type: string + path: + description: Path is the path to the file containing + the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters + which are passed to the helm template command upon + manifest generation + items: + description: HelmParameter is a parameter that's + passed to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether + to tell Helm to interpret booleans and numbers + as strings + type: boolean + name: + description: Name is the name of the Helm parameter + type: string + value: + description: Value is the value for the Helm + parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name + to use. If omitted it will use the application name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value files + to use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be passed + to helm template, typically defined as a block + type: string + version: + description: Version is the Helm version to use for + templating (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application + environment name + type: string + parameters: + description: Parameters are a list of ksonnet component + parameter override values + items: + description: KsonnetParameter is a ksonnet component + parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional + annotations to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional + labels to add to rendered manifests + type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies whether + to force applying common annotations to resources + for Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether to + force applying common labels to resources for Kustomize + apps + type: boolean + images: + description: Images is a list of Kustomize image override + specifications + items: + description: KustomizeImage represents a Kustomize + image definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to resources + for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to resources + for Kustomize apps + type: string + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git repository, + and is only valid for applications sourced from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management + plugin specific options + properties: + env: + description: Env is a list of environment variable + entries + items: + description: EnvEntry represents an entry in the + application's environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository (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. + type: string + required: + - repoURL + type: object + required: + - revision + type: object + required: + - operation + - phase + - startedAt + type: object + reconciledAt: + description: ReconciledAt indicates when the application state was + reconciled using the latest git version + format: date-time + type: string + resources: + 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' + properties: + group: + type: string + health: + description: HealthStatus contains information about the currently + observed health state of an application or resource + properties: + message: + description: Message is a human-readable informational message + describing the health status + type: string + status: + description: Status holds the status code of the application + or resource + type: string + type: object + hook: + type: boolean + kind: + type: string + name: + type: string + namespace: + type: string + requiresPruning: + type: boolean + status: + description: SyncStatusCode is a type which represents possible + comparison results + type: string + version: + type: string + type: object + type: array + sourceType: + description: SourceType specifies the type of this application + type: string + summary: + description: Summary contains a list of URLs and container images + used by this application + properties: + externalURLs: + description: ExternalURLs holds all external URLs of application + child resources. + items: + type: string + type: array + images: + description: Images holds all images of application child resources. + items: + type: string + type: array + type: object + sync: + description: Sync contains information about the application's current + sync status + properties: + comparedTo: + description: ComparedTo contains information about what has been + compared + properties: + destination: + description: Destination is a reference to the application's + destination used for comparison + properties: + name: + description: Name is an alternate way of specifying the + target cluster by its symbolic name + 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 + type: string + server: + description: Server specifies the URL of the target cluster + and must be set to the Kubernetes control plane API + type: string + type: object + source: + description: Source is a reference to the application's source + used for comparison + properties: + chart: + description: Chart is a Helm chart name, and must be specified + for applications sourced from a Helm repo. + type: string + directory: + description: Directory holds path/directory specific options + properties: + exclude: + description: Exclude contains a glob pattern to match + paths against that should be explicitly excluded + from being used during manifest generation + type: string + include: + description: Include contains a glob pattern to match + paths against that should be explicitly included + during manifest generation + type: string + jsonnet: + description: Jsonnet holds options specific to Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet External + Variables + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level + Arguments + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan a directory + recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters to + the helm template + items: + description: HelmFileParameter is a file parameter + that's passed to helm template during manifest + generation + properties: + name: + description: Name is the name of the Helm parameter + type: string + path: + description: Path is the path to the file containing + the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters + which are passed to the helm template command upon + manifest generation + items: + description: HelmParameter is a parameter that's + passed to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether + to tell Helm to interpret booleans and numbers + as strings + type: boolean + name: + description: Name is the name of the Helm parameter + type: string + value: + description: Value is the value for the Helm + parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name + to use. If omitted it will use the application name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value files + to use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be passed + to helm template, typically defined as a block + type: string + version: + description: Version is the Helm version to use for + templating (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application + environment name + type: string + parameters: + description: Parameters are a list of ksonnet component + parameter override values + items: + description: KsonnetParameter is a ksonnet component + parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional + annotations to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional + labels to add to rendered manifests + type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies whether + to force applying common annotations to resources + for Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether to + force applying common labels to resources for Kustomize + apps + type: boolean + images: + description: Images is a list of Kustomize image override + specifications + items: + description: KustomizeImage represents a Kustomize + image definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to resources + for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to resources + for Kustomize apps + type: string + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git repository, + and is only valid for applications sourced from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management + plugin specific options + properties: + env: + description: Env is a list of environment variable + entries + items: + description: EnvEntry represents an entry in the + application's environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository (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. + type: string + required: + - repoURL + type: object + required: + - destination + - source + type: object + revision: + description: Revision contains information about the revision + the comparison has been performed to + type: string + status: + description: Status is the sync state of the comparison + type: string + required: + - status + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true + subresources: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + app.kubernetes.io/name: appprojects.argoproj.io + app.kubernetes.io/part-of: argocd + name: appprojects.argoproj.io +spec: + group: argoproj.io + names: + kind: AppProject + listKind: AppProjectList + plural: appprojects + shortNames: + - appproj + - appprojs + singular: appproject + scope: Namespaced + versions: + - 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)' + 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' + 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' + type: string + metadata: + type: object + spec: + description: AppProjectSpec is the specification of an AppProject + properties: + clusterResourceBlacklist: + 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 + properties: + group: + type: string + kind: + type: string + required: + - group + - kind + type: object + type: array + clusterResourceWhitelist: + 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 + properties: + group: + type: string + kind: + type: string + required: + - group + - kind + type: object + type: array + description: + description: Description contains optional project description + type: string + destinations: + description: Destinations contains list of destinations available + for deployment + items: + description: ApplicationDestination holds information about the + application's destination + properties: + name: + description: Name is an alternate way of specifying the target + cluster by its symbolic name + 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 + type: string + server: + description: Server specifies the URL of the target cluster + and must be set to the Kubernetes control plane API + type: string + type: object + type: array + namespaceResourceBlacklist: + 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 + properties: + group: + type: string + kind: + type: string + required: + - group + - kind + type: object + type: array + namespaceResourceWhitelist: + 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 + properties: + group: + type: string + kind: + type: string + required: + - group + - kind + type: object + type: array + orphanedResources: + description: OrphanedResources specifies if controller should monitor + orphaned resources of apps in this project + properties: + ignore: + description: Ignore contains a list of resources that are to be + excluded from orphaned resources monitoring + items: + description: OrphanedResourceKey is a reference to a resource + to be ignored from + properties: + group: + type: string + kind: + type: string + name: + type: string + type: object + type: array + warn: + description: Warn indicates if warning condition should be created + for apps which have orphaned resources + type: boolean + type: object + roles: + description: Roles are user defined RBAC roles associated with this + project + items: + description: ProjectRole represents a role that has access to a + project + properties: + description: + description: Description is a description of the role + type: string + groups: + description: Groups are a list of OIDC group claims bound to + this role + items: + type: string + type: array + jwtTokens: + description: JWTTokens are a list of generated JWT tokens bound + to this role + items: + description: JWTToken holds the issuedAt and expiresAt values + of a token + properties: + exp: + format: int64 + type: integer + iat: + format: int64 + type: integer + id: + type: string + required: + - iat + type: object + type: array + name: + description: Name is a name for this role + type: string + policies: + description: Policies Stores a list of casbin formatted strings + that define access policies for the role in the project + items: + type: string + type: array + required: + - name + type: object + type: array + signatureKeys: + description: SignatureKeys contains a list of PGP key IDs that commits + in Git must be signed with in order to be allowed for sync + items: + description: SignatureKey is the specification of a key required + to verify commit signatures with + properties: + keyID: + description: The ID of the key in hexadecimal notation + type: string + required: + - keyID + type: object + type: array + sourceRepos: + description: SourceRepos contains list of repository URLs which can + be used for deployment + items: + type: string + type: array + syncWindows: + description: SyncWindows controls when syncs can be run for apps in + this project + items: + description: SyncWindow contains the kind, time, duration and attributes + that are used to assign the syncWindows to apps + properties: + applications: + description: Applications contains a list of applications that + the window will apply to + items: + type: string + type: array + clusters: + description: Clusters contains a list of clusters that the window + will apply to + items: + type: string + type: array + duration: + description: Duration is the amount of time the sync window + will be open + type: string + kind: + description: Kind defines if the window allows or blocks syncs + type: string + manualSync: + description: ManualSync enables manual syncs when they would + otherwise be blocked + type: boolean + namespaces: + description: Namespaces contains a list of namespaces that the + window will apply to + items: + type: string + type: array + schedule: + description: Schedule is the time the window will begin, specified + in cron format + type: string + type: object + type: array + type: object + status: + description: AppProjectStatus contains status information for AppProject + CRs + properties: + jwtTokensByRole: + additionalProperties: + description: JWTTokens represents a list of JWT tokens + properties: + items: + items: + description: JWTToken holds the issuedAt and expiresAt values + of a token + properties: + exp: + format: int64 + type: integer + iat: + format: int64 + type: integer + id: + type: string + required: + - iat + type: object + type: array + type: object + description: JWTTokensByRole contains a list of JWT tokens issued + for a given role + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: application-controller + app.kubernetes.io/name: argocd-application-controller + app.kubernetes.io/part-of: argocd + name: argocd-application-controller +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: redis + app.kubernetes.io/name: argocd-redis + app.kubernetes.io/part-of: argocd + name: argocd-redis +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: application-controller + app.kubernetes.io/name: argocd-application-controller + app.kubernetes.io/part-of: argocd + name: argocd-application-controller +rules: +- apiGroups: + - "" + resources: + - secrets + - configmaps + verbs: + - get + - list + - watch +- apiGroups: + - argoproj.io + resources: + - applications + - appprojects + verbs: + - create + - get + - list + - watch + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - list +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: application-controller + app.kubernetes.io/name: argocd-application-controller + app.kubernetes.io/part-of: argocd + name: argocd-application-controller +rules: +- apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +- nonResourceURLs: + - '*' + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: server + app.kubernetes.io/name: argocd-server + app.kubernetes.io/part-of: argocd + name: argocd-server +rules: +- apiGroups: + - '*' + resources: + - '*' + verbs: + - delete + - get + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - list +- apiGroups: + - "" + resources: + - pods + - pods/log + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: application-controller + app.kubernetes.io/name: argocd-application-controller + app.kubernetes.io/part-of: argocd + name: argocd-application-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: argocd-application-controller +subjects: +- kind: ServiceAccount + name: argocd-application-controller +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: redis + app.kubernetes.io/name: argocd-redis + app.kubernetes.io/part-of: argocd + name: argocd-redis +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: argocd-redis +subjects: +- kind: ServiceAccount + name: argocd-redis +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: application-controller + app.kubernetes.io/name: argocd-application-controller + app.kubernetes.io/part-of: argocd + name: argocd-application-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: argocd-application-controller +subjects: +- kind: ServiceAccount + name: argocd-application-controller + namespace: argocd +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: server + app.kubernetes.io/name: argocd-server + app.kubernetes.io/part-of: argocd + name: argocd-server +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: argocd-server +subjects: +- kind: ServiceAccount + name: argocd-server + namespace: argocd +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/name: argocd-cm + app.kubernetes.io/part-of: argocd + name: argocd-cm +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/name: argocd-cmd-params-cm + app.kubernetes.io/part-of: argocd + name: argocd-cmd-params-cm +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/name: argocd-gpg-keys-cm + app.kubernetes.io/part-of: argocd + name: argocd-gpg-keys-cm +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/name: argocd-rbac-cm + app.kubernetes.io/part-of: argocd + name: argocd-rbac-cm +--- +apiVersion: v1 +data: + ssh_known_hosts: | + bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== + github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= + gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf + gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 + ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H + vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/name: argocd-ssh-known-hosts-cm + app.kubernetes.io/part-of: argocd + name: argocd-ssh-known-hosts-cm +--- +apiVersion: v1 +data: null +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/name: argocd-tls-certs-cm + app.kubernetes.io/part-of: argocd + name: argocd-tls-certs-cm +--- +apiVersion: v1 +kind: Secret +metadata: + labels: + app.kubernetes.io/name: argocd-secret + app.kubernetes.io/part-of: argocd + name: argocd-secret +type: Opaque +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: metrics + app.kubernetes.io/name: argocd-metrics + app.kubernetes.io/part-of: argocd + name: argocd-metrics +spec: + ports: + - name: metrics + port: 8082 + protocol: TCP + targetPort: 8082 + selector: + app.kubernetes.io/name: argocd-application-controller +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: redis + app.kubernetes.io/name: argocd-redis + app.kubernetes.io/part-of: argocd + name: argocd-redis +spec: + ports: + - name: tcp-redis + port: 6379 + targetPort: 6379 + selector: + app.kubernetes.io/name: argocd-redis +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: repo-server + app.kubernetes.io/name: argocd-repo-server + app.kubernetes.io/part-of: argocd + name: argocd-repo-server +spec: + ports: + - name: server + port: 8081 + protocol: TCP + targetPort: 8081 + - name: metrics + port: 8084 + protocol: TCP + targetPort: 8084 + selector: + app.kubernetes.io/name: argocd-repo-server +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: redis + app.kubernetes.io/name: argocd-redis + app.kubernetes.io/part-of: argocd + name: argocd-redis +spec: + selector: + matchLabels: + app.kubernetes.io/name: argocd-redis + template: + metadata: + labels: + app.kubernetes.io/name: argocd-redis + spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: argocd-redis + topologyKey: kubernetes.io/hostname + weight: 100 + - podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/part-of: argocd + topologyKey: kubernetes.io/hostname + weight: 5 + containers: + - args: + - --save + - "" + - --appendonly + - "no" + image: redis:6.2.4-alpine + imagePullPolicy: Always + name: redis + ports: + - containerPort: 6379 + securityContext: + runAsNonRoot: true + runAsUser: 999 + serviceAccountName: argocd-redis +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: repo-server + app.kubernetes.io/name: argocd-repo-server + app.kubernetes.io/part-of: argocd + name: argocd-repo-server +spec: + selector: + matchLabels: + app.kubernetes.io/name: argocd-repo-server + template: + metadata: + labels: + app.kubernetes.io/name: argocd-repo-server + spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: argocd-repo-server + topologyKey: kubernetes.io/hostname + weight: 100 + - podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/part-of: argocd + topologyKey: kubernetes.io/hostname + weight: 5 + automountServiceAccountToken: false + containers: + - command: + - entrypoint.sh + - argocd-repo-server + - --redis + - argocd-redis:6379 + env: + - name: ARGOCD_RECONCILIATION_TIMEOUT + valueFrom: + configMapKeyRef: + key: timeout.reconciliation + name: argocd-cm + optional: true + - name: ARGOCD_REPO_SERVER_LOGFORMAT + valueFrom: + configMapKeyRef: + key: reposerver.log.format + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_LOGLEVEL + valueFrom: + configMapKeyRef: + key: reposerver.log.level + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: reposerver.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_DISABLE_TLS + valueFrom: + configMapKeyRef: + key: reposerver.disable.tls + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_TLS_MIN_VERSION + valueFrom: + configMapKeyRef: + key: reposerver.tls.minversion + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_TLS_MAX_VERSION + valueFrom: + configMapKeyRef: + key: reposerver.tls.maxversion + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_TLS_CIPHERS + valueFrom: + configMapKeyRef: + key: reposerver.tls.ciphers + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_CACHE_EXPIRATION + valueFrom: + configMapKeyRef: + key: reposerver.repo.cache.expiration + name: argocd-cmd-params-cm + optional: true + - name: REDIS_SERVER + valueFrom: + configMapKeyRef: + key: redis.server + name: argocd-cmd-params-cm + optional: true + - name: REDISDB + valueFrom: + configMapKeyRef: + key: redis.db + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_DEFAULT_CACHE_EXPIRATION + valueFrom: + configMapKeyRef: + key: reposerver.default.cache.expiration + 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 + imagePullPolicy: Always + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz?full=true + port: 8084 + initialDelaySeconds: 30 + periodSeconds: 5 + name: argocd-repo-server + ports: + - containerPort: 8081 + - containerPort: 8084 + readinessProbe: + httpGet: + path: /healthz + port: 8084 + initialDelaySeconds: 5 + periodSeconds: 10 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - all + readOnlyRootFilesystem: true + runAsNonRoot: true + volumeMounts: + - mountPath: /app/config/ssh + name: ssh-known-hosts + - mountPath: /app/config/tls + name: tls-certs + - mountPath: /app/config/gpg/source + name: gpg-keys + - mountPath: /app/config/gpg/keys + name: gpg-keyring + - mountPath: /app/config/reposerver/tls + name: argocd-repo-server-tls + - mountPath: /tmp + name: tmp + - mountPath: /helm-working-dir + name: helm-working-dir + volumes: + - configMap: + name: argocd-ssh-known-hosts-cm + name: ssh-known-hosts + - configMap: + name: argocd-tls-certs-cm + name: tls-certs + - configMap: + name: argocd-gpg-keys-cm + name: gpg-keys + - emptyDir: {} + name: gpg-keyring + - emptyDir: {} + name: tmp + - emptyDir: {} + name: helm-working-dir + - name: argocd-repo-server-tls + secret: + items: + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + - key: ca.crt + path: ca.crt + optional: true + secretName: argocd-repo-server-tls +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app.kubernetes.io/component: application-controller + app.kubernetes.io/name: argocd-application-controller + app.kubernetes.io/part-of: argocd + name: argocd-application-controller +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: argocd-application-controller + serviceName: argocd-application-controller + template: + metadata: + labels: + app.kubernetes.io/name: argocd-application-controller + spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: argocd-application-controller + topologyKey: kubernetes.io/hostname + weight: 100 + - podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/part-of: argocd + topologyKey: kubernetes.io/hostname + weight: 5 + containers: + - command: + - argocd-application-controller + env: + - name: ARGOCD_RECONCILIATION_TIMEOUT + valueFrom: + configMapKeyRef: + key: timeout.reconciliation + name: argocd-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER + valueFrom: + configMapKeyRef: + key: repo.server + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS + valueFrom: + configMapKeyRef: + key: controller.repo.server.timeout.seconds + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS + valueFrom: + configMapKeyRef: + key: controller.status.processors + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OPERATION_PROCESSORS + valueFrom: + configMapKeyRef: + key: controller.operation.processors + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_LOGFORMAT + valueFrom: + configMapKeyRef: + key: controller.log.format + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_LOGLEVEL + valueFrom: + configMapKeyRef: + key: controller.log.level + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_METRICS_CACHE_EXPIRATION + valueFrom: + configMapKeyRef: + key: controller.metrics.cache.expiration + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS + valueFrom: + configMapKeyRef: + key: controller.self.heal.timeout.seconds + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT + valueFrom: + configMapKeyRef: + key: controller.repo.server.plaintext + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS + valueFrom: + configMapKeyRef: + key: controller.repo.server.strict.tls + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APP_STATE_CACHE_EXPIRATION + valueFrom: + configMapKeyRef: + key: controller.app.state.cache.expiration + name: argocd-cmd-params-cm + optional: true + - name: REDIS_SERVER + valueFrom: + configMapKeyRef: + key: redis.server + name: argocd-cmd-params-cm + optional: true + - name: REDISDB + valueFrom: + configMapKeyRef: + key: redis.db + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_DEFAULT_CACHE_EXPIRATION + valueFrom: + configMapKeyRef: + key: controller.default.cache.expiration + name: argocd-cmd-params-cm + optional: true + image: quay.io/argoproj/argocd:latest + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /healthz + port: 8082 + initialDelaySeconds: 5 + periodSeconds: 10 + name: argocd-application-controller + ports: + - containerPort: 8082 + readinessProbe: + httpGet: + path: /healthz + port: 8082 + initialDelaySeconds: 5 + periodSeconds: 10 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - all + readOnlyRootFilesystem: true + runAsNonRoot: true + volumeMounts: + - mountPath: /app/config/controller/tls + name: argocd-repo-server-tls + - mountPath: /home/argocd + name: argocd-home + workingDir: /home/argocd + serviceAccountName: argocd-application-controller + volumes: + - emptyDir: {} + name: argocd-home + - name: argocd-repo-server-tls + secret: + items: + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + - key: ca.crt + path: ca.crt + optional: true + secretName: argocd-repo-server-tls +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: argocd-application-controller-network-policy +spec: + ingress: + - from: + - namespaceSelector: {} + ports: + - port: 8082 + podSelector: + matchLabels: + app.kubernetes.io/name: argocd-application-controller + policyTypes: + - Ingress +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: argocd-redis-network-policy +spec: + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-server + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-repo-server + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-application-controller + ports: + - port: 6379 + protocol: TCP + podSelector: + matchLabels: + app.kubernetes.io/name: argocd-redis + policyTypes: + - Ingress +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: argocd-repo-server-network-policy +spec: + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-server + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-application-controller + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-notifications-controller + ports: + - port: 8081 + protocol: TCP + - from: + - namespaceSelector: {} + ports: + - port: 8084 + podSelector: + matchLabels: + app.kubernetes.io/name: argocd-repo-server + policyTypes: + - Ingress diff --git a/manifests/core-install/kustomization.yaml b/manifests/core-install/kustomization.yaml new file mode 100644 index 0000000000000..f502d9beb1840 --- /dev/null +++ b/manifests/core-install/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../crds +- ../cluster-rbac +- ../base/config +- ../base/application-controller +- ../base/repo-server +- ../base/redis +images: +- name: quay.io/argoproj/argocd + newName: quay.io/argoproj/argocd + newTag: latest diff --git a/manifests/ha/base/overlays/argocd-repo-server-deployment.yaml b/manifests/ha/base/overlays/argocd-repo-server-deployment.yaml index a417f3d2ff130..496bb136f602a 100644 --- a/manifests/ha/base/overlays/argocd-repo-server-deployment.yaml +++ b/manifests/ha/base/overlays/argocd-repo-server-deployment.yaml @@ -23,7 +23,7 @@ spec: containers: - name: argocd-repo-server command: - - uid_entrypoint.sh + - entrypoint.sh - argocd-repo-server - --redis - "argocd-redis-ha-haproxy:6379" diff --git a/manifests/ha/base/overlays/argocd-server-deployment.yaml b/manifests/ha/base/overlays/argocd-server-deployment.yaml index 6d57900335315..0b09752afa3a0 100644 --- a/manifests/ha/base/overlays/argocd-server-deployment.yaml +++ b/manifests/ha/base/overlays/argocd-server-deployment.yaml @@ -27,7 +27,5 @@ spec: value: '2' command: - argocd-server - - --staticassets - - /shared/app - --redis - "argocd-redis-ha-haproxy:6379" diff --git a/manifests/ha/base/redis-ha/chart/upstream.yaml b/manifests/ha/base/redis-ha/chart/upstream.yaml index e2d73eadbd315..08b386672464a 100644 --- a/manifests/ha/base/redis-ha/chart/upstream.yaml +++ b/manifests/ha/base/redis-ha/chart/upstream.yaml @@ -770,7 +770,7 @@ spec: topologyKey: kubernetes.io/hostname initContainers: - name: config-init - image: haproxy:2.0.22-alpine + image: haproxy:2.0.25-alpine imagePullPolicy: IfNotPresent resources: {} @@ -790,7 +790,7 @@ spec: runAsUser: 1000 containers: - name: haproxy - image: haproxy:2.0.22-alpine + image: haproxy:2.0.25-alpine imagePullPolicy: IfNotPresent livenessProbe: httpGet: diff --git a/manifests/ha/base/redis-ha/chart/values.yaml b/manifests/ha/base/redis-ha/chart/values.yaml index a3188a005956a..bf3e60b2579df 100644 --- a/manifests/ha/base/redis-ha/chart/values.yaml +++ b/manifests/ha/base/redis-ha/chart/values.yaml @@ -9,7 +9,7 @@ redis-ha: haproxy: enabled: true image: - tag: 2.0.22-alpine + tag: 2.0.25-alpine timeout: server: 6m client: 6m diff --git a/manifests/ha/base/redis-ha/kustomization.yaml b/manifests/ha/base/redis-ha/kustomization.yaml index 7d7a4a3ce5f54..4766d0b7bfdaf 100644 --- a/manifests/ha/base/redis-ha/kustomization.yaml +++ b/manifests/ha/base/redis-ha/kustomization.yaml @@ -114,6 +114,12 @@ patchesJson6902: path: overlays/remove-namespace.yaml # Replace helm's app/release/chart/heritage labels with argocd common labels +- target: + version: v1 + group: "" + kind: ConfigMap + name: argocd-redis-ha-health-configmap + path: overlays/modify-labels.yaml - target: version: v1 group: "" @@ -162,12 +168,24 @@ patchesJson6902: kind: Role name: argocd-redis-ha path: overlays/modify-labels.yaml +- target: + group: rbac.authorization.k8s.io + version: v1 + kind: Role + name: argocd-redis-ha-haproxy + path: overlays/modify-labels.yaml - target: group: rbac.authorization.k8s.io version: v1 kind: RoleBinding name: argocd-redis-ha path: overlays/modify-labels.yaml +- target: + group: rbac.authorization.k8s.io + version: v1 + kind: RoleBinding + name: argocd-redis-ha-haproxy + path: overlays/modify-labels.yaml - target: version: v1 group: "" diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index fe91dc808e9d2..85fa855bfc76b 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -2650,11 +2650,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: - app: redis-ha - chart: redis-ha-4.12.15 - component: argocd-redis-ha-haproxy - heritage: Helm - release: argocd + app.kubernetes.io/component: redis + app.kubernetes.io/name: argocd-redis-ha + app.kubernetes.io/part-of: argocd name: argocd-redis-ha-haproxy rules: - apiGroups: @@ -2810,11 +2808,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app: redis-ha - chart: redis-ha-4.12.15 - component: argocd-redis-ha-haproxy - heritage: Helm - release: argocd + app.kubernetes.io/component: redis + app.kubernetes.io/name: argocd-redis-ha + app.kubernetes.io/part-of: argocd name: argocd-redis-ha-haproxy roleRef: apiGroup: rbac.authorization.k8s.io @@ -3369,10 +3365,9 @@ data: kind: ConfigMap metadata: labels: - app: argocd-redis-ha - chart: redis-ha-4.12.15 - heritage: Helm - release: argocd + app.kubernetes.io/component: redis + app.kubernetes.io/name: argocd-redis-ha + app.kubernetes.io/part-of: argocd name: argocd-redis-ha-health-configmap --- apiVersion: v1 @@ -3662,7 +3657,7 @@ spec: - command: - /shared/argocd-dex - rundex - image: ghcr.io/dexidp/dex:v2.27.0 + image: ghcr.io/dexidp/dex:v2.30.0 imagePullPolicy: Always name: dex ports: @@ -3731,7 +3726,7 @@ spec: app.kubernetes.io/name: argocd-redis-ha-haproxy topologyKey: kubernetes.io/hostname containers: - - image: haproxy:2.0.22-alpine + - image: haproxy:2.0.25-alpine imagePullPolicy: IfNotPresent lifecycle: {} livenessProbe: @@ -3760,7 +3755,7 @@ spec: - /readonly/haproxy_init.sh command: - sh - image: haproxy:2.0.22-alpine + image: haproxy:2.0.25-alpine imagePullPolicy: IfNotPresent name: config-init volumeMounts: @@ -3818,7 +3813,7 @@ spec: automountServiceAccountToken: false containers: - command: - - uid_entrypoint.sh + - entrypoint.sh - argocd-repo-server - --redis - argocd-redis-ha-haproxy:6379 @@ -3895,6 +3890,12 @@ spec: key: reposerver.default.cache.expiration 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 imagePullPolicy: Always livenessProbe: @@ -3934,6 +3935,8 @@ spec: name: argocd-repo-server-tls - mountPath: /tmp name: tmp + - mountPath: /helm-working-dir + name: helm-working-dir volumes: - configMap: name: argocd-ssh-known-hosts-cm @@ -3948,6 +3951,8 @@ spec: name: gpg-keyring - emptyDir: {} name: tmp + - emptyDir: {} + name: helm-working-dir - name: argocd-repo-server-tls secret: items: @@ -3995,8 +4000,6 @@ spec: containers: - command: - argocd-server - - --staticassets - - /shared/app - --redis - argocd-redis-ha-haproxy:6379 env: @@ -4116,6 +4119,12 @@ spec: key: server.login.attempts.expiration name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_STATIC_ASSETS + valueFrom: + configMapKeyRef: + key: server.staticassets + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APP_STATE_CACHE_EXPIRATION valueFrom: configMapKeyRef: diff --git a/manifests/ha/namespace-install.yaml b/manifests/ha/namespace-install.yaml index a61ab34a83861..311f6e719bbe0 100644 --- a/manifests/ha/namespace-install.yaml +++ b/manifests/ha/namespace-install.yaml @@ -122,11 +122,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: - app: redis-ha - chart: redis-ha-4.12.15 - component: argocd-redis-ha-haproxy - heritage: Helm - release: argocd + app.kubernetes.io/component: redis + app.kubernetes.io/name: argocd-redis-ha + app.kubernetes.io/part-of: argocd name: argocd-redis-ha-haproxy rules: - apiGroups: @@ -231,11 +229,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app: redis-ha - chart: redis-ha-4.12.15 - component: argocd-redis-ha-haproxy - heritage: Helm - release: argocd + app.kubernetes.io/component: redis + app.kubernetes.io/name: argocd-redis-ha + app.kubernetes.io/part-of: argocd name: argocd-redis-ha-haproxy roleRef: apiGroup: rbac.authorization.k8s.io @@ -756,10 +752,9 @@ data: kind: ConfigMap metadata: labels: - app: argocd-redis-ha - chart: redis-ha-4.12.15 - heritage: Helm - release: argocd + app.kubernetes.io/component: redis + app.kubernetes.io/name: argocd-redis-ha + app.kubernetes.io/part-of: argocd name: argocd-redis-ha-health-configmap --- apiVersion: v1 @@ -1049,7 +1044,7 @@ spec: - command: - /shared/argocd-dex - rundex - image: ghcr.io/dexidp/dex:v2.27.0 + image: ghcr.io/dexidp/dex:v2.30.0 imagePullPolicy: Always name: dex ports: @@ -1118,7 +1113,7 @@ spec: app.kubernetes.io/name: argocd-redis-ha-haproxy topologyKey: kubernetes.io/hostname containers: - - image: haproxy:2.0.22-alpine + - image: haproxy:2.0.25-alpine imagePullPolicy: IfNotPresent lifecycle: {} livenessProbe: @@ -1147,7 +1142,7 @@ spec: - /readonly/haproxy_init.sh command: - sh - image: haproxy:2.0.22-alpine + image: haproxy:2.0.25-alpine imagePullPolicy: IfNotPresent name: config-init volumeMounts: @@ -1205,7 +1200,7 @@ spec: automountServiceAccountToken: false containers: - command: - - uid_entrypoint.sh + - entrypoint.sh - argocd-repo-server - --redis - argocd-redis-ha-haproxy:6379 @@ -1282,6 +1277,12 @@ spec: key: reposerver.default.cache.expiration 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 imagePullPolicy: Always livenessProbe: @@ -1321,6 +1322,8 @@ spec: name: argocd-repo-server-tls - mountPath: /tmp name: tmp + - mountPath: /helm-working-dir + name: helm-working-dir volumes: - configMap: name: argocd-ssh-known-hosts-cm @@ -1335,6 +1338,8 @@ spec: name: gpg-keyring - emptyDir: {} name: tmp + - emptyDir: {} + name: helm-working-dir - name: argocd-repo-server-tls secret: items: @@ -1382,8 +1387,6 @@ spec: containers: - command: - argocd-server - - --staticassets - - /shared/app - --redis - argocd-redis-ha-haproxy:6379 env: @@ -1503,6 +1506,12 @@ spec: key: server.login.attempts.expiration name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_STATIC_ASSETS + valueFrom: + configMapKeyRef: + key: server.staticassets + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APP_STATE_CACHE_EXPIRATION valueFrom: configMapKeyRef: diff --git a/manifests/install.yaml b/manifests/install.yaml index 496237a5f38ff..b46cfd107e0bb 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -3027,7 +3027,7 @@ spec: - command: - /shared/argocd-dex - rundex - image: ghcr.io/dexidp/dex:v2.27.0 + image: ghcr.io/dexidp/dex:v2.30.0 imagePullPolicy: Always name: dex ports: @@ -3147,7 +3147,7 @@ spec: automountServiceAccountToken: false containers: - command: - - uid_entrypoint.sh + - entrypoint.sh - argocd-repo-server - --redis - argocd-redis:6379 @@ -3224,6 +3224,12 @@ spec: key: reposerver.default.cache.expiration 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 imagePullPolicy: Always livenessProbe: @@ -3263,6 +3269,8 @@ spec: name: argocd-repo-server-tls - mountPath: /tmp name: tmp + - mountPath: /helm-working-dir + name: helm-working-dir volumes: - configMap: name: argocd-ssh-known-hosts-cm @@ -3277,6 +3285,8 @@ spec: name: gpg-keyring - emptyDir: {} name: tmp + - emptyDir: {} + name: helm-working-dir - name: argocd-repo-server-tls secret: items: @@ -3324,8 +3334,6 @@ spec: containers: - command: - argocd-server - - --staticassets - - /shared/app env: - name: ARGOCD_SERVER_INSECURE valueFrom: @@ -3441,6 +3449,12 @@ spec: key: server.login.attempts.expiration name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_STATIC_ASSETS + valueFrom: + configMapKeyRef: + key: server.staticassets + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APP_STATE_CACHE_EXPIRATION valueFrom: configMapKeyRef: diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 3b4741b1a916e..bb91dd1e2ec9d 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -414,7 +414,7 @@ spec: - command: - /shared/argocd-dex - rundex - image: ghcr.io/dexidp/dex:v2.27.0 + image: ghcr.io/dexidp/dex:v2.30.0 imagePullPolicy: Always name: dex ports: @@ -534,7 +534,7 @@ spec: automountServiceAccountToken: false containers: - command: - - uid_entrypoint.sh + - entrypoint.sh - argocd-repo-server - --redis - argocd-redis:6379 @@ -611,6 +611,12 @@ spec: key: reposerver.default.cache.expiration 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 imagePullPolicy: Always livenessProbe: @@ -650,6 +656,8 @@ spec: name: argocd-repo-server-tls - mountPath: /tmp name: tmp + - mountPath: /helm-working-dir + name: helm-working-dir volumes: - configMap: name: argocd-ssh-known-hosts-cm @@ -664,6 +672,8 @@ spec: name: gpg-keyring - emptyDir: {} name: tmp + - emptyDir: {} + name: helm-working-dir - name: argocd-repo-server-tls secret: items: @@ -711,8 +721,6 @@ spec: containers: - command: - argocd-server - - --staticassets - - /shared/app env: - name: ARGOCD_SERVER_INSECURE valueFrom: @@ -828,6 +836,12 @@ spec: key: server.login.attempts.expiration name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_STATIC_ASSETS + valueFrom: + configMapKeyRef: + key: server.staticassets + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APP_STATE_CACHE_EXPIRATION valueFrom: configMapKeyRef: diff --git a/mkdocs.yml b/mkdocs.yml index d2d5d1f0bb754..020aa7b0fd44d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -31,6 +31,7 @@ nav: - Operator Manual: - operator-manual/index.md - operator-manual/architecture.md + - operator-manual/installation.md - operator-manual/declarative-setup.md - operator-manual/ingress.md - User Management: @@ -63,7 +64,6 @@ nav: - operator-manual/server-commands/argocd-repo-server.md - operator-manual/server-commands/argocd-dex.md - operator-manual/server-commands/additional-configuration-method.md - - argocd-util Tools: operator-manual/server-commands/argocd-util.md - Upgrading: - operator-manual/upgrading/overview.md - operator-manual/upgrading/2.0-2.1.md @@ -109,7 +109,8 @@ nav: - Command Reference: user-guide/commands/argocd.md - Developer Guide: - developer-guide/index.md - - developer-guide/contributing.md + - Code Contribution Guide: developer-guide/code-contributions.md + - Toolchain Guide: developer-guide/toolchain-guide.md - developer-guide/release-process-and-cadence.md - developer-guide/running-locally.md - developer-guide/debugging-remote-environment.md diff --git a/pkg/apiclient/apiclient.go b/pkg/apiclient/apiclient.go index 7ad1feb77b9ce..c4ce8234d7161 100644 --- a/pkg/apiclient/apiclient.go +++ b/pkg/apiclient/apiclient.go @@ -47,6 +47,7 @@ import ( argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/env" grpc_util "github.com/argoproj/argo-cd/v2/util/grpc" + http_util "github.com/argoproj/argo-cd/v2/util/http" argoio "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/kube" "github.com/argoproj/argo-cd/v2/util/localconfig" @@ -113,6 +114,7 @@ type ClientOptions struct { UserAgent string GRPCWeb bool GRPCWebRootPath string + Core bool PortForward bool PortForwardNamespace string Headers []string @@ -201,7 +203,7 @@ func NewClient(opts *ClientOptions) (Client, error) { if opts.KubeOverrides == nil { opts.KubeOverrides = &clientcmd.ConfigOverrides{} } - port, err := kube.PortForward("app.kubernetes.io/name=argocd-server", 8080, opts.PortForwardNamespace, opts.KubeOverrides) + port, err := kube.PortForward(8080, opts.PortForwardNamespace, opts.KubeOverrides, "app.kubernetes.io/name=argocd-server") if err != nil { return nil, err } @@ -278,10 +280,11 @@ func NewClient(opts *ClientOptions) (Client, error) { if !c.GRPCWeb { //test if we need to set it to true //if a call to grpc failed, then try again with GRPCWeb - conn, versionIf := c.NewVersionClientOrDie() - defer argoio.Close(conn) - - _, err := versionIf.Version(context.Background(), &empty.Empty{}) + conn, versionIf, err := c.NewVersionClient() + if err == nil { + defer argoio.Close(conn) + _, err = versionIf.Version(context.Background(), &empty.Empty{}) + } if err != nil { c.GRPCWeb = true conn, versionIf := c.NewVersionClientOrDie() @@ -352,16 +355,29 @@ func (c *client) HTTPClient() (*http.Client, error) { if err != nil { return nil, err } + + headers, err := parseHeaders(c.Headers) + if err != nil { + return nil, err + } + + if c.UserAgent != "" { + headers.Set("User-Agent", c.UserAgent) + } + return &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: tlsConfig, - Proxy: http.ProxyFromEnvironment, - Dial: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).Dial, - TLSHandshakeTimeout: 10 * time.Second, - ExpectContinueTimeout: 1 * time.Second, + Transport: &http_util.TransportWithHeader{ + RoundTripper: &http.Transport{ + TLSClientConfig: tlsConfig, + Proxy: http.ProxyFromEnvironment, + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }, + Header: headers, }, }, nil } @@ -506,11 +522,14 @@ func (c *client) newConn() (*grpc.ClientConn, io.Closer, error) { ctx := context.Background() - for _, kv := range c.Headers { - if len(strings.Split(kv, ":"))%2 == 1 { - return nil, nil, fmt.Errorf("additional headers must be colon(:)-separated: %s", kv) + headers, err := parseHeaders(c.Headers) + if err != nil { + return nil, nil, err + } + for k, vs := range headers { + for _, v := range vs { + ctx = metadata.AppendToOutgoingContext(ctx, k, v) } - ctx = metadata.AppendToOutgoingContext(ctx, strings.Split(kv, ":")[0], strings.Split(kv, ":")[1]) } if c.UserAgent != "" { @@ -794,3 +813,15 @@ func isCanceledContextErr(err error) bool { } return false } + +func parseHeaders(headerStrings []string) (http.Header, error) { + headers := http.Header{} + for _, kv := range headerStrings { + items := strings.Split(kv, ":") + if len(items)%2 == 1 { + return nil, fmt.Errorf("additional headers must be colon(:)-separated: %s", kv) + } + headers.Add(items[0], items[1]) + } + return headers, nil +} diff --git a/pkg/apiclient/project/project.pb.go b/pkg/apiclient/project/project.pb.go index 86171d8edde12..e7e82e4066330 100644 --- a/pkg/apiclient/project/project.pb.go +++ b/pkg/apiclient/project/project.pb.go @@ -567,6 +567,77 @@ func (m *GlobalProjectsResponse) GetItems() []*v1alpha1.AppProject { return nil } +type DetailedProjectsResponse struct { + GlobalProjects []*v1alpha1.AppProject `protobuf:"bytes,1,rep,name=globalProjects,proto3" json:"globalProjects,omitempty"` + Project *v1alpha1.AppProject `protobuf:"bytes,2,opt,name=project,proto3" json:"project,omitempty"` + Repositories []*v1alpha1.Repository `protobuf:"bytes,3,rep,name=repositories,proto3" json:"repositories,omitempty"` + Clusters []*v1alpha1.Cluster `protobuf:"bytes,4,rep,name=clusters,proto3" json:"clusters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DetailedProjectsResponse) Reset() { *m = DetailedProjectsResponse{} } +func (m *DetailedProjectsResponse) String() string { return proto.CompactTextString(m) } +func (*DetailedProjectsResponse) ProtoMessage() {} +func (*DetailedProjectsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5f0a51496972c9e2, []int{10} +} +func (m *DetailedProjectsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DetailedProjectsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DetailedProjectsResponse.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 *DetailedProjectsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DetailedProjectsResponse.Merge(m, src) +} +func (m *DetailedProjectsResponse) XXX_Size() int { + return m.Size() +} +func (m *DetailedProjectsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DetailedProjectsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DetailedProjectsResponse proto.InternalMessageInfo + +func (m *DetailedProjectsResponse) GetGlobalProjects() []*v1alpha1.AppProject { + if m != nil { + return m.GlobalProjects + } + return nil +} + +func (m *DetailedProjectsResponse) GetProject() *v1alpha1.AppProject { + if m != nil { + return m.Project + } + return nil +} + +func (m *DetailedProjectsResponse) GetRepositories() []*v1alpha1.Repository { + if m != nil { + return m.Repositories + } + return nil +} + +func (m *DetailedProjectsResponse) GetClusters() []*v1alpha1.Cluster { + if m != nil { + return m.Clusters + } + return nil +} + func init() { proto.RegisterType((*ProjectCreateRequest)(nil), "project.ProjectCreateRequest") proto.RegisterType((*ProjectTokenDeleteRequest)(nil), "project.ProjectTokenDeleteRequest") @@ -578,67 +649,75 @@ func init() { proto.RegisterType((*SyncWindowsQuery)(nil), "project.SyncWindowsQuery") proto.RegisterType((*SyncWindowsResponse)(nil), "project.SyncWindowsResponse") proto.RegisterType((*GlobalProjectsResponse)(nil), "project.GlobalProjectsResponse") + proto.RegisterType((*DetailedProjectsResponse)(nil), "project.DetailedProjectsResponse") } func init() { proto.RegisterFile("server/project/project.proto", fileDescriptor_5f0a51496972c9e2) } var fileDescriptor_5f0a51496972c9e2 = []byte{ - // 874 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xc1, 0x6e, 0x23, 0x45, - 0x10, 0x55, 0xc7, 0x89, 0x97, 0x74, 0x60, 0x09, 0xbd, 0xd9, 0xe0, 0x98, 0x6c, 0xd6, 0x6a, 0x44, - 0x64, 0x05, 0xd2, 0xad, 0x38, 0x8b, 0xb4, 0x82, 0x13, 0x0b, 0xab, 0x80, 0x94, 0x03, 0x38, 0x20, - 0x10, 0x07, 0x50, 0x7b, 0xa6, 0x34, 0xe9, 0xd8, 0x9e, 0x6e, 0x66, 0x3a, 0xb3, 0x6b, 0xac, 0x5c, - 0x90, 0x00, 0x89, 0x03, 0x07, 0x38, 0xf1, 0x03, 0x7c, 0x00, 0x5f, 0xc1, 0x11, 0x89, 0x1f, 0x40, - 0x11, 0x1f, 0x82, 0xba, 0xa7, 0x67, 0x6c, 0xc7, 0x1e, 0x04, 0xc2, 0xe2, 0xe4, 0x9a, 0x9a, 0x9a, - 0x7a, 0xaf, 0x5e, 0x57, 0x95, 0x1b, 0xef, 0xa6, 0x90, 0x64, 0x90, 0x70, 0x9d, 0xa8, 0x0b, 0x08, - 0x4c, 0xf1, 0xcb, 0x74, 0xa2, 0x8c, 0x22, 0xb7, 0xfc, 0x63, 0x73, 0x2b, 0x52, 0x91, 0x72, 0x3e, - 0x6e, 0xad, 0xfc, 0x75, 0x73, 0x37, 0x52, 0x2a, 0x1a, 0x00, 0x17, 0x5a, 0x72, 0x11, 0xc7, 0xca, - 0x08, 0x23, 0x55, 0x9c, 0xfa, 0xb7, 0xb4, 0xff, 0x30, 0x65, 0x52, 0xb9, 0xb7, 0x81, 0x4a, 0x80, - 0x67, 0x47, 0x3c, 0x82, 0x18, 0x12, 0x61, 0x20, 0xf4, 0x31, 0x0f, 0x26, 0x31, 0x43, 0x11, 0x9c, - 0xcb, 0x18, 0x92, 0x11, 0xd7, 0xfd, 0xc8, 0x3a, 0x52, 0x3e, 0x04, 0x23, 0x16, 0x7d, 0x75, 0x1a, - 0x49, 0x73, 0x7e, 0xd9, 0x63, 0x81, 0x1a, 0x72, 0x91, 0x38, 0x62, 0x17, 0xce, 0x38, 0x0c, 0x42, - 0x9e, 0x75, 0x26, 0x09, 0x84, 0xd6, 0x03, 0x19, 0x38, 0x56, 0x3c, 0x3b, 0x12, 0x03, 0x7d, 0x2e, - 0xe6, 0xb2, 0xd1, 0x1f, 0x10, 0xde, 0x7a, 0x3f, 0xaf, 0xf3, 0xed, 0x04, 0x84, 0x81, 0x2e, 0x7c, - 0x71, 0x09, 0xa9, 0x21, 0x3d, 0x5c, 0xd4, 0xdf, 0x40, 0x2d, 0xd4, 0xde, 0xe8, 0xbc, 0xcb, 0x26, - 0xc0, 0xac, 0x00, 0x76, 0xc6, 0xe7, 0x41, 0xc8, 0xb2, 0x0e, 0xd3, 0xfd, 0x88, 0x59, 0x60, 0x36, - 0x05, 0xcc, 0x0a, 0x60, 0xf6, 0x96, 0xd6, 0x1e, 0xa7, 0x5b, 0x24, 0x26, 0xdb, 0xb8, 0x7e, 0xa9, - 0x53, 0x48, 0x4c, 0x63, 0xa5, 0x85, 0xda, 0xcf, 0x74, 0xfd, 0x13, 0xed, 0xe3, 0x1d, 0x1f, 0xfb, - 0xa1, 0xea, 0x43, 0xfc, 0x0e, 0x0c, 0x60, 0x42, 0xac, 0x31, 0x4b, 0x6c, 0x7d, 0x92, 0x8e, 0xe0, - 0xd5, 0x44, 0x0d, 0xc0, 0x25, 0x5b, 0xef, 0x3a, 0x9b, 0x6c, 0xe2, 0x9a, 0x14, 0xa6, 0x51, 0x6b, - 0xa1, 0x76, 0xad, 0x6b, 0x4d, 0x72, 0x1b, 0xaf, 0xc8, 0xb0, 0xb1, 0xea, 0x62, 0x56, 0x64, 0x48, - 0x7f, 0x42, 0xb3, 0x68, 0xb3, 0x32, 0x54, 0xa3, 0xb5, 0xf0, 0x46, 0x08, 0x69, 0x90, 0x48, 0x6d, - 0x0b, 0xf5, 0xa0, 0xd3, 0xae, 0x92, 0x4f, 0x6d, 0x8a, 0xcf, 0x2e, 0x5e, 0x87, 0xa7, 0x5a, 0x26, - 0x90, 0xbe, 0x17, 0x3b, 0x12, 0xb5, 0xee, 0xc4, 0xe1, 0xb9, 0xad, 0x95, 0xdc, 0x5e, 0x2b, 0x0f, - 0xc7, 0x51, 0xeb, 0x42, 0xaa, 0x55, 0x9c, 0x02, 0xd9, 0xc2, 0x6b, 0xc6, 0x3a, 0x3c, 0xa7, 0xfc, - 0x81, 0x52, 0xfc, 0xac, 0x8f, 0xfe, 0xe0, 0x12, 0x92, 0x91, 0xc5, 0x8f, 0xc5, 0x10, 0x7c, 0x90, - 0xb3, 0xe9, 0x97, 0x65, 0xc6, 0x8f, 0x74, 0xf8, 0xff, 0x1e, 0x37, 0x7d, 0x1e, 0x3f, 0xf7, 0x78, - 0xa8, 0xcd, 0xa8, 0x28, 0x83, 0xee, 0xe3, 0xcd, 0xb3, 0x51, 0x1c, 0x7c, 0x2c, 0xe3, 0x50, 0x3d, - 0x49, 0xab, 0x49, 0x8f, 0xf0, 0x9d, 0xa9, 0xb8, 0x52, 0x85, 0x1e, 0xbe, 0xf5, 0x24, 0x77, 0x35, - 0x50, 0xab, 0xf6, 0xdf, 0x39, 0x4f, 0x30, 0xba, 0x45, 0x62, 0xfa, 0x14, 0x6f, 0x9f, 0x0c, 0x54, - 0x4f, 0x0c, 0x7c, 0x35, 0x13, 0xf4, 0xcf, 0xf0, 0x9a, 0x34, 0x30, 0x5c, 0x12, 0xf6, 0x94, 0x5e, - 0x79, 0xda, 0xce, 0x2f, 0x18, 0xdf, 0xf6, 0xae, 0x33, 0x48, 0x32, 0x19, 0x00, 0xf9, 0x0e, 0xe1, - 0x8d, 0xbc, 0x3d, 0x5d, 0x3b, 0x10, 0xca, 0x8a, 0x8d, 0x55, 0xd9, 0xc0, 0xcd, 0x7b, 0x0b, 0x63, - 0xca, 0x23, 0x78, 0xf8, 0xd5, 0xef, 0x7f, 0xfe, 0xb8, 0xd2, 0xa1, 0x87, 0x6e, 0x53, 0x65, 0x47, - 0xc5, 0x0e, 0x4c, 0xf9, 0xd8, 0x5b, 0x57, 0xdc, 0x36, 0x6e, 0xca, 0xc7, 0xf6, 0xe7, 0x8a, 0xbb, - 0x56, 0x7b, 0x03, 0x1d, 0x90, 0x6f, 0x10, 0xde, 0xc8, 0x27, 0xf3, 0xef, 0xc8, 0xcc, 0xcc, 0x6e, - 0x73, 0xbb, 0x8c, 0x99, 0x6d, 0x84, 0x37, 0x1d, 0x8b, 0xd7, 0x0f, 0x8e, 0xff, 0x15, 0x0b, 0x3e, - 0x96, 0xc2, 0x5c, 0x91, 0xef, 0x11, 0xae, 0xe7, 0x35, 0x93, 0xb9, 0x62, 0x67, 0xb5, 0x58, 0xda, - 0x19, 0xd1, 0x97, 0x1c, 0xe1, 0xbb, 0x74, 0xf3, 0x26, 0x61, 0xab, 0xcc, 0xd7, 0x08, 0xaf, 0x9e, - 0xca, 0xd4, 0x90, 0xbb, 0x37, 0xe9, 0xb8, 0x16, 0x6f, 0x9e, 0x2e, 0x8b, 0x86, 0x05, 0xa1, 0x0d, - 0x47, 0x85, 0x90, 0x39, 0x2a, 0xe4, 0x5b, 0x84, 0x6b, 0x27, 0x50, 0x49, 0x63, 0x79, 0x6a, 0xdc, - 0x77, 0x14, 0x76, 0xc8, 0x8b, 0xf3, 0xc7, 0x67, 0xe7, 0xf7, 0x8a, 0x8c, 0xf1, 0x0b, 0x27, 0x60, - 0x66, 0x07, 0xa9, 0x8a, 0xd6, 0xfd, 0xd2, 0xbd, 0x78, 0xf0, 0x28, 0x73, 0x68, 0x6d, 0xb2, 0x5f, - 0x81, 0xc6, 0x23, 0xf7, 0x5d, 0x29, 0xc3, 0xcf, 0x08, 0xd7, 0xf3, 0x65, 0x37, 0xdf, 0x1f, 0x33, - 0x4b, 0x70, 0x89, 0x8a, 0x1c, 0x3b, 0x8e, 0x87, 0xcd, 0x76, 0x65, 0x43, 0x33, 0xfb, 0xff, 0x1e, - 0x0a, 0x23, 0x98, 0x23, 0x6d, 0xfb, 0xe6, 0x13, 0x5c, 0xcf, 0xc7, 0xa5, 0x4a, 0x9a, 0xaa, 0xf1, - 0xf1, 0xfa, 0x1f, 0x54, 0xea, 0x7f, 0x81, 0xb1, 0xed, 0x95, 0xc7, 0x19, 0xc4, 0xd5, 0xc2, 0xdf, - 0x63, 0xf9, 0x7d, 0xc4, 0x56, 0xc8, 0xec, 0x9d, 0x85, 0x65, 0x47, 0xcc, 0x7d, 0xe2, 0xfa, 0x6c, - 0xdf, 0x81, 0xb4, 0xc8, 0x5e, 0x95, 0xec, 0x90, 0x67, 0x1f, 0xe3, 0x3b, 0x27, 0x60, 0xa6, 0xf6, - 0xf5, 0x99, 0xb1, 0xd2, 0xef, 0x94, 0xa0, 0x37, 0x57, 0x7e, 0x73, 0x77, 0xd1, 0xab, 0xb2, 0xb8, - 0x57, 0x1d, 0xee, 0x2b, 0xe4, 0xe5, 0x2a, 0xdc, 0x74, 0x14, 0x07, 0x7e, 0x5d, 0x3f, 0x7a, 0xf4, - 0xeb, 0xf5, 0x1e, 0xfa, 0xed, 0x7a, 0x0f, 0xfd, 0x71, 0xbd, 0x87, 0x3e, 0x7d, 0xf0, 0xcf, 0xae, - 0x4a, 0xc1, 0x40, 0x42, 0x5c, 0xde, 0xfe, 0x7a, 0x75, 0x77, 0x33, 0x3a, 0xfe, 0x2b, 0x00, 0x00, - 0xff, 0xff, 0x22, 0xe7, 0xcb, 0x09, 0x1e, 0x0a, 0x00, 0x00, + // 977 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x97, 0xcf, 0x6f, 0x23, 0x35, + 0x14, 0xc7, 0x35, 0x49, 0x9b, 0xdd, 0x3a, 0x4b, 0x29, 0xde, 0x6e, 0x99, 0x86, 0x6e, 0x37, 0x18, + 0x51, 0x45, 0x85, 0x7a, 0xd4, 0x74, 0x91, 0x56, 0x70, 0x62, 0x77, 0xab, 0x80, 0xd4, 0x03, 0x4c, + 0x41, 0x20, 0x0e, 0x20, 0x67, 0xe6, 0x29, 0x75, 0x33, 0x19, 0x0f, 0x63, 0x27, 0xdb, 0x10, 0xf5, + 0x82, 0x04, 0x48, 0x1c, 0x38, 0xc0, 0x89, 0x7f, 0x80, 0x13, 0xff, 0x04, 0x37, 0x8e, 0x48, 0xfc, + 0x03, 0xa8, 0xe2, 0x0f, 0x41, 0xf6, 0xfc, 0x48, 0x26, 0xe9, 0x20, 0x50, 0x03, 0xa7, 0x78, 0xde, + 0xbc, 0x79, 0xdf, 0xcf, 0x7b, 0xb6, 0x9f, 0x1d, 0xb4, 0x23, 0x21, 0x1e, 0x41, 0xec, 0x44, 0xb1, + 0x38, 0x07, 0x4f, 0x65, 0xbf, 0x34, 0x8a, 0x85, 0x12, 0xf8, 0x56, 0xfa, 0xd8, 0xd8, 0xec, 0x89, + 0x9e, 0x30, 0x36, 0x47, 0x8f, 0x92, 0xd7, 0x8d, 0x9d, 0x9e, 0x10, 0xbd, 0x00, 0x1c, 0x16, 0x71, + 0x87, 0x85, 0xa1, 0x50, 0x4c, 0x71, 0x11, 0xca, 0xf4, 0x2d, 0xe9, 0x3f, 0x92, 0x94, 0x0b, 0xf3, + 0xd6, 0x13, 0x31, 0x38, 0xa3, 0x43, 0xa7, 0x07, 0x21, 0xc4, 0x4c, 0x81, 0x9f, 0xfa, 0x3c, 0x9c, + 0xfa, 0x0c, 0x98, 0x77, 0xc6, 0x43, 0x88, 0xc7, 0x4e, 0xd4, 0xef, 0x69, 0x83, 0x74, 0x06, 0xa0, + 0xd8, 0x75, 0x5f, 0x9d, 0xf4, 0xb8, 0x3a, 0x1b, 0x76, 0xa9, 0x27, 0x06, 0x0e, 0x8b, 0x0d, 0xd8, + 0xb9, 0x19, 0x1c, 0x78, 0xbe, 0x33, 0x6a, 0x4f, 0x03, 0xb0, 0x28, 0x0a, 0xb8, 0x67, 0xa8, 0x9c, + 0xd1, 0x21, 0x0b, 0xa2, 0x33, 0xb6, 0x10, 0x8d, 0x7c, 0x6f, 0xa1, 0xcd, 0xf7, 0x92, 0x3c, 0x9f, + 0xc4, 0xc0, 0x14, 0xb8, 0xf0, 0xf9, 0x10, 0xa4, 0xc2, 0x5d, 0x94, 0xe5, 0x6f, 0x5b, 0x4d, 0xab, + 0x55, 0x6f, 0xbf, 0x43, 0xa7, 0xc2, 0x34, 0x13, 0x36, 0x83, 0xcf, 0x3c, 0x9f, 0x8e, 0xda, 0x34, + 0xea, 0xf7, 0xa8, 0x16, 0xa6, 0x33, 0xc2, 0x34, 0x13, 0xa6, 0x6f, 0x47, 0x51, 0xaa, 0xe3, 0x66, + 0x81, 0xf1, 0x16, 0xaa, 0x0d, 0x23, 0x09, 0xb1, 0xb2, 0x2b, 0x4d, 0xab, 0x75, 0xdb, 0x4d, 0x9f, + 0x48, 0x1f, 0x6d, 0xa7, 0xbe, 0x1f, 0x88, 0x3e, 0x84, 0x4f, 0x21, 0x80, 0x29, 0x98, 0x5d, 0x04, + 0x5b, 0x9b, 0x86, 0xc3, 0x68, 0x25, 0x16, 0x01, 0x98, 0x60, 0x6b, 0xae, 0x19, 0xe3, 0x0d, 0x54, + 0xe5, 0x4c, 0xd9, 0xd5, 0xa6, 0xd5, 0xaa, 0xba, 0x7a, 0x88, 0xd7, 0x51, 0x85, 0xfb, 0xf6, 0x8a, + 0xf1, 0xa9, 0x70, 0x9f, 0xfc, 0x68, 0x15, 0xd5, 0x8a, 0x65, 0x28, 0x57, 0x6b, 0xa2, 0xba, 0x0f, + 0xd2, 0x8b, 0x79, 0xa4, 0x13, 0x4d, 0x45, 0x67, 0x4d, 0x39, 0x4f, 0x75, 0x86, 0x67, 0x07, 0xad, + 0xc1, 0x45, 0xc4, 0x63, 0x90, 0xef, 0x86, 0x06, 0xa2, 0xea, 0x4e, 0x0d, 0x29, 0xdb, 0x6a, 0xce, + 0xf6, 0x7a, 0x3e, 0x39, 0x06, 0xcd, 0x05, 0x19, 0x89, 0x50, 0x02, 0xde, 0x44, 0xab, 0x4a, 0x1b, + 0x52, 0xa6, 0xe4, 0x81, 0x10, 0x74, 0x27, 0xf5, 0x7e, 0x7f, 0x08, 0xf1, 0x58, 0xeb, 0x87, 0x6c, + 0x00, 0xa9, 0x93, 0x19, 0x93, 0x2f, 0xf2, 0x88, 0x1f, 0x46, 0xfe, 0xff, 0x3b, 0xdd, 0xe4, 0x79, + 0xf4, 0xdc, 0xf1, 0x20, 0x52, 0xe3, 0x2c, 0x0d, 0xb2, 0x87, 0x36, 0x4e, 0xc7, 0xa1, 0xf7, 0x11, + 0x0f, 0x7d, 0xf1, 0x4c, 0x96, 0x43, 0x8f, 0xd1, 0xdd, 0x19, 0xbf, 0xbc, 0x0a, 0x5d, 0x74, 0xeb, + 0x59, 0x62, 0xb2, 0xad, 0x66, 0xf5, 0xe6, 0xcc, 0x53, 0x0d, 0x37, 0x0b, 0x4c, 0x2e, 0xd0, 0x56, + 0x27, 0x10, 0x5d, 0x16, 0xa4, 0xd9, 0x4c, 0xd5, 0x3f, 0x45, 0xab, 0x5c, 0xc1, 0x60, 0x49, 0xda, + 0x33, 0xf5, 0x4a, 0xc2, 0x92, 0x5f, 0xaa, 0xc8, 0x7e, 0x0a, 0x8a, 0xf1, 0x00, 0xfc, 0x05, 0xf1, + 0x08, 0xad, 0xf7, 0x0a, 0x58, 0x4b, 0xa7, 0x98, 0x8b, 0x3f, 0xbb, 0x40, 0x2a, 0xff, 0x55, 0x3f, + 0x08, 0xd0, 0x9d, 0x18, 0x22, 0x21, 0xb9, 0x12, 0x31, 0x07, 0x69, 0x57, 0x97, 0x91, 0x93, 0x9b, + 0x45, 0x1c, 0xbb, 0x85, 0xe8, 0x98, 0xa1, 0xdb, 0x5e, 0x30, 0x94, 0x0a, 0x62, 0x69, 0xaf, 0x18, + 0xa5, 0xe3, 0x9b, 0x29, 0x3d, 0x49, 0xa2, 0xb9, 0x79, 0xd8, 0xf6, 0xcf, 0x75, 0xb4, 0x9e, 0x66, + 0x79, 0x0a, 0xf1, 0x88, 0x7b, 0x80, 0xbf, 0xb5, 0x50, 0x3d, 0x69, 0x31, 0x66, 0x4b, 0x63, 0x42, + 0xb3, 0x53, 0xa7, 0xb4, 0x09, 0x35, 0xee, 0x5f, 0xeb, 0x93, 0x6f, 0xa3, 0x47, 0x5f, 0xfe, 0xfe, + 0xe7, 0x0f, 0x95, 0x36, 0x39, 0x30, 0xa7, 0xcd, 0xe8, 0x30, 0x3b, 0xc7, 0xa4, 0x33, 0x49, 0x47, + 0x97, 0x8e, 0x6e, 0x3e, 0xd2, 0x99, 0xe8, 0x9f, 0x4b, 0xc7, 0xb4, 0x8b, 0x37, 0xad, 0x7d, 0xfc, + 0xb5, 0x85, 0xea, 0x49, 0x77, 0xfd, 0x3b, 0x98, 0x42, 0xff, 0x6d, 0x6c, 0xe5, 0x3e, 0xc5, 0xcd, + 0xfc, 0x96, 0xa1, 0x78, 0x63, 0xff, 0xe8, 0x5f, 0x51, 0x38, 0x13, 0xce, 0xd4, 0x25, 0xfe, 0xce, + 0x42, 0xb5, 0x24, 0x67, 0xbc, 0x90, 0x6c, 0xb1, 0x16, 0x4b, 0x5b, 0x76, 0xe4, 0x25, 0x03, 0x7c, + 0x8f, 0x6c, 0xcc, 0x03, 0xeb, 0xca, 0x7c, 0x65, 0xa1, 0x95, 0x13, 0x2e, 0x15, 0xbe, 0x37, 0x8f, + 0x63, 0xda, 0x54, 0xe3, 0x64, 0x59, 0x18, 0x5a, 0x84, 0xd8, 0x06, 0x05, 0xe3, 0x05, 0x14, 0x7c, + 0x81, 0x70, 0x07, 0xd4, 0x5c, 0x1f, 0x28, 0x83, 0x7a, 0x39, 0x37, 0x97, 0x35, 0x0e, 0xd2, 0x32, + 0x4a, 0x04, 0x37, 0x17, 0x67, 0x49, 0xb7, 0xda, 0x4b, 0xc7, 0x4f, 0xbf, 0xc4, 0xdf, 0x58, 0xa8, + 0xda, 0x81, 0x52, 0xad, 0xe5, 0xcd, 0xc3, 0x03, 0x83, 0xb4, 0x8d, 0x5f, 0x2c, 0x41, 0xc2, 0x13, + 0xf4, 0x42, 0x07, 0x54, 0xb1, 0x0d, 0x97, 0x61, 0x3d, 0xc8, 0xcd, 0xd7, 0xb7, 0x6d, 0x42, 0x8d, + 0x5a, 0x0b, 0xef, 0x95, 0x15, 0x20, 0xe9, 0x7b, 0xf9, 0x04, 0xfc, 0x64, 0xa1, 0x5a, 0x72, 0x54, + 0x2e, 0xae, 0xcc, 0xc2, 0x11, 0xba, 0xc4, 0x8a, 0x1c, 0x19, 0xc6, 0x83, 0x46, 0xab, 0x74, 0x2b, + 0x51, 0x7d, 0x3b, 0xf4, 0x99, 0x62, 0xd4, 0x40, 0xeb, 0x15, 0xfb, 0x31, 0xaa, 0x25, 0x1b, 0xb5, + 0xac, 0x34, 0x65, 0x1b, 0x37, 0xad, 0xff, 0x7e, 0x69, 0xfd, 0xcf, 0x11, 0xd2, 0xab, 0xf4, 0x78, + 0x04, 0x61, 0x79, 0xe1, 0xef, 0xd3, 0xe4, 0x36, 0xab, 0x33, 0xa4, 0xfa, 0xc6, 0x4b, 0x47, 0x87, + 0xd4, 0x7c, 0x62, 0x56, 0xf8, 0x9e, 0x11, 0x69, 0xe2, 0xdd, 0xb2, 0xb2, 0x43, 0x12, 0x7d, 0x82, + 0xee, 0x76, 0x40, 0xcd, 0x9c, 0xf6, 0xa7, 0x4a, 0x97, 0x7e, 0x3b, 0x17, 0x9d, 0xbf, 0x30, 0x34, + 0x76, 0xae, 0x7b, 0x95, 0x27, 0xf7, 0x9a, 0xd1, 0x7d, 0x15, 0xbf, 0x52, 0xa6, 0x2b, 0xc7, 0xa1, + 0x97, 0x1e, 0xf6, 0x8f, 0x1f, 0xff, 0x7a, 0xb5, 0x6b, 0xfd, 0x76, 0xb5, 0x6b, 0xfd, 0x71, 0xb5, + 0x6b, 0x7d, 0xf2, 0xf0, 0x9f, 0x5d, 0xb4, 0xbd, 0x80, 0x43, 0x98, 0xff, 0x77, 0xe8, 0xd6, 0xcc, + 0xbd, 0xfa, 0xe8, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5b, 0x43, 0xdb, 0x65, 0x5c, 0x0c, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -661,6 +740,8 @@ type ProjectServiceClient interface { Create(ctx context.Context, in *ProjectCreateRequest, opts ...grpc.CallOption) (*v1alpha1.AppProject, error) // List returns list of projects List(ctx context.Context, in *ProjectQuery, opts ...grpc.CallOption) (*v1alpha1.AppProjectList, error) + // GetDetailedProject returns a project that include project, global project and scoped resources by name + GetDetailedProject(ctx context.Context, in *ProjectQuery, opts ...grpc.CallOption) (*DetailedProjectsResponse, error) // Get returns a project by name Get(ctx context.Context, in *ProjectQuery, opts ...grpc.CallOption) (*v1alpha1.AppProject, error) // Get returns a virtual project by name @@ -719,6 +800,15 @@ func (c *projectServiceClient) List(ctx context.Context, in *ProjectQuery, opts return out, nil } +func (c *projectServiceClient) GetDetailedProject(ctx context.Context, in *ProjectQuery, opts ...grpc.CallOption) (*DetailedProjectsResponse, error) { + out := new(DetailedProjectsResponse) + err := c.cc.Invoke(ctx, "/project.ProjectService/GetDetailedProject", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *projectServiceClient) Get(ctx context.Context, in *ProjectQuery, opts ...grpc.CallOption) (*v1alpha1.AppProject, error) { out := new(v1alpha1.AppProject) err := c.cc.Invoke(ctx, "/project.ProjectService/Get", in, out, opts...) @@ -783,6 +873,8 @@ type ProjectServiceServer interface { Create(context.Context, *ProjectCreateRequest) (*v1alpha1.AppProject, error) // List returns list of projects List(context.Context, *ProjectQuery) (*v1alpha1.AppProjectList, error) + // GetDetailedProject returns a project that include project, global project and scoped resources by name + GetDetailedProject(context.Context, *ProjectQuery) (*DetailedProjectsResponse, error) // Get returns a project by name Get(context.Context, *ProjectQuery) (*v1alpha1.AppProject, error) // Get returns a virtual project by name @@ -813,6 +905,9 @@ func (*UnimplementedProjectServiceServer) Create(ctx context.Context, req *Proje func (*UnimplementedProjectServiceServer) List(ctx context.Context, req *ProjectQuery) (*v1alpha1.AppProjectList, error) { return nil, status.Errorf(codes.Unimplemented, "method List not implemented") } +func (*UnimplementedProjectServiceServer) GetDetailedProject(ctx context.Context, req *ProjectQuery) (*DetailedProjectsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetDetailedProject not implemented") +} func (*UnimplementedProjectServiceServer) Get(ctx context.Context, req *ProjectQuery) (*v1alpha1.AppProject, error) { return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") } @@ -908,6 +1003,24 @@ func _ProjectService_List_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } +func _ProjectService_GetDetailedProject_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ProjectQuery) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProjectServiceServer).GetDetailedProject(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/project.ProjectService/GetDetailedProject", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProjectServiceServer).GetDetailedProject(ctx, req.(*ProjectQuery)) + } + return interceptor(ctx, in, info, handler) +} + func _ProjectService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ProjectQuery) if err := dec(in); err != nil { @@ -1036,6 +1149,10 @@ var _ProjectService_serviceDesc = grpc.ServiceDesc{ MethodName: "List", Handler: _ProjectService_List_Handler, }, + { + MethodName: "GetDetailedProject", + Handler: _ProjectService_GetDetailedProject_Handler, + }, { MethodName: "Get", Handler: _ProjectService_Get_Handler, @@ -1477,6 +1594,87 @@ func (m *GlobalProjectsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *DetailedProjectsResponse) 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 *DetailedProjectsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DetailedProjectsResponse) 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.Clusters) > 0 { + for iNdEx := len(m.Clusters) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Clusters[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProject(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.Repositories) > 0 { + for iNdEx := len(m.Repositories) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Repositories[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProject(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.Project != nil { + { + size, err := m.Project.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProject(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.GlobalProjects) > 0 { + for iNdEx := len(m.GlobalProjects) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.GlobalProjects[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProject(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func encodeVarintProject(dAtA []byte, offset int, v uint64) int { offset -= sovProject(v) base := offset @@ -1677,6 +1875,40 @@ func (m *GlobalProjectsResponse) Size() (n int) { return n } +func (m *DetailedProjectsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.GlobalProjects) > 0 { + for _, e := range m.GlobalProjects { + l = e.Size() + n += 1 + l + sovProject(uint64(l)) + } + } + if m.Project != nil { + l = m.Project.Size() + n += 1 + l + sovProject(uint64(l)) + } + if len(m.Repositories) > 0 { + for _, e := range m.Repositories { + l = e.Size() + n += 1 + l + sovProject(uint64(l)) + } + } + if len(m.Clusters) > 0 { + for _, e := range m.Clusters { + l = e.Size() + n += 1 + l + sovProject(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func sovProject(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2711,6 +2943,195 @@ func (m *GlobalProjectsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *DetailedProjectsResponse) 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 ErrIntOverflowProject + } + 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: DetailedProjectsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DetailedProjectsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GlobalProjects", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProject + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProject + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProject + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GlobalProjects = append(m.GlobalProjects, &v1alpha1.AppProject{}) + if err := m.GlobalProjects[len(m.GlobalProjects)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProject + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProject + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProject + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Project == nil { + m.Project = &v1alpha1.AppProject{} + } + if err := m.Project.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Repositories", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProject + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProject + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProject + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Repositories = append(m.Repositories, &v1alpha1.Repository{}) + if err := m.Repositories[len(m.Repositories)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Clusters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProject + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProject + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProject + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Clusters = append(m.Clusters, &v1alpha1.Cluster{}) + if err := m.Clusters[len(m.Clusters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProject(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProject + } + 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 skipProject(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/pkg/apiclient/project/project.pb.gw.go b/pkg/apiclient/project/project.pb.gw.go index 7dd8a526a1678..8ae2e95a62a69 100644 --- a/pkg/apiclient/project/project.pb.gw.go +++ b/pkg/apiclient/project/project.pb.gw.go @@ -311,6 +311,60 @@ func local_request_ProjectService_List_0(ctx context.Context, marshaler runtime. } +func request_ProjectService_GetDetailedProject_0(ctx context.Context, marshaler runtime.Marshaler, client ProjectServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ProjectQuery + 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) + } + + msg, err := client.GetDetailedProject(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ProjectService_GetDetailedProject_0(ctx context.Context, marshaler runtime.Marshaler, server ProjectServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ProjectQuery + 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) + } + + msg, err := server.GetDetailedProject(ctx, &protoReq) + return msg, metadata, err + +} + func request_ProjectService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client ProjectServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ProjectQuery var metadata runtime.ServerMetadata @@ -749,6 +803,29 @@ func RegisterProjectServiceHandlerServer(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_ProjectService_GetDetailedProject_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_ProjectService_GetDetailedProject_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_ProjectService_GetDetailedProject_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_ProjectService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1008,6 +1085,26 @@ func RegisterProjectServiceHandlerClient(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_ProjectService_GetDetailedProject_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_ProjectService_GetDetailedProject_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ProjectService_GetDetailedProject_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_ProjectService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1140,6 +1237,8 @@ var ( pattern_ProjectService_List_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "projects"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_ProjectService_GetDetailedProject_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "projects", "name", "detailed"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_ProjectService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "projects", "name"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ProjectService_GetGlobalProjects_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "projects", "name", "globalprojects"}, "", runtime.AssumeColonVerbOpt(true))) @@ -1162,6 +1261,8 @@ var ( forward_ProjectService_List_0 = runtime.ForwardResponseMessage + forward_ProjectService_GetDetailedProject_0 = runtime.ForwardResponseMessage + forward_ProjectService_Get_0 = runtime.ForwardResponseMessage forward_ProjectService_GetGlobalProjects_0 = runtime.ForwardResponseMessage diff --git a/pkg/apiclient/repository/repository.pb.go b/pkg/apiclient/repository/repository.pb.go index 1d78c363e2a53..6ce4bf9f131bd 100644 --- a/pkg/apiclient/repository/repository.pb.go +++ b/pkg/apiclient/repository/repository.pb.go @@ -340,7 +340,9 @@ type RepoAccessQuery struct { // Github App Enterprise base url if empty will default to https://api.github.com GithubAppEnterpriseBaseUrl string `protobuf:"bytes,15,opt,name=githubAppEnterpriseBaseUrl,proto3" json:"githubAppEnterpriseBaseUrl,omitempty"` // HTTP/HTTPS proxy to access the repository - Proxy string `protobuf:"bytes,16,opt,name=proxy,proto3" json:"proxy,omitempty"` + Proxy string `protobuf:"bytes,16,opt,name=proxy,proto3" json:"proxy,omitempty"` + // Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity + Project string `protobuf:"bytes,17,opt,name=project,proto3" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -484,6 +486,13 @@ func (m *RepoAccessQuery) GetProxy() string { return "" } +func (m *RepoAccessQuery) GetProject() string { + if m != nil { + return m.Project + } + return "" +} + type RepoResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -654,76 +663,77 @@ func init() { } var fileDescriptor_8d38260443475705 = []byte{ - // 1093 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x4f, 0x6f, 0x1b, 0x45, - 0x14, 0xd7, 0x36, 0xa9, 0x9b, 0x4c, 0xfe, 0xd4, 0x9d, 0x84, 0xb2, 0xb8, 0x69, 0x1a, 0x4d, 0x4b, - 0x15, 0xa2, 0xb2, 0xdb, 0x18, 0x21, 0xaa, 0x22, 0x40, 0x69, 0x12, 0xb5, 0x11, 0x11, 0x81, 0xad, - 0xc2, 0x01, 0x81, 0xd0, 0x64, 0xfd, 0x62, 0x2f, 0x59, 0xef, 0x4c, 0x67, 0xc6, 0x0b, 0x56, 0xd5, - 0x0b, 0x27, 0x24, 0xb8, 0x20, 0x84, 0xd4, 0x1b, 0x07, 0x90, 0x38, 0xf0, 0x45, 0x38, 0x22, 0xf1, - 0x05, 0x50, 0xc4, 0xe7, 0x40, 0x68, 0x66, 0xd6, 0xbb, 0xeb, 0xc4, 0x76, 0x52, 0x11, 0x72, 0x9b, - 0xf9, 0xbd, 0x37, 0xef, 0xfd, 0xde, 0xcf, 0x6f, 0xde, 0x78, 0x11, 0x91, 0x20, 0x52, 0x10, 0xbe, - 0x00, 0xce, 0x64, 0xa4, 0x98, 0xe8, 0x96, 0x96, 0x1e, 0x17, 0x4c, 0x31, 0x8c, 0x0a, 0xa4, 0x36, - 0xdf, 0x64, 0x4d, 0x66, 0x60, 0x5f, 0xaf, 0xac, 0x47, 0x6d, 0xa1, 0xc9, 0x58, 0x33, 0x06, 0x9f, - 0xf2, 0xc8, 0xa7, 0x49, 0xc2, 0x14, 0x55, 0x11, 0x4b, 0x64, 0x66, 0x25, 0x07, 0xf7, 0xa4, 0x17, - 0x31, 0x63, 0x0d, 0x99, 0x00, 0x3f, 0x5d, 0xf5, 0x9b, 0x90, 0x80, 0xa0, 0x0a, 0x1a, 0x99, 0xcf, - 0x76, 0x33, 0x52, 0xad, 0xce, 0x9e, 0x17, 0xb2, 0xb6, 0x4f, 0x85, 0x49, 0xf1, 0x85, 0x59, 0xbc, - 0x1e, 0x36, 0xfc, 0xb4, 0xee, 0xf3, 0x83, 0xa6, 0x3e, 0x2f, 0x7d, 0xca, 0x79, 0x1c, 0x85, 0x26, - 0xbe, 0x9f, 0xae, 0xd2, 0x98, 0xb7, 0xe8, 0xf1, 0x68, 0x9b, 0x27, 0x44, 0x33, 0x05, 0x9d, 0x58, - 0x38, 0x79, 0x0f, 0xcd, 0x04, 0xc0, 0xd9, 0x1a, 0xe7, 0xf2, 0xa3, 0x0e, 0x88, 0x2e, 0xc6, 0x68, - 0x5c, 0x3b, 0xb9, 0xce, 0x92, 0xb3, 0x3c, 0x19, 0x98, 0x35, 0xae, 0xa1, 0x09, 0x01, 0x69, 0x24, - 0x23, 0x96, 0xb8, 0x17, 0x0c, 0x9e, 0xef, 0xc9, 0x2a, 0xba, 0xb4, 0xc6, 0xf9, 0x56, 0xb2, 0xcf, - 0xf4, 0x51, 0xd5, 0xe5, 0xd0, 0x3b, 0xaa, 0xd7, 0x1a, 0xe3, 0x54, 0xb5, 0xb2, 0x63, 0x66, 0x4d, - 0x9e, 0x3b, 0x68, 0x2e, 0x4b, 0xba, 0x01, 0x8a, 0x46, 0x71, 0x96, 0xba, 0x89, 0x2a, 0x92, 0x75, - 0x44, 0x68, 0x23, 0x4c, 0xd5, 0x77, 0xbc, 0xa2, 0x46, 0xaf, 0x57, 0xa3, 0x59, 0x7c, 0x1e, 0x36, - 0xbc, 0xb4, 0xee, 0xf1, 0x83, 0xa6, 0xa7, 0x15, 0xf3, 0x4a, 0x8a, 0x79, 0x3d, 0xc5, 0xbc, 0xb5, - 0x02, 0x7c, 0x6c, 0xc2, 0x06, 0x59, 0x78, 0xec, 0xa2, 0x4b, 0x94, 0xf3, 0x0f, 0x68, 0x1b, 0x32, - 0x5e, 0xbd, 0x2d, 0x79, 0x07, 0x55, 0x7b, 0x72, 0x04, 0x20, 0x39, 0x4b, 0x24, 0xe0, 0xd7, 0xd0, - 0xc5, 0x48, 0x41, 0x5b, 0xba, 0xce, 0xd2, 0xd8, 0xf2, 0x54, 0x7d, 0xce, 0x2b, 0x89, 0x98, 0x95, - 0x1e, 0x58, 0x0f, 0xb2, 0x8e, 0x26, 0xf5, 0xf1, 0xe1, 0x4a, 0x12, 0x34, 0xbd, 0xcf, 0x34, 0x15, - 0xd8, 0x17, 0x20, 0xad, 0x2c, 0x13, 0x41, 0x1f, 0x46, 0x7e, 0x1e, 0x47, 0x97, 0x0d, 0x89, 0x30, - 0x04, 0x39, 0xfa, 0x57, 0xe9, 0x48, 0x10, 0x49, 0x51, 0x46, 0xbe, 0xd7, 0x36, 0x4e, 0xa5, 0xfc, - 0x92, 0x89, 0x86, 0x3b, 0x66, 0x6d, 0xbd, 0x3d, 0xbe, 0x85, 0x66, 0xa4, 0x6c, 0x7d, 0x28, 0xa2, - 0x94, 0x2a, 0x78, 0x1f, 0xba, 0xee, 0xb8, 0x71, 0xe8, 0x07, 0x75, 0x84, 0x28, 0x91, 0x10, 0x76, - 0x04, 0xb8, 0x17, 0x0d, 0xcb, 0x7c, 0x8f, 0xef, 0xa0, 0x2b, 0x2a, 0x96, 0xeb, 0x71, 0x04, 0x89, - 0x5a, 0x07, 0xa1, 0x36, 0xa8, 0xa2, 0x6e, 0xc5, 0x44, 0x39, 0x6e, 0xc0, 0x2b, 0xa8, 0xda, 0x07, - 0xea, 0x94, 0x97, 0x8c, 0xf3, 0x31, 0x3c, 0x6f, 0xa1, 0xc9, 0xfe, 0x16, 0x32, 0x35, 0x22, 0x8b, - 0x99, 0xfa, 0x16, 0xd0, 0x24, 0x24, 0x74, 0x2f, 0x86, 0x9d, 0x30, 0x72, 0xa7, 0x0c, 0xbd, 0x02, - 0xc0, 0x77, 0xd1, 0x9c, 0xed, 0x9c, 0x35, 0xce, 0x4b, 0x75, 0x4e, 0x9b, 0x00, 0x83, 0x4c, 0x78, - 0x09, 0x4d, 0xe5, 0xf0, 0xd6, 0x86, 0x3b, 0xb3, 0xe4, 0x2c, 0x8f, 0x05, 0x65, 0x08, 0xdf, 0x43, - 0x2f, 0x17, 0xdb, 0x44, 0x2a, 0x1a, 0xc7, 0xa6, 0xb5, 0xb6, 0x36, 0xdc, 0x59, 0xe3, 0x3d, 0xcc, - 0x8c, 0xdf, 0x45, 0xb5, 0xdc, 0xb4, 0x99, 0x28, 0x10, 0x5c, 0x44, 0x12, 0x1e, 0x50, 0x09, 0xbb, - 0x22, 0x76, 0x2f, 0x1b, 0x52, 0x23, 0x3c, 0xf0, 0x3c, 0xba, 0xc8, 0x05, 0xfb, 0xaa, 0xeb, 0x56, - 0x8d, 0xab, 0xdd, 0x90, 0x59, 0x34, 0xad, 0x9b, 0xa4, 0xd7, 0xa5, 0xe4, 0x57, 0x07, 0x5d, 0xd1, - 0xc0, 0xba, 0x00, 0xaa, 0x20, 0x80, 0x27, 0x1d, 0x90, 0x0a, 0x7f, 0x5a, 0xea, 0x9b, 0xa9, 0xfa, - 0xa3, 0xff, 0x76, 0xa1, 0x82, 0xbc, 0xef, 0xb3, 0x0e, 0xbc, 0x8a, 0x2a, 0x1d, 0x2e, 0x41, 0xa8, - 0xac, 0x8f, 0xb3, 0x9d, 0xfe, 0x75, 0x42, 0x01, 0x0d, 0xb9, 0x93, 0xc4, 0x5d, 0xd3, 0x7e, 0x13, - 0x41, 0x01, 0x90, 0x27, 0x96, 0xe8, 0x2e, 0x6f, 0x9c, 0x17, 0xd1, 0xfa, 0x3f, 0xb3, 0x36, 0xa7, - 0x05, 0x1f, 0x83, 0x48, 0xa3, 0x10, 0xf0, 0x77, 0x0e, 0x1a, 0xdf, 0x8e, 0xa4, 0xc2, 0x2f, 0x95, - 0xaf, 0x74, 0x7e, 0x81, 0x6b, 0xdb, 0x67, 0xc5, 0x42, 0x27, 0x21, 0x37, 0xbe, 0xfe, 0xf3, 0xef, - 0x1f, 0x2e, 0x5c, 0xc5, 0xf3, 0xe6, 0x91, 0x48, 0x57, 0x8b, 0x59, 0x1c, 0x81, 0xfc, 0xe6, 0x82, - 0x83, 0xbf, 0x75, 0xd0, 0xd8, 0x43, 0x18, 0xca, 0xe6, 0xcc, 0x34, 0x21, 0x37, 0x0d, 0x93, 0xeb, - 0xf8, 0xda, 0x20, 0x26, 0xfe, 0x53, 0xbd, 0x7b, 0x86, 0x7f, 0x74, 0x50, 0x55, 0xf3, 0x0e, 0x4a, - 0xb6, 0xf3, 0x11, 0x6a, 0x61, 0x94, 0x50, 0xf8, 0x33, 0x34, 0x61, 0x69, 0xed, 0x0f, 0xa5, 0x53, - 0xed, 0x87, 0xf7, 0x25, 0x59, 0x36, 0x21, 0x09, 0x5e, 0x1a, 0x51, 0xb1, 0x2f, 0x74, 0xc8, 0xb6, - 0x0d, 0xaf, 0x1f, 0x00, 0xfc, 0xca, 0xd1, 0xf0, 0xf9, 0x2b, 0x59, 0x5b, 0x18, 0x64, 0xca, 0xef, - 0xe2, 0xa9, 0xd2, 0x51, 0x9d, 0xe2, 0x7b, 0x07, 0xcd, 0x3c, 0x04, 0x55, 0xbc, 0x84, 0xf8, 0xc6, - 0x80, 0xc8, 0xe5, 0x57, 0xb2, 0x46, 0x86, 0x3b, 0xe4, 0x04, 0xde, 0x36, 0x04, 0xde, 0x24, 0x77, - 0x07, 0x13, 0xb0, 0xcf, 0xa0, 0x89, 0xb3, 0x1b, 0x6c, 0x1b, 0x2a, 0x0d, 0x1b, 0xe1, 0xbe, 0xb3, - 0x82, 0x53, 0x43, 0xe9, 0x11, 0xc4, 0xed, 0xf5, 0x16, 0x15, 0x6a, 0xa8, 0xcc, 0x8b, 0x65, 0xb8, - 0x70, 0xcf, 0x49, 0x78, 0x86, 0xc4, 0x32, 0xbe, 0x3d, 0x4a, 0x85, 0x16, 0xc4, 0xed, 0xd0, 0xa6, - 0x79, 0xee, 0xa0, 0x8a, 0x9d, 0x5e, 0xf8, 0xfa, 0xd1, 0x8c, 0x7d, 0x53, 0xed, 0x0c, 0xaf, 0xc2, - 0xab, 0x86, 0xe3, 0x02, 0x19, 0xd8, 0x6b, 0xf7, 0xcd, 0xf0, 0xd0, 0x57, 0xf3, 0x27, 0x07, 0x55, - 0x7b, 0x14, 0x7a, 0x67, 0xcf, 0x8f, 0x24, 0x39, 0x99, 0x24, 0xfe, 0xc5, 0x41, 0x15, 0x3b, 0x51, - 0x8f, 0xf3, 0xea, 0x9b, 0xb4, 0x67, 0xc8, 0x6b, 0xd5, 0xfe, 0xc0, 0xb5, 0x11, 0x6d, 0x6e, 0xa8, - 0x3c, 0x2b, 0x84, 0xfc, 0xcd, 0x41, 0xd5, 0x1e, 0x9d, 0xe1, 0x42, 0xfe, 0x5f, 0x84, 0xbd, 0x17, - 0x23, 0x8c, 0x29, 0xaa, 0x6c, 0x40, 0x0c, 0x0a, 0x86, 0x5d, 0x01, 0xf7, 0x28, 0x9c, 0x37, 0xff, - 0x6d, 0x3b, 0x63, 0x57, 0x46, 0xcd, 0x58, 0x2d, 0x48, 0x0b, 0x55, 0x6d, 0x8a, 0x92, 0x1e, 0x2f, - 0x9c, 0xec, 0xe6, 0x29, 0x92, 0xe1, 0xa7, 0x68, 0xf6, 0x63, 0x1a, 0x47, 0x5a, 0x59, 0xfb, 0xcf, - 0x12, 0x5f, 0x3b, 0x36, 0x49, 0x8a, 0x7f, 0x9c, 0x23, 0xb2, 0xd5, 0x4d, 0xb6, 0x3b, 0xe4, 0xd6, - 0xa8, 0x7b, 0x9d, 0x66, 0xa9, 0xac, 0x92, 0x0f, 0x36, 0x7f, 0x3f, 0x5c, 0x74, 0xfe, 0x38, 0x5c, - 0x74, 0xfe, 0x3a, 0x5c, 0x74, 0x3e, 0x79, 0xeb, 0x74, 0x5f, 0x42, 0xa1, 0xf9, 0x6b, 0x58, 0xfa, - 0x66, 0xd9, 0xab, 0x98, 0x8f, 0x96, 0x37, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xa8, 0xce, 0xfa, - 0x47, 0xd3, 0x0d, 0x00, 0x00, + // 1105 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x5f, 0x6f, 0x1b, 0x45, + 0x10, 0xd7, 0xe5, 0x8f, 0x93, 0x6c, 0xfe, 0xd4, 0xd9, 0x84, 0x72, 0xb8, 0x69, 0x1a, 0x6d, 0x4b, + 0x15, 0xa2, 0x72, 0xd7, 0x18, 0x21, 0xaa, 0x22, 0x40, 0x69, 0x12, 0xb5, 0x11, 0x11, 0x81, 0xab, + 0xc2, 0x03, 0x02, 0xa1, 0xcd, 0x79, 0x62, 0x1f, 0x39, 0xdf, 0x6e, 0x77, 0xd7, 0x06, 0xab, 0xea, + 0x0b, 0x4f, 0x48, 0xf0, 0x82, 0x10, 0x52, 0xdf, 0x78, 0x41, 0xe2, 0x81, 0xcf, 0xc0, 0x3b, 0x8f, + 0x48, 0x7c, 0x01, 0x14, 0xf1, 0x39, 0x10, 0xda, 0xdd, 0xf3, 0xdd, 0x39, 0xb1, 0x9d, 0x54, 0x84, + 0xbc, 0xed, 0xfc, 0x66, 0x76, 0xe6, 0x37, 0xe3, 0x99, 0x59, 0x1f, 0x22, 0x12, 0x44, 0x1b, 0x84, + 0x2f, 0x80, 0x33, 0x19, 0x29, 0x26, 0x3a, 0x85, 0xa3, 0xc7, 0x05, 0x53, 0x0c, 0xa3, 0x1c, 0xa9, + 0x2c, 0xd6, 0x59, 0x9d, 0x19, 0xd8, 0xd7, 0x27, 0x6b, 0x51, 0x59, 0xaa, 0x33, 0x56, 0x8f, 0xc1, + 0xa7, 0x3c, 0xf2, 0x69, 0x92, 0x30, 0x45, 0x55, 0xc4, 0x12, 0x99, 0x6a, 0xc9, 0xd1, 0x3d, 0xe9, + 0x45, 0xcc, 0x68, 0x43, 0x26, 0xc0, 0x6f, 0xaf, 0xfb, 0x75, 0x48, 0x40, 0x50, 0x05, 0xb5, 0xd4, + 0x66, 0xb7, 0x1e, 0xa9, 0x46, 0xeb, 0xc0, 0x0b, 0x59, 0xd3, 0xa7, 0xc2, 0x84, 0xf8, 0xc2, 0x1c, + 0x5e, 0x0f, 0x6b, 0x7e, 0xbb, 0xea, 0xf3, 0xa3, 0xba, 0xbe, 0x2f, 0x7d, 0xca, 0x79, 0x1c, 0x85, + 0xc6, 0xbf, 0xdf, 0x5e, 0xa7, 0x31, 0x6f, 0xd0, 0xd3, 0xde, 0xb6, 0xcf, 0xf0, 0x66, 0x12, 0x3a, + 0x33, 0x71, 0xf2, 0x1e, 0x9a, 0x0d, 0x80, 0xb3, 0x0d, 0xce, 0xe5, 0x47, 0x2d, 0x10, 0x1d, 0x8c, + 0xd1, 0x98, 0x36, 0x72, 0x9d, 0x15, 0x67, 0x75, 0x2a, 0x30, 0x67, 0x5c, 0x41, 0x93, 0x02, 0xda, + 0x91, 0x8c, 0x58, 0xe2, 0x8e, 0x18, 0x3c, 0x93, 0xc9, 0x3a, 0x9a, 0xd8, 0xe0, 0x7c, 0x27, 0x39, + 0x64, 0xfa, 0xaa, 0xea, 0x70, 0xe8, 0x5e, 0xd5, 0x67, 0x8d, 0x71, 0xaa, 0x1a, 0xe9, 0x35, 0x73, + 0x26, 0xcf, 0x1d, 0xb4, 0x90, 0x06, 0xdd, 0x02, 0x45, 0xa3, 0x38, 0x0d, 0x5d, 0x47, 0x25, 0xc9, + 0x5a, 0x22, 0xb4, 0x1e, 0xa6, 0xab, 0x7b, 0x5e, 0x9e, 0xa3, 0xd7, 0xcd, 0xd1, 0x1c, 0x3e, 0x0f, + 0x6b, 0x5e, 0xbb, 0xea, 0xf1, 0xa3, 0xba, 0xa7, 0x2b, 0xe6, 0x15, 0x2a, 0xe6, 0x75, 0x2b, 0xe6, + 0x6d, 0xe4, 0xe0, 0x63, 0xe3, 0x36, 0x48, 0xdd, 0x63, 0x17, 0x4d, 0x50, 0xce, 0x3f, 0xa0, 0x4d, + 0x48, 0x79, 0x75, 0x45, 0xf2, 0x0e, 0x2a, 0x77, 0xcb, 0x11, 0x80, 0xe4, 0x2c, 0x91, 0x80, 0x5f, + 0x43, 0xe3, 0x91, 0x82, 0xa6, 0x74, 0x9d, 0x95, 0xd1, 0xd5, 0xe9, 0xea, 0x82, 0x57, 0x28, 0x62, + 0x9a, 0x7a, 0x60, 0x2d, 0xc8, 0x26, 0x9a, 0xd2, 0xd7, 0x07, 0x57, 0x92, 0xa0, 0x99, 0x43, 0xa6, + 0xa9, 0xc0, 0xa1, 0x00, 0x69, 0xcb, 0x32, 0x19, 0xf4, 0x60, 0xe4, 0xb7, 0x31, 0x74, 0xc5, 0x90, + 0x08, 0x43, 0x90, 0xc3, 0x7f, 0x95, 0x96, 0x04, 0x91, 0xe4, 0x69, 0x64, 0xb2, 0xd6, 0x71, 0x2a, + 0xe5, 0x97, 0x4c, 0xd4, 0xdc, 0x51, 0xab, 0xeb, 0xca, 0xf8, 0x16, 0x9a, 0x95, 0xb2, 0xf1, 0xa1, + 0x88, 0xda, 0x54, 0xc1, 0xfb, 0xd0, 0x71, 0xc7, 0x8c, 0x41, 0x2f, 0xa8, 0x3d, 0x44, 0x89, 0x84, + 0xb0, 0x25, 0xc0, 0x1d, 0x37, 0x2c, 0x33, 0x19, 0xdf, 0x41, 0xf3, 0x2a, 0x96, 0x9b, 0x71, 0x04, + 0x89, 0xda, 0x04, 0xa1, 0xb6, 0xa8, 0xa2, 0x6e, 0xc9, 0x78, 0x39, 0xad, 0xc0, 0x6b, 0xa8, 0xdc, + 0x03, 0xea, 0x90, 0x13, 0xc6, 0xf8, 0x14, 0x9e, 0xb5, 0xd0, 0x54, 0x6f, 0x0b, 0x99, 0x1c, 0x91, + 0xc5, 0x4c, 0x7e, 0x4b, 0x68, 0x0a, 0x12, 0x7a, 0x10, 0xc3, 0x5e, 0x18, 0xb9, 0xd3, 0x86, 0x5e, + 0x0e, 0xe0, 0xbb, 0x68, 0xc1, 0x76, 0xce, 0x06, 0xe7, 0x85, 0x3c, 0x67, 0x8c, 0x83, 0x7e, 0x2a, + 0xbc, 0x82, 0xa6, 0x33, 0x78, 0x67, 0xcb, 0x9d, 0x5d, 0x71, 0x56, 0x47, 0x83, 0x22, 0x84, 0xef, + 0xa1, 0x97, 0x73, 0x31, 0x91, 0x8a, 0xc6, 0xb1, 0x69, 0xad, 0x9d, 0x2d, 0x77, 0xce, 0x58, 0x0f, + 0x52, 0xe3, 0x77, 0x51, 0x25, 0x53, 0x6d, 0x27, 0x0a, 0x04, 0x17, 0x91, 0x84, 0x07, 0x54, 0xc2, + 0xbe, 0x88, 0xdd, 0x2b, 0x86, 0xd4, 0x10, 0x0b, 0xbc, 0x88, 0xc6, 0xb9, 0x60, 0x5f, 0x75, 0xdc, + 0xb2, 0x31, 0xb5, 0x82, 0xee, 0x61, 0x3d, 0x0e, 0x10, 0x2a, 0x77, 0xde, 0xf6, 0x70, 0x2a, 0x92, + 0x39, 0x34, 0xa3, 0xdb, 0xa7, 0xdb, 0xbf, 0xe4, 0x17, 0x07, 0xcd, 0x6b, 0x60, 0x53, 0x00, 0x55, + 0x10, 0xc0, 0x93, 0x16, 0x48, 0x85, 0x3f, 0x2d, 0x74, 0xd4, 0x74, 0xf5, 0xd1, 0x7f, 0x1b, 0xb5, + 0x20, 0x9b, 0x88, 0xb4, 0x37, 0xaf, 0xa2, 0x52, 0x8b, 0x4b, 0x10, 0x2a, 0xed, 0xf0, 0x54, 0xd2, + 0xbf, 0x5b, 0x28, 0xa0, 0x26, 0xf7, 0x92, 0xb8, 0x63, 0x1a, 0x73, 0x32, 0xc8, 0x01, 0xf2, 0xc4, + 0x12, 0xdd, 0xe7, 0xb5, 0xcb, 0x22, 0x5a, 0xfd, 0x67, 0xce, 0xc6, 0xb4, 0xe0, 0x63, 0x10, 0xed, + 0x28, 0x04, 0xfc, 0x9d, 0x83, 0xc6, 0x76, 0x23, 0xa9, 0xf0, 0x4b, 0xc5, 0x61, 0xcf, 0x46, 0xbb, + 0xb2, 0x7b, 0x51, 0x2c, 0x74, 0x10, 0x72, 0xe3, 0xeb, 0x3f, 0xff, 0xfe, 0x61, 0xe4, 0x2a, 0x5e, + 0x34, 0xcf, 0x47, 0x7b, 0x3d, 0xdf, 0xd2, 0x11, 0xc8, 0x6f, 0x46, 0x1c, 0xfc, 0xad, 0x83, 0x46, + 0x1f, 0xc2, 0x40, 0x36, 0x17, 0x56, 0x13, 0x72, 0xd3, 0x30, 0xb9, 0x8e, 0xaf, 0xf5, 0x63, 0xe2, + 0x3f, 0xd5, 0xd2, 0x33, 0xfc, 0xa3, 0x83, 0xca, 0x9a, 0x77, 0x50, 0xd0, 0x5d, 0x4e, 0xa1, 0x96, + 0x86, 0x15, 0x0a, 0x7f, 0x86, 0x26, 0x2d, 0xad, 0xc3, 0x81, 0x74, 0xca, 0xbd, 0xf0, 0xa1, 0x24, + 0xab, 0xc6, 0x25, 0xc1, 0x2b, 0x43, 0x32, 0xf6, 0x85, 0x76, 0xd9, 0xb4, 0xee, 0xf5, 0xd3, 0x80, + 0x5f, 0x39, 0xe9, 0x3e, 0x7b, 0x3f, 0x2b, 0x4b, 0xfd, 0x54, 0xd9, 0x2c, 0x9e, 0x2b, 0x1c, 0xd5, + 0x21, 0xbe, 0x77, 0xd0, 0xec, 0x43, 0x50, 0xf9, 0x1b, 0x89, 0x6f, 0xf4, 0xf1, 0x5c, 0x7c, 0x3f, + 0x2b, 0x64, 0xb0, 0x41, 0x46, 0xe0, 0x6d, 0x43, 0xe0, 0x4d, 0x72, 0xb7, 0x3f, 0x01, 0xfb, 0x40, + 0x1a, 0x3f, 0xfb, 0xc1, 0xae, 0xa1, 0x52, 0xb3, 0x1e, 0xee, 0x3b, 0x6b, 0xb8, 0x6d, 0x28, 0x3d, + 0x82, 0xb8, 0xb9, 0xd9, 0xa0, 0x42, 0x0d, 0x2c, 0xf3, 0x72, 0x11, 0xce, 0xcd, 0x33, 0x12, 0x9e, + 0x21, 0xb1, 0x8a, 0x6f, 0x0f, 0xab, 0x42, 0x03, 0xe2, 0x66, 0x68, 0xc3, 0x3c, 0x77, 0x50, 0xc9, + 0x6e, 0x2f, 0x7c, 0xfd, 0x64, 0xc4, 0x9e, 0xad, 0x76, 0x81, 0xa3, 0xf0, 0xaa, 0xe1, 0xb8, 0x44, + 0xfa, 0xf6, 0xda, 0x7d, 0xb3, 0x3c, 0xf4, 0x68, 0xfe, 0xe4, 0xa0, 0x72, 0x97, 0x42, 0xf7, 0xee, + 0xe5, 0x91, 0x24, 0x67, 0x93, 0xc4, 0x3f, 0x3b, 0xa8, 0x64, 0x37, 0xea, 0x69, 0x5e, 0x3d, 0x9b, + 0xf6, 0x02, 0x79, 0xad, 0xdb, 0x1f, 0xb8, 0x32, 0xa4, 0xcd, 0x0d, 0x95, 0x67, 0x79, 0x21, 0x7f, + 0x75, 0x50, 0xb9, 0x4b, 0x67, 0x70, 0x21, 0xff, 0x2f, 0xc2, 0xde, 0x8b, 0x11, 0xc6, 0x14, 0x95, + 0xb6, 0x20, 0x06, 0x05, 0x83, 0x46, 0xc0, 0x3d, 0x09, 0x67, 0xcd, 0x7f, 0xdb, 0xee, 0xd8, 0xb5, + 0x61, 0x3b, 0x56, 0x17, 0xa4, 0x81, 0xca, 0x36, 0x44, 0xa1, 0x1e, 0x2f, 0x1c, 0xec, 0xe6, 0x39, + 0x82, 0xe1, 0xa7, 0x68, 0xee, 0x63, 0x1a, 0x47, 0xba, 0xb2, 0xf6, 0x3f, 0x27, 0xbe, 0x76, 0x6a, + 0x93, 0xe4, 0xff, 0x45, 0x87, 0x44, 0xab, 0x9a, 0x68, 0x77, 0xc8, 0xad, 0x61, 0x73, 0xdd, 0x4e, + 0x43, 0xd9, 0x4a, 0x3e, 0xd8, 0xfe, 0xfd, 0x78, 0xd9, 0xf9, 0xe3, 0x78, 0xd9, 0xf9, 0xeb, 0x78, + 0xd9, 0xf9, 0xe4, 0xad, 0xf3, 0x7d, 0x23, 0x85, 0xe6, 0x4f, 0x63, 0xe1, 0x6b, 0xe6, 0xa0, 0x64, + 0x3e, 0x67, 0xde, 0xf8, 0x37, 0x00, 0x00, 0xff, 0xff, 0x38, 0x35, 0xe9, 0x0a, 0xed, 0x0d, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1541,6 +1551,15 @@ func (m *RepoAccessQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.Project) > 0 { + i -= len(m.Project) + copy(dAtA[i:], m.Project) + i = encodeVarintRepository(dAtA, i, uint64(len(m.Project))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x8a + } if len(m.Proxy) > 0 { i -= len(m.Proxy) copy(dAtA[i:], m.Proxy) @@ -1948,6 +1967,10 @@ func (m *RepoAccessQuery) Size() (n int) { if l > 0 { n += 2 + l + sovRepository(uint64(l)) } + l = len(m.Project) + if l > 0 { + n += 2 + l + sovRepository(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -3006,6 +3029,38 @@ func (m *RepoAccessQuery) Unmarshal(dAtA []byte) error { } m.Proxy = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 17: + 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 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.Project = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) diff --git a/pkg/apiclient/settings/settings.pb.go b/pkg/apiclient/settings/settings.pb.go index ae457848ee6b1..818577c5a4fe4 100644 --- a/pkg/apiclient/settings/settings.pb.go +++ b/pkg/apiclient/settings/settings.pb.go @@ -92,6 +92,9 @@ type Settings struct { UiCssURL string `protobuf:"bytes,14,opt,name=uiCssURL,proto3" json:"uiCssURL,omitempty"` UiBannerContent string `protobuf:"bytes,15,opt,name=uiBannerContent,proto3" json:"uiBannerContent,omitempty"` UiBannerURL string `protobuf:"bytes,16,opt,name=uiBannerURL,proto3" json:"uiBannerURL,omitempty"` + PasswordPattern string `protobuf:"bytes,17,opt,name=passwordPattern,proto3" json:"passwordPattern,omitempty"` + TrackingMethod string `protobuf:"bytes,18,opt,name=trackingMethod,proto3" json:"trackingMethod,omitempty"` + UiBannerPermanent bool `protobuf:"varint,19,opt,name=uiBannerPermanent,proto3" json:"uiBannerPermanent,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -242,6 +245,27 @@ func (m *Settings) GetUiBannerURL() string { return "" } +func (m *Settings) GetPasswordPattern() string { + if m != nil { + return m.PasswordPattern + } + return "" +} + +func (m *Settings) GetTrackingMethod() string { + if m != nil { + return m.TrackingMethod + } + return "" +} + +func (m *Settings) GetUiBannerPermanent() bool { + if m != nil { + return m.UiBannerPermanent + } + 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"` @@ -609,68 +633,71 @@ func init() { func init() { proto.RegisterFile("server/settings/settings.proto", fileDescriptor_a480d494da040caa) } var fileDescriptor_a480d494da040caa = []byte{ - // 976 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xcf, 0x6f, 0x1b, 0xc5, - 0x17, 0xd7, 0xc6, 0x69, 0x62, 0x3f, 0x37, 0x71, 0x32, 0xdf, 0x2f, 0x61, 0xb1, 0x2a, 0xc7, 0xf8, - 0x50, 0x19, 0x09, 0x76, 0x89, 0x2b, 0x04, 0x42, 0x48, 0x80, 0xd7, 0x55, 0x6b, 0xea, 0x92, 0x32, - 0x4d, 0x7a, 0x40, 0x42, 0xd1, 0x64, 0x77, 0xd8, 0x0c, 0xde, 0xcc, 0xac, 0x66, 0x66, 0xad, 0xba, - 0x47, 0x6e, 0x5c, 0xb8, 0xc0, 0x1f, 0xd5, 0x23, 0x12, 0x77, 0x0b, 0x59, 0xfc, 0x21, 0x68, 0x67, - 0x7f, 0x64, 0x63, 0x9b, 0x1f, 0x52, 0x6f, 0x6f, 0xde, 0xe7, 0xfd, 0x9a, 0x37, 0x9f, 0x7d, 0x6f, - 0xa1, 0xa3, 0xa8, 0x9c, 0x51, 0xe9, 0x2a, 0xaa, 0x35, 0xe3, 0xa1, 0x2a, 0x05, 0x27, 0x96, 0x42, - 0x0b, 0xb4, 0xeb, 0x47, 0x89, 0xd2, 0x54, 0xb6, 0xff, 0x1f, 0x8a, 0x50, 0x18, 0x9d, 0x9b, 0x4a, - 0x19, 0xdc, 0xbe, 0x17, 0x0a, 0x11, 0x46, 0xd4, 0x25, 0x31, 0x73, 0x09, 0xe7, 0x42, 0x13, 0xcd, - 0x04, 0xcf, 0x9d, 0xdb, 0x93, 0x90, 0xe9, 0xab, 0xe4, 0xd2, 0xf1, 0xc5, 0xb5, 0x4b, 0xa4, 0x71, - 0xff, 0xc1, 0x08, 0x1f, 0xf8, 0x81, 0x3b, 0x1b, 0xb8, 0xf1, 0x34, 0x4c, 0x3d, 0x95, 0x4b, 0xe2, - 0x38, 0x62, 0xbe, 0xf1, 0x75, 0x67, 0x27, 0x24, 0x8a, 0xaf, 0xc8, 0x89, 0x1b, 0x52, 0x4e, 0x25, - 0xd1, 0x34, 0xc8, 0xa3, 0x7d, 0xf1, 0x2f, 0xd1, 0x56, 0x6f, 0x22, 0x58, 0xe0, 0xbb, 0x7e, 0x44, - 0xd8, 0x75, 0x5e, 0x4f, 0xaf, 0x05, 0x7b, 0xcf, 0x73, 0xf4, 0x9b, 0x84, 0xca, 0x79, 0xef, 0x75, - 0x1d, 0xea, 0x85, 0x06, 0xbd, 0x03, 0xb5, 0x44, 0x46, 0xb6, 0xd5, 0xb5, 0xfa, 0x8d, 0xe1, 0xee, - 0x72, 0x71, 0x5c, 0x3b, 0xc7, 0x13, 0x9c, 0xea, 0xd0, 0x87, 0xd0, 0x08, 0xe8, 0x4b, 0x4f, 0xf0, - 0xef, 0x59, 0x68, 0x6f, 0x75, 0xad, 0x7e, 0x73, 0x80, 0x9c, 0xbc, 0x33, 0xce, 0xa8, 0x40, 0xf0, - 0x8d, 0x11, 0xf2, 0x00, 0xd2, 0xfc, 0xb9, 0x4b, 0xcd, 0xb8, 0xfc, 0xaf, 0x74, 0x39, 0x1d, 0x8f, - 0xbc, 0x0c, 0x1a, 0xee, 0x2f, 0x17, 0xc7, 0x70, 0x73, 0xc6, 0x15, 0x37, 0xd4, 0x85, 0x26, 0x89, - 0xe3, 0x09, 0xb9, 0xa4, 0xd1, 0x13, 0x3a, 0xb7, 0xb7, 0xd3, 0xca, 0x70, 0x55, 0x85, 0x5e, 0xc0, - 0xa1, 0xa4, 0x4a, 0x24, 0xd2, 0xa7, 0xa7, 0x33, 0x2a, 0x25, 0x0b, 0xa8, 0xb2, 0xef, 0x74, 0x6b, - 0xfd, 0xe6, 0xa0, 0x5f, 0x66, 0x2b, 0x6e, 0xe8, 0xe0, 0x55, 0xd3, 0x87, 0x5c, 0xcb, 0x39, 0x5e, - 0x0f, 0x81, 0x1c, 0x40, 0x4a, 0x13, 0x9d, 0xa8, 0x21, 0x09, 0x42, 0xfa, 0x90, 0x93, 0xcb, 0x88, - 0x06, 0xf6, 0x4e, 0xd7, 0xea, 0xd7, 0xf1, 0x06, 0x04, 0x3d, 0x86, 0x56, 0xc6, 0x84, 0x2f, 0x39, - 0x89, 0xe6, 0x9a, 0xf9, 0xca, 0xde, 0x35, 0x77, 0xee, 0x94, 0x55, 0x3c, 0xba, 0x8d, 0xe7, 0xd7, - 0x5d, 0x75, 0x43, 0xaf, 0xe0, 0x60, 0x9a, 0x28, 0x2d, 0xae, 0xd9, 0x2b, 0x7a, 0x1a, 0x1b, 0x36, - 0xd9, 0x75, 0x13, 0xea, 0x6b, 0xe7, 0x86, 0x00, 0x4e, 0x41, 0x00, 0x23, 0x5c, 0xf8, 0x81, 0x33, - 0x1b, 0x38, 0xf1, 0x34, 0x74, 0x52, 0x3a, 0x39, 0x15, 0x3a, 0x39, 0x05, 0x9d, 0x9c, 0x27, 0x2b, - 0x51, 0xf1, 0x5a, 0x1e, 0xf4, 0x2e, 0x6c, 0x5f, 0xd1, 0x28, 0xb6, 0x1b, 0x26, 0xdf, 0x5e, 0x59, - 0xfa, 0x63, 0x1a, 0xc5, 0xd8, 0x40, 0xe8, 0x3d, 0xd8, 0x8d, 0xa3, 0x24, 0x64, 0x5c, 0xd9, 0x60, - 0xda, 0xdc, 0x2a, 0xad, 0x9e, 0x19, 0x3d, 0x2e, 0xf0, 0xb4, 0x87, 0x89, 0xa2, 0x72, 0x22, 0xd2, - 0xd3, 0x88, 0xa9, 0xac, 0x87, 0xcd, 0xac, 0x87, 0xeb, 0x08, 0xfa, 0xd9, 0x82, 0xb7, 0x7d, 0xd3, - 0x95, 0xa7, 0x84, 0x93, 0x90, 0x5e, 0x53, 0xae, 0x9f, 0xe5, 0xb9, 0xee, 0x9a, 0x5c, 0x67, 0x6f, - 0xd6, 0x01, 0x6f, 0x63, 0x70, 0xfc, 0x77, 0x49, 0xd1, 0xfb, 0x70, 0x58, 0xb6, 0xe8, 0x05, 0x95, - 0xca, 0xbc, 0xc5, 0x5e, 0xb7, 0xd6, 0x6f, 0xe0, 0x75, 0x00, 0xb5, 0xa1, 0x9e, 0x30, 0x4f, 0xa9, - 0x73, 0x3c, 0xb1, 0xf7, 0x0d, 0x53, 0xcb, 0x33, 0xea, 0x43, 0x2b, 0x61, 0x43, 0xc2, 0x39, 0x95, - 0x9e, 0xe0, 0x9a, 0x72, 0x6d, 0xb7, 0x8c, 0xc9, 0xaa, 0x3a, 0xa5, 0x7c, 0xa1, 0x4a, 0x03, 0x1d, - 0x64, 0x94, 0xaf, 0xa8, 0xda, 0xbf, 0x5a, 0x70, 0xb4, 0x99, 0xc8, 0xe8, 0x00, 0x6a, 0x53, 0x3a, - 0xcf, 0xbe, 0x60, 0x9c, 0x8a, 0x28, 0x80, 0x3b, 0x33, 0x12, 0x25, 0x34, 0xff, 0x68, 0xdf, 0x90, - 0x42, 0xab, 0x69, 0x71, 0x16, 0xfc, 0xd3, 0xad, 0x4f, 0xac, 0xde, 0x05, 0xbc, 0xb5, 0x91, 0xe1, - 0xa8, 0x03, 0xa0, 0x25, 0xf1, 0xa7, 0x8c, 0x87, 0xe3, 0x51, 0x5e, 0x5b, 0x45, 0x83, 0xee, 0xc3, - 0x3e, 0xe1, 0x82, 0xcf, 0xd3, 0x66, 0x9e, 0x2b, 0x2a, 0x95, 0xa9, 0xb5, 0x8e, 0x57, 0xb4, 0xbd, - 0xcf, 0x60, 0x3b, 0xe5, 0x21, 0xb2, 0x61, 0xd7, 0xbf, 0x22, 0xfa, 0xbc, 0x18, 0x55, 0xb8, 0x38, - 0xa6, 0x2f, 0x90, 0x8a, 0x67, 0xf4, 0xa5, 0x36, 0x31, 0x1a, 0xb8, 0x3c, 0xf7, 0xee, 0xc1, 0x4e, - 0xf6, 0xac, 0x08, 0xc1, 0x36, 0x27, 0xd7, 0x34, 0x77, 0x36, 0x72, 0xef, 0x73, 0x68, 0x94, 0x53, - 0x0c, 0x0d, 0x00, 0x7c, 0xc1, 0x39, 0xf5, 0xb5, 0x90, 0xca, 0xb6, 0x0c, 0xf3, 0x6e, 0xa6, 0x9d, - 0x57, 0x40, 0xb8, 0x62, 0xd5, 0x7b, 0x00, 0x8d, 0x12, 0xd8, 0x94, 0x21, 0xd5, 0xe9, 0x79, 0x4c, - 0xf3, 0xba, 0x8c, 0xdc, 0xfb, 0xa9, 0x06, 0x95, 0xc9, 0xb7, 0xd1, 0xed, 0x08, 0x76, 0x98, 0x52, - 0x09, 0x95, 0xb9, 0x63, 0x7e, 0x42, 0x7d, 0xa8, 0xfb, 0x11, 0xa3, 0x5c, 0x8f, 0x47, 0x66, 0xb8, - 0x36, 0x86, 0x77, 0x97, 0x8b, 0xe3, 0xba, 0x97, 0xeb, 0x70, 0x89, 0xa2, 0x13, 0x68, 0xfa, 0x11, - 0x2b, 0x80, 0x6c, 0x86, 0x0e, 0x5b, 0xcb, 0xc5, 0x71, 0xd3, 0x9b, 0x8c, 0x4b, 0xfb, 0xaa, 0x4d, - 0x9a, 0x54, 0xf9, 0x22, 0xce, 0x27, 0x69, 0x03, 0xe7, 0x27, 0x74, 0x01, 0x7b, 0x2c, 0x38, 0x13, - 0x53, 0xca, 0x3d, 0xb3, 0x55, 0xec, 0x1d, 0xd3, 0x9b, 0xfb, 0x1b, 0xc6, 0xba, 0x33, 0xae, 0x1a, - 0x1a, 0x76, 0x0e, 0x0f, 0x97, 0x8b, 0xe3, 0xbd, 0xf1, 0xa8, 0xa2, 0xc7, 0xb7, 0xe3, 0xb5, 0xe7, - 0x80, 0xd6, 0xfd, 0x36, 0xb0, 0xfa, 0xe9, 0x6d, 0x56, 0x7f, 0xfc, 0x8f, 0xac, 0xce, 0xd6, 0xa2, - 0x53, 0xee, 0xf5, 0x74, 0xbf, 0x38, 0x26, 0x7e, 0x85, 0xbe, 0x83, 0xef, 0xa0, 0x55, 0xac, 0x89, - 0xe7, 0x54, 0xce, 0x98, 0x4f, 0xd1, 0x57, 0x50, 0x7b, 0x44, 0x35, 0x3a, 0x5a, 0xdb, 0x23, 0x66, - 0x77, 0xb6, 0x0f, 0xd7, 0xf4, 0x3d, 0xfb, 0xc7, 0xdf, 0xff, 0xfc, 0x65, 0x0b, 0xa1, 0x03, 0xf3, - 0x3f, 0x30, 0x3b, 0x29, 0x77, 0xf1, 0xd0, 0x7b, 0xbd, 0xec, 0x58, 0xbf, 0x2d, 0x3b, 0xd6, 0x1f, - 0xcb, 0x8e, 0xf5, 0xed, 0x47, 0xff, 0xed, 0xbf, 0x20, 0x7b, 0xc3, 0x32, 0xc8, 0xe5, 0x8e, 0xd9, - 0xe2, 0x0f, 0xfe, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x71, 0x4d, 0xb0, 0xe8, 0xb4, 0x08, 0x00, 0x00, + // 1023 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0x96, 0xe3, 0x34, 0xb1, 0xc7, 0x4d, 0x9c, 0x4c, 0x21, 0x2c, 0x56, 0xe5, 0x18, 0x1f, 0x2a, + 0x23, 0xc1, 0x2e, 0x71, 0x85, 0x40, 0x08, 0x09, 0xf0, 0xba, 0x6a, 0x4d, 0x1d, 0x12, 0xa6, 0x49, + 0x0f, 0x48, 0x28, 0x9a, 0xec, 0x3e, 0x36, 0x8b, 0xd7, 0x33, 0xab, 0x99, 0x59, 0x53, 0xf7, 0xc8, + 0x8d, 0x0b, 0x17, 0xf8, 0xa3, 0x7a, 0x44, 0xe2, 0x1e, 0x21, 0x8b, 0x3f, 0x04, 0xcd, 0xec, 0x8f, + 0x6c, 0x6c, 0x03, 0x95, 0x7a, 0x9b, 0xf9, 0xbe, 0xf7, 0x6b, 0xde, 0x7e, 0xfb, 0x66, 0x50, 0x5b, + 0x82, 0x98, 0x81, 0x70, 0x24, 0x28, 0x15, 0xb2, 0x40, 0x16, 0x0b, 0x3b, 0x16, 0x5c, 0x71, 0xbc, + 0xed, 0x45, 0x89, 0x54, 0x20, 0x5a, 0x6f, 0x05, 0x3c, 0xe0, 0x06, 0x73, 0xf4, 0x2a, 0xa5, 0x5b, + 0xf7, 0x03, 0xce, 0x83, 0x08, 0x1c, 0x1a, 0x87, 0x0e, 0x65, 0x8c, 0x2b, 0xaa, 0x42, 0xce, 0x32, + 0xe7, 0xd6, 0x38, 0x08, 0xd5, 0x55, 0x72, 0x69, 0x7b, 0x7c, 0xea, 0x50, 0x61, 0xdc, 0x7f, 0x34, + 0x8b, 0x0f, 0x3d, 0xdf, 0x99, 0xf5, 0x9d, 0x78, 0x12, 0x68, 0x4f, 0xe9, 0xd0, 0x38, 0x8e, 0x42, + 0xcf, 0xf8, 0x3a, 0xb3, 0x23, 0x1a, 0xc5, 0x57, 0xf4, 0xc8, 0x09, 0x80, 0x81, 0xa0, 0x0a, 0xfc, + 0x2c, 0xda, 0x97, 0xff, 0x13, 0x6d, 0xf9, 0x24, 0x3c, 0xf4, 0x3d, 0xc7, 0x8b, 0x68, 0x38, 0xcd, + 0xea, 0xe9, 0x36, 0xd1, 0xce, 0xb3, 0x8c, 0xfd, 0x36, 0x01, 0x31, 0xef, 0xbe, 0xaa, 0xa3, 0x5a, + 0x8e, 0xe0, 0x77, 0x51, 0x35, 0x11, 0x91, 0x55, 0xe9, 0x54, 0x7a, 0xf5, 0xc1, 0xf6, 0xe2, 0xfa, + 0xb0, 0x7a, 0x4e, 0xc6, 0x44, 0x63, 0xf8, 0x23, 0x54, 0xf7, 0xe1, 0x85, 0xcb, 0xd9, 0x0f, 0x61, + 0x60, 0x6d, 0x74, 0x2a, 0xbd, 0x46, 0x1f, 0xdb, 0x59, 0x67, 0xec, 0x61, 0xce, 0x90, 0x1b, 0x23, + 0xec, 0x22, 0xa4, 0xf3, 0x67, 0x2e, 0x55, 0xe3, 0x72, 0xaf, 0x70, 0x39, 0x19, 0x0d, 0xdd, 0x94, + 0x1a, 0xec, 0x2e, 0xae, 0x0f, 0xd1, 0xcd, 0x9e, 0x94, 0xdc, 0x70, 0x07, 0x35, 0x68, 0x1c, 0x8f, + 0xe9, 0x25, 0x44, 0x4f, 0x61, 0x6e, 0x6d, 0xea, 0xca, 0x48, 0x19, 0xc2, 0xcf, 0xd1, 0xbe, 0x00, + 0xc9, 0x13, 0xe1, 0xc1, 0xc9, 0x0c, 0x84, 0x08, 0x7d, 0x90, 0xd6, 0x9d, 0x4e, 0xb5, 0xd7, 0xe8, + 0xf7, 0x8a, 0x6c, 0xf9, 0x09, 0x6d, 0xb2, 0x6c, 0xfa, 0x88, 0x29, 0x31, 0x27, 0xab, 0x21, 0xb0, + 0x8d, 0xb0, 0x54, 0x54, 0x25, 0x72, 0x40, 0xfd, 0x00, 0x1e, 0x31, 0x7a, 0x19, 0x81, 0x6f, 0x6d, + 0x75, 0x2a, 0xbd, 0x1a, 0x59, 0xc3, 0xe0, 0x27, 0xa8, 0x99, 0x2a, 0xe1, 0x2b, 0x46, 0xa3, 0xb9, + 0x0a, 0x3d, 0x69, 0x6d, 0x9b, 0x33, 0xb7, 0x8b, 0x2a, 0x1e, 0xdf, 0xe6, 0xb3, 0xe3, 0x2e, 0xbb, + 0xe1, 0x97, 0x68, 0x6f, 0x92, 0x48, 0xc5, 0xa7, 0xe1, 0x4b, 0x38, 0x89, 0x8d, 0x9a, 0xac, 0x9a, + 0x09, 0xf5, 0x8d, 0x7d, 0x23, 0x00, 0x3b, 0x17, 0x80, 0x59, 0x5c, 0x78, 0xbe, 0x3d, 0xeb, 0xdb, + 0xf1, 0x24, 0xb0, 0xb5, 0x9c, 0xec, 0x92, 0x9c, 0xec, 0x5c, 0x4e, 0xf6, 0xd3, 0xa5, 0xa8, 0x64, + 0x25, 0x0f, 0x7e, 0x0f, 0x6d, 0x5e, 0x41, 0x14, 0x5b, 0x75, 0x93, 0x6f, 0xa7, 0x28, 0xfd, 0x09, + 0x44, 0x31, 0x31, 0x14, 0x7e, 0x1f, 0x6d, 0xc7, 0x51, 0x12, 0x84, 0x4c, 0x5a, 0xc8, 0xb4, 0xb9, + 0x59, 0x58, 0x9d, 0x1a, 0x9c, 0xe4, 0xbc, 0xee, 0x61, 0x22, 0x41, 0x8c, 0xb9, 0xde, 0x0d, 0x43, + 0x99, 0xf6, 0xb0, 0x91, 0xf6, 0x70, 0x95, 0xc1, 0xbf, 0x56, 0xd0, 0x3b, 0x9e, 0xe9, 0xca, 0x31, + 0x65, 0x34, 0x80, 0x29, 0x30, 0x75, 0x9a, 0xe5, 0xba, 0x6b, 0x72, 0x9d, 0xbd, 0x59, 0x07, 0xdc, + 0xb5, 0xc1, 0xc9, 0xbf, 0x25, 0xc5, 0x1f, 0xa0, 0xfd, 0xa2, 0x45, 0xcf, 0x41, 0x48, 0xf3, 0x2d, + 0x76, 0x3a, 0xd5, 0x5e, 0x9d, 0xac, 0x12, 0xb8, 0x85, 0x6a, 0x49, 0xe8, 0x4a, 0x79, 0x4e, 0xc6, + 0xd6, 0xae, 0x51, 0x6a, 0xb1, 0xc7, 0x3d, 0xd4, 0x4c, 0xc2, 0x01, 0x65, 0x0c, 0x84, 0xcb, 0x99, + 0x02, 0xa6, 0xac, 0xa6, 0x31, 0x59, 0x86, 0xb5, 0xe4, 0x73, 0x48, 0x07, 0xda, 0x4b, 0x25, 0x5f, + 0x82, 0x74, 0xac, 0x98, 0x4a, 0xf9, 0x13, 0x17, 0xfe, 0x29, 0x55, 0x0a, 0x04, 0xb3, 0xf6, 0xd3, + 0x58, 0x4b, 0x30, 0x7e, 0x80, 0x76, 0x95, 0xa0, 0xde, 0x24, 0x64, 0xc1, 0x31, 0xa8, 0x2b, 0xee, + 0x5b, 0xd8, 0x18, 0x2e, 0xa1, 0xfa, 0x9c, 0x79, 0x82, 0x53, 0x10, 0x53, 0xca, 0x74, 0x7d, 0xf7, + 0xcc, 0x77, 0x5a, 0x25, 0x5a, 0xbf, 0x57, 0xd0, 0xc1, 0xfa, 0x1f, 0x09, 0xef, 0xa1, 0xea, 0x04, + 0xe6, 0xe9, 0x04, 0x21, 0x7a, 0x89, 0x7d, 0x74, 0x67, 0x46, 0xa3, 0x04, 0xb2, 0xa1, 0xf1, 0x86, + 0x12, 0x5e, 0x4e, 0x4b, 0xd2, 0xe0, 0x9f, 0x6d, 0x7c, 0x5a, 0xe9, 0x5e, 0xa0, 0xb7, 0xd7, 0xfe, + 0x61, 0xb8, 0x8d, 0x50, 0x7e, 0xde, 0xd1, 0x30, 0xab, 0xad, 0x84, 0xe8, 0x2e, 0x51, 0xc6, 0xd9, + 0x5c, 0x7f, 0xcc, 0x73, 0x09, 0x42, 0x9a, 0x5a, 0x6b, 0x64, 0x09, 0xed, 0x7e, 0x8e, 0x36, 0xf5, + 0x7f, 0x80, 0x2d, 0xb4, 0xed, 0x5d, 0x51, 0x75, 0x9e, 0x8f, 0x4a, 0x92, 0x6f, 0xb5, 0x02, 0xf4, + 0xf2, 0x0c, 0x5e, 0x28, 0x13, 0xa3, 0x4e, 0x8a, 0x7d, 0xf7, 0x3e, 0xda, 0x4a, 0x65, 0x85, 0x31, + 0xda, 0x64, 0x74, 0x0a, 0x99, 0xb3, 0x59, 0x77, 0xbf, 0x40, 0xf5, 0x62, 0x8a, 0xe2, 0x3e, 0x42, + 0x1e, 0x67, 0x0c, 0x3c, 0xc5, 0x85, 0xb4, 0x2a, 0x46, 0xf9, 0x37, 0xd3, 0xd6, 0xcd, 0x29, 0x52, + 0xb2, 0xea, 0x3e, 0x44, 0xf5, 0x82, 0x58, 0x97, 0x41, 0x63, 0x6a, 0x1e, 0x43, 0x56, 0x97, 0x59, + 0x77, 0x7f, 0xa9, 0xa2, 0xd2, 0xe4, 0x5d, 0xeb, 0x76, 0x80, 0xb6, 0x42, 0x29, 0x13, 0x10, 0x99, + 0x63, 0xb6, 0xc3, 0x3d, 0x54, 0xf3, 0xa2, 0x10, 0x98, 0x1a, 0x0d, 0xcd, 0x70, 0xaf, 0x0f, 0xee, + 0x2e, 0xae, 0x0f, 0x6b, 0x6e, 0x86, 0x91, 0x82, 0xc5, 0x47, 0xa8, 0xe1, 0x45, 0x61, 0x4e, 0xa4, + 0x33, 0x7c, 0xd0, 0x5c, 0x5c, 0x1f, 0x36, 0xdc, 0xf1, 0xa8, 0xb0, 0x2f, 0xdb, 0xe8, 0xa4, 0xd2, + 0xe3, 0x71, 0x36, 0xc9, 0xeb, 0x24, 0xdb, 0xe1, 0x0b, 0xb4, 0x13, 0xfa, 0x67, 0x7c, 0x02, 0xcc, + 0x35, 0xb7, 0x9a, 0xb5, 0x65, 0x7a, 0xf3, 0x60, 0xcd, 0xb5, 0x62, 0x8f, 0xca, 0x86, 0x46, 0x9d, + 0x83, 0xfd, 0xc5, 0xf5, 0xe1, 0xce, 0x68, 0x58, 0xc2, 0xc9, 0xed, 0x78, 0xad, 0x39, 0xc2, 0xab, + 0x7e, 0x6b, 0x54, 0x7d, 0x7c, 0x5b, 0xd5, 0x9f, 0xfc, 0xa7, 0xaa, 0xd3, 0x6b, 0xd9, 0x2e, 0xde, + 0x15, 0xfa, 0x7e, 0xb3, 0x4d, 0xfc, 0x92, 0x7c, 0xfb, 0xdf, 0xa3, 0x66, 0x7e, 0x4d, 0x3d, 0x03, + 0x31, 0x0b, 0x3d, 0xc0, 0x5f, 0xa3, 0xea, 0x63, 0x50, 0xf8, 0x60, 0xe5, 0x1e, 0x33, 0x77, 0x77, + 0x6b, 0x7f, 0x05, 0xef, 0x5a, 0x3f, 0xff, 0xf9, 0xf7, 0x6f, 0x1b, 0x18, 0xef, 0x99, 0xf7, 0xc8, + 0xec, 0xa8, 0x78, 0x0b, 0x0c, 0xdc, 0x57, 0x8b, 0x76, 0xe5, 0x8f, 0x45, 0xbb, 0xf2, 0xd7, 0xa2, + 0x5d, 0xf9, 0xee, 0xe3, 0xd7, 0x7b, 0x97, 0xa4, 0xdf, 0xb0, 0x08, 0x72, 0xb9, 0x65, 0x5e, 0x11, + 0x0f, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x6e, 0xc4, 0x09, 0x9f, 0x34, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -806,6 +833,36 @@ func (m *Settings) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.UiBannerPermanent { + i-- + if m.UiBannerPermanent { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } + if len(m.TrackingMethod) > 0 { + i -= len(m.TrackingMethod) + copy(dAtA[i:], m.TrackingMethod) + i = encodeVarintSettings(dAtA, i, uint64(len(m.TrackingMethod))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x92 + } + if len(m.PasswordPattern) > 0 { + i -= len(m.PasswordPattern) + copy(dAtA[i:], m.PasswordPattern) + i = encodeVarintSettings(dAtA, i, uint64(len(m.PasswordPattern))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x8a + } if len(m.UiBannerURL) > 0 { i -= len(m.UiBannerURL) copy(dAtA[i:], m.UiBannerURL) @@ -1386,6 +1443,17 @@ func (m *Settings) Size() (n int) { if l > 0 { n += 2 + l + sovSettings(uint64(l)) } + l = len(m.PasswordPattern) + if l > 0 { + n += 2 + l + sovSettings(uint64(l)) + } + l = len(m.TrackingMethod) + if l > 0 { + n += 2 + l + sovSettings(uint64(l)) + } + if m.UiBannerPermanent { + n += 3 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -2227,6 +2295,90 @@ func (m *Settings) Unmarshal(dAtA []byte) error { } m.UiBannerURL = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 17: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PasswordPattern", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSettings + } + 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 ErrInvalidLengthSettings + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSettings + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PasswordPattern = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 18: + 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 ErrIntOverflowSettings + } + 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 ErrInvalidLengthSettings + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSettings + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TrackingMethod = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UiBannerPermanent", 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.UiBannerPermanent = bool(v != 0) default: iNdEx = preIndex skippy, err := skipSettings(dAtA[iNdEx:]) diff --git a/pkg/apis/application/v1alpha1/generated.pb.go b/pkg/apis/application/v1alpha1/generated.pb.go index 5000c20a1a256..c33a9564d47c1 100644 --- a/pkg/apis/application/v1alpha1/generated.pb.go +++ b/pkg/apis/application/v1alpha1/generated.pb.go @@ -2555,6 +2555,8 @@ func init() { proto.RegisterType((*ApplicationWatchEvent)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationWatchEvent") proto.RegisterType((*Backoff)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Backoff") proto.RegisterType((*Cluster)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Cluster") + proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Cluster.AnnotationsEntry") + proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Cluster.LabelsEntry") proto.RegisterType((*ClusterCacheInfo)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ClusterCacheInfo") proto.RegisterType((*ClusterConfig)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ClusterConfig") proto.RegisterType((*ClusterInfo)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ClusterInfo") @@ -2631,421 +2633,427 @@ func init() { } var fileDescriptor_030104ce3b95bcac = []byte{ - // 6622 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x3d, 0x5b, 0x6c, 0x24, 0xd9, - 0x55, 0x5b, 0xdd, 0x7e, 0x74, 0x1f, 0x3f, 0x66, 0x7c, 0xe7, 0xb1, 0xce, 0xb0, 0x19, 0x8f, 0x6a, - 0x95, 0x64, 0x21, 0x89, 0xcd, 0x0e, 0x4b, 0x58, 0xb2, 0x21, 0xc1, 0x6d, 0xcf, 0xc3, 0x33, 0x9e, - 0x19, 0xef, 0xb1, 0x67, 0x86, 0x3c, 0x08, 0x5b, 0xae, 0xbe, 0xdd, 0xae, 0x71, 0x77, 0x55, 0x6f, - 0x55, 0xb5, 0xc7, 0x9d, 0x90, 0x17, 0x0a, 0x64, 0x45, 0x1e, 0x1b, 0x25, 0xf9, 0x48, 0x24, 0x04, - 0xe1, 0x21, 0x24, 0x3e, 0x22, 0xe0, 0x0b, 0x10, 0xf0, 0x93, 0xaf, 0x00, 0x12, 0x44, 0x02, 0x25, - 0x81, 0x08, 0x93, 0x0c, 0x41, 0x3c, 0x24, 0x40, 0x40, 0x7e, 0x98, 0x2f, 0x74, 0xdf, 0xb7, 0xaa, - 0xbb, 0xc7, 0xf6, 0xb8, 0x66, 0x12, 0x45, 0xfc, 0x75, 0x9d, 0x73, 0xee, 0x39, 0xe7, 0xbe, 0xce, - 0x3d, 0xf7, 0xdc, 0x73, 0x6f, 0xc3, 0x6a, 0x33, 0x48, 0xb7, 0xba, 0x9b, 0xf3, 0x7e, 0xd4, 0x5e, - 0xf0, 0xe2, 0x66, 0xd4, 0x89, 0xa3, 0x3b, 0xfc, 0xc7, 0x9b, 0xfd, 0xfa, 0xc2, 0xce, 0xf9, 0x85, - 0xce, 0x76, 0x73, 0xc1, 0xeb, 0x04, 0xc9, 0x82, 0xd7, 0xe9, 0xb4, 0x02, 0xdf, 0x4b, 0x83, 0x28, - 0x5c, 0xd8, 0x79, 0xd6, 0x6b, 0x75, 0xb6, 0xbc, 0x67, 0x17, 0x9a, 0x34, 0xa4, 0xb1, 0x97, 0xd2, - 0xfa, 0x7c, 0x27, 0x8e, 0xd2, 0x88, 0xbc, 0xcd, 0x70, 0x9b, 0x57, 0xdc, 0xf8, 0x8f, 0x9f, 0xf3, - 0xeb, 0xf3, 0x3b, 0xe7, 0xe7, 0x3b, 0xdb, 0xcd, 0x79, 0xc6, 0x6d, 0xde, 0xe2, 0x36, 0xaf, 0xb8, - 0x9d, 0x79, 0xb3, 0xa5, 0x4b, 0x33, 0x6a, 0x46, 0x0b, 0x9c, 0xe9, 0x66, 0xb7, 0xc1, 0xbf, 0xf8, - 0x07, 0xff, 0x25, 0x84, 0x9d, 0x71, 0xb7, 0x9f, 0x4f, 0xe6, 0x83, 0x88, 0xa9, 0xb7, 0xe0, 0x47, - 0x31, 0x5d, 0xd8, 0xe9, 0x53, 0xe8, 0xcc, 0x73, 0x86, 0xa6, 0xed, 0xf9, 0x5b, 0x41, 0x48, 0xe3, - 0x9e, 0xa9, 0x53, 0x9b, 0xa6, 0xde, 0xa0, 0x52, 0x0b, 0xc3, 0x4a, 0xc5, 0xdd, 0x30, 0x0d, 0xda, - 0xb4, 0xaf, 0xc0, 0x5b, 0xf6, 0x2b, 0x90, 0xf8, 0x5b, 0xb4, 0xed, 0xe5, 0xcb, 0xb9, 0x2f, 0xc3, - 0xd4, 0xe2, 0xed, 0xf5, 0xc5, 0x6e, 0xba, 0xb5, 0x14, 0x85, 0x8d, 0xa0, 0x49, 0x7e, 0x1c, 0x26, - 0xfc, 0x56, 0x37, 0x49, 0x69, 0x7c, 0xdd, 0x6b, 0xd3, 0x59, 0xe7, 0x9c, 0xf3, 0x4c, 0xb5, 0x76, - 0xe2, 0x2b, 0x7b, 0x73, 0x4f, 0xdc, 0xdb, 0x9b, 0x9b, 0x58, 0x32, 0x28, 0xb4, 0xe9, 0xc8, 0x0f, - 0xc3, 0x78, 0x1c, 0xb5, 0xe8, 0x22, 0x5e, 0x9f, 0x2d, 0xf1, 0x22, 0xc7, 0x64, 0x91, 0x71, 0x14, - 0x60, 0x54, 0x78, 0xf7, 0x6b, 0x25, 0x80, 0xc5, 0x4e, 0x67, 0x2d, 0x8e, 0xee, 0x50, 0x3f, 0x25, - 0x2f, 0x41, 0x85, 0xb5, 0x42, 0xdd, 0x4b, 0x3d, 0x2e, 0x6d, 0xe2, 0xfc, 0x8f, 0xce, 0x8b, 0xca, - 0xcc, 0xdb, 0x95, 0x31, 0x3d, 0xc7, 0xa8, 0xe7, 0x77, 0x9e, 0x9d, 0xbf, 0xb1, 0xc9, 0xca, 0x5f, - 0xa3, 0xa9, 0x57, 0x23, 0x52, 0x18, 0x18, 0x18, 0x6a, 0xae, 0x24, 0x84, 0x91, 0xa4, 0x43, 0x7d, - 0xae, 0xd8, 0xc4, 0xf9, 0xd5, 0xf9, 0xa3, 0x0c, 0x91, 0x79, 0xa3, 0xf9, 0x7a, 0x87, 0xfa, 0xb5, - 0x49, 0x29, 0x79, 0x84, 0x7d, 0x21, 0x97, 0x43, 0x76, 0x60, 0x2c, 0x49, 0xbd, 0xb4, 0x9b, 0xcc, - 0x96, 0xb9, 0xc4, 0xeb, 0x85, 0x49, 0xe4, 0x5c, 0x6b, 0xd3, 0x52, 0xe6, 0x98, 0xf8, 0x46, 0x29, - 0xcd, 0xfd, 0x7b, 0x07, 0xa6, 0x0d, 0xf1, 0x6a, 0x90, 0xa4, 0xe4, 0x3d, 0x7d, 0x8d, 0x3b, 0x7f, - 0xb0, 0xc6, 0x65, 0xa5, 0x79, 0xd3, 0x1e, 0x97, 0xc2, 0x2a, 0x0a, 0x62, 0x35, 0x6c, 0x1b, 0x46, - 0x83, 0x94, 0xb6, 0x93, 0xd9, 0xd2, 0xb9, 0xf2, 0x33, 0x13, 0xe7, 0x2f, 0x17, 0x55, 0xcf, 0xda, - 0x94, 0x14, 0x3a, 0xba, 0xc2, 0xd8, 0xa3, 0x90, 0xe2, 0x7e, 0x17, 0xec, 0xfa, 0xb1, 0x06, 0x27, - 0xcf, 0xc2, 0x44, 0x12, 0x75, 0x63, 0x9f, 0x22, 0xed, 0x44, 0xc9, 0xac, 0x73, 0xae, 0xcc, 0x86, - 0x1e, 0x1b, 0xa9, 0xeb, 0x06, 0x8c, 0x36, 0x0d, 0xf9, 0x94, 0x03, 0x93, 0x75, 0x9a, 0xa4, 0x41, - 0xc8, 0xe5, 0x2b, 0xe5, 0x37, 0x8e, 0xac, 0xbc, 0x02, 0x2e, 0x1b, 0xe6, 0xb5, 0x93, 0xb2, 0x22, - 0x93, 0x16, 0x30, 0xc1, 0x8c, 0x7c, 0x36, 0xe3, 0xea, 0x34, 0xf1, 0xe3, 0xa0, 0xc3, 0xbe, 0xf9, - 0x98, 0xb1, 0x66, 0xdc, 0xb2, 0x41, 0xa1, 0x4d, 0x47, 0x42, 0x18, 0x65, 0x33, 0x2a, 0x99, 0x1d, - 0xe1, 0xfa, 0xaf, 0x1c, 0x4d, 0x7f, 0xd9, 0xa8, 0x6c, 0xb2, 0x9a, 0xd6, 0x67, 0x5f, 0x09, 0x0a, - 0x31, 0xe4, 0x93, 0x0e, 0xcc, 0xca, 0x19, 0x8f, 0x54, 0x34, 0xe8, 0xed, 0xad, 0x20, 0xa5, 0xad, - 0x20, 0x49, 0x67, 0x47, 0xb9, 0x0e, 0x0b, 0x07, 0x1b, 0x5b, 0x97, 0xe2, 0xa8, 0xdb, 0xb9, 0x1a, - 0x84, 0xf5, 0xda, 0x39, 0x29, 0x69, 0x76, 0x69, 0x08, 0x63, 0x1c, 0x2a, 0x92, 0x7c, 0xd6, 0x81, - 0x33, 0xa1, 0xd7, 0xa6, 0x49, 0xc7, 0x63, 0x5d, 0x2b, 0xd0, 0xb5, 0x96, 0xe7, 0x6f, 0x73, 0x8d, - 0xc6, 0x1e, 0x4e, 0x23, 0x57, 0x6a, 0x74, 0xe6, 0xfa, 0x50, 0xd6, 0xf8, 0x00, 0xb1, 0xe4, 0x37, - 0x1d, 0x98, 0x89, 0xe2, 0xce, 0x96, 0x17, 0xd2, 0xba, 0xc2, 0x26, 0xb3, 0xe3, 0x7c, 0xea, 0xbd, - 0xf7, 0x68, 0x5d, 0x74, 0x23, 0xcf, 0xf6, 0x5a, 0x14, 0x06, 0x69, 0x14, 0xaf, 0xd3, 0x34, 0x0d, - 0xc2, 0x66, 0x52, 0x3b, 0x75, 0x6f, 0x6f, 0x6e, 0xa6, 0x8f, 0x0a, 0xfb, 0xf5, 0x21, 0xef, 0x87, - 0x89, 0xa4, 0x17, 0xfa, 0xb7, 0x83, 0xb0, 0x1e, 0xdd, 0x4d, 0x66, 0x2b, 0x45, 0x4c, 0xdf, 0x75, - 0xcd, 0x50, 0x4e, 0x40, 0x23, 0x00, 0x6d, 0x69, 0x83, 0x3b, 0xce, 0x0c, 0xa5, 0x6a, 0xd1, 0x1d, - 0x67, 0x06, 0xd3, 0x03, 0xc4, 0x92, 0x8f, 0x39, 0x30, 0x95, 0x04, 0xcd, 0xd0, 0x4b, 0xbb, 0x31, - 0xbd, 0x4a, 0x7b, 0xc9, 0x2c, 0x70, 0x45, 0xae, 0x1c, 0xb1, 0x55, 0x2c, 0x96, 0xb5, 0x53, 0x52, - 0xc7, 0x29, 0x1b, 0x9a, 0x60, 0x56, 0xee, 0xa0, 0x89, 0x66, 0x86, 0xf5, 0x44, 0xb1, 0x13, 0xcd, - 0x0c, 0xea, 0xa1, 0x22, 0xdd, 0x3f, 0x2b, 0xc1, 0xf1, 0xfc, 0x1a, 0x44, 0x7e, 0xdb, 0x81, 0x63, - 0x77, 0xee, 0xa6, 0x1b, 0xd1, 0x36, 0x0d, 0x93, 0x5a, 0x8f, 0x59, 0x0a, 0x6e, 0x7d, 0x27, 0xce, - 0xfb, 0xc5, 0xae, 0x76, 0xf3, 0x57, 0xb2, 0x52, 0x2e, 0x84, 0x69, 0xdc, 0xab, 0x3d, 0x29, 0xeb, - 0x73, 0xec, 0xca, 0xed, 0x0d, 0x1b, 0x8b, 0x79, 0xa5, 0xce, 0x7c, 0xdc, 0x81, 0x93, 0x83, 0x58, - 0x90, 0xe3, 0x50, 0xde, 0xa6, 0x3d, 0xe1, 0xe0, 0x20, 0xfb, 0x49, 0x7e, 0x16, 0x46, 0x77, 0xbc, - 0x56, 0x97, 0x4a, 0x47, 0xe1, 0xd2, 0xd1, 0x2a, 0xa2, 0x35, 0x43, 0xc1, 0xf5, 0xad, 0xa5, 0xe7, - 0x1d, 0xf7, 0xaf, 0xca, 0x30, 0x61, 0x2d, 0x15, 0x8f, 0xc1, 0xf9, 0x89, 0x32, 0xce, 0xcf, 0xb5, - 0xc2, 0x56, 0xb9, 0xa1, 0xde, 0xcf, 0xdd, 0x9c, 0xf7, 0x73, 0xa3, 0x38, 0x91, 0x0f, 0x74, 0x7f, - 0x48, 0x0a, 0xd5, 0xa8, 0xc3, 0x9c, 0x5b, 0xb6, 0x8a, 0x8e, 0x14, 0xd1, 0x85, 0x37, 0x14, 0xbb, - 0xda, 0xd4, 0xbd, 0xbd, 0xb9, 0xaa, 0xfe, 0x44, 0x23, 0xc8, 0xfd, 0xba, 0x03, 0x27, 0x2d, 0x1d, - 0x97, 0xa2, 0xb0, 0x1e, 0xf0, 0xae, 0x3d, 0x07, 0x23, 0x69, 0xaf, 0xa3, 0x3c, 0x68, 0xdd, 0x52, - 0x1b, 0xbd, 0x0e, 0x45, 0x8e, 0x61, 0x3e, 0x73, 0x9b, 0x26, 0x89, 0xd7, 0xa4, 0x79, 0x9f, 0xf9, - 0x9a, 0x00, 0xa3, 0xc2, 0x93, 0x18, 0x48, 0xcb, 0x4b, 0xd2, 0x8d, 0xd8, 0x0b, 0x13, 0xce, 0x7e, - 0x23, 0x68, 0x53, 0xd9, 0xc0, 0x3f, 0x72, 0xb0, 0x11, 0xc3, 0x4a, 0xd4, 0x4e, 0xdf, 0xdb, 0x9b, - 0x23, 0xab, 0x7d, 0x9c, 0x70, 0x00, 0x77, 0xf7, 0xb3, 0x0e, 0x9c, 0x1e, 0xec, 0xd6, 0x90, 0xd7, - 0xc3, 0x58, 0x42, 0xe3, 0x1d, 0x1a, 0xcb, 0xda, 0x99, 0x2e, 0xe1, 0x50, 0x94, 0x58, 0xb2, 0x00, - 0x55, 0x6d, 0x72, 0x65, 0x1d, 0x67, 0x24, 0x69, 0xd5, 0xd8, 0x69, 0x43, 0xc3, 0x1a, 0x8d, 0x7d, - 0x48, 0x27, 0x48, 0x37, 0x1a, 0xdf, 0x6f, 0x70, 0x8c, 0xfb, 0x0f, 0x0e, 0x1c, 0xb3, 0xb4, 0x7a, - 0x0c, 0x5e, 0x6e, 0x98, 0xf5, 0x72, 0x57, 0x0a, 0x1b, 0xcf, 0x43, 0xdc, 0xdc, 0x2f, 0x8f, 0xc1, - 0x8c, 0x3d, 0xea, 0xb9, 0x39, 0xe6, 0x1b, 0x2c, 0xda, 0x89, 0x6e, 0xe2, 0xaa, 0x6c, 0x73, 0xb3, - 0xc1, 0x12, 0x60, 0x54, 0x78, 0xd6, 0x88, 0x1d, 0x2f, 0xdd, 0x92, 0x0d, 0xae, 0x1b, 0x71, 0xcd, - 0x4b, 0xb7, 0x90, 0x63, 0xc8, 0xdb, 0x61, 0x3a, 0xf5, 0xe2, 0x26, 0x4d, 0x91, 0xee, 0x04, 0x89, - 0x9a, 0x2f, 0xd5, 0xda, 0x69, 0x49, 0x3b, 0xbd, 0x91, 0xc1, 0x62, 0x8e, 0x9a, 0xbc, 0x0c, 0x23, - 0x5b, 0xb4, 0xd5, 0x96, 0x7e, 0xcd, 0x7a, 0x71, 0x33, 0x9c, 0xd7, 0xf5, 0x32, 0x6d, 0xb5, 0x6b, - 0x15, 0xa6, 0x32, 0xfb, 0x85, 0x5c, 0x14, 0xf9, 0x45, 0x07, 0xaa, 0xdb, 0xdd, 0x24, 0x8d, 0xda, - 0xc1, 0xfb, 0xe8, 0x6c, 0x85, 0x0b, 0xfe, 0x99, 0x82, 0x05, 0x5f, 0x55, 0xfc, 0xc5, 0x7c, 0xd7, - 0x9f, 0x68, 0x24, 0x93, 0x0f, 0xc0, 0xf8, 0x76, 0x12, 0x85, 0x21, 0x65, 0x9e, 0x0a, 0x53, 0xe2, - 0x56, 0xd1, 0x4a, 0x08, 0xee, 0xb5, 0x09, 0xd6, 0xb7, 0xf2, 0x03, 0x95, 0x4c, 0xde, 0x0c, 0xf5, - 0x20, 0xa6, 0x7e, 0x1a, 0xc5, 0xbd, 0x59, 0x78, 0x24, 0xcd, 0xb0, 0xac, 0xf8, 0x8b, 0x66, 0xd0, - 0x9f, 0x68, 0x24, 0x93, 0x1e, 0x8c, 0x75, 0x5a, 0xdd, 0x66, 0x10, 0xce, 0x4e, 0x70, 0x1d, 0x6e, - 0x16, 0xac, 0xc3, 0x1a, 0x67, 0x5e, 0x03, 0x66, 0x54, 0xc4, 0x6f, 0x94, 0x02, 0xc9, 0xd3, 0x30, - 0xea, 0x6f, 0x79, 0x71, 0x3a, 0x3b, 0xc9, 0xc7, 0xac, 0x9e, 0x44, 0x4b, 0x0c, 0x88, 0x02, 0xe7, - 0xfe, 0x7a, 0x09, 0xce, 0x0c, 0xaf, 0x98, 0x98, 0x4d, 0x7e, 0x37, 0x4e, 0x84, 0x7d, 0xae, 0xd8, - 0xb3, 0x89, 0x83, 0x51, 0xe1, 0xc9, 0x47, 0x1c, 0x18, 0xbf, 0x23, 0x7b, 0xbc, 0xf4, 0x48, 0x7a, - 0xfc, 0x8a, 0xec, 0x71, 0xad, 0xc3, 0x15, 0xd5, 0xeb, 0x52, 0x2e, 0x53, 0x97, 0xee, 0xfa, 0xad, - 0x6e, 0x5d, 0x59, 0x46, 0x4d, 0x7a, 0x41, 0x80, 0x51, 0xe1, 0x19, 0x69, 0x10, 0x0a, 0xd2, 0x91, - 0x2c, 0xe9, 0x4a, 0x28, 0x49, 0x25, 0xde, 0xfd, 0x4e, 0x19, 0x4e, 0x0d, 0x9c, 0x7c, 0x64, 0x1e, - 0x80, 0xfb, 0x2c, 0x17, 0x03, 0xb6, 0xc1, 0x14, 0xbb, 0xea, 0x69, 0xe6, 0x62, 0xdc, 0xd2, 0x50, - 0xb4, 0x28, 0xc8, 0x87, 0x00, 0x3a, 0x5e, 0xec, 0xb5, 0x69, 0x4a, 0x63, 0x65, 0x27, 0xaf, 0x1e, - 0xad, 0x95, 0x98, 0x1e, 0x6b, 0x8a, 0xa7, 0xf1, 0x71, 0x34, 0x28, 0x41, 0x4b, 0x24, 0xdb, 0x43, - 0xc7, 0xb4, 0x45, 0xbd, 0x84, 0x5e, 0x37, 0xcb, 0x87, 0xde, 0x43, 0xa3, 0x41, 0xa1, 0x4d, 0xc7, - 0xd6, 0x31, 0x5e, 0x8b, 0x44, 0xb6, 0x95, 0x5e, 0xc7, 0x78, 0x3d, 0x13, 0x94, 0x58, 0xf2, 0xaa, - 0x03, 0xd3, 0x8d, 0xa0, 0x45, 0x8d, 0x74, 0xb9, 0xe3, 0xbd, 0x71, 0xf4, 0x4a, 0x5e, 0xb4, 0xf9, - 0x1a, 0x0b, 0x9c, 0x01, 0x27, 0x98, 0x13, 0xcf, 0xba, 0x79, 0x87, 0xc6, 0xdc, 0x74, 0x8f, 0x65, - 0xbb, 0xf9, 0x96, 0x00, 0xa3, 0xc2, 0xbb, 0x5f, 0x28, 0xc1, 0xec, 0xb0, 0x31, 0x47, 0x12, 0x36, - 0xb2, 0xd2, 0x5b, 0x5e, 0x9c, 0x48, 0xf7, 0xfd, 0x88, 0xbb, 0x40, 0xc9, 0xf7, 0x96, 0x17, 0xdb, - 0x63, 0x94, 0x0b, 0x40, 0x25, 0x89, 0xdc, 0x81, 0x91, 0xb4, 0xe5, 0x15, 0x14, 0x36, 0xb2, 0x24, - 0x1a, 0x27, 0x6b, 0x75, 0x31, 0x41, 0x2e, 0x83, 0x3c, 0x05, 0x23, 0xad, 0x60, 0x93, 0x39, 0xa3, - 0x6c, 0x10, 0xf3, 0x55, 0x65, 0x35, 0xd8, 0x4c, 0x90, 0x43, 0xdd, 0xaf, 0x39, 0x03, 0xda, 0x46, - 0x1a, 0x5d, 0x36, 0xa8, 0x68, 0xb8, 0x13, 0xc4, 0x51, 0xd8, 0xa6, 0x61, 0x9a, 0x0f, 0x85, 0x5e, - 0x30, 0x28, 0xb4, 0xe9, 0xc8, 0x2f, 0x38, 0x03, 0x66, 0xc3, 0x11, 0x63, 0x80, 0x52, 0xa5, 0x03, - 0x4f, 0x08, 0xf7, 0x3f, 0xc7, 0x06, 0xd8, 0x3f, 0xbd, 0xa0, 0x91, 0xf3, 0x00, 0xcc, 0x9b, 0x5a, - 0x8b, 0x69, 0x23, 0xd8, 0x95, 0x35, 0xd3, 0x2c, 0xaf, 0x6b, 0x0c, 0x5a, 0x54, 0xaa, 0xcc, 0x7a, - 0xb7, 0xc1, 0xca, 0x94, 0xfa, 0xcb, 0x08, 0x0c, 0x5a, 0x54, 0xe4, 0x39, 0x18, 0x0b, 0xda, 0x5e, - 0x93, 0xaa, 0xf6, 0x7f, 0x8a, 0x4d, 0xae, 0x15, 0x0e, 0xb9, 0xbf, 0x37, 0x37, 0xad, 0x15, 0xe2, - 0x20, 0x94, 0xb4, 0xe4, 0xb7, 0x1c, 0x98, 0xf4, 0xa3, 0x76, 0x3b, 0x0a, 0x57, 0xbd, 0x4d, 0xda, - 0x52, 0x21, 0xae, 0x3b, 0x8f, 0x6a, 0xb9, 0x9f, 0x5f, 0xb2, 0x84, 0x89, 0x0d, 0xa6, 0x0e, 0xdc, - 0xd9, 0x28, 0xcc, 0x68, 0x65, 0xcf, 0xc1, 0xd1, 0x07, 0xcf, 0x41, 0xf2, 0x87, 0x0e, 0xcc, 0x88, - 0xb2, 0x8b, 0x61, 0x18, 0xa5, 0x32, 0xf2, 0x28, 0x62, 0x54, 0xd1, 0x23, 0xae, 0x96, 0x25, 0x51, - 0xd4, 0xed, 0x35, 0x52, 0xcd, 0x99, 0x3e, 0x3c, 0xf6, 0x2b, 0x49, 0x2e, 0xc1, 0x4c, 0x23, 0x8a, - 0x7d, 0x6a, 0x37, 0x04, 0x77, 0xfc, 0x2a, 0x86, 0xd1, 0xc5, 0x3c, 0x01, 0xf6, 0x97, 0x21, 0xb7, - 0xe0, 0xb4, 0x05, 0xb4, 0xdb, 0xa1, 0xc2, 0xb9, 0x9d, 0x95, 0xdc, 0x4e, 0x5f, 0x1c, 0x48, 0x85, - 0x43, 0x4a, 0x9f, 0x79, 0x07, 0xcc, 0xf4, 0xf5, 0xdf, 0x80, 0xdd, 0xfd, 0x49, 0x7b, 0x77, 0x5f, - 0xb5, 0x36, 0xe5, 0x67, 0x96, 0xe1, 0xf4, 0xe0, 0x96, 0x3a, 0x0c, 0x17, 0xf7, 0x57, 0x1d, 0x78, - 0x72, 0x88, 0x1b, 0xa3, 0xb7, 0x35, 0xce, 0xb0, 0x6d, 0x0d, 0xf1, 0xa0, 0x4c, 0xc3, 0x1d, 0x69, - 0x2c, 0x2e, 0x1e, 0x6d, 0x44, 0x5c, 0x08, 0x77, 0x44, 0x47, 0x8f, 0xdf, 0xdb, 0x9b, 0x2b, 0x5f, - 0x08, 0x77, 0x90, 0xf1, 0x76, 0x3f, 0x37, 0x96, 0xd9, 0x39, 0xad, 0xab, 0xcd, 0x3a, 0x57, 0x54, - 0xee, 0x9b, 0x6e, 0x14, 0x3c, 0x16, 0xad, 0x9d, 0xa1, 0x08, 0xc1, 0x4b, 0x71, 0xe4, 0xe3, 0x0e, - 0x8f, 0x7a, 0xab, 0x1d, 0xa5, 0xf4, 0xac, 0x1e, 0x4d, 0x10, 0xde, 0x8e, 0xa5, 0x2b, 0x20, 0xda, - 0xd2, 0xd9, 0x4c, 0xee, 0x88, 0xa0, 0x53, 0xde, 0xbf, 0x52, 0x71, 0x71, 0x85, 0x27, 0xbb, 0x00, - 0x49, 0x2f, 0xf4, 0xd7, 0xa2, 0x56, 0xe0, 0xf7, 0x64, 0x98, 0xa1, 0x80, 0xc8, 0xa9, 0xe0, 0x27, - 0x9c, 0x2c, 0xf3, 0x8d, 0x96, 0x2c, 0xf2, 0x45, 0x07, 0x66, 0x82, 0x66, 0x18, 0xc5, 0x74, 0x39, - 0x68, 0x34, 0x68, 0x4c, 0x43, 0x9f, 0x2a, 0x3f, 0xe4, 0xf6, 0xd1, 0x34, 0x50, 0x41, 0xbf, 0x95, - 0x3c, 0x7b, 0x33, 0xc5, 0xfb, 0x50, 0xd8, 0xaf, 0x0c, 0xa9, 0xc3, 0x48, 0x10, 0x36, 0x22, 0x69, - 0xd8, 0x6a, 0x47, 0x53, 0x6a, 0x25, 0x6c, 0x44, 0x66, 0xae, 0xb0, 0x2f, 0xe4, 0xdc, 0xc9, 0x2a, - 0x9c, 0x8c, 0xe5, 0x4e, 0xf4, 0x72, 0x90, 0x30, 0x7f, 0x7e, 0x35, 0x68, 0x07, 0x29, 0x37, 0x4a, - 0xe5, 0xda, 0xec, 0xbd, 0xbd, 0xb9, 0x93, 0x38, 0x00, 0x8f, 0x03, 0x4b, 0xb9, 0xaf, 0x54, 0xb3, - 0xdb, 0x6d, 0x11, 0x4c, 0xfa, 0x00, 0x54, 0x63, 0x1d, 0xbe, 0x17, 0x9e, 0xd1, 0x6a, 0x31, 0x6d, - 0x2c, 0xa3, 0x58, 0x3a, 0x0e, 0x62, 0x02, 0xf5, 0x46, 0x22, 0xf3, 0x90, 0x58, 0xcf, 0xcb, 0x69, - 0x51, 0xc0, 0xf8, 0x92, 0x52, 0x4d, 0xc0, 0xae, 0x17, 0xfa, 0xc8, 0x65, 0x90, 0x18, 0xc6, 0xb6, - 0xa8, 0xd7, 0x4a, 0xb7, 0x64, 0x3c, 0xe9, 0xca, 0x51, 0x7d, 0x5a, 0xc6, 0x2b, 0x1f, 0xab, 0x13, - 0x50, 0x94, 0x92, 0xc8, 0x2e, 0x8c, 0x6f, 0x89, 0x4e, 0x90, 0x6b, 0xfb, 0xb5, 0xa3, 0x36, 0x6e, - 0xa6, 0x67, 0xcd, 0xfc, 0x95, 0x00, 0x54, 0xe2, 0xc8, 0x2f, 0x39, 0x00, 0xbe, 0x0a, 0xd2, 0xa9, - 0xe9, 0x83, 0x85, 0xd9, 0x1d, 0x1d, 0xff, 0x33, 0xae, 0x91, 0x06, 0x25, 0x68, 0x49, 0x26, 0x2f, - 0xc1, 0x64, 0x4c, 0xfd, 0x28, 0xf4, 0x83, 0x16, 0xad, 0x2f, 0xa6, 0xdc, 0x8d, 0x3f, 0x5c, 0x30, - 0xef, 0x38, 0xf3, 0x4f, 0xd0, 0xe2, 0x81, 0x19, 0x8e, 0xe4, 0x15, 0x07, 0xa6, 0x75, 0xa0, 0x92, - 0x75, 0x08, 0x95, 0x01, 0x9b, 0xd5, 0x82, 0xc2, 0xa2, 0x9c, 0x67, 0x8d, 0xb0, 0xed, 0x4a, 0x16, - 0x86, 0x39, 0xb9, 0xe4, 0x5d, 0x00, 0xd1, 0x26, 0x0f, 0x0a, 0xb2, 0xaa, 0x56, 0x0e, 0x5d, 0xd5, - 0x69, 0x11, 0xdf, 0x56, 0x1c, 0xd0, 0xe2, 0x46, 0xae, 0x02, 0x88, 0x69, 0xb3, 0xd1, 0xeb, 0x50, - 0x1e, 0x94, 0xa9, 0xd6, 0xde, 0xa8, 0x1a, 0x7f, 0x5d, 0x63, 0xee, 0xef, 0xcd, 0xf5, 0xef, 0x76, - 0x79, 0x34, 0xd6, 0x2a, 0x4e, 0xde, 0x0f, 0xe3, 0x49, 0xb7, 0xdd, 0xf6, 0x74, 0x70, 0x65, 0xad, - 0xb8, 0x15, 0x51, 0xf0, 0x35, 0x63, 0x53, 0x02, 0x50, 0x49, 0x74, 0x43, 0x20, 0xfd, 0xf4, 0xe4, - 0x39, 0x98, 0xa4, 0xbb, 0x29, 0x8d, 0x43, 0xaf, 0x75, 0x13, 0x57, 0xd5, 0x76, 0x9c, 0x77, 0xfe, - 0x05, 0x0b, 0x8e, 0x19, 0x2a, 0xe2, 0x6a, 0xcf, 0xbb, 0xc4, 0xe9, 0xc1, 0x78, 0xde, 0xca, 0xcf, - 0x76, 0xff, 0xb7, 0x94, 0xf1, 0x08, 0x36, 0x62, 0x4a, 0x49, 0x04, 0xa3, 0x61, 0x54, 0xd7, 0x46, - 0xef, 0x4a, 0x31, 0x46, 0xef, 0x7a, 0x54, 0xb7, 0xce, 0x95, 0xd9, 0x57, 0x82, 0x42, 0x0e, 0x3f, - 0x78, 0x53, 0x27, 0x94, 0x1c, 0x21, 0x9d, 0xa0, 0x22, 0x25, 0xeb, 0x83, 0xb7, 0x1b, 0xb6, 0x20, - 0xcc, 0xca, 0x25, 0xdb, 0x30, 0xba, 0x15, 0x25, 0xa9, 0xd8, 0xab, 0x1c, 0xd9, 0x0b, 0xbb, 0x1c, - 0x25, 0x29, 0x5f, 0xc2, 0x74, 0xb5, 0x19, 0x24, 0x41, 0x21, 0xc3, 0xfd, 0x67, 0x27, 0x13, 0x7c, - 0xb9, 0xed, 0xa5, 0xfe, 0xd6, 0x85, 0x1d, 0xb6, 0x7f, 0xbc, 0x9a, 0x39, 0x38, 0xf8, 0x09, 0xfb, - 0xe0, 0xe0, 0xfe, 0xde, 0xdc, 0x1b, 0x86, 0x25, 0xfa, 0xdc, 0x65, 0x1c, 0xe6, 0x39, 0x0b, 0xeb, - 0x8c, 0xe1, 0xc3, 0x0e, 0x4c, 0x58, 0xea, 0xc9, 0x05, 0xa5, 0xc0, 0x18, 0xb6, 0x76, 0xae, 0x2c, - 0x20, 0xda, 0x22, 0xdd, 0xcf, 0x38, 0x30, 0x5e, 0xf3, 0xfc, 0xed, 0xa8, 0xd1, 0x20, 0x6f, 0x82, - 0x4a, 0xbd, 0x2b, 0x8f, 0x68, 0x44, 0xfd, 0x74, 0xe4, 0x7d, 0x59, 0xc2, 0x51, 0x53, 0xb0, 0x31, - 0xdc, 0xf0, 0xfc, 0x34, 0x8a, 0xb9, 0xda, 0x65, 0x31, 0x86, 0x2f, 0x72, 0x08, 0x4a, 0x0c, 0xdb, - 0xa4, 0xb7, 0xbd, 0x5d, 0x55, 0x38, 0x1f, 0xf9, 0xb9, 0x66, 0x50, 0x68, 0xd3, 0xb9, 0x7f, 0x3e, - 0x0a, 0xe3, 0xf2, 0x2c, 0xf4, 0xc0, 0xa7, 0x19, 0xca, 0x8b, 0x2f, 0x0d, 0xf5, 0xe2, 0x13, 0x18, - 0xf3, 0x79, 0x1a, 0x95, 0x5c, 0x4a, 0x8f, 0x18, 0x03, 0x93, 0x0a, 0x8a, 0xcc, 0x2c, 0xa3, 0x96, - 0xf8, 0x46, 0x29, 0x8a, 0x7c, 0xda, 0x81, 0x63, 0x7e, 0x14, 0x86, 0xd4, 0x37, 0x76, 0x7e, 0xa4, - 0x88, 0xd3, 0xbe, 0xa5, 0x2c, 0x53, 0x73, 0xe8, 0x9a, 0x43, 0x60, 0x5e, 0x3c, 0x79, 0x01, 0xa6, - 0x44, 0x9b, 0xdd, 0xca, 0xec, 0x8f, 0xcd, 0xf9, 0xb7, 0x8d, 0xc4, 0x2c, 0x2d, 0x99, 0x17, 0x71, - 0x06, 0x7e, 0x20, 0x24, 0xf6, 0xc8, 0x32, 0xf8, 0xa8, 0x4f, 0x8c, 0x12, 0xb4, 0x28, 0x48, 0x0c, - 0x24, 0xa6, 0x8d, 0x98, 0x26, 0x5b, 0x48, 0x5f, 0xee, 0xd2, 0x24, 0xe5, 0x6b, 0xcc, 0xf8, 0xc3, - 0x9d, 0x8d, 0x61, 0x1f, 0x27, 0x1c, 0xc0, 0x9d, 0x6c, 0x4b, 0x47, 0xb7, 0x52, 0xc4, 0x74, 0x92, - 0xdd, 0x3c, 0xd4, 0xdf, 0x9d, 0x83, 0xd1, 0x64, 0xcb, 0x8b, 0xeb, 0x7c, 0x6d, 0x2b, 0xd7, 0xaa, - 0xcc, 0x96, 0xac, 0x33, 0x00, 0x0a, 0xb8, 0xfb, 0x5d, 0x07, 0x8e, 0xab, 0xb1, 0xe2, 0xf9, 0x5b, - 0x94, 0x95, 0x25, 0x6f, 0x87, 0x69, 0xed, 0x4f, 0x2e, 0x45, 0x5d, 0x19, 0xc0, 0x2a, 0x9b, 0x08, - 0x23, 0x66, 0xb0, 0x98, 0xa3, 0x26, 0x0b, 0x50, 0x65, 0x2a, 0x8b, 0xa2, 0x62, 0xfe, 0x69, 0x9f, - 0x75, 0x71, 0x6d, 0x45, 0x96, 0x32, 0x34, 0x24, 0x82, 0x99, 0x96, 0x97, 0xa4, 0x5c, 0x03, 0xe6, - 0x5e, 0x3e, 0xe4, 0x11, 0x25, 0xcf, 0x62, 0x59, 0xcd, 0x33, 0xc2, 0x7e, 0xde, 0xee, 0xd7, 0x47, - 0x60, 0x2a, 0x33, 0x45, 0x98, 0x79, 0xe9, 0x26, 0x6c, 0x0d, 0xd4, 0x7b, 0x6d, 0x6d, 0x5e, 0x6e, - 0x4a, 0x38, 0x6a, 0x0a, 0x46, 0xdd, 0xf1, 0x92, 0xe4, 0x6e, 0x14, 0xd7, 0xe5, 0x9c, 0xd6, 0xd4, - 0x6b, 0x12, 0x8e, 0x9a, 0x82, 0x19, 0x9a, 0x4d, 0xea, 0xc5, 0x34, 0xe6, 0xa7, 0xfa, 0x79, 0x43, - 0x53, 0x33, 0x28, 0xb4, 0xe9, 0xf8, 0xec, 0x4c, 0x5b, 0xc9, 0x52, 0x2b, 0xa0, 0x61, 0x2a, 0xd4, - 0x2c, 0x66, 0x76, 0x6e, 0xac, 0xae, 0xdb, 0x4c, 0xcd, 0xec, 0xcc, 0x21, 0x30, 0x2f, 0x9e, 0x7c, - 0xd4, 0x81, 0x29, 0xef, 0x6e, 0x62, 0x92, 0x3e, 0xf9, 0xf4, 0x3c, 0xb2, 0xb5, 0xca, 0xe4, 0x91, - 0xd6, 0x66, 0xd8, 0x3c, 0xcf, 0x80, 0x30, 0x2b, 0x94, 0x7c, 0xde, 0x01, 0x42, 0x77, 0xa9, 0xbf, - 0x16, 0x47, 0x3b, 0x41, 0x5d, 0xf5, 0xa1, 0xf4, 0x83, 0x8f, 0xe8, 0x76, 0x5d, 0xe8, 0xe3, 0x2b, - 0xa6, 0x77, 0x3f, 0x1c, 0x07, 0xe8, 0xe0, 0xfe, 0x5d, 0x19, 0x26, 0xac, 0x59, 0x39, 0xd0, 0xc4, - 0x3a, 0xdf, 0x67, 0x26, 0xb6, 0x74, 0x08, 0x13, 0xfb, 0x21, 0xa8, 0xfa, 0xca, 0x50, 0x14, 0x93, - 0xa4, 0x9a, 0x37, 0x3f, 0xc6, 0x56, 0x68, 0x10, 0x1a, 0x99, 0xe4, 0x12, 0xcc, 0x58, 0x6c, 0xa4, - 0x91, 0x19, 0xe1, 0x46, 0x46, 0x47, 0x1c, 0x16, 0xf3, 0x04, 0xd8, 0x5f, 0x86, 0x3c, 0xcb, 0xdc, - 0x9b, 0x40, 0xd6, 0x4b, 0x6c, 0xe7, 0x64, 0x02, 0xe8, 0xe2, 0xda, 0x8a, 0x02, 0xa3, 0x4d, 0xe3, - 0x7e, 0xdd, 0xd1, 0x9d, 0xfb, 0x18, 0xb2, 0x07, 0xee, 0x64, 0xb3, 0x07, 0x2e, 0x14, 0xd2, 0xcc, - 0x43, 0x32, 0x07, 0xae, 0xc3, 0xf8, 0x52, 0xd4, 0x6e, 0x7b, 0x61, 0x9d, 0xbc, 0x0e, 0xc6, 0x7d, - 0xf1, 0x53, 0xee, 0x17, 0xf8, 0x71, 0xb2, 0xc4, 0xa2, 0xc2, 0x91, 0xa7, 0x60, 0xc4, 0x8b, 0x9b, - 0x6a, 0x8f, 0xc0, 0x4f, 0x47, 0x16, 0xe3, 0x66, 0x82, 0x1c, 0xea, 0x7e, 0xb6, 0x04, 0xb0, 0x14, - 0xb5, 0x3b, 0x5e, 0x4c, 0xeb, 0x1b, 0xd1, 0xff, 0x07, 0x0b, 0x85, 0xeb, 0xf8, 0x09, 0x07, 0x08, - 0x6b, 0x95, 0x28, 0xa4, 0xa1, 0x39, 0x91, 0x61, 0xeb, 0xa5, 0xaf, 0xa0, 0x72, 0xf1, 0x31, 0x73, - 0x40, 0x21, 0xd0, 0xd0, 0x1c, 0xc0, 0x9d, 0x7c, 0x5a, 0x05, 0x9b, 0xcb, 0xd9, 0x93, 0x6e, 0x7e, - 0x3a, 0x29, 0x63, 0xcf, 0xee, 0xe7, 0x4a, 0x70, 0x5a, 0x98, 0xad, 0x6b, 0x5e, 0xe8, 0x35, 0x69, - 0x9b, 0x69, 0x75, 0xd0, 0xb0, 0xb3, 0xcf, 0xfc, 0x98, 0x40, 0x1d, 0x6c, 0x1f, 0x75, 0x70, 0x8a, - 0x41, 0x25, 0x86, 0xd1, 0x4a, 0x18, 0xa4, 0xc8, 0x99, 0x93, 0x04, 0x2a, 0xea, 0xda, 0x81, 0x34, - 0x36, 0x05, 0x09, 0xd2, 0xf3, 0xee, 0x92, 0x64, 0x8f, 0x5a, 0x90, 0xfb, 0x65, 0x07, 0xf2, 0x46, - 0x94, 0x3b, 0xfa, 0x22, 0x35, 0x2d, 0xef, 0xe8, 0x67, 0x33, 0xc9, 0x0e, 0x91, 0x98, 0xf5, 0x1e, - 0x98, 0xf0, 0xd2, 0x94, 0xb6, 0x3b, 0xc2, 0xeb, 0x2c, 0x3f, 0x5c, 0x64, 0xe3, 0x5a, 0x54, 0x0f, - 0x1a, 0x01, 0xf7, 0x36, 0x6d, 0x76, 0xee, 0x8b, 0x50, 0x51, 0xc1, 0xfc, 0x03, 0x74, 0xe6, 0xd3, - 0x99, 0xb3, 0x89, 0x21, 0xc3, 0xe5, 0x7e, 0x09, 0x06, 0xac, 0x82, 0xac, 0xca, 0xc6, 0x5e, 0x64, - 0xaa, 0x7c, 0x38, 0x9b, 0x41, 0x76, 0xc5, 0x41, 0x86, 0xd8, 0x42, 0xbf, 0xb3, 0xe8, 0x55, 0xdc, - 0x9c, 0x6d, 0x4c, 0x48, 0xfd, 0xf4, 0xf9, 0x06, 0x39, 0x0f, 0x60, 0xcc, 0xbc, 0x3c, 0xd0, 0xd7, - 0x41, 0x38, 0xb3, 0x1a, 0xa0, 0x45, 0xc5, 0x9c, 0xba, 0x20, 0x4c, 0x52, 0xaf, 0xd5, 0xba, 0x1c, - 0x84, 0xa9, 0xdc, 0xa6, 0x68, 0x13, 0xb0, 0x62, 0x50, 0x68, 0xd3, 0x9d, 0x79, 0x8b, 0xd5, 0x2f, - 0x87, 0x39, 0x23, 0xfa, 0x44, 0x09, 0xa6, 0x2f, 0x85, 0xdd, 0xb5, 0x4b, 0x6b, 0xdd, 0xcd, 0x56, - 0xe0, 0x5f, 0xa5, 0x3d, 0xd6, 0x69, 0xdb, 0xb4, 0xb7, 0xb2, 0x2c, 0x9b, 0x5d, 0x77, 0xda, 0x55, - 0x06, 0x44, 0x81, 0x63, 0x6a, 0x36, 0x82, 0xb0, 0x49, 0xe3, 0x4e, 0x1c, 0x48, 0x6f, 0xdc, 0x52, - 0xf3, 0xa2, 0x41, 0xa1, 0x4d, 0xc7, 0x78, 0x47, 0x77, 0x43, 0x1a, 0xe7, 0xed, 0xc7, 0x0d, 0x06, - 0x44, 0x81, 0x63, 0x44, 0x69, 0xdc, 0x4d, 0x52, 0xd9, 0x62, 0x9a, 0x68, 0x83, 0x01, 0x51, 0xe0, - 0xd8, 0xf0, 0x48, 0xba, 0x9b, 0x3c, 0xc0, 0x96, 0x3b, 0xea, 0x5c, 0x17, 0x60, 0x54, 0x78, 0x46, - 0xba, 0x4d, 0x7b, 0xcb, 0x6c, 0x35, 0xcd, 0x65, 0x26, 0x5c, 0x15, 0x60, 0x54, 0x78, 0xf7, 0x9f, - 0x1c, 0x20, 0xd9, 0xe6, 0x78, 0x0c, 0x0b, 0xf2, 0xcb, 0xd9, 0x05, 0xf9, 0x88, 0xb1, 0xd0, 0xac, - 0xfa, 0x43, 0xd6, 0xe5, 0xdf, 0x70, 0x60, 0xd2, 0x0e, 0x8b, 0x93, 0x66, 0xce, 0x10, 0xdd, 0xc8, - 0x1a, 0xa2, 0xfb, 0x7b, 0x73, 0x3f, 0x35, 0xe8, 0x56, 0x5c, 0x33, 0x48, 0xa3, 0x4e, 0xf2, 0x66, - 0x1a, 0x36, 0x83, 0x90, 0xf2, 0xa0, 0x8f, 0x08, 0xa7, 0x67, 0x62, 0xee, 0x4b, 0x51, 0x9d, 0x3e, - 0x84, 0x25, 0x73, 0x6f, 0xc3, 0x4c, 0x5f, 0x3a, 0xca, 0x01, 0x8c, 0xce, 0xbe, 0xc9, 0x86, 0xee, - 0x27, 0x1d, 0x98, 0xca, 0x64, 0xf3, 0x14, 0x64, 0xca, 0xf8, 0xac, 0x88, 0xf8, 0x89, 0x4a, 0x1c, - 0x84, 0x22, 0xe4, 0x52, 0xb1, 0x66, 0x85, 0x41, 0xa1, 0x4d, 0xe7, 0x7e, 0xa6, 0x04, 0x15, 0x15, - 0x9c, 0x3b, 0x80, 0x2a, 0x1f, 0x77, 0x60, 0x4a, 0x6f, 0x8d, 0xb9, 0xc3, 0x5c, 0x48, 0x46, 0x07, - 0xd3, 0x40, 0x1f, 0xbb, 0x31, 0x87, 0x59, 0x7b, 0xee, 0x68, 0x0b, 0xc3, 0xac, 0x6c, 0x72, 0x0b, - 0x20, 0xe9, 0x25, 0x29, 0x6d, 0x5b, 0xae, 0xbb, 0x6b, 0xcd, 0x8e, 0x79, 0x3f, 0x8a, 0x29, 0x9b, - 0x0b, 0xd7, 0xa3, 0x3a, 0x5d, 0xd7, 0x94, 0xc6, 0x10, 0x1a, 0x18, 0x5a, 0x9c, 0xdc, 0xdf, 0x2d, - 0xc1, 0xf1, 0xbc, 0x4a, 0xe4, 0xdd, 0x30, 0xa9, 0xa4, 0x5b, 0x97, 0x01, 0x55, 0x44, 0x72, 0x12, - 0x2d, 0xdc, 0xfd, 0xbd, 0xb9, 0xb9, 0xfe, 0xdb, 0x90, 0xf3, 0x36, 0x09, 0x66, 0x98, 0x89, 0xf8, - 0x84, 0x8c, 0xa8, 0xd4, 0x7a, 0x8b, 0x9d, 0x8e, 0x0c, 0x32, 0x58, 0xf1, 0x09, 0x1b, 0x8b, 0x39, - 0x6a, 0xb2, 0x06, 0x27, 0x2d, 0xc8, 0x75, 0x1a, 0x34, 0xb7, 0x36, 0xa3, 0x58, 0x64, 0x9d, 0x97, - 0x6b, 0x4f, 0x49, 0x2e, 0x27, 0x71, 0x00, 0x0d, 0x0e, 0x2c, 0x49, 0xde, 0x04, 0x15, 0xdf, 0xeb, - 0x78, 0x7e, 0x90, 0xf6, 0xe4, 0x5e, 0x44, 0xdb, 0x91, 0x25, 0x09, 0x47, 0x4d, 0xe1, 0x5e, 0x83, - 0x91, 0x03, 0x8e, 0xa0, 0x03, 0xad, 0xcb, 0x2f, 0x42, 0x85, 0xb1, 0x63, 0x76, 0xa3, 0x28, 0x96, - 0x11, 0x54, 0xd4, 0x25, 0x04, 0xe2, 0x42, 0x39, 0xf0, 0x54, 0x08, 0x48, 0x57, 0x6b, 0x25, 0x49, - 0xba, 0xdc, 0xeb, 0x60, 0x48, 0xf2, 0x34, 0x94, 0xe9, 0x6e, 0x27, 0x1f, 0xeb, 0xb9, 0xb0, 0xdb, - 0x09, 0x62, 0x9a, 0x30, 0x22, 0xba, 0xdb, 0x21, 0x67, 0xa0, 0x14, 0xd4, 0xe5, 0x82, 0x02, 0x92, - 0xa6, 0xb4, 0xb2, 0x8c, 0xa5, 0xa0, 0xee, 0xee, 0x42, 0x55, 0xdf, 0x7a, 0x20, 0xdb, 0xca, 0xce, - 0x3a, 0x45, 0x44, 0xd3, 0x15, 0xdf, 0x21, 0x16, 0xb6, 0x0b, 0x60, 0xf2, 0xc0, 0x8a, 0xb2, 0x2f, - 0xe7, 0x60, 0xc4, 0x8f, 0x64, 0xca, 0x65, 0xc5, 0xb0, 0xe1, 0x06, 0x96, 0x63, 0xdc, 0xdb, 0x30, - 0x7d, 0x35, 0x8c, 0xee, 0x86, 0x6c, 0xe1, 0xbb, 0x18, 0xd0, 0x56, 0x9d, 0x31, 0x6e, 0xb0, 0x1f, - 0xf9, 0xe5, 0x9c, 0x63, 0x51, 0xe0, 0xf4, 0xd5, 0x80, 0xd2, 0xb0, 0xab, 0x01, 0xee, 0x2f, 0x3b, - 0x70, 0x3c, 0x9f, 0xf3, 0xf5, 0x3d, 0xdb, 0x61, 0x7c, 0x98, 0x29, 0xa3, 0x92, 0x8a, 0x6e, 0x74, - 0xc4, 0xf1, 0xe5, 0xf3, 0x30, 0xb9, 0xd9, 0x0d, 0x5a, 0x75, 0xf9, 0x2d, 0xf5, 0xd1, 0x69, 0x53, - 0x35, 0x0b, 0x87, 0x19, 0x4a, 0xe6, 0xa7, 0x6d, 0x06, 0xa1, 0x17, 0xf7, 0xd6, 0xcc, 0xba, 0xa1, - 0xcd, 0x53, 0x4d, 0x63, 0xd0, 0xa2, 0x72, 0xff, 0xa6, 0x0c, 0xe6, 0xfa, 0x05, 0x09, 0xe4, 0xe9, - 0xb8, 0x53, 0x44, 0xd8, 0x6a, 0xbd, 0x17, 0xfa, 0xe6, 0xa2, 0x47, 0x25, 0x77, 0x38, 0xfe, 0x31, - 0x87, 0x79, 0x88, 0x41, 0x1a, 0x78, 0xdc, 0x58, 0xc8, 0x8d, 0xd2, 0x5a, 0x41, 0x07, 0xa8, 0x2b, - 0x82, 0x73, 0x14, 0xdb, 0x3e, 0xa7, 0x16, 0x86, 0xb6, 0x64, 0xf2, 0x92, 0x0c, 0x39, 0x97, 0x0b, - 0xcb, 0xad, 0xa8, 0xe4, 0xe2, 0xcc, 0x1d, 0x18, 0x8d, 0x69, 0x1a, 0xab, 0xac, 0x96, 0xab, 0x47, - 0x3d, 0x80, 0x4b, 0xe3, 0xde, 0x7a, 0xca, 0x36, 0x63, 0x4d, 0xcb, 0x31, 0xe2, 0x60, 0x14, 0x82, - 0xdc, 0x04, 0x48, 0x7f, 0x5b, 0x1c, 0x32, 0x8a, 0xbb, 0x00, 0x55, 0xaf, 0x9b, 0x46, 0x6d, 0xd6, - 0x4c, 0xbc, 0x7b, 0x2a, 0x56, 0x9c, 0x5a, 0x21, 0xd0, 0xd0, 0xb8, 0xaf, 0x8e, 0x42, 0xee, 0xb8, - 0x9a, 0xec, 0xda, 0x57, 0x87, 0x9c, 0x62, 0xaf, 0x0e, 0x69, 0x65, 0x06, 0x5d, 0x1f, 0x22, 0x4d, - 0x18, 0xed, 0x6c, 0x79, 0x89, 0x9a, 0xa3, 0x2f, 0xaa, 0x66, 0x5a, 0x63, 0xc0, 0xfb, 0x7b, 0x73, - 0x3f, 0x7d, 0x30, 0x3f, 0x90, 0x8d, 0xd5, 0x05, 0x91, 0xbb, 0x67, 0x44, 0x73, 0x1e, 0x28, 0xf8, - 0xdb, 0x9e, 0x60, 0x79, 0x9f, 0x3d, 0xed, 0x47, 0x1c, 0x91, 0xe3, 0x84, 0x34, 0xe9, 0xb6, 0x52, - 0x39, 0x1a, 0x5e, 0x2c, 0x70, 0x96, 0x09, 0xc6, 0x26, 0xd9, 0x49, 0x7c, 0xa3, 0x25, 0x94, 0xbc, - 0x1b, 0xaa, 0x49, 0xea, 0xc5, 0xe9, 0x43, 0xa6, 0x46, 0xe8, 0x46, 0x5f, 0x57, 0x4c, 0xd0, 0xf0, - 0x23, 0xef, 0x02, 0x68, 0x04, 0x61, 0x90, 0x6c, 0x3d, 0xe4, 0x49, 0x11, 0x57, 0xfc, 0xa2, 0xe6, - 0x80, 0x16, 0x37, 0x66, 0xdd, 0xf8, 0xd8, 0x16, 0x21, 0xcd, 0x0a, 0x5f, 0x4b, 0xb5, 0x75, 0x43, - 0x8d, 0x41, 0x8b, 0xca, 0xfd, 0x20, 0x9c, 0xc8, 0x5f, 0xdb, 0x95, 0x5b, 0xc3, 0x66, 0x1c, 0x75, - 0x3b, 0xf9, 0xb5, 0x84, 0x5f, 0xeb, 0x44, 0x81, 0x63, 0x36, 0x7e, 0x3b, 0x08, 0xeb, 0x79, 0x1b, - 0x7f, 0x35, 0x08, 0xeb, 0xc8, 0x31, 0x07, 0xb8, 0x53, 0xf5, 0xc7, 0x0e, 0x9c, 0xdb, 0xef, 0x76, - 0x31, 0xdb, 0xf6, 0xdf, 0xf5, 0xe2, 0x50, 0xde, 0x97, 0xe0, 0xb6, 0xe3, 0xb6, 0x17, 0x87, 0xc8, - 0xa1, 0xa4, 0x07, 0x63, 0x22, 0x1d, 0x4c, 0x7a, 0xc7, 0x2f, 0x16, 0x7b, 0xd7, 0x99, 0xed, 0xad, - 0x74, 0xb4, 0x46, 0xa4, 0xa2, 0xa1, 0x14, 0xe8, 0xbe, 0xea, 0x00, 0xb9, 0xb1, 0x43, 0xe3, 0x38, - 0xa8, 0x5b, 0x09, 0x6c, 0xe4, 0x39, 0x98, 0xbc, 0xb3, 0x7e, 0xe3, 0xfa, 0x5a, 0x14, 0x84, 0x3c, - 0x0f, 0xdb, 0x4a, 0x9b, 0xb8, 0x62, 0xc1, 0x31, 0x43, 0x45, 0x96, 0x60, 0xe6, 0xce, 0xcb, 0x6c, - 0xc9, 0xb9, 0xb0, 0xdb, 0x89, 0x69, 0x92, 0xe8, 0x17, 0x02, 0xaa, 0xe2, 0x60, 0xea, 0xca, 0x8b, - 0x39, 0x24, 0xf6, 0xd3, 0xbb, 0x5f, 0x2a, 0xc1, 0x84, 0x75, 0xa1, 0xfe, 0x00, 0xfe, 0x48, 0xee, - 0x0d, 0x80, 0xd2, 0x01, 0xdf, 0x00, 0x78, 0x06, 0x2a, 0x9d, 0xa8, 0x15, 0xf8, 0x81, 0x4e, 0xb0, - 0x9e, 0xe4, 0xa7, 0x57, 0x12, 0x86, 0x1a, 0x4b, 0xee, 0x42, 0x55, 0xdf, 0x8c, 0x95, 0x29, 0x57, - 0x45, 0x79, 0x64, 0x7a, 0xae, 0x99, 0x1b, 0xaf, 0x46, 0x16, 0x71, 0x61, 0x8c, 0x0f, 0x54, 0x15, - 0x9b, 0xe7, 0x67, 0xf8, 0x7c, 0x04, 0x27, 0x28, 0x31, 0xee, 0xbf, 0x8d, 0x42, 0x15, 0x69, 0x27, - 0x5a, 0x8a, 0x69, 0x3d, 0x21, 0xaf, 0x85, 0x72, 0x37, 0x6e, 0xc9, 0xc6, 0xd2, 0x61, 0x9e, 0x9b, - 0xb8, 0x8a, 0x0c, 0x9e, 0x59, 0x1d, 0x4a, 0x87, 0x3a, 0xe3, 0x2b, 0xef, 0x7b, 0xc6, 0xf7, 0x02, - 0x4c, 0x25, 0xc9, 0xd6, 0x5a, 0x1c, 0xec, 0x78, 0x29, 0x1b, 0x73, 0x32, 0x26, 0x62, 0x0e, 0x55, - 0xd6, 0x2f, 0x1b, 0x24, 0x66, 0x69, 0xc9, 0x25, 0x98, 0x31, 0x27, 0x6d, 0x34, 0x4e, 0x79, 0x08, - 0x44, 0x44, 0x4b, 0xf4, 0x99, 0x86, 0x39, 0x9b, 0x93, 0x04, 0xd8, 0x5f, 0x86, 0x2c, 0xc3, 0xf1, - 0x0c, 0x90, 0x29, 0x22, 0x42, 0x29, 0xb3, 0x92, 0xcf, 0xf1, 0x0c, 0x1f, 0xa6, 0x4b, 0x5f, 0x09, - 0x72, 0x0d, 0x4e, 0x88, 0xfe, 0xe5, 0x37, 0xaa, 0x75, 0x8d, 0xc6, 0x39, 0xa3, 0x1f, 0x92, 0x8c, - 0x4e, 0x5c, 0xea, 0x27, 0xc1, 0x41, 0xe5, 0xd8, 0x08, 0xd5, 0xe0, 0x95, 0x65, 0x69, 0xd8, 0xf4, - 0x08, 0xd5, 0x6c, 0x56, 0xea, 0x68, 0xd3, 0x91, 0x77, 0xc2, 0x93, 0xe6, 0x53, 0x44, 0xd0, 0xc4, - 0x6a, 0xbf, 0x2c, 0x4f, 0xb3, 0xe7, 0x24, 0x8b, 0x27, 0x2f, 0x0d, 0x24, 0xab, 0xe3, 0xb0, 0xf2, - 0x64, 0x13, 0xce, 0x68, 0xd4, 0x05, 0x36, 0x7b, 0x3b, 0x71, 0x90, 0xd0, 0x9a, 0x97, 0xd0, 0x9b, - 0x71, 0x8b, 0x67, 0x6f, 0x55, 0xcd, 0xab, 0x00, 0x97, 0x82, 0xf4, 0xf2, 0x20, 0x4a, 0x5c, 0xc5, - 0x07, 0x70, 0x61, 0xce, 0x05, 0x0d, 0xbd, 0xcd, 0x16, 0xbd, 0xb1, 0xb4, 0xc2, 0x6f, 0xba, 0x59, - 0xce, 0xc5, 0x05, 0x85, 0x40, 0x43, 0xa3, 0x5d, 0xfb, 0xc9, 0xa1, 0xae, 0xfd, 0x37, 0x1d, 0x98, - 0xd2, 0x83, 0xfd, 0x31, 0xc4, 0xbb, 0x5a, 0xd9, 0x78, 0xd7, 0xa5, 0xa3, 0x7a, 0x75, 0x52, 0xf3, - 0x21, 0x1b, 0xb1, 0x3f, 0xa9, 0x02, 0xf0, 0x77, 0x56, 0x02, 0x9e, 0x6d, 0x79, 0x0e, 0x46, 0x62, - 0xda, 0x89, 0xf2, 0x96, 0x8f, 0x51, 0x20, 0xc7, 0x7c, 0xff, 0x4e, 0xe7, 0x41, 0x67, 0xbe, 0xa3, - 0xdf, 0xdb, 0x33, 0xdf, 0x75, 0x38, 0x15, 0x84, 0x09, 0xf5, 0xbb, 0xb1, 0x5c, 0xe8, 0x2e, 0x47, - 0x89, 0xb6, 0x0e, 0x95, 0xda, 0x6b, 0x25, 0xa3, 0x53, 0x2b, 0x83, 0x88, 0x70, 0x70, 0x59, 0xd6, - 0xa4, 0x0a, 0x21, 0xaf, 0x75, 0x98, 0xf0, 0x80, 0x84, 0xa3, 0xa6, 0x30, 0x13, 0x62, 0xb5, 0xa1, - 0xee, 0x6d, 0xe4, 0x26, 0xc4, 0xea, 0xc5, 0x75, 0x34, 0x34, 0x83, 0xad, 0x62, 0xb5, 0x20, 0xab, - 0x08, 0x87, 0xb6, 0x8a, 0x6a, 0x7e, 0x4e, 0x0c, 0xbd, 0x95, 0xaf, 0x16, 0xeb, 0xc9, 0xa1, 0x8b, - 0xf5, 0xdb, 0x61, 0x3a, 0x08, 0xb7, 0x68, 0x1c, 0xa4, 0xb4, 0xce, 0xe7, 0xc2, 0xec, 0x14, 0x6f, - 0x08, 0x1d, 0xb9, 0x5a, 0xc9, 0x60, 0x31, 0x47, 0x9d, 0x35, 0x2a, 0xd3, 0x07, 0x30, 0x2a, 0x43, - 0x4c, 0xf9, 0xb1, 0x62, 0x4c, 0xf9, 0xf1, 0xa3, 0x9b, 0xf2, 0x99, 0x47, 0x6a, 0xca, 0x49, 0x21, - 0xa6, 0xfc, 0x69, 0x18, 0xed, 0xc4, 0xd1, 0x6e, 0x6f, 0xf6, 0x44, 0xd6, 0x9b, 0x5e, 0x63, 0x40, - 0x14, 0x38, 0xf7, 0x95, 0x12, 0x9c, 0x32, 0xe6, 0x8b, 0x0d, 0x9a, 0xa0, 0xc1, 0x26, 0x30, 0xbf, - 0x31, 0x27, 0x72, 0x28, 0xac, 0x48, 0xa8, 0x09, 0xaa, 0x6a, 0x0c, 0x5a, 0x54, 0x3c, 0xa0, 0x48, - 0x63, 0x9e, 0x8e, 0x99, 0xb7, 0x6d, 0x4b, 0x12, 0x8e, 0x9a, 0x82, 0xbf, 0xbc, 0x46, 0xe3, 0x54, - 0x1e, 0xa8, 0xe4, 0x13, 0x8c, 0x96, 0x0c, 0x0a, 0x6d, 0x3a, 0xe6, 0x03, 0xfa, 0x6a, 0x5e, 0x31, - 0xfb, 0x36, 0x29, 0x7c, 0x40, 0x3d, 0x95, 0x34, 0x56, 0xa9, 0xc3, 0x23, 0xc7, 0xa3, 0xfd, 0xea, - 0xf0, 0x48, 0x80, 0xa6, 0x70, 0xff, 0xc7, 0x81, 0xd7, 0x0c, 0x6c, 0x8a, 0xc7, 0xb0, 0x66, 0xed, - 0x66, 0xd7, 0xac, 0xf5, 0xa3, 0xaf, 0x59, 0x7d, 0xb5, 0x18, 0xb2, 0x7e, 0xfd, 0xad, 0x03, 0xd3, - 0x86, 0xfe, 0x31, 0x54, 0x35, 0x28, 0xf4, 0x0d, 0x35, 0xa3, 0xba, 0x48, 0x13, 0xcc, 0xd4, 0xed, - 0x9b, 0xbc, 0x6e, 0x62, 0x43, 0xb5, 0xe8, 0xab, 0x47, 0x4a, 0xf6, 0xd9, 0x99, 0xf4, 0x60, 0x8c, - 0x5f, 0x2b, 0x4d, 0x8a, 0xd9, 0xd8, 0x65, 0xe5, 0xf3, 0xd8, 0xa6, 0xd9, 0xd8, 0xf1, 0xcf, 0x04, - 0xa5, 0x40, 0x9e, 0x2c, 0x1c, 0x24, 0xcc, 0x08, 0xd6, 0x65, 0x0c, 0xd6, 0x24, 0x0b, 0x4b, 0x38, - 0x6a, 0x0a, 0xb7, 0x0d, 0xb3, 0x59, 0xe6, 0xcb, 0xb4, 0xc1, 0xe3, 0x67, 0x07, 0xaa, 0xe6, 0x02, - 0x54, 0x3d, 0x5e, 0x6a, 0xb5, 0xeb, 0xe5, 0x5f, 0x2a, 0x59, 0x54, 0x08, 0x34, 0x34, 0xee, 0xef, - 0x38, 0x70, 0x62, 0x40, 0x65, 0x0a, 0x8c, 0x3d, 0xa7, 0xc6, 0x0a, 0x0c, 0x79, 0x3d, 0xa6, 0x4e, - 0x1b, 0x9e, 0x8a, 0xd0, 0x58, 0x01, 0x9d, 0x65, 0x01, 0x46, 0x85, 0x77, 0xff, 0xdd, 0x81, 0x63, - 0x59, 0x5d, 0x13, 0x72, 0x05, 0x88, 0xa8, 0xcc, 0x72, 0x90, 0xf8, 0xd1, 0x0e, 0x8d, 0x7b, 0xac, - 0xe6, 0x42, 0xeb, 0x33, 0x92, 0x13, 0x59, 0xec, 0xa3, 0xc0, 0x01, 0xa5, 0xc8, 0x27, 0x79, 0x7e, - 0x8e, 0x6a, 0x6d, 0x35, 0x52, 0x6e, 0x15, 0x39, 0x52, 0x4c, 0x67, 0xda, 0xdb, 0x62, 0x2d, 0x12, - 0x6d, 0xf9, 0xee, 0xb7, 0x46, 0x40, 0x1f, 0x4e, 0xf1, 0x58, 0x40, 0x41, 0x91, 0x94, 0xcc, 0x73, - 0x36, 0xe5, 0x43, 0x3c, 0x67, 0x33, 0xf2, 0xa0, 0x8d, 0xbf, 0x78, 0x5b, 0xc5, 0x38, 0x98, 0x96, - 0xd1, 0xdf, 0x30, 0x28, 0xb4, 0xe9, 0x98, 0x26, 0xad, 0x60, 0x87, 0x8a, 0x42, 0x63, 0x59, 0x4d, - 0x56, 0x15, 0x02, 0x0d, 0x0d, 0xd3, 0xa4, 0x1e, 0x34, 0x1a, 0x72, 0xfb, 0xa7, 0x35, 0x61, 0xad, - 0x83, 0x1c, 0xc3, 0x28, 0xb6, 0xa2, 0x68, 0x5b, 0x3a, 0x75, 0x9a, 0xe2, 0x72, 0x14, 0x6d, 0x23, - 0xc7, 0x30, 0x37, 0x24, 0x8c, 0xe2, 0xb6, 0xd7, 0x0a, 0xde, 0x47, 0xeb, 0x5a, 0x8a, 0x74, 0xe6, - 0xb4, 0x1b, 0x72, 0xbd, 0x9f, 0x04, 0x07, 0x95, 0x63, 0x23, 0xb0, 0x13, 0xd3, 0x7a, 0xe0, 0xa7, - 0x36, 0x37, 0xc8, 0x8e, 0xc0, 0xb5, 0x3e, 0x0a, 0x1c, 0x50, 0x8a, 0x2c, 0xc2, 0x31, 0x75, 0xb8, - 0xa8, 0x12, 0x40, 0x84, 0x87, 0xa7, 0x9d, 0x6b, 0xcc, 0xa2, 0x31, 0x4f, 0xcf, 0xac, 0x4d, 0x5b, - 0xa6, 0xe1, 0x70, 0xdf, 0xcf, 0xb2, 0x36, 0x2a, 0x3d, 0x07, 0x35, 0x85, 0xfb, 0x7b, 0x25, 0xb6, - 0x3a, 0x0e, 0xb9, 0x35, 0xf9, 0xd8, 0x22, 0x77, 0xd9, 0x11, 0x39, 0x72, 0x80, 0x11, 0xf9, 0x1c, - 0x4c, 0xde, 0x49, 0xa2, 0x50, 0x47, 0xc5, 0x46, 0x87, 0x46, 0xc5, 0x2c, 0xaa, 0xc1, 0x51, 0xb1, - 0xb1, 0x43, 0x46, 0xc5, 0xfe, 0x62, 0x14, 0x4e, 0xeb, 0xf3, 0x60, 0x9a, 0xde, 0x8d, 0xe2, 0xed, - 0x20, 0x6c, 0xf2, 0x33, 0xd4, 0x2f, 0x3a, 0x30, 0x29, 0x86, 0xb7, 0xbc, 0x5f, 0x2e, 0xce, 0x0c, - 0x1b, 0x05, 0x5d, 0x01, 0xca, 0x08, 0x9b, 0xdf, 0xb0, 0x04, 0xe5, 0x2e, 0xfb, 0xdb, 0x28, 0xcc, - 0x68, 0x44, 0x3e, 0x00, 0xa0, 0x1e, 0x41, 0x6a, 0x14, 0xf4, 0x14, 0x94, 0xd2, 0x0f, 0x69, 0xc3, - 0xb8, 0x92, 0x1b, 0x5a, 0x08, 0x5a, 0x02, 0xc9, 0x2b, 0x0e, 0x8c, 0xb5, 0x44, 0xdb, 0x88, 0x03, - 0xa0, 0x97, 0x1e, 0x49, 0xdb, 0xd8, 0xad, 0xa2, 0x97, 0x65, 0xd9, 0x1e, 0x52, 0x3e, 0x41, 0x18, - 0x0f, 0xc2, 0x26, 0xeb, 0x56, 0x19, 0x48, 0x7c, 0xc3, 0xa0, 0xfc, 0x83, 0xd5, 0xc8, 0xab, 0xd7, - 0xbc, 0x96, 0x17, 0xfa, 0x34, 0x5e, 0x11, 0xe4, 0xf6, 0x53, 0x34, 0x1c, 0x80, 0x8a, 0x51, 0xdf, - 0x1d, 0xb7, 0xd1, 0x83, 0xdc, 0x71, 0x3b, 0xf3, 0x0e, 0x98, 0xe9, 0xeb, 0xcc, 0x43, 0xdd, 0xfc, - 0xff, 0x49, 0x98, 0x78, 0xc8, 0xa2, 0xee, 0x9f, 0x8e, 0x99, 0x35, 0xe6, 0x7a, 0x54, 0x17, 0x37, - 0xad, 0x62, 0xd3, 0xa3, 0xd2, 0x55, 0x2c, 0x70, 0x88, 0x58, 0xcf, 0xd9, 0x68, 0x20, 0xda, 0x22, - 0xd9, 0x18, 0xed, 0x78, 0x31, 0x0d, 0x1f, 0xf5, 0x18, 0x5d, 0xd3, 0x42, 0xd0, 0x12, 0x48, 0xb6, - 0x32, 0x27, 0x94, 0x17, 0x8f, 0x7e, 0x42, 0xc9, 0xbc, 0xd7, 0x81, 0x37, 0x62, 0x3e, 0xed, 0xc0, - 0x74, 0x98, 0x19, 0xb9, 0xf2, 0x94, 0x6a, 0xe3, 0x51, 0xcc, 0x0a, 0x71, 0xc3, 0x35, 0x0b, 0xc3, - 0x9c, 0xfc, 0x41, 0x2b, 0xd0, 0xe8, 0x21, 0x57, 0x20, 0x73, 0x65, 0x73, 0x6c, 0xd8, 0x95, 0x4d, - 0x12, 0xea, 0xcb, 0xda, 0xe3, 0x85, 0x5f, 0xd6, 0x86, 0x01, 0x17, 0xb5, 0x6f, 0x43, 0xd5, 0x8f, - 0xa9, 0x97, 0x3e, 0xe4, 0xbd, 0x5d, 0xfe, 0x80, 0xd8, 0x92, 0x62, 0x80, 0x86, 0x97, 0xfb, 0xd7, - 0x65, 0x38, 0xae, 0x5a, 0x44, 0x9d, 0xde, 0xb0, 0xe5, 0x4c, 0xc8, 0x35, 0xbe, 0xa8, 0x5e, 0xce, - 0x2e, 0x2b, 0x04, 0x1a, 0x1a, 0xe6, 0x3e, 0x75, 0x13, 0x7a, 0xa3, 0x43, 0xc3, 0xd5, 0x60, 0x33, - 0xe1, 0x2d, 0x6e, 0xa5, 0x80, 0xdd, 0x34, 0x28, 0xb4, 0xe9, 0x98, 0xef, 0x2c, 0xdc, 0xd8, 0x24, - 0x7f, 0x18, 0x2a, 0xdd, 0x63, 0x54, 0x78, 0xf2, 0x85, 0x81, 0xaf, 0x2e, 0x14, 0x93, 0x06, 0xd0, - 0x77, 0x68, 0x75, 0xc8, 0xe7, 0x16, 0x5e, 0x75, 0xe0, 0xd8, 0x76, 0x26, 0xff, 0x44, 0x99, 0xe4, - 0x23, 0x66, 0x35, 0x66, 0x93, 0x5a, 0xcc, 0x10, 0xce, 0xc2, 0x13, 0xcc, 0x4b, 0x77, 0xff, 0xcb, - 0x01, 0xdb, 0x3c, 0x1d, 0xcc, 0x11, 0xb2, 0xde, 0xd1, 0x29, 0xed, 0xf3, 0x8e, 0x8e, 0xf2, 0x99, - 0xca, 0x07, 0xf3, 0xd1, 0x47, 0x0e, 0xe1, 0xa3, 0x8f, 0x0e, 0x75, 0xb2, 0x5e, 0x0b, 0xe5, 0x6e, - 0x50, 0x97, 0x6e, 0xb6, 0x39, 0x90, 0x5a, 0x59, 0x46, 0x06, 0x77, 0xff, 0x68, 0xd4, 0x6c, 0xab, - 0xe5, 0xe9, 0xf5, 0x0f, 0x44, 0xb5, 0x1b, 0x3a, 0x49, 0x55, 0xd4, 0xfc, 0x7a, 0x5f, 0x92, 0xea, - 0xdb, 0x0e, 0x9f, 0x9c, 0x20, 0x1a, 0x68, 0x58, 0x8e, 0xea, 0xf8, 0x3e, 0x99, 0x09, 0x77, 0xa0, - 0xc2, 0x76, 0x22, 0x3c, 0x3e, 0x56, 0xc9, 0x28, 0x55, 0xb9, 0x2c, 0xe1, 0xf7, 0xf7, 0xe6, 0xde, - 0x7a, 0x78, 0xb5, 0x54, 0x69, 0xd4, 0xfc, 0x49, 0x02, 0x55, 0xf6, 0x9b, 0x27, 0x51, 0xc8, 0x3d, - 0xce, 0x4d, 0x6d, 0x8b, 0x14, 0xa2, 0x90, 0x0c, 0x0d, 0x23, 0x87, 0x84, 0x50, 0xe5, 0x2f, 0xbe, - 0x70, 0xa1, 0x62, 0x2b, 0xb4, 0xa6, 0x53, 0x19, 0x14, 0xe2, 0xfe, 0xde, 0xdc, 0x0b, 0x87, 0x17, - 0xaa, 0x8b, 0xa3, 0x11, 0xe1, 0x7e, 0xa7, 0x6c, 0xc6, 0xae, 0xcc, 0x4d, 0xfe, 0x81, 0x18, 0xbb, - 0xcf, 0xe7, 0xc6, 0xee, 0xb9, 0xbe, 0xb1, 0x3b, 0x6d, 0x5e, 0x45, 0xc9, 0x8c, 0xc6, 0xc7, 0xbd, - 0xc0, 0xee, 0xbf, 0xed, 0xe6, 0x9e, 0xc5, 0xcb, 0xdd, 0x20, 0xa6, 0xc9, 0x5a, 0xdc, 0x0d, 0x83, - 0xb0, 0xc9, 0x87, 0x63, 0xc5, 0xf6, 0x2c, 0x32, 0x68, 0xcc, 0xd3, 0xbb, 0x5f, 0xe2, 0x67, 0x8e, - 0x56, 0x3e, 0x16, 0xeb, 0xe5, 0x16, 0x7f, 0x34, 0x47, 0x64, 0x84, 0xea, 0x5e, 0x16, 0x2f, 0xe5, - 0x08, 0x1c, 0xb9, 0x0b, 0xe3, 0x9b, 0xe2, 0xe2, 0x7e, 0x31, 0x17, 0x84, 0xe4, 0x2b, 0x00, 0xfc, - 0x2a, 0xa6, 0x7a, 0x12, 0xe0, 0xbe, 0xf9, 0x89, 0x4a, 0x9a, 0xfb, 0x6b, 0x65, 0x38, 0x96, 0x7b, - 0xd2, 0x85, 0xed, 0xcf, 0xd5, 0xfb, 0x3d, 0xf9, 0x60, 0xba, 0x7e, 0xa7, 0x56, 0x53, 0x90, 0xf7, - 0x02, 0xd4, 0x69, 0xa7, 0x15, 0xf5, 0xb8, 0xe3, 0x32, 0x72, 0x68, 0xc7, 0x45, 0xfb, 0xba, 0xcb, - 0x9a, 0x0b, 0x5a, 0x1c, 0x65, 0x1a, 0xec, 0xa8, 0x78, 0x96, 0x20, 0x9b, 0x06, 0x6b, 0xdd, 0x93, - 0x1b, 0x7b, 0xbc, 0xf7, 0xe4, 0x02, 0x38, 0x26, 0x54, 0xd4, 0x59, 0x4f, 0x0f, 0x91, 0xdc, 0x74, - 0x82, 0x8d, 0xa8, 0xe5, 0x2c, 0x1b, 0xcc, 0xf3, 0x75, 0x3f, 0x55, 0x62, 0xee, 0x9b, 0x68, 0xec, - 0x6b, 0x2a, 0x96, 0xfd, 0x7a, 0x18, 0xf3, 0xba, 0xe9, 0x56, 0xd4, 0xf7, 0x90, 0xc2, 0x22, 0x87, - 0xa2, 0xc4, 0x92, 0x55, 0x18, 0xa9, 0x7b, 0xa9, 0x7a, 0x67, 0xfd, 0x30, 0xca, 0x99, 0xc0, 0x95, - 0x97, 0x52, 0xe4, 0x5c, 0xc8, 0x53, 0x30, 0x92, 0x7a, 0xcd, 0xcc, 0x0b, 0x8f, 0x1b, 0x5e, 0x33, - 0x41, 0x0e, 0xb5, 0x57, 0x97, 0x91, 0x7d, 0x56, 0x97, 0x17, 0xac, 0x7f, 0x00, 0xb0, 0x0e, 0x49, - 0xfa, 0x5f, 0xed, 0x17, 0x89, 0xf9, 0x19, 0x5a, 0xf7, 0xc7, 0x60, 0xd2, 0x7e, 0xd5, 0xff, 0x40, - 0xf7, 0x7a, 0xdc, 0x7f, 0x1d, 0x81, 0xa9, 0x4c, 0x66, 0x5c, 0x66, 0x94, 0x3b, 0xfb, 0x8e, 0x72, - 0x7e, 0xa6, 0xd5, 0x0d, 0xa9, 0xcc, 0x7b, 0xb4, 0xce, 0xb4, 0xba, 0x21, 0x45, 0x81, 0x63, 0xbd, - 0x52, 0x8f, 0x7b, 0xd8, 0x0d, 0x65, 0x10, 0x5d, 0xf7, 0xca, 0x32, 0x87, 0xa2, 0xc4, 0xb2, 0x0d, - 0xec, 0x64, 0xc2, 0x8d, 0xa2, 0xb0, 0x11, 0x72, 0xd6, 0x5c, 0x29, 0xe2, 0xf1, 0x29, 0x99, 0x05, - 0xca, 0x37, 0xf4, 0x36, 0x04, 0x33, 0x12, 0xc9, 0x47, 0x1d, 0xfb, 0xd9, 0xad, 0xb1, 0x22, 0x0e, - 0x7f, 0xf2, 0x89, 0x87, 0x62, 0x06, 0x3d, 0xf8, 0xf5, 0xad, 0x44, 0x4f, 0xe0, 0xf1, 0x47, 0x33, - 0x81, 0x61, 0xc0, 0xe4, 0x7d, 0x23, 0x54, 0xdb, 0x5e, 0x18, 0x34, 0x68, 0x92, 0x8a, 0x7f, 0xe4, - 0xa8, 0x8a, 0xdd, 0xd3, 0x35, 0x05, 0x44, 0x83, 0xe7, 0xff, 0x7b, 0xc3, 0x2b, 0x26, 0x36, 0x31, - 0x55, 0xeb, 0x7f, 0x6f, 0x0c, 0x18, 0x6d, 0x1a, 0xf7, 0xf7, 0x1d, 0x38, 0x35, 0xb0, 0x31, 0xbe, - 0x7f, 0xa3, 0x95, 0xee, 0x1f, 0x94, 0xe0, 0xc4, 0x80, 0xcc, 0x51, 0xd2, 0x7b, 0x64, 0xaf, 0xb3, - 0xc9, 0xd4, 0xd4, 0xa9, 0xa1, 0x63, 0xe3, 0x70, 0xcb, 0x90, 0x59, 0x0a, 0xca, 0x8f, 0x75, 0x29, - 0x70, 0xbf, 0x54, 0x02, 0xeb, 0x1d, 0x41, 0xf2, 0x41, 0x3b, 0x49, 0xda, 0x29, 0x2a, 0xa1, 0x57, - 0x30, 0xd7, 0x49, 0xd6, 0xa2, 0xd5, 0x06, 0xe5, 0x5c, 0xe7, 0xc7, 0x6b, 0x69, 0xff, 0xf1, 0x4a, - 0x5a, 0x2a, 0x1b, 0xbd, 0x5c, 0x7c, 0x36, 0x7a, 0xb5, 0x2f, 0x13, 0xfd, 0x57, 0x1c, 0x31, 0xd2, - 0x72, 0x55, 0x32, 0x16, 0xd6, 0x79, 0x80, 0x85, 0x7d, 0x13, 0x54, 0x12, 0xda, 0x6a, 0x30, 0xcf, - 0x4e, 0x5a, 0x62, 0x3d, 0x26, 0xd6, 0x25, 0x1c, 0x35, 0x05, 0xbf, 0xa7, 0xda, 0x6a, 0x45, 0x77, - 0x2f, 0xb4, 0x3b, 0x69, 0x4f, 0xda, 0x64, 0x73, 0x4f, 0x55, 0x63, 0xd0, 0xa2, 0x72, 0xff, 0xdb, - 0x11, 0xdd, 0x29, 0x7d, 0xf4, 0xe7, 0x73, 0xf7, 0x07, 0x0f, 0xee, 0xde, 0xfe, 0x3c, 0x80, 0xaf, - 0x6f, 0xf4, 0x17, 0xf3, 0xbc, 0xa0, 0x79, 0x21, 0xc0, 0x7e, 0xf3, 0x4e, 0xc1, 0xd0, 0x92, 0x97, - 0x99, 0x3c, 0xe5, 0xfd, 0x26, 0x8f, 0xfb, 0x1f, 0x0e, 0x64, 0x16, 0x0b, 0xd2, 0x81, 0x51, 0xa6, - 0x41, 0xaf, 0x98, 0xf7, 0x07, 0x6c, 0xd6, 0x6c, 0x62, 0xc9, 0x61, 0xc1, 0x7f, 0xa2, 0x10, 0x44, - 0x5a, 0xd2, 0x3b, 0x2f, 0x15, 0xf1, 0x46, 0x86, 0x2d, 0x90, 0xf9, 0xf7, 0xf2, 0x3f, 0x0e, 0xb4, - 0xa7, 0xef, 0x3e, 0x0f, 0x33, 0x7d, 0x4a, 0xf1, 0x1b, 0x45, 0x91, 0x7a, 0x74, 0xc1, 0x1a, 0x81, - 0xfc, 0x7e, 0x23, 0x0a, 0x1c, 0x73, 0xf0, 0x8f, 0xe7, 0xd9, 0x93, 0xcf, 0x3b, 0x30, 0x93, 0xe4, - 0xf9, 0x3d, 0xaa, 0xb6, 0xd3, 0x91, 0xab, 0x3e, 0x14, 0xf6, 0x2b, 0xe1, 0xfe, 0xa5, 0x34, 0x4f, - 0xe2, 0x3f, 0xa1, 0xf4, 0xe2, 0xe2, 0x0c, 0x5d, 0x5c, 0xd8, 0x14, 0xf3, 0xb7, 0x68, 0xbd, 0xdb, - 0xea, 0x4b, 0xa5, 0x59, 0x97, 0x70, 0xd4, 0x14, 0x99, 0x67, 0xc6, 0xca, 0xfb, 0x3e, 0x33, 0xf6, - 0x1c, 0x4c, 0xda, 0x0f, 0x8b, 0xf0, 0x10, 0x9a, 0x3c, 0x7c, 0xb0, 0xdf, 0x20, 0xc1, 0x0c, 0x55, - 0xee, 0x99, 0xaa, 0xd1, 0x7d, 0x9f, 0xa9, 0x7a, 0x06, 0x2a, 0xf2, 0x2f, 0x96, 0x54, 0x7c, 0x57, - 0xe4, 0xe9, 0x48, 0x18, 0x6a, 0x2c, 0x33, 0x10, 0x6d, 0x2f, 0xec, 0x7a, 0x2d, 0xd6, 0x42, 0x32, - 0x27, 0x4f, 0xcf, 0xac, 0x6b, 0x1a, 0x83, 0x16, 0x95, 0xfb, 0x2f, 0x0e, 0xe4, 0x1f, 0xfe, 0xc9, - 0x64, 0xf6, 0x39, 0xfb, 0x66, 0xf6, 0x65, 0x13, 0x9c, 0x4a, 0x07, 0x4a, 0x70, 0xb2, 0x73, 0x8f, - 0xca, 0x0f, 0xcc, 0x3d, 0x7a, 0x9d, 0xb9, 0x15, 0x2e, 0x92, 0x94, 0x26, 0x06, 0xdd, 0x08, 0x27, - 0x2e, 0x8c, 0xf9, 0x9e, 0x4e, 0x9c, 0x9e, 0x14, 0x8e, 0xd2, 0xd2, 0x22, 0x27, 0x92, 0x98, 0xda, - 0xfc, 0x57, 0xbe, 0x7d, 0xf6, 0x89, 0xaf, 0x7e, 0xfb, 0xec, 0x13, 0xdf, 0xf8, 0xf6, 0xd9, 0x27, - 0x3e, 0x7c, 0xef, 0xac, 0xf3, 0x95, 0x7b, 0x67, 0x9d, 0xaf, 0xde, 0x3b, 0xeb, 0x7c, 0xe3, 0xde, - 0x59, 0xe7, 0x5b, 0xf7, 0xce, 0x3a, 0x9f, 0xfe, 0xc7, 0xb3, 0x4f, 0xbc, 0xab, 0xa2, 0xc6, 0xea, - 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xf7, 0x6e, 0xec, 0x2c, 0x61, 0x74, 0x00, 0x00, + // 6715 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x24, 0xd9, + 0x55, 0xf0, 0x56, 0xb7, 0x7f, 0xba, 0x8f, 0x7f, 0x66, 0x7c, 0x67, 0x76, 0xd6, 0xf1, 0xb7, 0x19, + 0x8f, 0x6a, 0x95, 0x64, 0xbf, 0x2f, 0x89, 0xfd, 0xed, 0xb0, 0x84, 0x25, 0x1b, 0x36, 0xb8, 0x6d, + 0xcf, 0x8c, 0x67, 0x3c, 0xb6, 0xe7, 0xd8, 0x33, 0x43, 0x7e, 0x08, 0x5b, 0xae, 0xbe, 0xdd, 0x5d, + 0xe3, 0xee, 0xaa, 0xde, 0xaa, 0x6a, 0x8f, 0x3b, 0x21, 0x7f, 0x28, 0x90, 0x15, 0xf9, 0xd9, 0x28, + 0xc9, 0x43, 0x22, 0x21, 0x08, 0x3f, 0x42, 0xe2, 0x21, 0x02, 0x9e, 0x00, 0x21, 0x1e, 0xc8, 0x53, + 0x10, 0x12, 0x44, 0x02, 0x25, 0x81, 0x08, 0x93, 0x0c, 0x41, 0x01, 0x24, 0x40, 0x40, 0x5e, 0x98, + 0x27, 0x74, 0x7f, 0xea, 0xde, 0x5b, 0xd5, 0xdd, 0x63, 0x7b, 0xba, 0x66, 0x12, 0x45, 0xbc, 0x75, + 0x9d, 0x73, 0xee, 0x39, 0xe7, 0xfe, 0x9d, 0x7b, 0xee, 0xb9, 0xe7, 0xde, 0x86, 0xf5, 0xba, 0x17, + 0x37, 0x3a, 0xbb, 0x0b, 0x6e, 0xd0, 0x5a, 0x74, 0xc2, 0x7a, 0xd0, 0x0e, 0x83, 0x3b, 0xfc, 0xc7, + 0x5b, 0xdd, 0xea, 0xe2, 0xfe, 0xc5, 0xc5, 0xf6, 0x5e, 0x7d, 0xd1, 0x69, 0x7b, 0xd1, 0xa2, 0xd3, + 0x6e, 0x37, 0x3d, 0xd7, 0x89, 0xbd, 0xc0, 0x5f, 0xdc, 0x7f, 0xce, 0x69, 0xb6, 0x1b, 0xce, 0x73, + 0x8b, 0x75, 0xea, 0xd3, 0xd0, 0x89, 0x69, 0x75, 0xa1, 0x1d, 0x06, 0x71, 0x40, 0xde, 0xa1, 0xb9, + 0x2d, 0x24, 0xdc, 0xf8, 0x8f, 0x9f, 0x73, 0xab, 0x0b, 0xfb, 0x17, 0x17, 0xda, 0x7b, 0xf5, 0x05, + 0xc6, 0x6d, 0xc1, 0xe0, 0xb6, 0x90, 0x70, 0x9b, 0x7b, 0xab, 0xa1, 0x4b, 0x3d, 0xa8, 0x07, 0x8b, + 0x9c, 0xe9, 0x6e, 0xa7, 0xc6, 0xbf, 0xf8, 0x07, 0xff, 0x25, 0x84, 0xcd, 0xd9, 0x7b, 0x2f, 0x44, + 0x0b, 0x5e, 0xc0, 0xd4, 0x5b, 0x74, 0x83, 0x90, 0x2e, 0xee, 0xf7, 0x28, 0x34, 0xf7, 0xbc, 0xa6, + 0x69, 0x39, 0x6e, 0xc3, 0xf3, 0x69, 0xd8, 0xd5, 0x75, 0x6a, 0xd1, 0xd8, 0xe9, 0x57, 0x6a, 0x71, + 0x50, 0xa9, 0xb0, 0xe3, 0xc7, 0x5e, 0x8b, 0xf6, 0x14, 0x78, 0xdb, 0x51, 0x05, 0x22, 0xb7, 0x41, + 0x5b, 0x4e, 0xb6, 0x9c, 0xfd, 0x0a, 0x4c, 0x2d, 0xdd, 0xde, 0x5e, 0xea, 0xc4, 0x8d, 0xe5, 0xc0, + 0xaf, 0x79, 0x75, 0xf2, 0xe3, 0x30, 0xe1, 0x36, 0x3b, 0x51, 0x4c, 0xc3, 0x0d, 0xa7, 0x45, 0x67, + 0xad, 0x0b, 0xd6, 0xb3, 0xe5, 0xca, 0x99, 0xaf, 0x1e, 0xce, 0x3f, 0x71, 0xef, 0x70, 0x7e, 0x62, + 0x59, 0xa3, 0xd0, 0xa4, 0x23, 0xff, 0x17, 0xc6, 0xc3, 0xa0, 0x49, 0x97, 0x70, 0x63, 0xb6, 0xc0, + 0x8b, 0x9c, 0x92, 0x45, 0xc6, 0x51, 0x80, 0x31, 0xc1, 0xdb, 0x5f, 0x2f, 0x00, 0x2c, 0xb5, 0xdb, + 0x5b, 0x61, 0x70, 0x87, 0xba, 0x31, 0x79, 0x19, 0x4a, 0xac, 0x15, 0xaa, 0x4e, 0xec, 0x70, 0x69, + 0x13, 0x17, 0xff, 0xff, 0x82, 0xa8, 0xcc, 0x82, 0x59, 0x19, 0xdd, 0x73, 0x8c, 0x7a, 0x61, 0xff, + 0xb9, 0x85, 0xcd, 0x5d, 0x56, 0xfe, 0x3a, 0x8d, 0x9d, 0x0a, 0x91, 0xc2, 0x40, 0xc3, 0x50, 0x71, + 0x25, 0x3e, 0x8c, 0x44, 0x6d, 0xea, 0x72, 0xc5, 0x26, 0x2e, 0xae, 0x2f, 0x0c, 0x33, 0x44, 0x16, + 0xb4, 0xe6, 0xdb, 0x6d, 0xea, 0x56, 0x26, 0xa5, 0xe4, 0x11, 0xf6, 0x85, 0x5c, 0x0e, 0xd9, 0x87, + 0xb1, 0x28, 0x76, 0xe2, 0x4e, 0x34, 0x5b, 0xe4, 0x12, 0x37, 0x72, 0x93, 0xc8, 0xb9, 0x56, 0xa6, + 0xa5, 0xcc, 0x31, 0xf1, 0x8d, 0x52, 0x9a, 0xfd, 0x77, 0x16, 0x4c, 0x6b, 0xe2, 0x75, 0x2f, 0x8a, + 0xc9, 0x7b, 0x7b, 0x1a, 0x77, 0xe1, 0x78, 0x8d, 0xcb, 0x4a, 0xf3, 0xa6, 0x3d, 0x2d, 0x85, 0x95, + 0x12, 0x88, 0xd1, 0xb0, 0x2d, 0x18, 0xf5, 0x62, 0xda, 0x8a, 0x66, 0x0b, 0x17, 0x8a, 0xcf, 0x4e, + 0x5c, 0xbc, 0x92, 0x57, 0x3d, 0x2b, 0x53, 0x52, 0xe8, 0xe8, 0x1a, 0x63, 0x8f, 0x42, 0x8a, 0xfd, + 0x7d, 0x30, 0xeb, 0xc7, 0x1a, 0x9c, 0x3c, 0x07, 0x13, 0x51, 0xd0, 0x09, 0x5d, 0x8a, 0xb4, 0x1d, + 0x44, 0xb3, 0xd6, 0x85, 0x22, 0x1b, 0x7a, 0x6c, 0xa4, 0x6e, 0x6b, 0x30, 0x9a, 0x34, 0xe4, 0xd3, + 0x16, 0x4c, 0x56, 0x69, 0x14, 0x7b, 0x3e, 0x97, 0x9f, 0x28, 0xbf, 0x33, 0xb4, 0xf2, 0x09, 0x70, + 0x45, 0x33, 0xaf, 0x9c, 0x95, 0x15, 0x99, 0x34, 0x80, 0x11, 0xa6, 0xe4, 0xb3, 0x19, 0x57, 0xa5, + 0x91, 0x1b, 0x7a, 0x6d, 0xf6, 0xcd, 0xc7, 0x8c, 0x31, 0xe3, 0x56, 0x34, 0x0a, 0x4d, 0x3a, 0xe2, + 0xc3, 0x28, 0x9b, 0x51, 0xd1, 0xec, 0x08, 0xd7, 0x7f, 0x6d, 0x38, 0xfd, 0x65, 0xa3, 0xb2, 0xc9, + 0xaa, 0x5b, 0x9f, 0x7d, 0x45, 0x28, 0xc4, 0x90, 0x4f, 0x59, 0x30, 0x2b, 0x67, 0x3c, 0x52, 0xd1, + 0xa0, 0xb7, 0x1b, 0x5e, 0x4c, 0x9b, 0x5e, 0x14, 0xcf, 0x8e, 0x72, 0x1d, 0x16, 0x8f, 0x37, 0xb6, + 0x2e, 0x87, 0x41, 0xa7, 0x7d, 0xcd, 0xf3, 0xab, 0x95, 0x0b, 0x52, 0xd2, 0xec, 0xf2, 0x00, 0xc6, + 0x38, 0x50, 0x24, 0xf9, 0x9c, 0x05, 0x73, 0xbe, 0xd3, 0xa2, 0x51, 0xdb, 0x61, 0x5d, 0x2b, 0xd0, + 0x95, 0xa6, 0xe3, 0xee, 0x71, 0x8d, 0xc6, 0x1e, 0x4e, 0x23, 0x5b, 0x6a, 0x34, 0xb7, 0x31, 0x90, + 0x35, 0x3e, 0x40, 0x2c, 0xf9, 0x4d, 0x0b, 0x66, 0x82, 0xb0, 0xdd, 0x70, 0x7c, 0x5a, 0x4d, 0xb0, + 0xd1, 0xec, 0x38, 0x9f, 0x7a, 0xef, 0x1b, 0xae, 0x8b, 0x36, 0xb3, 0x6c, 0xaf, 0x07, 0xbe, 0x17, + 0x07, 0xe1, 0x36, 0x8d, 0x63, 0xcf, 0xaf, 0x47, 0x95, 0x27, 0xef, 0x1d, 0xce, 0xcf, 0xf4, 0x50, + 0x61, 0xaf, 0x3e, 0xe4, 0x03, 0x30, 0x11, 0x75, 0x7d, 0xf7, 0xb6, 0xe7, 0x57, 0x83, 0xbb, 0xd1, + 0x6c, 0x29, 0x8f, 0xe9, 0xbb, 0xad, 0x18, 0xca, 0x09, 0xa8, 0x05, 0xa0, 0x29, 0xad, 0x7f, 0xc7, + 0xe9, 0xa1, 0x54, 0xce, 0xbb, 0xe3, 0xf4, 0x60, 0x7a, 0x80, 0x58, 0xf2, 0x71, 0x0b, 0xa6, 0x22, + 0xaf, 0xee, 0x3b, 0x71, 0x27, 0xa4, 0xd7, 0x68, 0x37, 0x9a, 0x05, 0xae, 0xc8, 0xd5, 0x21, 0x5b, + 0xc5, 0x60, 0x59, 0x79, 0x52, 0xea, 0x38, 0x65, 0x42, 0x23, 0x4c, 0xcb, 0xed, 0x37, 0xd1, 0xf4, + 0xb0, 0x9e, 0xc8, 0x77, 0xa2, 0xe9, 0x41, 0x3d, 0x50, 0xa4, 0xfd, 0x67, 0x05, 0x38, 0x9d, 0x5d, + 0x83, 0xc8, 0x6f, 0x5b, 0x70, 0xea, 0xce, 0xdd, 0x78, 0x27, 0xd8, 0xa3, 0x7e, 0x54, 0xe9, 0x32, + 0x4b, 0xc1, 0xad, 0xef, 0xc4, 0x45, 0x37, 0xdf, 0xd5, 0x6e, 0xe1, 0x6a, 0x5a, 0xca, 0xaa, 0x1f, + 0x87, 0xdd, 0xca, 0x53, 0xb2, 0x3e, 0xa7, 0xae, 0xde, 0xde, 0x31, 0xb1, 0x98, 0x55, 0x6a, 0xee, + 0x13, 0x16, 0x9c, 0xed, 0xc7, 0x82, 0x9c, 0x86, 0xe2, 0x1e, 0xed, 0x0a, 0x07, 0x07, 0xd9, 0x4f, + 0xf2, 0xb3, 0x30, 0xba, 0xef, 0x34, 0x3b, 0x54, 0x3a, 0x0a, 0x97, 0x87, 0xab, 0x88, 0xd2, 0x0c, + 0x05, 0xd7, 0xb7, 0x17, 0x5e, 0xb0, 0xec, 0xbf, 0x2c, 0xc2, 0x84, 0xb1, 0x54, 0x3c, 0x06, 0xe7, + 0x27, 0x48, 0x39, 0x3f, 0xd7, 0x73, 0x5b, 0xe5, 0x06, 0x7a, 0x3f, 0x77, 0x33, 0xde, 0xcf, 0x66, + 0x7e, 0x22, 0x1f, 0xe8, 0xfe, 0x90, 0x18, 0xca, 0x41, 0x9b, 0x39, 0xb7, 0x6c, 0x15, 0x1d, 0xc9, + 0xa3, 0x0b, 0x37, 0x13, 0x76, 0x95, 0xa9, 0x7b, 0x87, 0xf3, 0x65, 0xf5, 0x89, 0x5a, 0x90, 0xfd, + 0x0d, 0x0b, 0xce, 0x1a, 0x3a, 0x2e, 0x07, 0x7e, 0xd5, 0xe3, 0x5d, 0x7b, 0x01, 0x46, 0xe2, 0x6e, + 0x3b, 0xf1, 0xa0, 0x55, 0x4b, 0xed, 0x74, 0xdb, 0x14, 0x39, 0x86, 0xf9, 0xcc, 0x2d, 0x1a, 0x45, + 0x4e, 0x9d, 0x66, 0x7d, 0xe6, 0xeb, 0x02, 0x8c, 0x09, 0x9e, 0x84, 0x40, 0x9a, 0x4e, 0x14, 0xef, + 0x84, 0x8e, 0x1f, 0x71, 0xf6, 0x3b, 0x5e, 0x8b, 0xca, 0x06, 0xfe, 0x7f, 0xc7, 0x1b, 0x31, 0xac, + 0x44, 0xe5, 0xdc, 0xbd, 0xc3, 0x79, 0xb2, 0xde, 0xc3, 0x09, 0xfb, 0x70, 0xb7, 0x3f, 0x67, 0xc1, + 0xb9, 0xfe, 0x6e, 0x0d, 0x79, 0x23, 0x8c, 0x45, 0x34, 0xdc, 0xa7, 0xa1, 0xac, 0x9d, 0xee, 0x12, + 0x0e, 0x45, 0x89, 0x25, 0x8b, 0x50, 0x56, 0x26, 0x57, 0xd6, 0x71, 0x46, 0x92, 0x96, 0xb5, 0x9d, + 0xd6, 0x34, 0xac, 0xd1, 0xd8, 0x87, 0x74, 0x82, 0x54, 0xa3, 0xf1, 0xfd, 0x06, 0xc7, 0xd8, 0x7f, + 0x6f, 0xc1, 0x29, 0x43, 0xab, 0xc7, 0xe0, 0xe5, 0xfa, 0x69, 0x2f, 0x77, 0x2d, 0xb7, 0xf1, 0x3c, + 0xc0, 0xcd, 0xfd, 0xca, 0x18, 0xcc, 0x98, 0xa3, 0x9e, 0x9b, 0x63, 0xbe, 0xc1, 0xa2, 0xed, 0xe0, + 0x26, 0xae, 0xcb, 0x36, 0xd7, 0x1b, 0x2c, 0x01, 0xc6, 0x04, 0xcf, 0x1a, 0xb1, 0xed, 0xc4, 0x0d, + 0xd9, 0xe0, 0xaa, 0x11, 0xb7, 0x9c, 0xb8, 0x81, 0x1c, 0x43, 0x5e, 0x82, 0xe9, 0xd8, 0x09, 0xeb, + 0x34, 0x46, 0xba, 0xef, 0x45, 0xc9, 0x7c, 0x29, 0x57, 0xce, 0x49, 0xda, 0xe9, 0x9d, 0x14, 0x16, + 0x33, 0xd4, 0xe4, 0x15, 0x18, 0x69, 0xd0, 0x66, 0x4b, 0xfa, 0x35, 0xdb, 0xf9, 0xcd, 0x70, 0x5e, + 0xd7, 0x2b, 0xb4, 0xd9, 0xaa, 0x94, 0x98, 0xca, 0xec, 0x17, 0x72, 0x51, 0xe4, 0x17, 0x2d, 0x28, + 0xef, 0x75, 0xa2, 0x38, 0x68, 0x79, 0xef, 0xa7, 0xb3, 0x25, 0x2e, 0xf8, 0x67, 0x72, 0x16, 0x7c, + 0x2d, 0xe1, 0x2f, 0xe6, 0xbb, 0xfa, 0x44, 0x2d, 0x99, 0x7c, 0x10, 0xc6, 0xf7, 0xa2, 0xc0, 0xf7, + 0x29, 0xf3, 0x54, 0x98, 0x12, 0xb7, 0xf2, 0x56, 0x42, 0x70, 0xaf, 0x4c, 0xb0, 0xbe, 0x95, 0x1f, + 0x98, 0xc8, 0xe4, 0xcd, 0x50, 0xf5, 0x42, 0xea, 0xc6, 0x41, 0xd8, 0x9d, 0x85, 0x47, 0xd2, 0x0c, + 0x2b, 0x09, 0x7f, 0xd1, 0x0c, 0xea, 0x13, 0xb5, 0x64, 0xd2, 0x85, 0xb1, 0x76, 0xb3, 0x53, 0xf7, + 0xfc, 0xd9, 0x09, 0xae, 0xc3, 0xcd, 0x9c, 0x75, 0xd8, 0xe2, 0xcc, 0x2b, 0xc0, 0x8c, 0x8a, 0xf8, + 0x8d, 0x52, 0x20, 0x79, 0x06, 0x46, 0xdd, 0x86, 0x13, 0xc6, 0xb3, 0x93, 0x7c, 0xcc, 0xaa, 0x49, + 0xb4, 0xcc, 0x80, 0x28, 0x70, 0xf6, 0xaf, 0x17, 0x60, 0x6e, 0x70, 0xc5, 0xc4, 0x6c, 0x72, 0x3b, + 0x61, 0x24, 0xec, 0x73, 0xc9, 0x9c, 0x4d, 0x1c, 0x8c, 0x09, 0x9e, 0x7c, 0xd4, 0x82, 0xf1, 0x3b, + 0xb2, 0xc7, 0x0b, 0x8f, 0xa4, 0xc7, 0xaf, 0xca, 0x1e, 0x57, 0x3a, 0x5c, 0x4d, 0x7a, 0x5d, 0xca, + 0x65, 0xea, 0xd2, 0x03, 0xb7, 0xd9, 0xa9, 0x26, 0x96, 0x51, 0x91, 0xae, 0x0a, 0x30, 0x26, 0x78, + 0x46, 0xea, 0xf9, 0x82, 0x74, 0x24, 0x4d, 0xba, 0xe6, 0x4b, 0x52, 0x89, 0xb7, 0xbf, 0x5b, 0x84, + 0x27, 0xfb, 0x4e, 0x3e, 0xb2, 0x00, 0xc0, 0x7d, 0x96, 0x4b, 0x1e, 0xdb, 0x60, 0x8a, 0x5d, 0xf5, + 0x34, 0x73, 0x31, 0x6e, 0x29, 0x28, 0x1a, 0x14, 0xe4, 0xc3, 0x00, 0x6d, 0x27, 0x74, 0x5a, 0x34, + 0xa6, 0x61, 0x62, 0x27, 0xaf, 0x0d, 0xd7, 0x4a, 0x4c, 0x8f, 0xad, 0x84, 0xa7, 0xf6, 0x71, 0x14, + 0x28, 0x42, 0x43, 0x24, 0xdb, 0x43, 0x87, 0xb4, 0x49, 0x9d, 0x88, 0x6e, 0xe8, 0xe5, 0x43, 0xed, + 0xa1, 0x51, 0xa3, 0xd0, 0xa4, 0x63, 0xeb, 0x18, 0xaf, 0x45, 0x24, 0xdb, 0x4a, 0xad, 0x63, 0xbc, + 0x9e, 0x11, 0x4a, 0x2c, 0x79, 0xcd, 0x82, 0xe9, 0x9a, 0xd7, 0xa4, 0x5a, 0xba, 0xdc, 0xf1, 0x6e, + 0x0e, 0x5f, 0xc9, 0x4b, 0x26, 0x5f, 0x6d, 0x81, 0x53, 0xe0, 0x08, 0x33, 0xe2, 0x59, 0x37, 0xef, + 0xd3, 0x90, 0x9b, 0xee, 0xb1, 0x74, 0x37, 0xdf, 0x12, 0x60, 0x4c, 0xf0, 0xf6, 0x17, 0x0b, 0x30, + 0x3b, 0x68, 0xcc, 0x91, 0x88, 0x8d, 0xac, 0xf8, 0x96, 0x13, 0x46, 0xd2, 0x7d, 0x1f, 0x72, 0x17, + 0x28, 0xf9, 0xde, 0x72, 0x42, 0x73, 0x8c, 0x72, 0x01, 0x98, 0x48, 0x22, 0x77, 0x60, 0x24, 0x6e, + 0x3a, 0x39, 0x85, 0x8d, 0x0c, 0x89, 0xda, 0xc9, 0x5a, 0x5f, 0x8a, 0x90, 0xcb, 0x20, 0x4f, 0xc3, + 0x48, 0xd3, 0xdb, 0x65, 0xce, 0x28, 0x1b, 0xc4, 0x7c, 0x55, 0x59, 0xf7, 0x76, 0x23, 0xe4, 0x50, + 0xfb, 0xeb, 0x56, 0x9f, 0xb6, 0x91, 0x46, 0x97, 0x0d, 0x2a, 0xea, 0xef, 0x7b, 0x61, 0xe0, 0xb7, + 0xa8, 0x1f, 0x67, 0x43, 0xa1, 0xab, 0x1a, 0x85, 0x26, 0x1d, 0xf9, 0x05, 0xab, 0xcf, 0x6c, 0x18, + 0x32, 0x06, 0x28, 0x55, 0x3a, 0xf6, 0x84, 0xb0, 0xff, 0x7d, 0xac, 0x8f, 0xfd, 0x53, 0x0b, 0x1a, + 0xb9, 0x08, 0xc0, 0xbc, 0xa9, 0xad, 0x90, 0xd6, 0xbc, 0x03, 0x59, 0x33, 0xc5, 0x72, 0x43, 0x61, + 0xd0, 0xa0, 0x4a, 0xca, 0x6c, 0x77, 0x6a, 0xac, 0x4c, 0xa1, 0xb7, 0x8c, 0xc0, 0xa0, 0x41, 0x45, + 0x9e, 0x87, 0x31, 0xaf, 0xe5, 0xd4, 0x69, 0xd2, 0xfe, 0x4f, 0xb3, 0xc9, 0xb5, 0xc6, 0x21, 0xf7, + 0x0f, 0xe7, 0xa7, 0x95, 0x42, 0x1c, 0x84, 0x92, 0x96, 0xfc, 0x96, 0x05, 0x93, 0x6e, 0xd0, 0x6a, + 0x05, 0xfe, 0xba, 0xb3, 0x4b, 0x9b, 0x49, 0x88, 0xeb, 0xce, 0xa3, 0x5a, 0xee, 0x17, 0x96, 0x0d, + 0x61, 0x62, 0x83, 0xa9, 0x02, 0x77, 0x26, 0x0a, 0x53, 0x5a, 0x99, 0x73, 0x70, 0xf4, 0xc1, 0x73, + 0x90, 0xfc, 0xa1, 0x05, 0x33, 0xa2, 0xec, 0x92, 0xef, 0x07, 0xb1, 0x8c, 0x3c, 0x8a, 0x18, 0x55, + 0xf0, 0x88, 0xab, 0x65, 0x48, 0x14, 0x75, 0x7b, 0x9d, 0x54, 0x73, 0xa6, 0x07, 0x8f, 0xbd, 0x4a, + 0x92, 0xcb, 0x30, 0x53, 0x0b, 0x42, 0x97, 0x9a, 0x0d, 0xc1, 0x1d, 0xbf, 0x92, 0x66, 0x74, 0x29, + 0x4b, 0x80, 0xbd, 0x65, 0xc8, 0x2d, 0x38, 0x67, 0x00, 0xcd, 0x76, 0x28, 0x71, 0x6e, 0xe7, 0x25, + 0xb7, 0x73, 0x97, 0xfa, 0x52, 0xe1, 0x80, 0xd2, 0x73, 0xef, 0x84, 0x99, 0x9e, 0xfe, 0xeb, 0xb3, + 0xbb, 0x3f, 0x6b, 0xee, 0xee, 0xcb, 0xc6, 0xa6, 0x7c, 0x6e, 0x05, 0xce, 0xf5, 0x6f, 0xa9, 0x93, + 0x70, 0xb1, 0x7f, 0xd5, 0x82, 0xa7, 0x06, 0xb8, 0x31, 0x6a, 0x5b, 0x63, 0x0d, 0xda, 0xd6, 0x10, + 0x07, 0x8a, 0xd4, 0xdf, 0x97, 0xc6, 0xe2, 0xd2, 0x70, 0x23, 0x62, 0xd5, 0xdf, 0x17, 0x1d, 0x3d, + 0x7e, 0xef, 0x70, 0xbe, 0xb8, 0xea, 0xef, 0x23, 0xe3, 0x6d, 0x7f, 0x7e, 0x2c, 0xb5, 0x73, 0xda, + 0x4e, 0x36, 0xeb, 0x5c, 0x51, 0xb9, 0x6f, 0xda, 0xcc, 0x79, 0x2c, 0x1a, 0x3b, 0x43, 0x11, 0x82, + 0x97, 0xe2, 0xc8, 0x27, 0x2c, 0x1e, 0xf5, 0x4e, 0x76, 0x94, 0xd2, 0xb3, 0x7a, 0x34, 0x41, 0x78, + 0x33, 0x96, 0x9e, 0x00, 0xd1, 0x94, 0xce, 0x66, 0x72, 0x5b, 0x04, 0x9d, 0xb2, 0xfe, 0x55, 0x12, + 0x17, 0x4f, 0xf0, 0xe4, 0x00, 0x20, 0xea, 0xfa, 0xee, 0x56, 0xd0, 0xf4, 0xdc, 0xae, 0x0c, 0x33, + 0xe4, 0x10, 0x39, 0x15, 0xfc, 0x84, 0x93, 0xa5, 0xbf, 0xd1, 0x90, 0x45, 0xbe, 0x64, 0xc1, 0x8c, + 0x57, 0xf7, 0x83, 0x90, 0xae, 0x78, 0xb5, 0x1a, 0x0d, 0xa9, 0xef, 0xd2, 0xc4, 0x0f, 0xb9, 0x3d, + 0x9c, 0x06, 0x49, 0xd0, 0x6f, 0x2d, 0xcb, 0x5e, 0x4f, 0xf1, 0x1e, 0x14, 0xf6, 0x2a, 0x43, 0xaa, + 0x30, 0xe2, 0xf9, 0xb5, 0x40, 0x1a, 0xb6, 0xca, 0x70, 0x4a, 0xad, 0xf9, 0xb5, 0x40, 0xcf, 0x15, + 0xf6, 0x85, 0x9c, 0x3b, 0x59, 0x87, 0xb3, 0xa1, 0xdc, 0x89, 0x5e, 0xf1, 0x22, 0xe6, 0xcf, 0xaf, + 0x7b, 0x2d, 0x2f, 0xe6, 0x46, 0xa9, 0x58, 0x99, 0xbd, 0x77, 0x38, 0x7f, 0x16, 0xfb, 0xe0, 0xb1, + 0x6f, 0x29, 0xfb, 0xd5, 0x72, 0x7a, 0xbb, 0x2d, 0x82, 0x49, 0x1f, 0x84, 0x72, 0xa8, 0xc2, 0xf7, + 0xc2, 0x33, 0x5a, 0xcf, 0xa7, 0x8d, 0x65, 0x14, 0x4b, 0xc5, 0x41, 0x74, 0xa0, 0x5e, 0x4b, 0x64, + 0x1e, 0x12, 0xeb, 0x79, 0x39, 0x2d, 0x72, 0x18, 0x5f, 0x52, 0xaa, 0x0e, 0xd8, 0x75, 0x7d, 0x17, + 0xb9, 0x0c, 0x12, 0xc2, 0x58, 0x83, 0x3a, 0xcd, 0xb8, 0x21, 0xe3, 0x49, 0x57, 0x87, 0xf5, 0x69, + 0x19, 0xaf, 0x6c, 0xac, 0x4e, 0x40, 0x51, 0x4a, 0x22, 0x07, 0x30, 0xde, 0x10, 0x9d, 0x20, 0xd7, + 0xf6, 0xeb, 0xc3, 0x36, 0x6e, 0xaa, 0x67, 0xf5, 0xfc, 0x95, 0x00, 0x4c, 0xc4, 0x91, 0x5f, 0xb2, + 0x00, 0xdc, 0x24, 0x48, 0x97, 0x4c, 0x1f, 0xcc, 0xcd, 0xee, 0xa8, 0xf8, 0x9f, 0x76, 0x8d, 0x14, + 0x28, 0x42, 0x43, 0x32, 0x79, 0x19, 0x26, 0x43, 0xea, 0x06, 0xbe, 0xeb, 0x35, 0x69, 0x75, 0x29, + 0xe6, 0x6e, 0xfc, 0xc9, 0x82, 0x79, 0xa7, 0x99, 0x7f, 0x82, 0x06, 0x0f, 0x4c, 0x71, 0x24, 0xaf, + 0x5a, 0x30, 0xad, 0x02, 0x95, 0xac, 0x43, 0xa8, 0x0c, 0xd8, 0xac, 0xe7, 0x14, 0x16, 0xe5, 0x3c, + 0x2b, 0x84, 0x6d, 0x57, 0xd2, 0x30, 0xcc, 0xc8, 0x25, 0xef, 0x06, 0x08, 0x76, 0x79, 0x50, 0x90, + 0x55, 0xb5, 0x74, 0xe2, 0xaa, 0x4e, 0x8b, 0xf8, 0x76, 0xc2, 0x01, 0x0d, 0x6e, 0xe4, 0x1a, 0x80, + 0x98, 0x36, 0x3b, 0xdd, 0x36, 0xe5, 0x41, 0x99, 0x72, 0xe5, 0xcd, 0x49, 0xe3, 0x6f, 0x2b, 0xcc, + 0xfd, 0xc3, 0xf9, 0xde, 0xdd, 0x2e, 0x8f, 0xc6, 0x1a, 0xc5, 0xc9, 0x07, 0x60, 0x3c, 0xea, 0xb4, + 0x5a, 0x8e, 0x0a, 0xae, 0x6c, 0xe5, 0xb7, 0x22, 0x0a, 0xbe, 0x7a, 0x6c, 0x4a, 0x00, 0x26, 0x12, + 0x6d, 0x1f, 0x48, 0x2f, 0x3d, 0x79, 0x1e, 0x26, 0xe9, 0x41, 0x4c, 0x43, 0xdf, 0x69, 0xde, 0xc4, + 0xf5, 0x64, 0x3b, 0xce, 0x3b, 0x7f, 0xd5, 0x80, 0x63, 0x8a, 0x8a, 0xd8, 0xca, 0xf3, 0x2e, 0x70, + 0x7a, 0xd0, 0x9e, 0x77, 0xe2, 0x67, 0xdb, 0xff, 0x5d, 0x48, 0x79, 0x04, 0x3b, 0x21, 0xa5, 0x24, + 0x80, 0x51, 0x3f, 0xa8, 0x2a, 0xa3, 0x77, 0x35, 0x1f, 0xa3, 0xb7, 0x11, 0x54, 0x8d, 0x73, 0x65, + 0xf6, 0x15, 0xa1, 0x90, 0xc3, 0x0f, 0xde, 0x92, 0x13, 0x4a, 0x8e, 0x90, 0x4e, 0x50, 0x9e, 0x92, + 0xd5, 0xc1, 0xdb, 0xa6, 0x29, 0x08, 0xd3, 0x72, 0xc9, 0x1e, 0x8c, 0x36, 0x82, 0x28, 0x16, 0x7b, + 0x95, 0xa1, 0xbd, 0xb0, 0x2b, 0x41, 0x14, 0xf3, 0x25, 0x4c, 0x55, 0x9b, 0x41, 0x22, 0x14, 0x32, + 0xec, 0xef, 0x59, 0xa9, 0xe0, 0xcb, 0x6d, 0x27, 0x76, 0x1b, 0xab, 0xfb, 0x6c, 0xff, 0x78, 0x2d, + 0x75, 0x70, 0xf0, 0x13, 0xe6, 0xc1, 0xc1, 0xfd, 0xc3, 0xf9, 0x37, 0x0d, 0x4a, 0xf4, 0xb9, 0xcb, + 0x38, 0x2c, 0x70, 0x16, 0xc6, 0x19, 0xc3, 0x47, 0x2c, 0x98, 0x30, 0xd4, 0x93, 0x0b, 0x4a, 0x8e, + 0x31, 0x6c, 0xe5, 0x5c, 0x19, 0x40, 0x34, 0x45, 0xda, 0x9f, 0xb5, 0x60, 0xbc, 0xe2, 0xb8, 0x7b, + 0x41, 0xad, 0x46, 0xde, 0x02, 0xa5, 0x6a, 0x47, 0x1e, 0xd1, 0x88, 0xfa, 0xa9, 0xc8, 0xfb, 0x8a, + 0x84, 0xa3, 0xa2, 0x60, 0x63, 0xb8, 0xe6, 0xb8, 0x71, 0x10, 0x72, 0xb5, 0x8b, 0x62, 0x0c, 0x5f, + 0xe2, 0x10, 0x94, 0x18, 0xb6, 0x49, 0x6f, 0x39, 0x07, 0x49, 0xe1, 0x6c, 0xe4, 0xe7, 0xba, 0x46, + 0xa1, 0x49, 0x67, 0xff, 0x69, 0x19, 0xc6, 0xe5, 0x59, 0xe8, 0xb1, 0x4f, 0x33, 0x12, 0x2f, 0xbe, + 0x30, 0xd0, 0x8b, 0x8f, 0x60, 0xcc, 0xe5, 0x69, 0x54, 0x72, 0x29, 0x1d, 0x32, 0x06, 0x26, 0x15, + 0x14, 0x99, 0x59, 0x5a, 0x2d, 0xf1, 0x8d, 0x52, 0x14, 0xf9, 0x8c, 0x05, 0xa7, 0xdc, 0xc0, 0xf7, + 0xa9, 0xab, 0xed, 0xfc, 0x48, 0x1e, 0xa7, 0x7d, 0xcb, 0x69, 0xa6, 0xfa, 0xd0, 0x35, 0x83, 0xc0, + 0xac, 0x78, 0xf2, 0x22, 0x4c, 0x89, 0x36, 0xbb, 0x95, 0xda, 0x1f, 0xeb, 0xf3, 0x6f, 0x13, 0x89, + 0x69, 0x5a, 0xb2, 0x20, 0xe2, 0x0c, 0xfc, 0x40, 0x48, 0xec, 0x91, 0x65, 0xf0, 0x51, 0x9d, 0x18, + 0x45, 0x68, 0x50, 0x90, 0x10, 0x48, 0x48, 0x6b, 0x21, 0x8d, 0x1a, 0x48, 0x5f, 0xe9, 0xd0, 0x28, + 0xe6, 0x6b, 0xcc, 0xf8, 0xc3, 0x9d, 0x8d, 0x61, 0x0f, 0x27, 0xec, 0xc3, 0x9d, 0xec, 0x49, 0x47, + 0xb7, 0x94, 0xc7, 0x74, 0x92, 0xdd, 0x3c, 0xd0, 0xdf, 0x9d, 0x87, 0xd1, 0xa8, 0xe1, 0x84, 0x55, + 0xbe, 0xb6, 0x15, 0x2b, 0x65, 0x66, 0x4b, 0xb6, 0x19, 0x00, 0x05, 0x9c, 0xac, 0xc0, 0xe9, 0xcc, + 0xe9, 0x7d, 0xc4, 0x57, 0xaf, 0x52, 0x65, 0x56, 0xb2, 0x3b, 0x9d, 0x39, 0xf7, 0x8f, 0xb0, 0xa7, + 0x84, 0xb9, 0x09, 0x9a, 0x38, 0x62, 0x13, 0xd4, 0x85, 0xb1, 0xa6, 0x08, 0x04, 0x4c, 0x72, 0x53, + 0x79, 0x23, 0x97, 0x06, 0x58, 0x30, 0x03, 0x30, 0x6a, 0xb4, 0xcb, 0x80, 0x82, 0x14, 0x48, 0x3e, + 0xc5, 0x0c, 0x9a, 0x11, 0x3b, 0x98, 0xe2, 0x0a, 0xdc, 0xca, 0x47, 0x81, 0x9e, 0x50, 0x89, 0xb6, + 0x6e, 0x46, 0x20, 0xc2, 0x94, 0x3f, 0xf7, 0x93, 0x30, 0xf1, 0xb0, 0x71, 0x87, 0x97, 0xe0, 0xf4, + 0x50, 0x11, 0x87, 0xef, 0x5b, 0x90, 0xf4, 0xeb, 0xb2, 0xe3, 0x36, 0x28, 0x1b, 0x32, 0xe4, 0x25, + 0x98, 0x56, 0xdb, 0x88, 0xe5, 0xa0, 0x23, 0xe3, 0x96, 0x45, 0x1d, 0x58, 0xc6, 0x14, 0x16, 0x33, + 0xd4, 0x64, 0x11, 0xca, 0xac, 0x9d, 0x44, 0x51, 0x61, 0x76, 0xd5, 0x56, 0x65, 0x69, 0x6b, 0x4d, + 0x96, 0xd2, 0x34, 0x24, 0x80, 0x99, 0xa6, 0x13, 0xc5, 0x5c, 0x03, 0xb6, 0xab, 0x78, 0xc8, 0x93, + 0x69, 0x9e, 0xbc, 0xb4, 0x9e, 0x65, 0x84, 0xbd, 0xbc, 0xed, 0x6f, 0x8c, 0xc0, 0x54, 0xca, 0x32, + 0xb2, 0x55, 0xa5, 0x13, 0x31, 0xd7, 0x47, 0x85, 0x58, 0xd4, 0xaa, 0x72, 0x53, 0xc2, 0x51, 0x51, + 0x30, 0xea, 0xb6, 0x13, 0x45, 0x77, 0x83, 0xb0, 0x2a, 0x4d, 0xb9, 0xa2, 0xde, 0x92, 0x70, 0x54, + 0x14, 0x6c, 0x7d, 0xd9, 0xa5, 0x4e, 0x48, 0x43, 0x9e, 0xcc, 0x91, 0x5d, 0x5f, 0x2a, 0x1a, 0x85, + 0x26, 0x1d, 0x37, 0xca, 0x71, 0x33, 0x5a, 0x6e, 0x7a, 0xd4, 0x8f, 0x85, 0x9a, 0xf9, 0x18, 0xe5, + 0x9d, 0xf5, 0x6d, 0x93, 0xa9, 0x36, 0xca, 0x19, 0x04, 0x66, 0xc5, 0x93, 0x8f, 0x59, 0x30, 0xe5, + 0xdc, 0x8d, 0x74, 0xae, 0x2f, 0xb7, 0xca, 0x43, 0x2f, 0x52, 0xa9, 0xf4, 0xe1, 0xca, 0x0c, 0x33, + 0xef, 0x29, 0x10, 0xa6, 0x85, 0x92, 0x2f, 0x58, 0x40, 0xe8, 0x01, 0x75, 0xb7, 0xc2, 0x60, 0xdf, + 0xab, 0x26, 0x7d, 0x28, 0xb7, 0x3f, 0x43, 0x7a, 0xdb, 0xab, 0x3d, 0x7c, 0x85, 0x55, 0xef, 0x85, + 0x63, 0x1f, 0x1d, 0xec, 0xbf, 0x2d, 0xc2, 0x84, 0x61, 0x8c, 0xfb, 0xae, 0xac, 0xd6, 0x0f, 0xd9, + 0xca, 0x5a, 0x38, 0xc1, 0xca, 0xfa, 0x61, 0x28, 0xbb, 0x89, 0xa1, 0xc8, 0x27, 0x37, 0x39, 0x6b, + 0x7e, 0xb4, 0xad, 0x50, 0x20, 0xd4, 0x32, 0xc9, 0x65, 0x98, 0x31, 0xd8, 0x48, 0x23, 0x33, 0xc2, + 0x8d, 0x8c, 0x0a, 0x34, 0x2d, 0x65, 0x09, 0xb0, 0xb7, 0x0c, 0x79, 0x8e, 0x79, 0xb5, 0x9e, 0xac, + 0x97, 0xd8, 0xc5, 0xcb, 0xbc, 0xdf, 0xa5, 0xad, 0xb5, 0x04, 0x8c, 0x26, 0x8d, 0xfd, 0x0d, 0x4b, + 0x75, 0xee, 0x63, 0x48, 0x1a, 0xb9, 0x93, 0x4e, 0x1a, 0x59, 0xcd, 0xa5, 0x99, 0x07, 0x24, 0x8c, + 0x6c, 0xc0, 0xf8, 0x72, 0xd0, 0x6a, 0x39, 0x7e, 0x95, 0xbc, 0x01, 0xc6, 0x5d, 0xf1, 0x53, 0x6e, + 0x13, 0x79, 0x16, 0x81, 0xc4, 0x62, 0x82, 0x23, 0x4f, 0xc3, 0x88, 0x13, 0xd6, 0x93, 0xad, 0x21, + 0x3f, 0x14, 0x5b, 0x0a, 0xeb, 0x11, 0x72, 0xa8, 0xfd, 0xb9, 0x02, 0xc0, 0x72, 0xd0, 0x6a, 0x3b, + 0x21, 0xad, 0xee, 0x04, 0xff, 0x1b, 0x23, 0x16, 0x3b, 0x86, 0x4f, 0x5a, 0x40, 0x58, 0xab, 0x04, + 0x3e, 0xf5, 0xf5, 0x41, 0x1c, 0x5b, 0x2f, 0xdd, 0x04, 0x2a, 0x17, 0x1f, 0x3d, 0x07, 0x12, 0x04, + 0x6a, 0x9a, 0x63, 0xec, 0x22, 0x9e, 0x49, 0x56, 0xfc, 0x62, 0x3a, 0xc1, 0x81, 0x1f, 0x4a, 0x4b, + 0x07, 0xc0, 0xfe, 0x7c, 0x01, 0xce, 0x09, 0xb3, 0x75, 0xdd, 0xf1, 0x9d, 0x3a, 0x6d, 0x31, 0xad, + 0x8e, 0x7b, 0xda, 0xe0, 0x32, 0xf7, 0xd5, 0x4b, 0xf2, 0x19, 0x86, 0x1d, 0x9c, 0x62, 0x50, 0x89, + 0x61, 0xb4, 0xe6, 0x7b, 0x31, 0x72, 0xe6, 0x24, 0x82, 0x52, 0x72, 0xdb, 0x44, 0x1a, 0x9b, 0x9c, + 0x04, 0xa9, 0x79, 0x77, 0x59, 0xb2, 0x47, 0x25, 0xc8, 0xfe, 0x8a, 0x05, 0x59, 0x23, 0xca, 0xf7, + 0x77, 0x22, 0x23, 0x31, 0xbb, 0xbf, 0x4b, 0x27, 0x10, 0x9e, 0x20, 0x1f, 0xef, 0xbd, 0x30, 0xe1, + 0xc4, 0x31, 0x6d, 0xb5, 0xc5, 0x66, 0xa3, 0xf8, 0x70, 0x01, 0xad, 0xeb, 0x41, 0xd5, 0xab, 0x79, + 0x7c, 0x93, 0x61, 0xb2, 0xb3, 0x6f, 0x40, 0x29, 0x39, 0xc3, 0x39, 0x46, 0x67, 0x3e, 0x93, 0x72, + 0x10, 0x07, 0x0c, 0x97, 0xfb, 0x05, 0xe8, 0xb3, 0x0a, 0xb2, 0x2a, 0x6b, 0x7b, 0x91, 0xaa, 0xf2, + 0xc9, 0x6c, 0x06, 0x39, 0x10, 0xe7, 0x57, 0x22, 0x72, 0xf2, 0xae, 0xbc, 0x57, 0x71, 0x7d, 0xa4, + 0x35, 0x21, 0xf5, 0x53, 0xc7, 0x5a, 0xe4, 0x22, 0x80, 0x36, 0xf3, 0x32, 0x8f, 0x43, 0xc5, 0x5e, + 0xf5, 0x6a, 0x80, 0x06, 0x15, 0x73, 0xea, 0x3c, 0x3f, 0x8a, 0x9d, 0x66, 0xf3, 0x8a, 0xe7, 0xc7, + 0x72, 0x77, 0xaa, 0x4c, 0xc0, 0x9a, 0x46, 0xa1, 0x49, 0x37, 0xf7, 0x36, 0xa3, 0x5f, 0x4e, 0xe2, + 0xa8, 0x7f, 0xb2, 0x00, 0xd3, 0x97, 0xfd, 0xce, 0xd6, 0xe5, 0xad, 0xce, 0x6e, 0xd3, 0x73, 0xaf, + 0xd1, 0x2e, 0xeb, 0xb4, 0x3d, 0xda, 0x5d, 0x5b, 0x91, 0xcd, 0xae, 0x3a, 0xed, 0x1a, 0x03, 0xa2, + 0xc0, 0x31, 0x35, 0x6b, 0x9e, 0x5f, 0xa7, 0x61, 0x3b, 0xf4, 0xa4, 0x37, 0x6e, 0xa8, 0x79, 0x49, + 0xa3, 0xd0, 0xa4, 0x63, 0xbc, 0x83, 0xbb, 0x3e, 0x0d, 0xb3, 0xf6, 0x63, 0x93, 0x01, 0x51, 0xe0, + 0x18, 0x51, 0x1c, 0x76, 0xa2, 0x58, 0xb6, 0x98, 0x22, 0xda, 0x61, 0x40, 0x14, 0x38, 0x36, 0x3c, + 0xa2, 0xce, 0x2e, 0x8f, 0xab, 0x66, 0x4e, 0xb8, 0xb7, 0x05, 0x18, 0x13, 0x3c, 0x23, 0xdd, 0xa3, + 0xdd, 0x15, 0xb6, 0x9a, 0x66, 0x12, 0x52, 0xae, 0x09, 0x30, 0x26, 0x78, 0xfb, 0x1f, 0x2d, 0x20, + 0xe9, 0xe6, 0x78, 0x0c, 0x0b, 0xf2, 0x2b, 0xe9, 0x05, 0x79, 0xc8, 0x10, 0x78, 0x5a, 0xfd, 0x01, + 0xeb, 0xf2, 0x6f, 0x58, 0x30, 0x69, 0x9e, 0x86, 0x90, 0x7a, 0xc6, 0x10, 0x6d, 0xa6, 0x0d, 0xd1, + 0xfd, 0xc3, 0xf9, 0x9f, 0xea, 0x77, 0x19, 0xb2, 0xee, 0xc5, 0x41, 0x3b, 0x7a, 0x2b, 0xf5, 0xeb, + 0x9e, 0x4f, 0x79, 0xac, 0x4f, 0x9c, 0xa2, 0xa4, 0x8e, 0x5a, 0x96, 0x83, 0x2a, 0x7d, 0x08, 0x4b, + 0x66, 0xdf, 0x86, 0x99, 0x9e, 0x2c, 0xa4, 0x63, 0x18, 0x9d, 0x23, 0x73, 0x4c, 0xed, 0x4f, 0x59, + 0x30, 0x95, 0x4a, 0xe2, 0xca, 0xc9, 0x94, 0xf1, 0x59, 0x11, 0xf0, 0x83, 0xb4, 0xd0, 0xf3, 0x45, + 0xa4, 0xad, 0x64, 0xcc, 0x0a, 0x8d, 0x42, 0x93, 0xce, 0xfe, 0x6c, 0x01, 0x4a, 0x49, 0x4c, 0xf6, + 0x18, 0xaa, 0x7c, 0xc2, 0x82, 0x29, 0xb5, 0x35, 0xe6, 0x0e, 0x73, 0x2e, 0x89, 0x3c, 0x4c, 0x03, + 0x75, 0xda, 0xca, 0x1c, 0x66, 0xe5, 0xb9, 0xa3, 0x29, 0x0c, 0xd3, 0xb2, 0xc9, 0x2d, 0x80, 0xa8, + 0x1b, 0xc5, 0xb4, 0x65, 0xb8, 0xee, 0xb6, 0x31, 0x3b, 0x16, 0xdc, 0x20, 0xa4, 0x6c, 0x2e, 0x6c, + 0x04, 0x55, 0xba, 0xad, 0x28, 0xb5, 0x21, 0xd4, 0x30, 0x34, 0x38, 0xd9, 0xbf, 0x5b, 0x80, 0xd3, + 0x59, 0x95, 0xc8, 0x7b, 0x60, 0x32, 0x91, 0x6e, 0xdc, 0x01, 0x4d, 0x02, 0xd1, 0x93, 0x68, 0xe0, + 0xee, 0x1f, 0xce, 0xcf, 0xf7, 0x5e, 0x82, 0x5d, 0x30, 0x49, 0x30, 0xc5, 0x4c, 0xc4, 0x27, 0x64, + 0x20, 0xad, 0xd2, 0x5d, 0x6a, 0xb7, 0x65, 0x90, 0xc1, 0x88, 0x4f, 0x98, 0x58, 0xcc, 0x50, 0x93, + 0x2d, 0x38, 0x6b, 0x40, 0x36, 0xa8, 0x57, 0x6f, 0xec, 0x06, 0xa1, 0xb8, 0x6c, 0x50, 0xac, 0x3c, + 0x2d, 0xb9, 0x9c, 0xc5, 0x3e, 0x34, 0xd8, 0xb7, 0x24, 0x79, 0x0b, 0x94, 0x5c, 0xa7, 0xed, 0xb8, + 0x5e, 0xdc, 0x95, 0x7b, 0x11, 0x65, 0x47, 0x96, 0x25, 0x1c, 0x15, 0x85, 0x7d, 0x1d, 0x46, 0x8e, + 0x39, 0x82, 0x8e, 0xb5, 0x2e, 0xdf, 0x80, 0x12, 0x63, 0xc7, 0xec, 0x46, 0x5e, 0x2c, 0x03, 0x28, + 0x25, 0x77, 0x4f, 0x88, 0x0d, 0x45, 0xcf, 0x49, 0x42, 0x40, 0xaa, 0x5a, 0x6b, 0x51, 0xd4, 0xe1, + 0x5e, 0x07, 0x43, 0x92, 0x67, 0xa0, 0x48, 0x0f, 0xda, 0xd9, 0x58, 0xcf, 0xea, 0x41, 0xdb, 0x0b, + 0x69, 0xc4, 0x88, 0xe8, 0x41, 0x9b, 0xcc, 0x41, 0xc1, 0xab, 0xca, 0x05, 0x05, 0x24, 0x4d, 0x61, + 0x6d, 0x05, 0x0b, 0x5e, 0xd5, 0x3e, 0x80, 0xb2, 0xba, 0xec, 0x42, 0xf6, 0x12, 0x3b, 0x6b, 0xe5, + 0x71, 0x88, 0x92, 0xf0, 0x1d, 0x60, 0x61, 0x3b, 0x00, 0x3a, 0xfd, 0x2f, 0x2f, 0xfb, 0x72, 0x01, + 0x46, 0xdc, 0x40, 0x66, 0xda, 0x96, 0x34, 0x1b, 0x6e, 0x60, 0x39, 0xc6, 0xbe, 0x0d, 0xd3, 0xd7, + 0xfc, 0xe0, 0xae, 0xcf, 0x16, 0xbe, 0x4b, 0x1e, 0x6d, 0x56, 0x19, 0xe3, 0x1a, 0xfb, 0x91, 0x5d, + 0xce, 0x39, 0x16, 0x05, 0x4e, 0xdd, 0x08, 0x29, 0x0c, 0xba, 0x11, 0x62, 0xff, 0xb2, 0x05, 0xa7, + 0xb3, 0xa9, 0x7e, 0x3f, 0xb0, 0x1d, 0xc6, 0x47, 0x98, 0x32, 0x49, 0x2e, 0xd9, 0x66, 0x5b, 0x9c, + 0x5a, 0xbf, 0x00, 0x93, 0xbb, 0x1d, 0xaf, 0x59, 0x95, 0xdf, 0x52, 0x1f, 0x95, 0x2d, 0x57, 0x31, + 0x70, 0x98, 0xa2, 0x64, 0x7e, 0xda, 0xae, 0xe7, 0x3b, 0x61, 0x77, 0x4b, 0xaf, 0x1b, 0xca, 0x3c, + 0x55, 0x14, 0x06, 0x0d, 0x2a, 0xfb, 0xaf, 0x8b, 0xa0, 0x6f, 0xdd, 0x10, 0x4f, 0x26, 0x45, 0x58, + 0x79, 0x84, 0xad, 0xb6, 0xbb, 0xbe, 0xab, 0xef, 0xf7, 0x94, 0x32, 0x39, 0x11, 0x1f, 0xb7, 0x98, + 0x87, 0xe8, 0xc5, 0x9e, 0xc3, 0x8d, 0x85, 0xdc, 0x28, 0x6d, 0xe5, 0x74, 0x6e, 0xbe, 0x26, 0x38, + 0x07, 0xa1, 0xe9, 0x73, 0x2a, 0x61, 0x68, 0x4a, 0x26, 0x2f, 0xcb, 0x93, 0x86, 0x62, 0x6e, 0x29, + 0x35, 0xa5, 0xcc, 0xf1, 0x42, 0x1b, 0x46, 0x43, 0x1a, 0x87, 0x49, 0x32, 0xd3, 0xb5, 0x61, 0xcf, + 0x5d, 0xe3, 0xb0, 0xbb, 0x1d, 0xb3, 0xcd, 0x58, 0xdd, 0x70, 0x8c, 0x38, 0x18, 0x85, 0x20, 0x3b, + 0x02, 0xd2, 0xdb, 0x16, 0x27, 0x8c, 0xe2, 0x2e, 0x42, 0xd9, 0xe9, 0xc4, 0x41, 0x8b, 0x35, 0x13, + 0xef, 0x9e, 0x92, 0x11, 0xa7, 0x4e, 0x10, 0xa8, 0x69, 0xec, 0xd7, 0x46, 0x21, 0x93, 0xa5, 0x40, + 0x0e, 0xcc, 0x1b, 0x63, 0x56, 0xbe, 0x37, 0xc6, 0x94, 0x32, 0xfd, 0x6e, 0x8d, 0x91, 0x3a, 0x8c, + 0xb6, 0x1b, 0x4e, 0x94, 0xcc, 0xd1, 0x1b, 0x49, 0x33, 0x6d, 0x31, 0xe0, 0xfd, 0xc3, 0xf9, 0x9f, + 0x3e, 0x9e, 0x1f, 0xc8, 0xc6, 0xea, 0xa2, 0x48, 0xd9, 0xd4, 0xa2, 0x39, 0x0f, 0x14, 0xfc, 0x4d, + 0x4f, 0xb0, 0x78, 0xc4, 0x9e, 0xf6, 0xa3, 0x96, 0x48, 0x6d, 0x43, 0x1a, 0x75, 0x9a, 0xb1, 0x1c, + 0x0d, 0x37, 0x72, 0x9c, 0x65, 0x82, 0xb1, 0xce, 0x71, 0x13, 0xdf, 0x68, 0x08, 0x25, 0xef, 0x81, + 0x72, 0x14, 0x3b, 0x61, 0xfc, 0x90, 0x19, 0x31, 0xaa, 0xd1, 0xb7, 0x13, 0x26, 0xa8, 0xf9, 0x91, + 0x77, 0x03, 0xd4, 0x3c, 0xdf, 0x8b, 0x1a, 0x0f, 0x79, 0x40, 0xc8, 0x15, 0xbf, 0xa4, 0x38, 0xa0, + 0xc1, 0x8d, 0x59, 0x37, 0x3e, 0xb6, 0x45, 0x48, 0xb3, 0xc4, 0xd7, 0x52, 0x65, 0xdd, 0x50, 0x61, + 0xd0, 0xa0, 0xb2, 0x3f, 0x04, 0x67, 0xb2, 0xb7, 0xb5, 0xe5, 0xd6, 0xb0, 0x1e, 0x06, 0x9d, 0x76, + 0x76, 0x2d, 0xe1, 0xb7, 0x79, 0x51, 0xe0, 0x98, 0x8d, 0xdf, 0xf3, 0xfc, 0x6a, 0xd6, 0xc6, 0x5f, + 0xf3, 0xfc, 0x2a, 0x72, 0xcc, 0x31, 0xae, 0xd2, 0xfd, 0xb1, 0x05, 0x17, 0x8e, 0xba, 0x54, 0xce, + 0xb6, 0xfd, 0x77, 0x9d, 0xd0, 0x97, 0xd7, 0x64, 0xb8, 0xed, 0xb8, 0xed, 0x84, 0x3e, 0x72, 0x28, + 0xe9, 0xc2, 0x98, 0xc8, 0x02, 0x94, 0xde, 0xf1, 0x8d, 0x7c, 0xaf, 0xb8, 0xb3, 0xbd, 0x95, 0x8a, + 0xd6, 0x88, 0x0c, 0x44, 0x94, 0x02, 0xed, 0xd7, 0x2c, 0x20, 0x9b, 0xfb, 0x34, 0x0c, 0xbd, 0xaa, + 0x91, 0xb7, 0x48, 0x9e, 0x87, 0xc9, 0x3b, 0xdb, 0x9b, 0x1b, 0x5b, 0x81, 0xe7, 0xf3, 0xf4, 0x7b, + 0x23, 0x5b, 0xe6, 0xaa, 0x01, 0xc7, 0x14, 0x15, 0x59, 0x86, 0x99, 0x3b, 0xaf, 0xb0, 0x25, 0x67, + 0xf5, 0xa0, 0x1d, 0xd2, 0x28, 0x52, 0x0f, 0x43, 0x94, 0xc5, 0xc1, 0xd4, 0xd5, 0x1b, 0x19, 0x24, + 0xf6, 0xd2, 0xdb, 0x5f, 0x2e, 0xc0, 0x84, 0xf1, 0x8e, 0xc2, 0x31, 0xfc, 0x91, 0xcc, 0xd3, 0x0f, + 0x85, 0x63, 0x3e, 0xfd, 0xf0, 0x2c, 0x94, 0xda, 0x41, 0xd3, 0x73, 0x3d, 0x95, 0x57, 0x3f, 0xc9, + 0x4f, 0xaf, 0x24, 0x0c, 0x15, 0x96, 0xdc, 0x85, 0xb2, 0xba, 0x10, 0x2d, 0x33, 0xed, 0xf2, 0xf2, + 0xc8, 0xd4, 0x5c, 0xd3, 0x17, 0x9d, 0xb5, 0x2c, 0x62, 0xc3, 0x18, 0x1f, 0xa8, 0x49, 0x6c, 0x9e, + 0xa7, 0x6e, 0xf0, 0x11, 0x1c, 0xa1, 0xc4, 0xd8, 0xff, 0x32, 0x0a, 0x65, 0xa4, 0xed, 0x60, 0x39, + 0xa4, 0xd5, 0x88, 0xbc, 0x1e, 0x8a, 0x9d, 0xb0, 0x29, 0x1b, 0x4b, 0x85, 0x79, 0x6e, 0xe2, 0x3a, + 0x32, 0x78, 0x6a, 0x75, 0x28, 0x9c, 0xe8, 0x8c, 0xaf, 0x78, 0xe4, 0x19, 0xdf, 0x8b, 0x30, 0x15, + 0x45, 0x8d, 0xad, 0xd0, 0xdb, 0x77, 0x62, 0x36, 0xe6, 0x64, 0x4c, 0x44, 0x1f, 0xaa, 0x6c, 0x5f, + 0xd1, 0x48, 0x4c, 0xd3, 0x92, 0xcb, 0x30, 0xa3, 0x4f, 0xda, 0x68, 0x18, 0xf3, 0x10, 0x88, 0x88, + 0x96, 0xa8, 0x33, 0x0d, 0x7d, 0x36, 0x27, 0x09, 0xb0, 0xb7, 0x0c, 0x59, 0x81, 0xd3, 0x29, 0x20, + 0x53, 0x44, 0x84, 0x52, 0xd4, 0x29, 0x7e, 0x8a, 0x0f, 0xd3, 0xa5, 0xa7, 0x04, 0xb9, 0x0e, 0x67, + 0x44, 0xff, 0xf2, 0x8b, 0xf4, 0xaa, 0x46, 0xe3, 0x9c, 0xd1, 0xff, 0x91, 0x8c, 0xce, 0x5c, 0xee, + 0x25, 0xc1, 0x7e, 0xe5, 0xd8, 0x08, 0x55, 0xe0, 0xb5, 0x15, 0x69, 0xd8, 0xd4, 0x08, 0x55, 0x6c, + 0xd6, 0xaa, 0x68, 0xd2, 0x91, 0x77, 0xc1, 0x53, 0xfa, 0x53, 0x44, 0xd0, 0xc4, 0x6a, 0xbf, 0x22, + 0x93, 0x18, 0xe6, 0x25, 0x8b, 0xa7, 0x2e, 0xf7, 0x25, 0xab, 0xe2, 0xa0, 0xf2, 0x64, 0x17, 0xe6, + 0x14, 0x6a, 0x95, 0xcd, 0xde, 0x76, 0xe8, 0x45, 0xb4, 0xe2, 0x44, 0xf4, 0x66, 0xd8, 0xe4, 0x69, + 0x0f, 0x65, 0xfd, 0x18, 0xc4, 0x65, 0x2f, 0xbe, 0xd2, 0x8f, 0x12, 0xd7, 0xf1, 0x01, 0x5c, 0x98, + 0x73, 0x41, 0x7d, 0x67, 0xb7, 0x49, 0x37, 0x97, 0xd7, 0x78, 0x32, 0x84, 0xe1, 0x5c, 0xac, 0x26, + 0x08, 0xd4, 0x34, 0xca, 0xb5, 0x9f, 0x1c, 0xe8, 0xda, 0x7f, 0xcb, 0x82, 0x29, 0x35, 0xd8, 0x1f, + 0x43, 0xbc, 0xab, 0x99, 0x8e, 0x77, 0x5d, 0x1e, 0xd6, 0xab, 0x93, 0x9a, 0x0f, 0xd8, 0x88, 0x7d, + 0xaf, 0x0c, 0xc0, 0x9f, 0xd7, 0xf1, 0x78, 0x92, 0xed, 0x05, 0x18, 0x09, 0x69, 0x3b, 0xc8, 0x5a, + 0x3e, 0x46, 0x81, 0x1c, 0xf3, 0xc3, 0x3b, 0x9d, 0xfb, 0x9d, 0xf9, 0x8e, 0xfe, 0x60, 0xcf, 0x7c, + 0xb7, 0xe1, 0x49, 0xcf, 0x8f, 0xa8, 0xdb, 0x09, 0xe5, 0x42, 0x77, 0x25, 0x88, 0x94, 0x75, 0x28, + 0x55, 0x5e, 0x2f, 0x19, 0x3d, 0xb9, 0xd6, 0x8f, 0x08, 0xfb, 0x97, 0x65, 0x4d, 0x9a, 0x20, 0xe4, + 0x6d, 0x1e, 0x1d, 0x1e, 0x90, 0x70, 0x54, 0x14, 0x7a, 0x42, 0xac, 0xd7, 0x92, 0xeb, 0x3a, 0x99, + 0x09, 0xb1, 0x7e, 0x69, 0x1b, 0x35, 0x4d, 0x7f, 0xab, 0x58, 0xce, 0xc9, 0x2a, 0xc2, 0x89, 0xad, + 0x62, 0x32, 0x3f, 0x27, 0x06, 0x3e, 0xc6, 0x90, 0x2c, 0xd6, 0x93, 0x03, 0x17, 0xeb, 0x97, 0x60, + 0xda, 0xf3, 0x1b, 0x34, 0xf4, 0x62, 0x5a, 0xe5, 0x73, 0x61, 0x76, 0x8a, 0x37, 0x84, 0x8a, 0x5c, + 0xad, 0xa5, 0xb0, 0x98, 0xa1, 0x4e, 0x1b, 0x95, 0xe9, 0x63, 0x18, 0x95, 0x01, 0xa6, 0xfc, 0x54, + 0x3e, 0xa6, 0xfc, 0xf4, 0xf0, 0xa6, 0x7c, 0xe6, 0x91, 0x9a, 0x72, 0x92, 0x8b, 0x29, 0x7f, 0x06, + 0x46, 0xdb, 0x61, 0x70, 0xd0, 0x9d, 0x3d, 0x93, 0xf6, 0xa6, 0xb7, 0x18, 0x10, 0x05, 0xce, 0x4c, + 0x7d, 0x3b, 0xfb, 0xe0, 0xd4, 0x37, 0xfb, 0xd5, 0x02, 0x3c, 0xa9, 0x2d, 0x1d, 0x1b, 0x5f, 0x5e, + 0x8d, 0xcd, 0x75, 0x7e, 0xa7, 0x52, 0xa4, 0x5b, 0x18, 0x41, 0x53, 0x1d, 0x7f, 0x55, 0x18, 0x34, + 0xa8, 0x78, 0xec, 0x91, 0x86, 0x3c, 0x61, 0x37, 0x6b, 0x06, 0x97, 0x25, 0x1c, 0x15, 0x05, 0x7f, + 0x9b, 0x8f, 0x86, 0xb1, 0x3c, 0x7b, 0xc9, 0xe6, 0x22, 0x2d, 0x6b, 0x14, 0x9a, 0x74, 0xcc, 0x5d, + 0x74, 0x93, 0x29, 0xc8, 0x4c, 0xe1, 0xa4, 0x70, 0x17, 0xd5, 0xac, 0x53, 0xd8, 0x44, 0x1d, 0x1e, + 0x64, 0x1e, 0xed, 0x55, 0x87, 0x07, 0x0d, 0x14, 0x85, 0xfd, 0x5f, 0x16, 0xbc, 0xae, 0x6f, 0x53, + 0x3c, 0x86, 0xe5, 0xed, 0x20, 0xbd, 0xbc, 0x6d, 0x0f, 0xbf, 0xbc, 0xf5, 0xd4, 0x62, 0xc0, 0x52, + 0xf7, 0x37, 0x16, 0x4c, 0x6b, 0xfa, 0xc7, 0x50, 0x55, 0x2f, 0xd7, 0x57, 0xf6, 0xb4, 0xea, 0x22, + 0x91, 0x34, 0x55, 0xb7, 0x6f, 0xf1, 0xba, 0x89, 0xbd, 0xd7, 0x92, 0x9b, 0x3c, 0x63, 0x73, 0xc4, + 0x26, 0xa6, 0x0b, 0x63, 0xfc, 0xe2, 0x71, 0x94, 0xcf, 0x1e, 0x30, 0x2d, 0x9f, 0x87, 0x41, 0xf5, + 0x1e, 0x90, 0x7f, 0x46, 0x28, 0x05, 0xf2, 0x74, 0x72, 0x2f, 0x62, 0xf6, 0xb2, 0x2a, 0xc3, 0xb5, + 0x3a, 0x9d, 0x5c, 0xc2, 0x51, 0x51, 0xd8, 0x2d, 0x98, 0x4d, 0x33, 0x5f, 0xa1, 0x35, 0x1e, 0x6a, + 0x3b, 0x56, 0x35, 0x17, 0xa1, 0xec, 0xf0, 0x52, 0xeb, 0x1d, 0x27, 0xfb, 0x96, 0xcd, 0x52, 0x82, + 0x40, 0x4d, 0x63, 0xff, 0x8e, 0x05, 0x67, 0xfa, 0x54, 0x26, 0xc7, 0x30, 0x75, 0xac, 0xad, 0xc0, + 0x80, 0xf7, 0x85, 0xaa, 0xb4, 0xe6, 0x24, 0xc1, 0x1c, 0xc3, 0xaa, 0xad, 0x08, 0x30, 0x26, 0x78, + 0xfb, 0x5f, 0x2d, 0x38, 0x95, 0xd6, 0x35, 0x22, 0x57, 0x81, 0x88, 0xca, 0xac, 0x78, 0x91, 0x1b, + 0xec, 0xd3, 0xb0, 0xcb, 0x6a, 0x2e, 0xb4, 0x9e, 0x93, 0x9c, 0xc8, 0x52, 0x0f, 0x05, 0xf6, 0x29, + 0xc5, 0xb3, 0x76, 0xab, 0xaa, 0xb5, 0x93, 0x91, 0x72, 0x2b, 0xcf, 0x91, 0xa2, 0x3b, 0xd3, 0xdc, + 0x41, 0x2b, 0x91, 0x68, 0xca, 0xb7, 0xbf, 0x3d, 0x02, 0xea, 0x1c, 0x8b, 0x87, 0x0d, 0x72, 0x0a, + 0xba, 0xa4, 0x1e, 0x3c, 0x2a, 0x9e, 0xe0, 0xc1, 0xa3, 0x91, 0x07, 0xc5, 0x08, 0xc4, 0xeb, 0x3b, + 0xda, 0x17, 0x35, 0x8c, 0xfe, 0x8e, 0x46, 0xa1, 0x49, 0xc7, 0x34, 0x69, 0x7a, 0xfb, 0x54, 0x14, + 0x1a, 0x4b, 0x6b, 0xb2, 0x9e, 0x20, 0x50, 0xd3, 0x30, 0x4d, 0xaa, 0x5e, 0xad, 0x26, 0x77, 0x8a, + 0x4a, 0x13, 0xd6, 0x3a, 0xc8, 0x31, 0x8c, 0xa2, 0x11, 0x04, 0x7b, 0xd2, 0xff, 0x53, 0x14, 0x57, + 0x82, 0x60, 0x0f, 0x39, 0x86, 0x79, 0x2c, 0x7e, 0x10, 0xb6, 0x9c, 0xa6, 0xf7, 0x7e, 0x5a, 0x55, + 0x52, 0xa4, 0xdf, 0xa7, 0x3c, 0x96, 0x8d, 0x5e, 0x12, 0xec, 0x57, 0x8e, 0x8d, 0xc0, 0x76, 0x48, + 0xab, 0x9e, 0x1b, 0x9b, 0xdc, 0x20, 0x3d, 0x02, 0xb7, 0x7a, 0x28, 0xb0, 0x4f, 0x29, 0xb2, 0x04, + 0xa7, 0x92, 0x73, 0xc8, 0x24, 0x57, 0x44, 0x38, 0x83, 0xca, 0x0f, 0xc7, 0x34, 0x1a, 0xb3, 0xf4, + 0xcc, 0xda, 0xb4, 0x64, 0xc6, 0x0e, 0x77, 0x13, 0x0d, 0x6b, 0x93, 0x64, 0xf2, 0xa0, 0xa2, 0xb0, + 0x7f, 0xaf, 0xc0, 0x56, 0xc7, 0x01, 0xf7, 0x6a, 0x1f, 0x5b, 0x90, 0x2f, 0x3d, 0x22, 0x47, 0x8e, + 0x31, 0x22, 0x9f, 0x87, 0xc9, 0x3b, 0x51, 0xe0, 0xab, 0x00, 0xda, 0xe8, 0xc0, 0x00, 0x9a, 0x41, + 0xd5, 0x3f, 0x80, 0x36, 0x76, 0xc2, 0x00, 0xda, 0x9f, 0x8f, 0xc2, 0x39, 0x75, 0x74, 0x4c, 0xe3, + 0xbb, 0x41, 0xb8, 0xe7, 0xf9, 0x75, 0x7e, 0xdc, 0xfa, 0x25, 0x0b, 0x26, 0xc5, 0xf0, 0x96, 0x2f, + 0x10, 0x88, 0xe3, 0xc5, 0x5a, 0x4e, 0x97, 0xc4, 0x52, 0xc2, 0x16, 0x76, 0x0c, 0x41, 0x99, 0xe7, + 0x20, 0x4c, 0x14, 0xa6, 0x34, 0x22, 0x1f, 0x04, 0x48, 0x9e, 0xc9, 0xaa, 0xe5, 0xf4, 0x58, 0x58, + 0xa2, 0x1f, 0xd2, 0x9a, 0x76, 0x25, 0x77, 0x94, 0x10, 0x34, 0x04, 0x92, 0x57, 0x2d, 0x75, 0x29, + 0x43, 0x9c, 0x15, 0xbd, 0xfc, 0x48, 0xda, 0xe6, 0x38, 0x77, 0x34, 0x10, 0xc6, 0x3d, 0xbf, 0xce, + 0xba, 0x55, 0xc6, 0x1c, 0xdf, 0xd4, 0x2f, 0x55, 0x61, 0x3d, 0x70, 0xaa, 0x15, 0xa7, 0xe9, 0xf8, + 0x2e, 0x0d, 0xd7, 0x04, 0xb9, 0xf9, 0x58, 0x11, 0x07, 0x60, 0xc2, 0xa8, 0xe7, 0x16, 0xe4, 0xe8, + 0x71, 0x6e, 0x41, 0xce, 0xbd, 0x13, 0x66, 0x7a, 0x3a, 0xf3, 0x44, 0x77, 0x34, 0x1e, 0xfe, 0x7a, + 0x87, 0xfd, 0x27, 0x63, 0x7a, 0x8d, 0xd9, 0x08, 0xaa, 0xe2, 0x2e, 0x5e, 0xa8, 0x7b, 0x54, 0xba, + 0x8a, 0x39, 0x0e, 0x11, 0xe3, 0xc1, 0x23, 0x05, 0x44, 0x53, 0x24, 0x1b, 0xa3, 0x6d, 0x27, 0xa4, + 0xfe, 0xa3, 0x1e, 0xa3, 0x5b, 0x4a, 0x08, 0x1a, 0x02, 0x49, 0x23, 0x75, 0x98, 0x79, 0x69, 0xf8, + 0xc3, 0x4c, 0xe6, 0xbd, 0xf6, 0xbd, 0x33, 0xf5, 0x19, 0x0b, 0xa6, 0xfd, 0xd4, 0xc8, 0x95, 0x07, + 0x5a, 0x3b, 0x8f, 0x62, 0x56, 0x88, 0x3b, 0xd0, 0x69, 0x18, 0x66, 0xe4, 0xf7, 0x5b, 0x81, 0x46, + 0x4f, 0xb8, 0x02, 0xe9, 0x4b, 0xbd, 0x63, 0x83, 0x2e, 0xf5, 0x12, 0x5f, 0x5d, 0xe7, 0x1f, 0xcf, + 0xfd, 0x3a, 0x3f, 0xf4, 0xb9, 0xca, 0x7f, 0x1b, 0xca, 0x6e, 0x48, 0x9d, 0xf8, 0x21, 0x6f, 0x76, + 0xf3, 0x27, 0xe6, 0x96, 0x13, 0x06, 0xa8, 0x79, 0xd9, 0x7f, 0x55, 0x84, 0xd3, 0x49, 0x8b, 0x24, + 0x07, 0x3d, 0x6c, 0x39, 0x13, 0x72, 0xb5, 0x2f, 0xaa, 0x96, 0xb3, 0x2b, 0x09, 0x02, 0x35, 0x0d, + 0x73, 0x9f, 0x3a, 0x11, 0xdd, 0x6c, 0x53, 0x7f, 0xdd, 0xdb, 0x8d, 0x78, 0x8b, 0x1b, 0xd9, 0x62, + 0x37, 0x35, 0x0a, 0x4d, 0x3a, 0xe6, 0x3b, 0x0b, 0x37, 0x36, 0xca, 0x9e, 0x9b, 0x4a, 0xf7, 0x18, + 0x13, 0x3c, 0xf9, 0x62, 0xdf, 0x77, 0x39, 0xf2, 0xc9, 0x18, 0xe8, 0x39, 0xdf, 0x3a, 0xe1, 0x83, + 0x1c, 0xaf, 0x59, 0x70, 0x6a, 0x2f, 0x95, 0xaa, 0x92, 0x98, 0xe4, 0x21, 0x13, 0x20, 0xd3, 0xf9, + 0x2f, 0x7a, 0x08, 0xa7, 0xe1, 0x11, 0x66, 0xa5, 0xdb, 0xff, 0x61, 0x81, 0x69, 0x9e, 0x8e, 0xe7, + 0x08, 0x19, 0x2f, 0x2d, 0x15, 0x8e, 0x78, 0x69, 0x29, 0xf1, 0x99, 0x8a, 0xc7, 0xf3, 0xd1, 0x47, + 0x4e, 0xe0, 0xa3, 0x8f, 0x0e, 0x74, 0xb2, 0x5e, 0x0f, 0xc5, 0x8e, 0x57, 0x95, 0x6e, 0xb6, 0x3e, + 0xbb, 0x5a, 0x5b, 0x41, 0x06, 0xb7, 0xff, 0x68, 0x54, 0x6f, 0xab, 0xe5, 0x41, 0xf7, 0x8f, 0x44, + 0xb5, 0x6b, 0x2a, 0x9f, 0x55, 0xd4, 0x7c, 0xa3, 0x27, 0x9f, 0xf5, 0x1d, 0x27, 0xcf, 0x63, 0x10, + 0x0d, 0x34, 0x28, 0x9d, 0x75, 0xfc, 0x88, 0x24, 0x86, 0x3b, 0x50, 0x62, 0x3b, 0x11, 0x1e, 0x1f, + 0x2b, 0xa5, 0x94, 0x2a, 0x5d, 0x91, 0xf0, 0xfb, 0x87, 0xf3, 0x6f, 0x3f, 0xb9, 0x5a, 0x49, 0x69, + 0x54, 0xfc, 0x49, 0x04, 0x65, 0xf6, 0x9b, 0xe7, 0x5b, 0xc8, 0x3d, 0xce, 0x4d, 0x65, 0x8b, 0x12, + 0x44, 0x2e, 0xc9, 0x1c, 0x5a, 0x0e, 0xf1, 0xa1, 0xcc, 0xdf, 0x04, 0xe2, 0x42, 0xc5, 0x56, 0x68, + 0x4b, 0x65, 0x3d, 0x24, 0x88, 0xfb, 0x87, 0xf3, 0x2f, 0x9e, 0x5c, 0xa8, 0x2a, 0x8e, 0x5a, 0x84, + 0xfd, 0xdd, 0xa2, 0x1e, 0xbb, 0x32, 0x8d, 0xf9, 0x47, 0x62, 0xec, 0xbe, 0x90, 0x19, 0xbb, 0x17, + 0x7a, 0xc6, 0xee, 0xb4, 0x7e, 0x37, 0x27, 0x35, 0x1a, 0x1f, 0xf7, 0x02, 0x7b, 0xf4, 0xb6, 0x9b, + 0x7b, 0x16, 0xaf, 0x74, 0xbc, 0x90, 0x46, 0x5b, 0x61, 0xc7, 0xf7, 0xfc, 0x3a, 0x1f, 0x8e, 0x25, + 0xd3, 0xb3, 0x48, 0xa1, 0x31, 0x4b, 0x6f, 0x7f, 0x99, 0x1f, 0x4f, 0x1a, 0xa9, 0x5b, 0xac, 0x97, + 0x9b, 0xfc, 0x59, 0x25, 0x91, 0x3c, 0xaa, 0x7a, 0x59, 0xbc, 0xa5, 0x24, 0x70, 0xe4, 0x2e, 0x8c, + 0xef, 0x8a, 0xa7, 0x1d, 0xf2, 0xb9, 0x4b, 0x24, 0xdf, 0x89, 0xe0, 0xb7, 0x36, 0x93, 0x47, 0x23, + 0xee, 0xeb, 0x9f, 0x98, 0x48, 0xb3, 0x7f, 0xad, 0x08, 0xa7, 0x32, 0x8f, 0xfe, 0xb0, 0xfd, 0x79, + 0xf2, 0xc2, 0x53, 0x36, 0x98, 0xae, 0x5e, 0x32, 0x56, 0x14, 0xe4, 0x7d, 0x00, 0x55, 0xda, 0x6e, + 0x06, 0x5d, 0xee, 0xb8, 0x8c, 0x9c, 0xd8, 0x71, 0x51, 0xbe, 0xee, 0x8a, 0xe2, 0x82, 0x06, 0x47, + 0x99, 0x31, 0x3b, 0x2a, 0x1e, 0xae, 0x48, 0x67, 0xcc, 0x1a, 0x57, 0xea, 0xc6, 0x1e, 0xef, 0x95, + 0x3a, 0x0f, 0x4e, 0x09, 0x15, 0x55, 0x82, 0xd4, 0x43, 0xe4, 0x41, 0x9d, 0x61, 0x23, 0x6a, 0x25, + 0xcd, 0x06, 0xb3, 0x7c, 0xed, 0x4f, 0x17, 0x98, 0xfb, 0x26, 0x1a, 0xfb, 0x7a, 0x12, 0xcb, 0x7e, + 0x23, 0x8c, 0x39, 0x9d, 0xb8, 0x11, 0xf4, 0x3c, 0xb5, 0xb1, 0xc4, 0xa1, 0x28, 0xb1, 0x64, 0x1d, + 0x46, 0xaa, 0x4e, 0x9c, 0xbc, 0xc4, 0x7f, 0x12, 0xe5, 0x74, 0xe0, 0xca, 0x89, 0x29, 0x72, 0x2e, + 0xe4, 0x69, 0x18, 0x89, 0x9d, 0x7a, 0xea, 0x0d, 0xd0, 0x1d, 0xa7, 0x1e, 0x21, 0x87, 0x9a, 0xab, + 0xcb, 0xc8, 0x11, 0xab, 0xcb, 0x8b, 0xc6, 0x7f, 0x44, 0x18, 0x87, 0x24, 0xbd, 0xff, 0xeb, 0x20, + 0x72, 0xf8, 0x53, 0xb4, 0xf6, 0x8f, 0xc1, 0xa4, 0xf9, 0xbf, 0x0f, 0xc7, 0xba, 0x02, 0x64, 0xff, + 0xf3, 0x08, 0x4c, 0xa5, 0x92, 0xe8, 0x52, 0xa3, 0xdc, 0x3a, 0x72, 0x94, 0xf3, 0xe3, 0xaf, 0x8e, + 0x4f, 0x65, 0x8a, 0xa4, 0x71, 0xfc, 0xd5, 0xf1, 0x29, 0x0a, 0x1c, 0xeb, 0x95, 0x6a, 0xd8, 0xc5, + 0x8e, 0x2f, 0x83, 0xe8, 0xaa, 0x57, 0x56, 0x38, 0x14, 0x25, 0x96, 0x6d, 0x60, 0x27, 0x23, 0x6e, + 0x14, 0x85, 0x8d, 0x90, 0xb3, 0xe6, 0x6a, 0x1e, 0xcf, 0x93, 0xc9, 0x84, 0x51, 0xbe, 0xa1, 0x37, + 0x21, 0x98, 0x92, 0x48, 0x3e, 0x66, 0x99, 0x0f, 0xb3, 0x8d, 0xe5, 0x71, 0xf8, 0x93, 0xcd, 0x51, + 0x14, 0x33, 0xe8, 0xc1, 0xef, 0xb3, 0x45, 0x6a, 0x02, 0x8f, 0x3f, 0x9a, 0x09, 0x0c, 0x7d, 0x26, + 0xef, 0x9b, 0xa1, 0xdc, 0x72, 0x7c, 0xaf, 0x46, 0xa3, 0x58, 0xfc, 0x67, 0x4b, 0x59, 0xec, 0x9e, + 0xae, 0x27, 0x40, 0xd4, 0x78, 0xfe, 0xcf, 0x48, 0xbc, 0x62, 0x62, 0x13, 0x53, 0x36, 0xfe, 0x19, + 0x49, 0x83, 0xd1, 0xa4, 0xb1, 0x7f, 0xdf, 0x82, 0x27, 0xfb, 0x36, 0xc6, 0x0f, 0x6f, 0xb4, 0xd2, + 0xfe, 0x83, 0x02, 0x9c, 0xe9, 0x93, 0x64, 0x4a, 0xba, 0x8f, 0xec, 0xfd, 0x3e, 0x99, 0xc5, 0x3a, + 0x35, 0x70, 0x6c, 0x9c, 0x6c, 0x19, 0xd2, 0x4b, 0x41, 0xf1, 0xb1, 0x2e, 0x05, 0xf6, 0x97, 0x0b, + 0x60, 0xbc, 0x34, 0x49, 0x3e, 0x64, 0xe6, 0x53, 0x5b, 0x79, 0xe5, 0xfe, 0x0a, 0xe6, 0x2a, 0x1f, + 0x5b, 0xb4, 0x5a, 0xbf, 0xf4, 0xec, 0xec, 0x78, 0x2d, 0x1c, 0x3d, 0x5e, 0x49, 0x33, 0x49, 0x5c, + 0x2f, 0xe6, 0x9f, 0xb8, 0x5e, 0xee, 0x49, 0x5a, 0xff, 0x15, 0x4b, 0x8c, 0xb4, 0x4c, 0x95, 0xb4, + 0x85, 0xb5, 0x1e, 0x60, 0x61, 0xdf, 0x02, 0xa5, 0x88, 0x36, 0x6b, 0xcc, 0xb3, 0x93, 0x96, 0x58, + 0x8d, 0x89, 0x6d, 0x09, 0x47, 0x45, 0xc1, 0xaf, 0xb4, 0x36, 0x9b, 0xc1, 0xdd, 0xd5, 0x56, 0x3b, + 0xee, 0x4a, 0x9b, 0xac, 0xaf, 0xb4, 0x2a, 0x0c, 0x1a, 0x54, 0xf6, 0x7f, 0x5a, 0xa2, 0x3b, 0xa5, + 0x8f, 0xfe, 0x42, 0xe6, 0xaa, 0xe1, 0xf1, 0xdd, 0xdb, 0x9f, 0x07, 0x70, 0xd5, 0xe5, 0xff, 0x7c, + 0x1e, 0xa0, 0xd4, 0x8f, 0x09, 0x98, 0xaf, 0x22, 0x26, 0x30, 0x34, 0xe4, 0xa5, 0x26, 0x4f, 0xf1, + 0xa8, 0xc9, 0x63, 0xff, 0x9b, 0x05, 0xa9, 0xc5, 0x82, 0xb4, 0x61, 0x94, 0x69, 0xd0, 0xcd, 0xe7, + 0xa9, 0x02, 0x93, 0x35, 0x9b, 0x58, 0x72, 0x58, 0xf0, 0x9f, 0x28, 0x04, 0x91, 0xa6, 0xf4, 0xce, + 0x0b, 0x79, 0x3c, 0xa7, 0x61, 0x0a, 0x64, 0xfe, 0xbd, 0xfc, 0x17, 0x0c, 0xe5, 0xe9, 0xdb, 0x2f, + 0xc0, 0x4c, 0x8f, 0x52, 0xfc, 0xf2, 0x51, 0x90, 0xbc, 0xcf, 0x60, 0x8c, 0x40, 0x7e, 0x15, 0x12, + 0x05, 0x8e, 0x39, 0xf8, 0xa7, 0xb3, 0xec, 0xc9, 0x17, 0x2c, 0x98, 0x89, 0xb2, 0xfc, 0x1e, 0x55, + 0xdb, 0xa9, 0xc8, 0x55, 0x0f, 0x0a, 0x7b, 0x95, 0xb0, 0xff, 0x42, 0x9a, 0x27, 0xf1, 0xaf, 0x61, + 0x6a, 0x71, 0xb1, 0x06, 0x2e, 0x2e, 0x6c, 0x8a, 0xb9, 0x0d, 0x5a, 0xed, 0x34, 0x7b, 0x52, 0x69, + 0xb6, 0x25, 0x1c, 0x15, 0x45, 0xea, 0x21, 0xba, 0xe2, 0x91, 0x0f, 0xd1, 0x3d, 0x0f, 0x93, 0xe6, + 0x1b, 0x24, 0x3c, 0x84, 0x26, 0x0f, 0x1f, 0xcc, 0xe7, 0x4a, 0x30, 0x45, 0x95, 0x79, 0xc8, 0x6c, + 0xf4, 0xc8, 0x87, 0xcc, 0x9e, 0x85, 0x92, 0x7c, 0x94, 0x2b, 0x89, 0xef, 0x8a, 0x3c, 0x1d, 0x09, + 0x43, 0x85, 0x65, 0x06, 0xa2, 0xe5, 0xf8, 0x1d, 0xa7, 0xc9, 0x5a, 0x48, 0xa6, 0xef, 0xa9, 0x99, + 0x75, 0x5d, 0x61, 0xd0, 0xa0, 0xb2, 0xff, 0xc9, 0x82, 0xec, 0x1b, 0x41, 0xa9, 0x24, 0x40, 0xeb, + 0xc8, 0x24, 0xc0, 0x74, 0x82, 0x53, 0xe1, 0x58, 0x09, 0x4e, 0x66, 0xee, 0x51, 0xf1, 0x81, 0xb9, + 0x47, 0x6f, 0xd0, 0x17, 0xc8, 0x45, 0x92, 0xd2, 0x44, 0xbf, 0xcb, 0xe3, 0xc4, 0x86, 0x31, 0xd7, + 0x51, 0x39, 0xd6, 0x93, 0xc2, 0x51, 0x5a, 0x5e, 0xe2, 0x44, 0x12, 0x53, 0x59, 0xf8, 0xea, 0x77, + 0xce, 0x3f, 0xf1, 0xb5, 0xef, 0x9c, 0x7f, 0xe2, 0x9b, 0xdf, 0x39, 0xff, 0xc4, 0x47, 0xee, 0x9d, + 0xb7, 0xbe, 0x7a, 0xef, 0xbc, 0xf5, 0xb5, 0x7b, 0xe7, 0xad, 0x6f, 0xde, 0x3b, 0x6f, 0x7d, 0xfb, + 0xde, 0x79, 0xeb, 0x33, 0xff, 0x70, 0xfe, 0x89, 0x77, 0x97, 0x92, 0xb1, 0xfa, 0x3f, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x0f, 0x7f, 0x2b, 0x5c, 0x83, 0x76, 0x00, 0x00, } func (m *AWSAuthConfig) Marshal() (dAtA []byte, err error) { @@ -4504,6 +4512,67 @@ func (m *Cluster) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Annotations) > 0 { + keysForAnnotations := make([]string, 0, len(m.Annotations)) + for k := range m.Annotations { + keysForAnnotations = append(keysForAnnotations, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForAnnotations) + for iNdEx := len(keysForAnnotations) - 1; iNdEx >= 0; iNdEx-- { + v := m.Annotations[string(keysForAnnotations[iNdEx])] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintGenerated(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(keysForAnnotations[iNdEx]) + copy(dAtA[i:], keysForAnnotations[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForAnnotations[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x6a + } + } + if len(m.Labels) > 0 { + keysForLabels := make([]string, 0, len(m.Labels)) + for k := range m.Labels { + keysForLabels = append(keysForLabels, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + for iNdEx := len(keysForLabels) - 1; iNdEx >= 0; iNdEx-- { + v := m.Labels[string(keysForLabels[iNdEx])] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintGenerated(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(keysForLabels[iNdEx]) + copy(dAtA[i:], keysForLabels[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForLabels[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x62 + } + } + i -= len(m.Project) + copy(dAtA[i:], m.Project) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Project))) + i-- + dAtA[i] = 0x5a + i-- + if m.ClusterResources { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x50 if m.Shard != nil { i = encodeVarintGenerated(dAtA, i, uint64(*m.Shard)) i-- @@ -6222,6 +6291,13 @@ func (m *Repository) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + i -= len(m.Project) + copy(dAtA[i:], m.Project) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Project))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa2 i -= len(m.Proxy) copy(dAtA[i:], m.Proxy) i = encodeVarintGenerated(dAtA, i, uint64(len(m.Proxy))) @@ -8651,6 +8727,25 @@ func (m *Cluster) Size() (n int) { if m.Shard != nil { n += 1 + sovGenerated(uint64(*m.Shard)) } + n += 2 + l = len(m.Project) + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v))) + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } + if len(m.Annotations) > 0 { + for k, v := range m.Annotations { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v))) + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } return n } @@ -9308,6 +9403,8 @@ func (m *Repository) Size() (n int) { n += 2 + l + sovGenerated(uint64(l)) l = len(m.Proxy) n += 2 + l + sovGenerated(uint64(l)) + l = len(m.Project) + n += 2 + l + sovGenerated(uint64(l)) return n } @@ -10415,6 +10512,26 @@ func (this *Cluster) String() string { if this == nil { return "nil" } + keysForLabels := make([]string, 0, len(this.Labels)) + for k := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + keysForAnnotations := make([]string, 0, len(this.Annotations)) + for k := range this.Annotations { + keysForAnnotations = append(keysForAnnotations, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForAnnotations) + mapStringForAnnotations := "map[string]string{" + for _, k := range keysForAnnotations { + mapStringForAnnotations += fmt.Sprintf("%v: %v,", k, this.Annotations[k]) + } + mapStringForAnnotations += "}" s := strings.Join([]string{`&Cluster{`, `Server:` + fmt.Sprintf("%v", this.Server) + `,`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, @@ -10425,6 +10542,10 @@ func (this *Cluster) String() string { `RefreshRequestedAt:` + strings.Replace(fmt.Sprintf("%v", this.RefreshRequestedAt), "Time", "v1.Time", 1) + `,`, `Info:` + strings.Replace(strings.Replace(this.Info.String(), "ClusterInfo", "ClusterInfo", 1), `&`, ``, 1) + `,`, `Shard:` + valueToStringGenerated(this.Shard) + `,`, + `ClusterResources:` + fmt.Sprintf("%v", this.ClusterResources) + `,`, + `Project:` + fmt.Sprintf("%v", this.Project) + `,`, + `Labels:` + mapStringForLabels + `,`, + `Annotations:` + mapStringForAnnotations + `,`, `}`, }, "") return s @@ -10933,6 +11054,7 @@ func (this *Repository) String() string { `GithubAppInstallationId:` + fmt.Sprintf("%v", this.GithubAppInstallationId) + `,`, `GitHubAppEnterpriseBaseURL:` + fmt.Sprintf("%v", this.GitHubAppEnterpriseBaseURL) + `,`, `Proxy:` + fmt.Sprintf("%v", this.Proxy) + `,`, + `Project:` + fmt.Sprintf("%v", this.Project) + `,`, `}`, }, "") return s @@ -16118,6 +16240,312 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } } m.Shard = &v + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ClusterResources", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ClusterResources = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Project = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + 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.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", 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.Annotations == nil { + m.Annotations = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Annotations[mapkey] = mapvalue + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -22076,6 +22504,38 @@ func (m *Repository) Unmarshal(dAtA []byte) error { } m.Proxy = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 20: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Project = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex 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 65bec61c05a0d..3978741aabd0b 100644 --- a/pkg/apis/application/v1alpha1/generated.proto +++ b/pkg/apis/application/v1alpha1/generated.proto @@ -403,6 +403,18 @@ message Cluster { // Shard contains optional shard number. Calculated on the fly by the application controller if not specified. optional int64 shard = 9; + + // Indicates if cluster level resources should be managed. This setting is used only if cluster is connected in a namespaced mode. + optional bool clusterResources = 10; + + // Reference between project and cluster that allow you automatically to be added as item inside Destinations project entity + optional string project = 11; + + // Labels for cluster secret metadata + map labels = 12; + + // Annotations for cluster secret metadata + map annotations = 13; } // ClusterCacheInfo contains information about the cluster cache @@ -880,6 +892,9 @@ message Repository { // Proxy specifies the HTTP/HTTPS proxy used to access the repo optional string proxy = 19; + + // Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity + optional string project = 20; } // A RepositoryCertificate is either SSH known hosts entry or TLS certificate diff --git a/pkg/apis/application/v1alpha1/openapi_generated.go b/pkg/apis/application/v1alpha1/openapi_generated.go index cffff9bf5bff4..682a319465333 100644 --- a/pkg/apis/application/v1alpha1/openapi_generated.go +++ b/pkg/apis/application/v1alpha1/openapi_generated.go @@ -1433,6 +1433,52 @@ func schema_pkg_apis_application_v1alpha1_Cluster(ref common.ReferenceCallback) Format: "int64", }, }, + "clusterResources": { + SchemaProps: spec.SchemaProps{ + Description: "Indicates if cluster level resources should be managed. This setting is used only if cluster is connected in a namespaced mode.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "project": { + SchemaProps: spec.SchemaProps{ + Description: "Reference between project and cluster that allow you automatically to be added as item inside Destinations project entity", + Type: []string{"string"}, + Format: "", + }, + }, + "labels": { + SchemaProps: spec.SchemaProps{ + Description: "Labels for cluster secret metadata", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Annotations for cluster secret metadata", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, Required: []string{"server", "name", "config"}, }, @@ -2990,6 +3036,13 @@ func schema_pkg_apis_application_v1alpha1_Repository(ref common.ReferenceCallbac Format: "", }, }, + "project": { + SchemaProps: spec.SchemaProps{ + Description: "Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity", + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"repo"}, }, diff --git a/pkg/apis/application/v1alpha1/repository_types.go b/pkg/apis/application/v1alpha1/repository_types.go index abceb0a14ed96..dc54c604b0b39 100644 --- a/pkg/apis/application/v1alpha1/repository_types.go +++ b/pkg/apis/application/v1alpha1/repository_types.go @@ -80,6 +80,8 @@ 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 allow you automatically to be added as item inside SourceRepos project entity + Project string `json:"project,omitempty" protobuf:"bytes,20,opt,name=project"` } // IsInsecure returns true if the repository has been configured to skip server verification @@ -168,7 +170,7 @@ func (repo *Repository) GetGitCreds() git.Creds { if repo == nil { return git.NopCreds{} } - if repo.Username != "" && repo.Password != "" { + if repo.Password != "" { return git.NewHTTPSCreds(repo.Username, repo.Password, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure(), repo.Proxy) } if repo.SSHPrivateKey != "" { diff --git a/pkg/apis/application/v1alpha1/types.go b/pkg/apis/application/v1alpha1/types.go index d29d421adc0ac..8990aae5b46d3 100644 --- a/pkg/apis/application/v1alpha1/types.go +++ b/pkg/apis/application/v1alpha1/types.go @@ -3,7 +3,7 @@ package v1alpha1 import ( "encoding/json" "fmt" - math "math" + "math" "net" "net/http" "os" @@ -31,6 +31,7 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" + "github.com/argoproj/argo-cd/v2/util/collections" "github.com/argoproj/argo-cd/v2/util/helm" ) @@ -73,6 +74,8 @@ type ApplicationSpec struct { RevisionHistoryLimit *int64 `json:"revisionHistoryLimit,omitempty" protobuf:"bytes,7,name=revisionHistoryLimit"` } +type TrackingMethod string + // ResourceIgnoreDifferences contains resource filter and list of json paths which should be ignored during comparison with live state. type ResourceIgnoreDifferences struct { Group string `json:"group,omitempty" protobuf:"bytes,1,opt,name=group"` @@ -763,11 +766,12 @@ func (r *RetryStrategy) NextRetryAt(lastAttempt time.Time, retryCounts int64) (t } // Formula: timeToWait = duration * factor^retry_number // Note that timeToWait should equal to duration for the first retry attempt. - timeToWait := duration * time.Duration(math.Pow(float64(factor), float64(retryCounts))) + // When timeToWait is more than maxDuration retry should be performed at maxDuration. + timeToWait := float64(duration) * (math.Pow(float64(factor), float64(retryCounts))) if maxDuration > 0 { - timeToWait = time.Duration(math.Min(float64(maxDuration), float64(timeToWait))) + timeToWait = math.Min(float64(maxDuration), timeToWait) } - return lastAttempt.Add(timeToWait), nil + return lastAttempt.Add(time.Duration(timeToWait)), nil } // Backoff is the backoff strategy to use on subsequent retries for failing syncs @@ -1271,6 +1275,14 @@ type Cluster struct { Info ClusterInfo `json:"info,omitempty" protobuf:"bytes,8,opt,name=info"` // Shard contains optional shard number. Calculated on the fly by the application controller if not specified. Shard *int64 `json:"shard,omitempty" protobuf:"bytes,9,opt,name=shard"` + // Indicates if cluster level resources should be managed. This setting is used only if cluster is connected in a namespaced mode. + ClusterResources bool `json:"clusterResources,omitempty" protobuf:"bytes,10,opt,name=clusterResources"` + // Reference between project and cluster that allow you automatically to be added as item inside Destinations project entity + Project string `json:"project,omitempty" protobuf:"bytes,11,opt,name=project"` + // Labels for cluster secret metadata + Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,12,opt,name=labels"` + // Annotations for cluster secret metadata + Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,13,opt,name=annotations"` } // Equals returns true if two cluster objects are considered to be equal @@ -1295,6 +1307,19 @@ func (c *Cluster) Equals(other *Cluster) bool { if shard != otherShard { return false } + + if c.ClusterResources != other.ClusterResources { + return false + } + + if !collections.StringMapsEqual(c.Annotations, other.Annotations) { + return false + } + + if !collections.StringMapsEqual(c.Labels, other.Labels) { + return false + } + return reflect.DeepEqual(c.Config, other.Config) } @@ -1601,7 +1626,7 @@ type OrphanedResourceKey struct { // IsWarn returns true if warnings are enabled for orphan resources monitoring func (s *OrphanedResourcesMonitorSettings) IsWarn() bool { - return s.Warn == nil || *s.Warn + return s.Warn != nil && *s.Warn } // SignatureKey is the specification of a key required to verify commit signatures with diff --git a/pkg/apis/application/v1alpha1/types_test.go b/pkg/apis/application/v1alpha1/types_test.go index cd18fe8cc749d..1d2944a4061c8 100644 --- a/pkg/apis/application/v1alpha1/types_test.go +++ b/pkg/apis/application/v1alpha1/types_test.go @@ -2109,12 +2109,14 @@ func TestProjectNormalize(t *testing.T) { func TestRetryStrategy_NextRetryAtDefaultBackoff(t *testing.T) { retry := RetryStrategy{} now := time.Now() - expectedTimes := []time.Time{ - now.Add(5 * time.Second), - now.Add(10 * time.Second), - now.Add(20 * time.Second), - now.Add(40 * time.Second), - now.Add(80 * time.Second), + expectedTimes := map[int]time.Time{ + 0: now.Add(5 * time.Second), + 1: now.Add(10 * time.Second), + 2: now.Add(20 * time.Second), + 3: now.Add(40 * time.Second), + 4: now.Add(80 * time.Second), + 80: now.Add(DefaultSyncRetryMaxDuration), + 100: now.Add(DefaultSyncRetryMaxDuration), } for i, expected := range expectedTimes { @@ -2122,6 +2124,7 @@ func TestRetryStrategy_NextRetryAtDefaultBackoff(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expected.Format(time.RFC850), retryAt.Format(time.RFC850)) } + } func TestRetryStrategy_NextRetryAtCustomBackoff(t *testing.T) { @@ -2228,3 +2231,14 @@ func TestRemoveEnvEntry(t *testing.T) { assert.EqualError(t, err, `unable to find env variable with key "key" for plugin "test"`) }) } + +func TestOrphanedResourcesMonitorSettings_IsWarn(t *testing.T) { + settings := OrphanedResourcesMonitorSettings{} + assert.False(t, settings.IsWarn()) + + settings.Warn = pointer.BoolPtr(false) + assert.False(t, settings.IsWarn()) + + settings.Warn = pointer.BoolPtr(true) + assert.True(t, settings.IsWarn()) +} diff --git a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go index 85ece26b4d483..baf46e93095ec 100644 --- a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go @@ -703,6 +703,20 @@ func (in *Cluster) DeepCopyInto(out *Cluster) { *out = new(int64) **out = **in } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } return } diff --git a/reposerver/apiclient/repository.pb.go b/reposerver/apiclient/repository.pb.go index d8b0f1e182584..e8c1ad1baa8b2 100644 --- a/reposerver/apiclient/repository.pb.go +++ b/reposerver/apiclient/repository.pb.go @@ -50,6 +50,7 @@ type ManifestRequest struct { VerifySignature bool `protobuf:"varint,16,opt,name=verifySignature,proto3" json:"verifySignature,omitempty"` HelmRepoCreds []*v1alpha1.RepoCreds `protobuf:"bytes,17,rep,name=helmRepoCreds,proto3" json:"helmRepoCreds,omitempty"` NoRevisionCache bool `protobuf:"varint,18,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"` + TrackingMethod string `protobuf:"bytes,19,opt,name=trackingMethod,proto3" json:"trackingMethod,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -193,6 +194,13 @@ func (m *ManifestRequest) GetNoRevisionCache() bool { return false } +func (m *ManifestRequest) GetTrackingMethod() string { + if m != nil { + return m.TrackingMethod + } + return "" +} + // TestRepositoryRequest is a query to test repository is valid or not and has valid access. type TestRepositoryRequest struct { Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` @@ -594,6 +602,8 @@ type RepoServerAppDetailsQuery struct { KustomizeOptions *v1alpha1.KustomizeOptions `protobuf:"bytes,4,opt,name=kustomizeOptions,proto3" json:"kustomizeOptions,omitempty"` AppName string `protobuf:"bytes,5,opt,name=appName,proto3" json:"appName,omitempty"` NoCache bool `protobuf:"varint,6,opt,name=noCache,proto3" json:"noCache,omitempty"` + NoRevisionCache bool `protobuf:"varint,7,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"` + TrackingMethod string `protobuf:"bytes,8,opt,name=trackingMethod,proto3" json:"trackingMethod,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -674,6 +684,20 @@ func (m *RepoServerAppDetailsQuery) GetNoCache() bool { return false } +func (m *RepoServerAppDetailsQuery) GetNoRevisionCache() bool { + if m != nil { + return m.NoRevisionCache + } + return false +} + +func (m *RepoServerAppDetailsQuery) GetTrackingMethod() string { + if m != nil { + return m.TrackingMethod + } + return "" +} + // RepoAppDetailsResponse application details type RepoAppDetailsResponse struct { Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` @@ -1359,94 +1383,96 @@ func init() { } var fileDescriptor_dd8723cfcc820480 = []byte{ - // 1392 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x18, 0xcb, 0x6e, 0xdb, 0xc6, - 0xd6, 0x94, 0xe4, 0x87, 0x8e, 0x12, 0x5b, 0x9e, 0x3c, 0x2e, 0xaf, 0xae, 0x23, 0x38, 0x04, 0x6e, - 0xe0, 0x36, 0x0d, 0x85, 0x28, 0x41, 0x1b, 0x24, 0x40, 0x01, 0xd5, 0x49, 0x1c, 0xc0, 0x49, 0xec, - 0xd2, 0x69, 0x81, 0x16, 0x41, 0x83, 0x31, 0x75, 0x4c, 0x4d, 0x25, 0x91, 0x13, 0x92, 0x52, 0xe1, - 0x00, 0x5d, 0x16, 0x5d, 0x74, 0xdd, 0xfe, 0x4e, 0x57, 0x7d, 0x2c, 0xdb, 0x3f, 0x28, 0xf2, 0x09, - 0xfd, 0x82, 0x62, 0x86, 0xaf, 0x21, 0x45, 0xbb, 0x0b, 0x25, 0xce, 0xc6, 0x9e, 0x39, 0xef, 0x73, - 0xe6, 0xbc, 0x28, 0xb8, 0xe6, 0x23, 0xf7, 0x02, 0xf4, 0xa7, 0xe8, 0x77, 0xe4, 0x91, 0x85, 0x9e, - 0x7f, 0xac, 0x1c, 0x4d, 0xee, 0x7b, 0xa1, 0x47, 0x20, 0x83, 0xb4, 0x2e, 0x3a, 0x9e, 0xe3, 0x49, - 0x70, 0x47, 0x9c, 0x22, 0x8a, 0xd6, 0x86, 0xe3, 0x79, 0xce, 0x08, 0x3b, 0x94, 0xb3, 0x0e, 0x75, - 0x5d, 0x2f, 0xa4, 0x21, 0xf3, 0xdc, 0x20, 0xc6, 0x1a, 0xc3, 0x3b, 0x81, 0xc9, 0x3c, 0x89, 0xb5, - 0x3d, 0x1f, 0x3b, 0xd3, 0x9b, 0x1d, 0x07, 0x5d, 0xf4, 0x69, 0x88, 0xfd, 0x98, 0xe6, 0xb1, 0xc3, - 0xc2, 0xc1, 0xe4, 0xd0, 0xb4, 0xbd, 0x71, 0x87, 0xfa, 0x52, 0xc5, 0xd7, 0xf2, 0x70, 0xc3, 0xee, - 0x77, 0xa6, 0xdd, 0x0e, 0x1f, 0x3a, 0x82, 0x3f, 0xe8, 0x50, 0xce, 0x47, 0xcc, 0x96, 0xf2, 0x3b, - 0xd3, 0x9b, 0x74, 0xc4, 0x07, 0x74, 0x46, 0x9a, 0xf1, 0xe7, 0x32, 0xac, 0x3d, 0xa1, 0x2e, 0x3b, - 0xc2, 0x20, 0xb4, 0xf0, 0xe5, 0x04, 0x83, 0x90, 0x3c, 0x87, 0x9a, 0xf0, 0x43, 0xd7, 0x36, 0xb5, - 0xad, 0x46, 0xf7, 0x91, 0x99, 0x29, 0x34, 0x13, 0x85, 0xf2, 0xf0, 0xc2, 0xee, 0x9b, 0xd3, 0xae, - 0xc9, 0x87, 0x8e, 0x29, 0x14, 0x9a, 0x8a, 0x42, 0x33, 0x51, 0x68, 0x5a, 0x69, 0x44, 0x2c, 0x29, - 0x95, 0xb4, 0x60, 0xc5, 0xc7, 0x29, 0x0b, 0x98, 0xe7, 0xea, 0x95, 0x4d, 0x6d, 0xab, 0x6e, 0xa5, - 0x77, 0xa2, 0xc3, 0xb2, 0xeb, 0x6d, 0x53, 0x7b, 0x80, 0x7a, 0x75, 0x53, 0xdb, 0x5a, 0xb1, 0x92, - 0x2b, 0xd9, 0x84, 0x06, 0xe5, 0xfc, 0x31, 0x3d, 0xc4, 0xd1, 0x2e, 0x1e, 0xeb, 0x35, 0xc9, 0xa8, - 0x82, 0x04, 0x2f, 0xe5, 0xfc, 0x29, 0x1d, 0xa3, 0xbe, 0x28, 0xb1, 0xc9, 0x95, 0x6c, 0x40, 0xdd, - 0xa5, 0x63, 0x0c, 0x38, 0xb5, 0x51, 0x5f, 0x91, 0xb8, 0x0c, 0x40, 0xbe, 0x85, 0x75, 0xc5, 0xf0, - 0x03, 0x6f, 0xe2, 0xdb, 0xa8, 0x83, 0x74, 0x7d, 0x6f, 0x3e, 0xd7, 0x7b, 0x45, 0xb1, 0xd6, 0xac, - 0x26, 0xf2, 0x15, 0x2c, 0xca, 0xa4, 0xd1, 0x1b, 0x9b, 0xd5, 0x37, 0x1a, 0xed, 0x48, 0x2c, 0x71, - 0x61, 0x99, 0x8f, 0x26, 0x0e, 0x73, 0x03, 0xfd, 0x9c, 0xd4, 0xf0, 0x6c, 0x3e, 0x0d, 0xdb, 0x9e, - 0x7b, 0xc4, 0x9c, 0x27, 0xd4, 0xa5, 0x0e, 0x8e, 0xd1, 0x0d, 0xf7, 0xa5, 0x70, 0x2b, 0x51, 0x42, - 0x5e, 0x41, 0x73, 0x38, 0x09, 0x42, 0x6f, 0xcc, 0x5e, 0xe1, 0x1e, 0x97, 0xc9, 0xad, 0x9f, 0x97, - 0xd1, 0x7c, 0x3a, 0x9f, 0xe2, 0xdd, 0x82, 0x54, 0x6b, 0x46, 0x8f, 0x48, 0x92, 0xe1, 0xe4, 0x10, - 0x3f, 0x47, 0x5f, 0x66, 0xd7, 0x6a, 0x94, 0x24, 0x0a, 0x28, 0x4a, 0x23, 0x16, 0xdf, 0x02, 0x7d, - 0x6d, 0xb3, 0x1a, 0xa5, 0x51, 0x0a, 0x22, 0x5b, 0xb0, 0x36, 0x45, 0x9f, 0x1d, 0x1d, 0x1f, 0x30, - 0xc7, 0xa5, 0xe1, 0xc4, 0x47, 0xbd, 0x29, 0x53, 0xb1, 0x08, 0x26, 0x63, 0x38, 0x3f, 0xc0, 0xd1, - 0x58, 0x84, 0x7c, 0xdb, 0xc7, 0x7e, 0xa0, 0xaf, 0xcb, 0xf8, 0xee, 0xcc, 0xff, 0x82, 0x52, 0x9c, - 0x95, 0x97, 0x2e, 0x0c, 0x73, 0x3d, 0x2b, 0xae, 0x94, 0xa8, 0x46, 0x48, 0x64, 0x58, 0x01, 0x6c, - 0x4c, 0xe0, 0xd2, 0x33, 0x59, 0xce, 0x69, 0x2e, 0x9c, 0x45, 0x61, 0x1b, 0x8f, 0xe0, 0x72, 0x51, - 0x6d, 0xc0, 0x3d, 0x37, 0x40, 0x62, 0x02, 0x91, 0xc1, 0x63, 0xd8, 0xcf, 0xb0, 0xd2, 0x8a, 0x15, - 0xab, 0x04, 0x63, 0xfc, 0xaa, 0x41, 0x33, 0x6b, 0x4a, 0xb1, 0x90, 0x0d, 0xa8, 0x8f, 0x63, 0x58, - 0xa0, 0x6b, 0xf2, 0xe1, 0x32, 0x40, 0xbe, 0xc6, 0x2b, 0xc5, 0x1a, 0xbf, 0x0c, 0x4b, 0x51, 0xf7, - 0x96, 0x6d, 0xa5, 0x6e, 0xc5, 0xb7, 0x5c, 0x2f, 0xaa, 0x15, 0x7a, 0x51, 0x1b, 0x20, 0x90, 0x25, - 0xfa, 0xec, 0x98, 0xa3, 0xbe, 0x24, 0xb1, 0x0a, 0x84, 0x18, 0x70, 0x2e, 0xca, 0x08, 0x0b, 0x83, - 0xc9, 0x28, 0xd4, 0x97, 0x25, 0x45, 0x0e, 0x66, 0x78, 0xb0, 0xf6, 0x98, 0x09, 0x1f, 0x8e, 0x82, - 0xb3, 0x79, 0x83, 0x0f, 0xa1, 0x26, 0x94, 0x09, 0xc7, 0x0e, 0x7d, 0xea, 0xda, 0x03, 0x4c, 0x62, - 0x95, 0xde, 0x09, 0x81, 0x5a, 0x48, 0x9d, 0x40, 0xaf, 0x48, 0xb8, 0x3c, 0x1b, 0x3f, 0x68, 0x91, - 0xa5, 0x3d, 0xce, 0x83, 0x77, 0x3e, 0x06, 0x8c, 0x09, 0x2c, 0xf7, 0x38, 0x17, 0xf6, 0x90, 0x9b, - 0x50, 0xa3, 0x9c, 0x47, 0x4e, 0x34, 0xba, 0x57, 0x4c, 0x65, 0xe4, 0xc6, 0x24, 0xe2, 0x7f, 0xf0, - 0xc0, 0x0d, 0x85, 0x64, 0x41, 0xda, 0xfa, 0x08, 0xea, 0x29, 0x88, 0x34, 0xa1, 0x3a, 0xc4, 0x28, - 0xd7, 0xea, 0x96, 0x38, 0x92, 0x8b, 0xb0, 0x38, 0xa5, 0xa3, 0x49, 0x92, 0x25, 0xd1, 0xe5, 0x6e, - 0xe5, 0x8e, 0x66, 0xfc, 0x5d, 0x85, 0xff, 0x0a, 0x3b, 0x0f, 0x64, 0x72, 0xf4, 0x38, 0xbf, 0x8f, - 0x21, 0x65, 0xa3, 0xe0, 0xd3, 0x09, 0xfa, 0xc7, 0x6f, 0x39, 0x1c, 0x0e, 0x2c, 0x45, 0xb9, 0x25, - 0xcd, 0x7a, 0x0b, 0xa3, 0x27, 0x16, 0x9f, 0xcd, 0x9b, 0xea, 0xdb, 0x99, 0x37, 0x65, 0xfd, 0xbf, - 0x76, 0x46, 0xfd, 0xff, 0xe4, 0x15, 0x40, 0x59, 0x2c, 0x96, 0x72, 0x8b, 0x85, 0xf1, 0x7d, 0x05, - 0x2e, 0x0b, 0x2f, 0xb2, 0xe7, 0x4e, 0x3b, 0x8e, 0x28, 0x14, 0x51, 0xfb, 0x51, 0xf2, 0xc8, 0x33, - 0xb9, 0x0d, 0xcb, 0xc3, 0xc0, 0x73, 0x5d, 0x0c, 0xe3, 0x87, 0x6a, 0xa9, 0x29, 0xb9, 0x1b, 0xa1, - 0x7a, 0x9c, 0x1f, 0x70, 0xb4, 0xad, 0x84, 0x94, 0x5c, 0x87, 0x9a, 0x68, 0xe6, 0xb2, 0xfb, 0x34, - 0xba, 0xff, 0x51, 0x59, 0x1e, 0xe1, 0x68, 0x9c, 0xd0, 0x4b, 0x22, 0x72, 0x17, 0xea, 0xa9, 0x67, - 0x71, 0xe8, 0x36, 0x72, 0x4a, 0x12, 0x64, 0xc2, 0x96, 0x91, 0x0b, 0xde, 0x3e, 0xf3, 0xd1, 0x96, - 0x0d, 0x76, 0x71, 0x96, 0xf7, 0x7e, 0x82, 0x4c, 0x79, 0x53, 0x72, 0xe3, 0x17, 0x0d, 0xae, 0x66, - 0xe9, 0x9f, 0x8c, 0x94, 0x27, 0x18, 0xd2, 0x3e, 0x0d, 0xe9, 0xbb, 0x5f, 0x0e, 0xaf, 0xc1, 0xaa, - 0x3d, 0x40, 0x7b, 0x98, 0x0d, 0xe6, 0x68, 0x47, 0x2c, 0x40, 0x8d, 0xdf, 0x2a, 0xb0, 0x9a, 0x7f, - 0x08, 0xf1, 0x92, 0x62, 0x18, 0x24, 0x2f, 0x29, 0xce, 0x64, 0x1f, 0xce, 0xa1, 0x3b, 0x65, 0xbe, - 0xe7, 0x8a, 0x35, 0x26, 0xa9, 0x87, 0x0f, 0x4e, 0x7e, 0x4e, 0xf3, 0x81, 0x42, 0x1e, 0x35, 0x9c, - 0x9c, 0x04, 0xe2, 0x02, 0x70, 0xea, 0xd3, 0x31, 0x86, 0xe8, 0x8b, 0xa4, 0xaf, 0xbe, 0x81, 0xa4, - 0x8f, 0x2c, 0xd8, 0x4f, 0xc4, 0x5a, 0x8a, 0x86, 0xd6, 0x0b, 0x58, 0x9f, 0x31, 0xa9, 0xa4, 0xe1, - 0xdd, 0x56, 0x1b, 0x5e, 0xa3, 0xdb, 0x2e, 0xf1, 0x50, 0x11, 0xa3, 0x36, 0xc4, 0x9f, 0x2b, 0xd0, - 0x50, 0xf2, 0xb3, 0x34, 0x8c, 0x6d, 0x00, 0xc9, 0xf0, 0x90, 0x8d, 0x30, 0x0a, 0x62, 0xdd, 0x52, - 0x20, 0x64, 0x58, 0x12, 0x94, 0xdd, 0xf9, 0x82, 0x22, 0x4c, 0x2a, 0x8d, 0x88, 0x98, 0xf3, 0x52, - 0x75, 0x10, 0xd7, 0x7f, 0x7c, 0x23, 0xdf, 0xc0, 0xea, 0x11, 0x1b, 0xe1, 0x7e, 0x66, 0xc8, 0x92, - 0x34, 0x64, 0x6f, 0x7e, 0x43, 0x1e, 0xaa, 0x72, 0xad, 0x82, 0x1a, 0xe3, 0x7d, 0x68, 0x16, 0xcb, - 0x55, 0x18, 0xc9, 0xc6, 0xd4, 0x49, 0xa3, 0x15, 0xdf, 0x8c, 0x1f, 0x35, 0x20, 0xb3, 0xef, 0x71, - 0x52, 0xd0, 0x87, 0x77, 0x82, 0x64, 0xcf, 0x8d, 0x0a, 0x45, 0x81, 0x90, 0x5d, 0x68, 0xf4, 0x31, - 0x08, 0x99, 0x2b, 0x0d, 0x8e, 0x9b, 0xc8, 0x7b, 0xa7, 0x3f, 0xfc, 0xfd, 0x8c, 0xc1, 0x52, 0xb9, - 0x8d, 0xcf, 0xe0, 0xca, 0xa9, 0xd4, 0xca, 0x76, 0xa5, 0xe5, 0xb6, 0xab, 0x53, 0x77, 0x32, 0x83, - 0x40, 0xb3, 0xd8, 0x8d, 0x8c, 0x97, 0xb0, 0x2e, 0x62, 0xba, 0x3d, 0xa0, 0x7e, 0x78, 0x46, 0x1b, - 0xd3, 0x3d, 0xa8, 0xa7, 0x2a, 0x4b, 0x63, 0xdd, 0x82, 0x95, 0x69, 0xf2, 0xbd, 0x10, 0xad, 0x4c, - 0xe9, 0xdd, 0xe8, 0x01, 0x51, 0xed, 0x8d, 0xe7, 0xc6, 0x75, 0x58, 0x64, 0x21, 0x8e, 0x93, 0xa5, - 0xe5, 0x52, 0xb1, 0xdd, 0x4b, 0x72, 0x2b, 0xa2, 0xe9, 0x7e, 0xb7, 0x08, 0xeb, 0x59, 0xd7, 0x15, - 0x7f, 0x99, 0x8d, 0x64, 0x0f, 0x9a, 0x3b, 0xf1, 0x97, 0x7a, 0xb2, 0x08, 0x93, 0xff, 0xa9, 0x72, - 0x0a, 0xdf, 0xec, 0xad, 0x8d, 0x72, 0x64, 0x64, 0x91, 0xb1, 0x40, 0xbe, 0x80, 0xd5, 0xfc, 0x72, - 0x4e, 0xae, 0xaa, 0x1c, 0xa5, 0xdf, 0x0b, 0x2d, 0xe3, 0x34, 0x92, 0x54, 0xf4, 0x3d, 0x58, 0x49, - 0x96, 0xdc, 0xbc, 0x8d, 0x85, 0xd5, 0xb7, 0xd5, 0x54, 0x91, 0x02, 0x61, 0x2c, 0x90, 0x8f, 0x23, - 0x66, 0xb1, 0xb0, 0xcd, 0x32, 0x2b, 0xdb, 0x68, 0xeb, 0x42, 0xc9, 0xea, 0x67, 0x2c, 0x90, 0xe7, - 0x70, 0x7e, 0x47, 0x76, 0xe8, 0x78, 0x78, 0x93, 0xff, 0xe7, 0x95, 0x9c, 0xb0, 0xcd, 0xe5, 0x5d, - 0x2b, 0x9f, 0xff, 0xc6, 0x02, 0xf9, 0x49, 0x83, 0x0b, 0x3b, 0x18, 0x16, 0x67, 0x21, 0xb9, 0x51, - 0xae, 0xe4, 0x84, 0x99, 0xd9, 0x7a, 0x3a, 0x6f, 0xce, 0xe6, 0xc5, 0x1a, 0x0b, 0x64, 0x5f, 0xba, - 0x9d, 0xe5, 0x1e, 0xb9, 0x52, 0x9a, 0x64, 0x69, 0xf4, 0xda, 0x27, 0xa1, 0x13, 0x57, 0x3f, 0xe9, - 0xfd, 0xfe, 0xba, 0xad, 0xfd, 0xf1, 0xba, 0xad, 0xfd, 0xf5, 0xba, 0xad, 0x7d, 0x79, 0xeb, 0x5f, - 0x7e, 0x64, 0x52, 0x7e, 0x0f, 0xa3, 0x9c, 0xd9, 0x23, 0x86, 0x6e, 0x78, 0xb8, 0x24, 0x7f, 0x52, - 0xba, 0xf5, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf6, 0x7e, 0x8e, 0xd3, 0x2e, 0x13, 0x00, 0x00, + // 1423 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x5b, 0x6f, 0x1b, 0xc5, + 0x17, 0xcf, 0x26, 0x4e, 0x62, 0x1f, 0xb7, 0x89, 0x33, 0xbd, 0xfc, 0xf7, 0x6f, 0x52, 0x2b, 0x5d, + 0x89, 0x2a, 0x50, 0xba, 0x56, 0xdd, 0x0a, 0xaa, 0x56, 0x42, 0x32, 0x69, 0x9b, 0x4a, 0x69, 0x9a, + 0xb0, 0x29, 0x48, 0xa0, 0x8a, 0x6a, 0xb2, 0x3e, 0x59, 0x0f, 0xb6, 0x77, 0xa7, 0xbb, 0x6b, 0xa3, + 0x54, 0xe2, 0x11, 0xf1, 0xc0, 0x33, 0x7c, 0x1d, 0x5e, 0xb8, 0x3d, 0xf2, 0x11, 0x50, 0xbf, 0x05, + 0x6f, 0x68, 0x66, 0x6f, 0xb3, 0xeb, 0x4d, 0x40, 0x72, 0x9b, 0xbe, 0x24, 0x33, 0xe7, 0x3e, 0x67, + 0xce, 0xfc, 0xce, 0xf1, 0xc2, 0x35, 0x1f, 0xb9, 0x17, 0xa0, 0x3f, 0x41, 0xbf, 0x2d, 0x97, 0x2c, + 0xf4, 0xfc, 0x63, 0x65, 0x69, 0x72, 0xdf, 0x0b, 0x3d, 0x02, 0x19, 0xa5, 0x79, 0xd1, 0xf1, 0x1c, + 0x4f, 0x92, 0xdb, 0x62, 0x15, 0x49, 0x34, 0xd7, 0x1d, 0xcf, 0x73, 0x86, 0xd8, 0xa6, 0x9c, 0xb5, + 0xa9, 0xeb, 0x7a, 0x21, 0x0d, 0x99, 0xe7, 0x06, 0x31, 0xd7, 0x18, 0xdc, 0x09, 0x4c, 0xe6, 0x49, + 0xae, 0xed, 0xf9, 0xd8, 0x9e, 0xdc, 0x6c, 0x3b, 0xe8, 0xa2, 0x4f, 0x43, 0xec, 0xc5, 0x32, 0x8f, + 0x1d, 0x16, 0xf6, 0xc7, 0x87, 0xa6, 0xed, 0x8d, 0xda, 0xd4, 0x97, 0x2e, 0xbe, 0x96, 0x8b, 0x1b, + 0x76, 0xaf, 0x3d, 0xe9, 0xb4, 0xf9, 0xc0, 0x11, 0xfa, 0x41, 0x9b, 0x72, 0x3e, 0x64, 0xb6, 0xb4, + 0xdf, 0x9e, 0xdc, 0xa4, 0x43, 0xde, 0xa7, 0x53, 0xd6, 0x8c, 0xbf, 0x97, 0x61, 0x75, 0x97, 0xba, + 0xec, 0x08, 0x83, 0xd0, 0xc2, 0x17, 0x63, 0x0c, 0x42, 0xf2, 0x0c, 0x2a, 0xe2, 0x1c, 0xba, 0xb6, + 0xa1, 0x6d, 0xd6, 0x3b, 0x8f, 0xcc, 0xcc, 0xa1, 0x99, 0x38, 0x94, 0x8b, 0xe7, 0x76, 0xcf, 0x9c, + 0x74, 0x4c, 0x3e, 0x70, 0x4c, 0xe1, 0xd0, 0x54, 0x1c, 0x9a, 0x89, 0x43, 0xd3, 0x4a, 0x33, 0x62, + 0x49, 0xab, 0xa4, 0x09, 0x55, 0x1f, 0x27, 0x2c, 0x60, 0x9e, 0xab, 0xcf, 0x6f, 0x68, 0x9b, 0x35, + 0x2b, 0xdd, 0x13, 0x1d, 0x96, 0x5d, 0x6f, 0x8b, 0xda, 0x7d, 0xd4, 0x17, 0x36, 0xb4, 0xcd, 0xaa, + 0x95, 0x6c, 0xc9, 0x06, 0xd4, 0x29, 0xe7, 0x8f, 0xe9, 0x21, 0x0e, 0x77, 0xf0, 0x58, 0xaf, 0x48, + 0x45, 0x95, 0x24, 0x74, 0x29, 0xe7, 0x4f, 0xe8, 0x08, 0xf5, 0x45, 0xc9, 0x4d, 0xb6, 0x64, 0x1d, + 0x6a, 0x2e, 0x1d, 0x61, 0xc0, 0xa9, 0x8d, 0x7a, 0x55, 0xf2, 0x32, 0x02, 0xf9, 0x16, 0xd6, 0x94, + 0xc0, 0x0f, 0xbc, 0xb1, 0x6f, 0xa3, 0x0e, 0xf2, 0xe8, 0x7b, 0xb3, 0x1d, 0xbd, 0x5b, 0x34, 0x6b, + 0x4d, 0x7b, 0x22, 0x5f, 0xc1, 0xa2, 0x2c, 0x1a, 0xbd, 0xbe, 0xb1, 0xf0, 0x5a, 0xb3, 0x1d, 0x99, + 0x25, 0x2e, 0x2c, 0xf3, 0xe1, 0xd8, 0x61, 0x6e, 0xa0, 0x9f, 0x93, 0x1e, 0x9e, 0xce, 0xe6, 0x61, + 0xcb, 0x73, 0x8f, 0x98, 0xb3, 0x4b, 0x5d, 0xea, 0xe0, 0x08, 0xdd, 0x70, 0x5f, 0x1a, 0xb7, 0x12, + 0x27, 0xe4, 0x25, 0x34, 0x06, 0xe3, 0x20, 0xf4, 0x46, 0xec, 0x25, 0xee, 0x71, 0x59, 0xdc, 0xfa, + 0x79, 0x99, 0xcd, 0x27, 0xb3, 0x39, 0xde, 0x29, 0x58, 0xb5, 0xa6, 0xfc, 0x88, 0x22, 0x19, 0x8c, + 0x0f, 0xf1, 0x73, 0xf4, 0x65, 0x75, 0xad, 0x44, 0x45, 0xa2, 0x90, 0xa2, 0x32, 0x62, 0xf1, 0x2e, + 0xd0, 0x57, 0x37, 0x16, 0xa2, 0x32, 0x4a, 0x49, 0x64, 0x13, 0x56, 0x27, 0xe8, 0xb3, 0xa3, 0xe3, + 0x03, 0xe6, 0xb8, 0x34, 0x1c, 0xfb, 0xa8, 0x37, 0x64, 0x29, 0x16, 0xc9, 0x64, 0x04, 0xe7, 0xfb, + 0x38, 0x1c, 0x89, 0x94, 0x6f, 0xf9, 0xd8, 0x0b, 0xf4, 0x35, 0x99, 0xdf, 0xed, 0xd9, 0x6f, 0x50, + 0x9a, 0xb3, 0xf2, 0xd6, 0x45, 0x60, 0xae, 0x67, 0xc5, 0x2f, 0x25, 0x7a, 0x23, 0x24, 0x0a, 0xac, + 0x40, 0x26, 0xd7, 0x60, 0x25, 0xf4, 0xa9, 0x3d, 0x60, 0xae, 0xb3, 0x8b, 0x61, 0xdf, 0xeb, 0xe9, + 0x17, 0x64, 0x26, 0x0a, 0x54, 0x63, 0x0c, 0x97, 0x9e, 0xca, 0x67, 0x9f, 0xd6, 0xcc, 0x59, 0x00, + 0x80, 0xf1, 0x08, 0x2e, 0x17, 0xdd, 0x06, 0xdc, 0x73, 0x03, 0x24, 0x26, 0x10, 0x99, 0x64, 0x86, + 0xbd, 0x8c, 0x2b, 0xa3, 0xa8, 0x5a, 0x25, 0x1c, 0xe3, 0x37, 0x0d, 0x1a, 0x19, 0x78, 0xc5, 0x46, + 0xd6, 0xa1, 0x36, 0x8a, 0x69, 0x81, 0xae, 0xc9, 0x0b, 0xce, 0x08, 0x79, 0x2c, 0x98, 0x2f, 0x62, + 0xc1, 0x65, 0x58, 0x8a, 0x50, 0x5e, 0xc2, 0x4f, 0xcd, 0x8a, 0x77, 0x39, 0xcc, 0xaa, 0x14, 0x30, + 0xab, 0x05, 0x10, 0xc8, 0xa7, 0xfc, 0xf4, 0x98, 0xa3, 0xbe, 0x24, 0xb9, 0x0a, 0x85, 0x18, 0x70, + 0x2e, 0xaa, 0x1c, 0x0b, 0x83, 0xf1, 0x30, 0xd4, 0x97, 0xa5, 0x44, 0x8e, 0x66, 0x78, 0xb0, 0xfa, + 0x98, 0x89, 0x33, 0x1c, 0x05, 0x67, 0x73, 0x07, 0x1f, 0x42, 0x45, 0x38, 0x13, 0x07, 0x3b, 0xf4, + 0xa9, 0x6b, 0xf7, 0x31, 0xc9, 0x55, 0xba, 0x27, 0x04, 0x2a, 0x21, 0x75, 0x02, 0x7d, 0x5e, 0xd2, + 0xe5, 0xda, 0xf8, 0x41, 0x8b, 0x22, 0xed, 0x72, 0x1e, 0xbc, 0xf5, 0x76, 0x61, 0x8c, 0x61, 0xb9, + 0xcb, 0xb9, 0x88, 0x87, 0xdc, 0x84, 0x0a, 0xe5, 0x3c, 0x3a, 0x44, 0xbd, 0x73, 0xc5, 0x54, 0x5a, + 0x73, 0x2c, 0x22, 0xfe, 0x07, 0x0f, 0xdc, 0x50, 0x58, 0x16, 0xa2, 0xcd, 0x8f, 0xa0, 0x96, 0x92, + 0x48, 0x03, 0x16, 0x06, 0x18, 0xd5, 0x5a, 0xcd, 0x12, 0x4b, 0x72, 0x11, 0x16, 0x27, 0x74, 0x38, + 0x4e, 0xaa, 0x24, 0xda, 0xdc, 0x9d, 0xbf, 0xa3, 0x19, 0xbf, 0x54, 0xe0, 0xff, 0x22, 0xce, 0x03, + 0x59, 0x1c, 0x5d, 0xce, 0xef, 0x63, 0x48, 0xd9, 0x30, 0xf8, 0x74, 0x8c, 0xfe, 0xf1, 0x1b, 0x4e, + 0x87, 0x03, 0x4b, 0x51, 0x6d, 0xc9, 0xb0, 0xde, 0x40, 0x8b, 0x8a, 0xcd, 0x67, 0x7d, 0x69, 0xe1, + 0xcd, 0xf4, 0xa5, 0xb2, 0x3e, 0x51, 0x39, 0xa3, 0x3e, 0x71, 0xf2, 0xa8, 0xa0, 0x0c, 0x20, 0x4b, + 0xf9, 0x01, 0xa4, 0x04, 0x7e, 0x97, 0xff, 0x2b, 0xfc, 0x56, 0x4b, 0xe1, 0xf7, 0xfb, 0x79, 0xb8, + 0x2c, 0xf2, 0x92, 0x15, 0x50, 0x8a, 0x61, 0xe2, 0xe9, 0x09, 0x34, 0x89, 0xca, 0x51, 0xae, 0xc9, + 0x6d, 0x58, 0x1e, 0x04, 0x9e, 0xeb, 0x62, 0x18, 0x5f, 0x7d, 0x53, 0x2d, 0xf2, 0x9d, 0x88, 0xd5, + 0xe5, 0xfc, 0x80, 0xa3, 0x6d, 0x25, 0xa2, 0xe4, 0x3a, 0x54, 0x44, 0x1b, 0x91, 0x78, 0x56, 0xef, + 0xfc, 0x4f, 0x55, 0x79, 0x84, 0xc3, 0x51, 0x22, 0x2f, 0x85, 0xc8, 0x5d, 0xa8, 0xa5, 0xb9, 0x8a, + 0x2f, 0x63, 0x3d, 0xe7, 0x24, 0x61, 0x26, 0x6a, 0x99, 0xb8, 0xd0, 0xed, 0x31, 0x1f, 0x6d, 0x09, + 0xd9, 0x8b, 0xd3, 0xba, 0xf7, 0x13, 0x66, 0xaa, 0x9b, 0x8a, 0x1b, 0xbf, 0x6a, 0x70, 0x35, 0x7b, + 0x50, 0x49, 0x36, 0x77, 0x31, 0xa4, 0x3d, 0x1a, 0xd2, 0xb7, 0x3f, 0x96, 0x5e, 0x83, 0x15, 0xbb, + 0x8f, 0xf6, 0x20, 0x1b, 0x09, 0xa2, 0xe9, 0xb4, 0x40, 0x35, 0x7e, 0x9f, 0x87, 0x95, 0xfc, 0x45, + 0x88, 0x9b, 0x14, 0xed, 0x25, 0xb9, 0x49, 0xb1, 0x26, 0xfb, 0x70, 0x0e, 0xdd, 0x09, 0xf3, 0x3d, + 0x57, 0x0c, 0x50, 0xc9, 0x0b, 0xfb, 0xe0, 0xe4, 0xeb, 0x34, 0x1f, 0x28, 0xe2, 0x11, 0x84, 0xe5, + 0x2c, 0x10, 0x17, 0x80, 0x53, 0x9f, 0x8e, 0x30, 0x44, 0x5f, 0x3c, 0xa3, 0x85, 0xd7, 0xf0, 0x8c, + 0xa2, 0x08, 0xf6, 0x13, 0xb3, 0x96, 0xe2, 0xa1, 0xf9, 0x1c, 0xd6, 0xa6, 0x42, 0x2a, 0x81, 0xd0, + 0xdb, 0x2a, 0x84, 0xd6, 0x3b, 0xad, 0x92, 0x13, 0x2a, 0x66, 0x54, 0x88, 0xfd, 0x79, 0x1e, 0xea, + 0x4a, 0x7d, 0x96, 0xa6, 0xb1, 0x05, 0x20, 0x15, 0x1e, 0xb2, 0x21, 0x46, 0x49, 0xac, 0x59, 0x0a, + 0x85, 0x0c, 0x4a, 0x92, 0xb2, 0x33, 0x5b, 0x52, 0x44, 0x48, 0xa5, 0x19, 0x11, 0x93, 0x83, 0x74, + 0x1d, 0xc4, 0x88, 0x12, 0xef, 0xc8, 0x37, 0xb0, 0x72, 0xc4, 0x86, 0xb8, 0x9f, 0x05, 0xb2, 0x24, + 0x03, 0xd9, 0x9b, 0x3d, 0x90, 0x87, 0xaa, 0x5d, 0xab, 0xe0, 0xc6, 0x78, 0x1f, 0x1a, 0xc5, 0xe7, + 0x2a, 0x82, 0x64, 0x23, 0xea, 0xa4, 0xd9, 0x8a, 0x77, 0xc6, 0x8f, 0x1a, 0x90, 0xe9, 0xfb, 0x38, + 0x29, 0xe9, 0x83, 0x3b, 0x41, 0x32, 0x61, 0x47, 0x0f, 0x45, 0xa1, 0x90, 0x1d, 0xa8, 0xf7, 0x30, + 0x08, 0x99, 0x2b, 0x03, 0x8e, 0x41, 0xe4, 0xbd, 0xd3, 0x2f, 0xfe, 0x7e, 0xa6, 0x60, 0xa9, 0xda, + 0xc6, 0x67, 0x70, 0xe5, 0x54, 0x69, 0x65, 0x5e, 0xd3, 0x72, 0xf3, 0xda, 0xa9, 0x53, 0x9e, 0x41, + 0xa0, 0x51, 0x44, 0x23, 0xe3, 0x05, 0xac, 0x89, 0x9c, 0x6e, 0xf5, 0xa9, 0x1f, 0x9e, 0xd1, 0x0c, + 0x76, 0x0f, 0x6a, 0xa9, 0xcb, 0xd2, 0x5c, 0x37, 0xa1, 0x3a, 0x49, 0x7e, 0xa9, 0x44, 0x43, 0x58, + 0xba, 0x37, 0xba, 0x40, 0xd4, 0x78, 0xe3, 0xbe, 0x71, 0x1d, 0x16, 0x59, 0x88, 0xa3, 0x64, 0x0c, + 0xba, 0x54, 0x84, 0x7b, 0x29, 0x6e, 0x45, 0x32, 0x9d, 0xef, 0x16, 0x61, 0x2d, 0x43, 0x5d, 0xf1, + 0x97, 0xd9, 0x48, 0xf6, 0xa0, 0xb1, 0x1d, 0x7f, 0x23, 0x48, 0x46, 0x6b, 0xf2, 0x8e, 0x6a, 0xa7, + 0xf0, 0xb5, 0xa0, 0xb9, 0x5e, 0xce, 0x8c, 0x22, 0x32, 0xe6, 0xc8, 0x17, 0xb0, 0x92, 0x1f, 0xf7, + 0xc9, 0x55, 0x55, 0xa3, 0xf4, 0x17, 0x48, 0xd3, 0x38, 0x4d, 0x24, 0x35, 0x7d, 0x0f, 0xaa, 0xc9, + 0xd8, 0x9c, 0x8f, 0xb1, 0x30, 0x4c, 0x37, 0x1b, 0x2a, 0x53, 0x30, 0x8c, 0x39, 0xf2, 0x71, 0xa4, + 0x2c, 0x46, 0xc0, 0x69, 0x65, 0x65, 0xbe, 0x6d, 0x5e, 0x28, 0x19, 0x26, 0x8d, 0x39, 0xf2, 0x0c, + 0xce, 0x6f, 0x4b, 0x84, 0x8e, 0x9b, 0x37, 0x79, 0x37, 0xef, 0xe4, 0x84, 0xf9, 0x30, 0x7f, 0xb4, + 0xf2, 0xfe, 0x6f, 0xcc, 0x91, 0x9f, 0x34, 0xb8, 0xb0, 0x8d, 0x61, 0xb1, 0x17, 0x92, 0x1b, 0xe5, + 0x4e, 0x4e, 0xe8, 0x99, 0xcd, 0x27, 0xb3, 0xd6, 0x6c, 0xde, 0xac, 0x31, 0x47, 0xf6, 0xe5, 0xb1, + 0xb3, 0xda, 0x23, 0x57, 0x4a, 0x8b, 0x2c, 0xcd, 0x5e, 0xeb, 0x24, 0x76, 0x72, 0xd4, 0x4f, 0xba, + 0x7f, 0xbc, 0x6a, 0x69, 0x7f, 0xbe, 0x6a, 0x69, 0x7f, 0xbd, 0x6a, 0x69, 0x5f, 0xde, 0xfa, 0x97, + 0xcf, 0x5b, 0xca, 0x97, 0x38, 0xca, 0x99, 0x3d, 0x64, 0xe8, 0x86, 0x87, 0x4b, 0xf2, 0x63, 0xd6, + 0xad, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x25, 0x4e, 0x84, 0xa1, 0xa8, 0x13, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1783,6 +1809,15 @@ func (m *ManifestRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + 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] = 0x1 + i-- + dAtA[i] = 0x9a + } if m.NoRevisionCache { i-- if m.NoRevisionCache { @@ -2291,6 +2326,23 @@ func (m *RepoServerAppDetailsQuery) MarshalToSizedBuffer(dAtA []byte) (int, erro i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + 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] = 0x42 + } + if m.NoRevisionCache { + i-- + if m.NoRevisionCache { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } if m.NoCache { i-- if m.NoCache { @@ -3013,6 +3065,10 @@ func (m *ManifestRequest) Size() (n int) { if m.NoRevisionCache { n += 3 } + l = len(m.TrackingMethod) + if l > 0 { + n += 2 + l + sovRepository(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -3199,6 +3255,13 @@ func (m *RepoServerAppDetailsQuery) Size() (n int) { if m.NoCache { n += 2 } + if m.NoRevisionCache { + n += 2 + } + l = len(m.TrackingMethod) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -3960,6 +4023,38 @@ func (m *ManifestRequest) Unmarshal(dAtA []byte) error { } } m.NoRevisionCache = bool(v != 0) + case 19: + 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 default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) @@ -5105,6 +5200,58 @@ func (m *RepoServerAppDetailsQuery) Unmarshal(dAtA []byte) error { } } m.NoCache = bool(v != 0) + case 7: + 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) + case 8: + 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 default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) diff --git a/reposerver/cache/cache.go b/reposerver/cache/cache.go index 9c9f1c3f37448..265d9a0aabab6 100644 --- a/reposerver/cache/cache.go +++ b/reposerver/cache/cache.go @@ -10,6 +10,8 @@ import ( "strings" "time" + "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/go-git/go-git/v5/plumbing" "github.com/go-redis/redis/v8" log "github.com/sirupsen/logrus" @@ -46,8 +48,8 @@ func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) var repoCacheExpiration time.Duration var revisionCacheExpiration time.Duration - cmd.Flags().DurationVar(&repoCacheExpiration, "repo-cache-expiration", env.ParseDurationFromEnv("ARGOCD_REPO_CACHE_EXPIRATION", 24*time.Hour, 0, math.MaxInt32), "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.MaxInt32), "Cache expiration for cached revision") + 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") repoFactory := cacheutil.AddCacheFlagsToCmd(cmd, opts...) @@ -152,9 +154,9 @@ func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, c return fmt.Errorf("Unable to generate hash value: %s", err) } - // If the expected hash of the cache entry does not match the actual hash value... - if hash != res.CacheEntryHash { - log.Warnf("Manifest hash did not match expected value, treating as a cache miss: %s", appName) + // If cached result does not have manifests or the expected hash of the cache entry does not match the actual hash value... + if hash != res.CacheEntryHash || res.ManifestResponse == nil && res.MostRecentError == "" { + log.Warnf("Manifest hash did not match expected value or cached manifests response is empty, treating as a cache miss: %s", appName) err = c.DeleteManifests(revision, appSrc, clusterInfo, namespace, appLabelKey, appName) if err != nil { @@ -190,16 +192,19 @@ func (c *Cache) DeleteManifests(revision string, appSrc *appv1.ApplicationSource return c.cache.SetItem(manifestCacheKey(revision, appSrc, namespace, appLabelKey, appName, clusterInfo), "", c.repoCacheExpiration, true) } -func appDetailsCacheKey(revision string, appSrc *appv1.ApplicationSource) string { - return fmt.Sprintf("appdetails|%s|%d", revision, appSourceKey(appSrc)) +func appDetailsCacheKey(revision string, appSrc *appv1.ApplicationSource, trackingMethod appv1.TrackingMethod) string { + if trackingMethod == "" { + trackingMethod = argo.TrackingMethodLabel + } + return fmt.Sprintf("appdetails|%s|%d|%s", revision, appSourceKey(appSrc), trackingMethod) } -func (c *Cache) GetAppDetails(revision string, appSrc *appv1.ApplicationSource, res *apiclient.RepoAppDetailsResponse) error { - return c.cache.GetItem(appDetailsCacheKey(revision, appSrc), res) +func (c *Cache) GetAppDetails(revision string, appSrc *appv1.ApplicationSource, res *apiclient.RepoAppDetailsResponse, trackingMethod appv1.TrackingMethod) error { + return c.cache.GetItem(appDetailsCacheKey(revision, appSrc, trackingMethod), res) } -func (c *Cache) SetAppDetails(revision string, appSrc *appv1.ApplicationSource, res *apiclient.RepoAppDetailsResponse) error { - return c.cache.SetItem(appDetailsCacheKey(revision, appSrc), res, c.repoCacheExpiration, res == nil) +func (c *Cache) SetAppDetails(revision string, appSrc *appv1.ApplicationSource, res *apiclient.RepoAppDetailsResponse, trackingMethod appv1.TrackingMethod) error { + return c.cache.SetItem(appDetailsCacheKey(revision, appSrc, trackingMethod), res, c.repoCacheExpiration, res == nil) } func revisionMetadataKey(repoURL, revision string) string { diff --git a/reposerver/cache/cache_test.go b/reposerver/cache/cache_test.go index b7cfef0ea67cf..46841c00a229c 100644 --- a/reposerver/cache/cache_test.go +++ b/reposerver/cache/cache_test.go @@ -104,19 +104,19 @@ func TestCache_GetAppDetails(t *testing.T) { cache := newFixtures().Cache // cache miss value := &apiclient.RepoAppDetailsResponse{} - err := cache.GetAppDetails("my-revision", &ApplicationSource{}, value) + err := cache.GetAppDetails("my-revision", &ApplicationSource{}, value, "") assert.Equal(t, ErrCacheMiss, err) res := &apiclient.RepoAppDetailsResponse{Type: "my-type"} - err = cache.SetAppDetails("my-revision", &ApplicationSource{}, res) + err = cache.SetAppDetails("my-revision", &ApplicationSource{}, res, "") assert.NoError(t, err) //cache miss - err = cache.GetAppDetails("other-revision", &ApplicationSource{}, value) + err = cache.GetAppDetails("other-revision", &ApplicationSource{}, value, "") assert.Equal(t, ErrCacheMiss, err) //cache miss - err = cache.GetAppDetails("my-revision", &ApplicationSource{Path: "other-path"}, value) + err = cache.GetAppDetails("my-revision", &ApplicationSource{Path: "other-path"}, value, "") assert.Equal(t, ErrCacheMiss, err) // cache hit - err = cache.GetAppDetails("my-revision", &ApplicationSource{}, value) + err = cache.GetAppDetails("my-revision", &ApplicationSource{}, value, "") assert.NoError(t, err) assert.Equal(t, &apiclient.RepoAppDetailsResponse{Type: "my-type"}, value) } diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index 6f84981dd2949..a6b40f06c5a6f 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -16,6 +16,8 @@ import ( "strings" "time" + "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/Masterminds/semver" "github.com/TomOnTime/utfutil" "github.com/argoproj/gitops-engine/pkg/utils/kube" @@ -47,7 +49,6 @@ import ( "github.com/argoproj/argo-cd/v2/util/helm" "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/ksonnet" - argokube "github.com/argoproj/argo-cd/v2/util/kube" "github.com/argoproj/argo-cd/v2/util/kustomize" "github.com/argoproj/argo-cd/v2/util/security" "github.com/argoproj/argo-cd/v2/util/text" @@ -68,6 +69,7 @@ type Service struct { cache *reposervercache.Cache parallelismLimitSemaphore *semaphore.Weighted metricsServer *metrics.MetricsServer + resourceTracking argo.ResourceTracking newGitClient func(rawRepoURL 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 @@ -83,7 +85,7 @@ type RepoServerInitConstants struct { } // NewService returns a new instance of the Manifest service -func NewService(metricsServer *metrics.MetricsServer, cache *reposervercache.Cache, initConstants RepoServerInitConstants) *Service { +func NewService(metricsServer *metrics.MetricsServer, cache *reposervercache.Cache, initConstants RepoServerInitConstants, resourceTracking argo.ResourceTracking) *Service { var parallelismLimitSemaphore *semaphore.Weighted if initConstants.ParallelismLimit > 0 { parallelismLimitSemaphore = semaphore.NewWeighted(initConstants.ParallelismLimit) @@ -95,6 +97,7 @@ func NewService(metricsServer *metrics.MetricsServer, cache *reposervercache.Cac cache: cache, metricsServer: metricsServer, newGitClient: git.NewClient, + resourceTracking: resourceTracking, 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...) }, @@ -490,7 +493,7 @@ func getHelmDependencyRepos(appPath string) ([]*v1alpha1.Repository, error) { if u, err := url.Parse(r.Repository); err == nil && (u.Scheme == "https" || u.Scheme == "oci") { repo := &v1alpha1.Repository{ Repo: r.Repository, - Name: u.Host, + Name: r.Repository, } repos = append(repos, repo) } @@ -712,6 +715,7 @@ func GenerateManifests(appPath, repoRoot, revision string, q *apiclient.Manifest var targetObjs []*unstructured.Unstructured var dest *v1alpha1.ApplicationDestination + resourceTracking := argo.NewResourceTracking() appSourceType, err := GetAppSourceType(q.ApplicationSource, appPath, q.AppName) if err != nil { return nil, err @@ -770,7 +774,7 @@ func GenerateManifests(appPath, repoRoot, revision string, q *apiclient.Manifest for _, target := range targets { if q.AppLabelKey != "" && q.AppName != "" && !kube.IsCRD(target) { - err = argokube.SetAppInstanceLabel(target, q.AppLabelKey, q.AppName) + err = resourceTracking.SetAppInstance(target, q.AppLabelKey, q.AppName, q.Namespace, v1alpha1.TrackingMethod(q.TrackingMethod)) if err != nil { return nil, err } @@ -1175,11 +1179,11 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD return err } } - _ = s.cache.SetAppDetails(revision, q.Source, res) + _ = s.cache.SetAppDetails(revision, q.Source, res, v1alpha1.TrackingMethod(q.TrackingMethod)) return nil } - settings := operationSettings{allowConcurrent: q.Source.AllowsConcurrentProcessing(), noCache: q.NoCache, noRevisionCache: q.NoCache} + 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) return res, err @@ -1187,7 +1191,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD func (s *Service) createGetAppDetailsCacheHandler(res *apiclient.RepoAppDetailsResponse, q *apiclient.RepoServerAppDetailsQuery) func(revision string, _ bool) (bool, error) { return func(revision string, _ bool) (bool, error) { - err := s.cache.GetAppDetails(revision, q.Source, res) + err := s.cache.GetAppDetails(revision, q.Source, res, v1alpha1.TrackingMethod(q.TrackingMethod)) if err == nil { log.Infof("app details cache hit: %s/%s", revision, q.Source.Path) return true, nil @@ -1230,14 +1234,18 @@ func populateKsonnetAppDetails(res *apiclient.RepoAppDetailsResponse, appPath st } func populateHelmAppDetails(res *apiclient.RepoAppDetailsResponse, appPath string, q *apiclient.RepoServerAppDetailsQuery) error { - var valueFiles []string + var selectedValueFiles []string + + if q.Source.Helm != nil { + selectedValueFiles = q.Source.Helm.ValueFiles + } - valueFiles, err := findHelmValueFilesInPath(appPath) + availableValueFiles, err := findHelmValueFilesInPath(appPath) if err != nil { return err } - res.Helm = &apiclient.HelmAppSpec{ValueFiles: valueFiles} + res.Helm = &apiclient.HelmAppSpec{ValueFiles: availableValueFiles} var version string if q.Source.Helm != nil { if q.Source.Helm.Version != "" { @@ -1257,7 +1265,7 @@ func populateHelmAppDetails(res *apiclient.RepoAppDetailsResponse, appPath strin if err := loadFileIntoIfExists(filepath.Join(appPath, "values.yaml"), &res.Helm.Values); err != nil { return err } - params, err := h.GetParameters(valueFiles) + params, err := h.GetParameters(selectedValueFiles) if err != nil { return err } diff --git a/reposerver/repository/repository.proto b/reposerver/repository/repository.proto index ff98ccb68d05b..5d4724bb78c8e 100644 --- a/reposerver/repository/repository.proto +++ b/reposerver/repository/repository.proto @@ -28,6 +28,7 @@ message ManifestRequest { bool verifySignature = 16; repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.RepoCreds helmRepoCreds = 17; bool noRevisionCache = 18; + string trackingMethod = 19; } // TestRepositoryRequest is a query to test repository is valid or not and has valid access. @@ -81,6 +82,8 @@ message RepoServerAppDetailsQuery { github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KustomizeOptions kustomizeOptions = 4; string appName = 5; bool noCache = 6; + bool noRevisionCache = 7; + string trackingMethod = 8; } // RepoAppDetailsResponse application details diff --git a/reposerver/repository/repository_test.go b/reposerver/repository/repository_test.go index 7994856412d6f..6b70b9aa0eb23 100644 --- a/reposerver/repository/repository_test.go +++ b/reposerver/repository/repository_test.go @@ -14,6 +14,8 @@ import ( "testing" "time" + "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -70,7 +72,7 @@ func newServiceWithOpt(cf clientFunc) (*Service, *gitmocks.Client) { cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Minute)), 1*time.Minute, 1*time.Minute, - ), RepoServerInitConstants{ParallelismLimit: 1}) + ), RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking()) chart := "my-chart" version := "1.1.0" @@ -168,6 +170,22 @@ func TestGenerateManifests_K8SAPIResetCache(t *testing.T) { assert.True(t, len(res.Manifests) > 1) } +func TestGenerateManifests_EmptyCache(t *testing.T) { + service := newService("../..") + + src := argoappv1.ApplicationSource{Path: "manifests/base"} + q := apiclient.ManifestRequest{ + Repo: &argoappv1.Repository{}, ApplicationSource: &src, + } + + err := service.cache.SetManifests(mock.Anything, &src, &q, "", "", "", &cache.CachedManifestResponse{ManifestResponse: nil}) + assert.NoError(t, err) + + res, err := service.GenerateManifest(context.Background(), &q) + assert.NoError(t, err) + assert.True(t, len(res.Manifests) > 0) +} + // ensure we can use a semver constraint range (>= 1.0.0) and get back the correct chart (1.0.0) func TestHelmManifestFromChartRepo(t *testing.T) { service := newService(".") diff --git a/reposerver/server.go b/reposerver/server.go index a0a206efe0706..ecc86ca95de65 100644 --- a/reposerver/server.go +++ b/reposerver/server.go @@ -5,6 +5,8 @@ import ( "fmt" "os" + "github.com/argoproj/argo-cd/v2/util/argo" + grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" @@ -92,7 +94,7 @@ func (a *ArgoCDRepoServer) CreateGRPC() *grpc.Server { versionpkg.RegisterVersionServiceServer(server, version.NewServer(nil, func() (bool, error) { return true, nil })) - manifestService := repository.NewService(a.metricsServer, a.cache, a.initConstants) + manifestService := repository.NewService(a.metricsServer, a.cache, a.initConstants, argo.NewResourceTracking()) apiclient.RegisterRepoServerServiceServer(server, manifestService) healthService := health.NewServer() diff --git a/resource_customizations/apps.openshift.io/DeploymentConfig/health.lua b/resource_customizations/apps.openshift.io/DeploymentConfig/health.lua new file mode 100644 index 0000000000000..4fdc157dc5247 --- /dev/null +++ b/resource_customizations/apps.openshift.io/DeploymentConfig/health.lua @@ -0,0 +1,27 @@ +health_check = {} +if obj.status ~= nil then + if obj.status.conditions ~= nil then + numTrue = 0 + for i, condition in pairs(obj.status.conditions) do + if (condition.type == "Available" or condition.type == "Progressing") and condition.status == "True" then + numTrue = numTrue + 1 + end + end + if numTrue == 2 then + health_check.status = "Healthy" + health_check.message = "replication controller successfully rolled out" + return health_check + elseif numTrue == 1 then + health_check.status = "Progressing" + health_check.message = "replication controller is waiting for pods to run" + return health_check + else + health_check.status = "Degraded" + health_check.message = "Deployment config is degraded" + return health_check + end + end +end +health_check.status = "Progressing" +health_check.message = "replication controller is waiting for pods to run" +return health_check \ No newline at end of file diff --git a/resource_customizations/apps.openshift.io/DeploymentConfig/health_test.yaml b/resource_customizations/apps.openshift.io/DeploymentConfig/health_test.yaml new file mode 100644 index 0000000000000..1df01ee61bd39 --- /dev/null +++ b/resource_customizations/apps.openshift.io/DeploymentConfig/health_test.yaml @@ -0,0 +1,13 @@ +tests: +- healthStatus: + status: Progressing + message: "replication controller is waiting for pods to run" + inputPath: testdata/progressing.yaml +- healthStatus: + status: Degraded + message: "Deployment config is degraded" + inputPath: testdata/degraded.yaml +- healthStatus: + status: Healthy + message: "replication controller successfully rolled out" + inputPath: testdata/healthy.yaml \ No newline at end of file diff --git a/resource_customizations/apps.openshift.io/DeploymentConfig/testdata/degraded.yaml b/resource_customizations/apps.openshift.io/DeploymentConfig/testdata/degraded.yaml new file mode 100644 index 0000000000000..32e89d872f658 --- /dev/null +++ b/resource_customizations/apps.openshift.io/DeploymentConfig/testdata/degraded.yaml @@ -0,0 +1,156 @@ +kind: DeploymentConfig +apiVersion: apps.openshift.io/v1 +metadata: + name: example + namespace: default + uid: ba99eac7-ed9c-4154-bda1-69d25a90b278 + resourceVersion: '412743' + generation: 1 + creationTimestamp: '2021-08-25T23:48:11Z' + managedFields: + - manager: Mozilla + operation: Update + apiVersion: apps.openshift.io/v1 + time: '2021-08-25T23:48:11Z' + fieldsType: FieldsV1 + fieldsV1: + 'f:spec': + 'f:replicas': {} + 'f:selector': + .: {} + 'f:app': {} + 'f:strategy': + 'f:activeDeadlineSeconds': {} + 'f:rollingParams': + .: {} + 'f:intervalSeconds': {} + 'f:maxSurge': {} + 'f:maxUnavailable': {} + 'f:timeoutSeconds': {} + 'f:updatePeriodSeconds': {} + 'f:type': {} + 'f:template': + .: {} + 'f:metadata': + .: {} + 'f:creationTimestamp': {} + 'f:labels': + .: {} + 'f:app': {} + 'f:spec': + .: {} + 'f:containers': + .: {} + 'k:{"name":"httpd"}': + .: {} + 'f:image': {} + 'f:imagePullPolicy': {} + 'f:name': {} + 'f:ports': + .: {} + 'k:{"containerPort":8080,"protocol":"TCP"}': + .: {} + 'f:containerPort': {} + 'f:protocol': {} + 'f:resources': {} + 'f:terminationMessagePath': {} + 'f:terminationMessagePolicy': {} + 'f:dnsPolicy': {} + 'f:restartPolicy': {} + 'f:schedulerName': {} + 'f:securityContext': {} + 'f:terminationGracePeriodSeconds': {} + 'f:triggers': {} + - manager: openshift-controller-manager + operation: Update + apiVersion: apps.openshift.io/v1 + time: '2021-08-25T23:48:11Z' + fieldsType: FieldsV1 + fieldsV1: + 'f:status': + 'f:conditions': + .: {} + 'k:{"type":"Available"}': + .: {} + 'f:lastTransitionTime': {} + 'f:lastUpdateTime': {} + 'f:message': {} + 'f:status': {} + 'f:type': {} + 'k:{"type":"Progressing"}': + .: {} + 'f:lastTransitionTime': {} + 'f:lastUpdateTime': {} + 'f:message': {} + 'f:status': {} + 'f:type': {} + 'f:details': + .: {} + 'f:causes': {} + 'f:message': {} + 'f:latestVersion': {} + 'f:observedGeneration': {} +spec: + strategy: + type: Rolling + rollingParams: + updatePeriodSeconds: 1 + intervalSeconds: 1 + timeoutSeconds: 600 + maxUnavailable: 25% + maxSurge: 25% + resources: {} + activeDeadlineSeconds: 21600 + triggers: + - type: ConfigChange + replicas: 3 + revisionHistoryLimit: 10 + test: false + selector: + app: httpd + template: + metadata: + creationTimestamp: null + labels: + app: httpd + spec: + containers: + - name: httpd + image: >- + image-registry.openshift-image-registry.svc:5000/openshift/httpd:latest + ports: + - containerPort: 8080 + protocol: TCP + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + imagePullPolicy: Always + restartPolicy: Always + terminationGracePeriodSeconds: 30 + dnsPolicy: ClusterFirst + securityContext: {} + schedulerName: default-scheduler +status: + latestVersion: 1 + observedGeneration: 1 + replicas: 0 + updatedReplicas: 0 + availableReplicas: 0 + unavailableReplicas: 0 + details: + message: config change + causes: + - type: ConfigChange + conditions: + - type: Available + status: 'False' + lastUpdateTime: '2021-08-25T23:48:11Z' + lastTransitionTime: '2021-08-25T23:48:11Z' + message: Deployment config does not have minimum availability. + - type: Progressing + status: Unknown + lastUpdateTime: '2021-08-25T23:48:11Z' + lastTransitionTime: '2021-08-25T23:48:11Z' + message: >- + replication controller "example-1" is waiting for pod "example-1-deploy" + to run \ No newline at end of file diff --git a/resource_customizations/apps.openshift.io/DeploymentConfig/testdata/healthy.yaml b/resource_customizations/apps.openshift.io/DeploymentConfig/testdata/healthy.yaml new file mode 100644 index 0000000000000..266f655349c26 --- /dev/null +++ b/resource_customizations/apps.openshift.io/DeploymentConfig/testdata/healthy.yaml @@ -0,0 +1,66 @@ +kind: DeploymentConfig +apiVersion: apps.openshift.io/v1 +metadata: + name: example + namespace: default +spec: + strategy: + type: Rolling + rollingParams: + updatePeriodSeconds: 1 + intervalSeconds: 1 + timeoutSeconds: 600 + maxUnavailable: 25% + maxSurge: 25% + resources: {} + activeDeadlineSeconds: 21600 + triggers: + - type: ConfigChange + replicas: 3 + revisionHistoryLimit: 10 + test: false + selector: + app: httpd + template: + metadata: + creationTimestamp: null + labels: + app: httpd + spec: + containers: + - name: httpd + image: >- + image-registry.openshift-image-registry.svc:5000/openshift/httpd:latest + ports: + - containerPort: 8080 + protocol: TCP + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + imagePullPolicy: Always + restartPolicy: Always + terminationGracePeriodSeconds: 30 + dnsPolicy: ClusterFirst + securityContext: {} + schedulerName: default-scheduler +status: + observedGeneration: 1 + details: + message: config change + causes: + - type: ConfigChange + availableReplicas: 3 + conditions: + - type: Available + status: 'True' + lastUpdateTime: '2021-08-25T23:48:29Z' + lastTransitionTime: '2021-08-25T23:48:29Z' + message: Deployment config has minimum availability. + - type: Progressing + status: 'True' + lastUpdateTime: '2021-08-25T23:48:29Z' + lastTransitionTime: '2021-08-25T23:48:15Z' + reason: NewReplicationControllerAvailable + message: replication controller "example-1" successfully rolled out + replicas: 3 + readyReplicas: 3 \ No newline at end of file diff --git a/resource_customizations/apps.openshift.io/DeploymentConfig/testdata/progressing.yaml b/resource_customizations/apps.openshift.io/DeploymentConfig/testdata/progressing.yaml new file mode 100644 index 0000000000000..6f832578501b7 --- /dev/null +++ b/resource_customizations/apps.openshift.io/DeploymentConfig/testdata/progressing.yaml @@ -0,0 +1,163 @@ +kind: DeploymentConfig +apiVersion: apps.openshift.io/v1 +metadata: + name: example + namespace: default + uid: 365e7b5a-a72e-4fdf-be9d-cd5590d956bb + resourceVersion: '88832' + generation: 1 + creationTimestamp: '2021-08-26T04:37:06Z' + managedFields: + - manager: Mozilla + operation: Update + apiVersion: apps.openshift.io/v1 + time: '2021-08-26T04:37:06Z' + fieldsType: FieldsV1 + fieldsV1: + 'f:spec': + 'f:replicas': {} + 'f:revisionHistoryLimit': {} + 'f:selector': + .: {} + 'f:app': {} + 'f:strategy': + 'f:activeDeadlineSeconds': {} + 'f:rollingParams': + .: {} + 'f:intervalSeconds': {} + 'f:maxSurge': {} + 'f:maxUnavailable': {} + 'f:timeoutSeconds': {} + 'f:updatePeriodSeconds': {} + 'f:type': {} + 'f:template': + .: {} + 'f:metadata': + .: {} + 'f:creationTimestamp': {} + 'f:labels': + .: {} + 'f:app': {} + 'f:spec': + .: {} + 'f:containers': + .: {} + 'k:{"name":"httpd"}': + .: {} + 'f:image': {} + 'f:imagePullPolicy': {} + 'f:name': {} + 'f:ports': + .: {} + 'k:{"containerPort":8080,"protocol":"TCP"}': + .: {} + 'f:containerPort': {} + 'f:protocol': {} + 'f:resources': {} + 'f:terminationMessagePath': {} + 'f:terminationMessagePolicy': {} + 'f:dnsPolicy': {} + 'f:restartPolicy': {} + 'f:schedulerName': {} + 'f:securityContext': {} + 'f:terminationGracePeriodSeconds': {} + 'f:triggers': {} + - manager: openshift-controller-manager + operation: Update + apiVersion: apps.openshift.io/v1 + time: '2021-08-26T04:37:11Z' + fieldsType: FieldsV1 + fieldsV1: + 'f:status': + 'f:updatedReplicas': {} + 'f:readyReplicas': {} + 'f:conditions': + .: {} + 'k:{"type":"Available"}': + .: {} + 'f:lastTransitionTime': {} + 'f:lastUpdateTime': {} + 'f:message': {} + 'f:status': {} + 'f:type': {} + 'k:{"type":"Progressing"}': + .: {} + 'f:lastTransitionTime': {} + 'f:lastUpdateTime': {} + 'f:message': {} + 'f:status': {} + 'f:type': {} + 'f:details': + .: {} + 'f:causes': {} + 'f:message': {} + 'f:replicas': {} + 'f:availableReplicas': {} + 'f:observedGeneration': {} + 'f:unavailableReplicas': {} + 'f:latestVersion': {} +spec: + strategy: + type: Rolling + rollingParams: + updatePeriodSeconds: 1 + intervalSeconds: 1 + timeoutSeconds: 600 + maxUnavailable: 25% + maxSurge: 25% + resources: {} + activeDeadlineSeconds: 21600 + triggers: + - type: ConfigChange + replicas: 20 + revisionHistoryLimit: 10 + test: false + selector: + app: httpd + template: + metadata: + creationTimestamp: null + labels: + app: httpd + spec: + containers: + - name: httpd + image: >- + image-registry.openshift-image-registry.svc:5000/openshift/httpd:latest + ports: + - containerPort: 8080 + protocol: TCP + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + imagePullPolicy: Always + restartPolicy: Always + terminationGracePeriodSeconds: 30 + dnsPolicy: ClusterFirst + securityContext: {} + schedulerName: default-scheduler +status: + observedGeneration: 1 + details: + message: config change + causes: + - type: ConfigChange + availableReplicas: 20 + unavailableReplicas: 0 + latestVersion: 1 + updatedReplicas: 20 + conditions: + - type: Progressing + status: Unknown + lastUpdateTime: '2021-08-26T04:37:06Z' + lastTransitionTime: '2021-08-26T04:37:06Z' + message: >- + replication controller "example-1" is waiting for pod "example-1-deploy" + to run + - type: Available + status: 'True' + lastUpdateTime: '2021-08-26T04:37:12Z' + lastTransitionTime: '2021-08-26T04:37:12Z' + message: Deployment config has minimum availability. + replicas: 20 + readyReplicas: 20 diff --git a/resource_customizations/apps/Deployment/actions/action_test.yaml b/resource_customizations/apps/Deployment/actions/action_test.yaml index 65b58b66a1afc..14538c8d0b63f 100644 --- a/resource_customizations/apps/Deployment/actions/action_test.yaml +++ b/resource_customizations/apps/Deployment/actions/action_test.yaml @@ -2,3 +2,9 @@ actionTests: - action: restart inputPath: testdata/deployment.yaml expectedOutputPath: testdata/deployment-restarted.yaml +- action: pause + inputPath: testdata/deployment.yaml + expectedOutputPath: testdata/deployment-pause.yaml +- action: resume + inputPath: testdata/deployment-pause.yaml + expectedOutputPath: testdata/deployment-resume.yaml \ No newline at end of file diff --git a/resource_customizations/apps/Deployment/actions/discovery.lua b/resource_customizations/apps/Deployment/actions/discovery.lua index 49557377a831b..52b52fba81a06 100644 --- a/resource_customizations/apps/Deployment/actions/discovery.lua +++ b/resource_customizations/apps/Deployment/actions/discovery.lua @@ -1,3 +1,10 @@ actions = {} actions["restart"] = {} + +local paused = false +if obj.spec.paused ~= nil then + paused = obj.spec.paused + actions["pause"] = {paused} +end +actions["resume"] = {["disabled"] = not(paused)} return actions diff --git a/resource_customizations/apps/Deployment/actions/pause/action.lua b/resource_customizations/apps/Deployment/actions/pause/action.lua new file mode 100644 index 0000000000000..ba3e1089c9676 --- /dev/null +++ b/resource_customizations/apps/Deployment/actions/pause/action.lua @@ -0,0 +1,2 @@ +obj.spec.paused = true +return obj \ No newline at end of file diff --git a/resource_customizations/apps/Deployment/actions/resume/action.lua b/resource_customizations/apps/Deployment/actions/resume/action.lua new file mode 100644 index 0000000000000..94cff65ff6cdf --- /dev/null +++ b/resource_customizations/apps/Deployment/actions/resume/action.lua @@ -0,0 +1,2 @@ +obj.spec.paused = nil +return obj \ No newline at end of file diff --git a/resource_customizations/apps/Deployment/actions/testdata/deployment-pause.yaml b/resource_customizations/apps/Deployment/actions/testdata/deployment-pause.yaml new file mode 100644 index 0000000000000..38cb1faf8498f --- /dev/null +++ b/resource_customizations/apps/Deployment/actions/testdata/deployment-pause.yaml @@ -0,0 +1,57 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "1" + creationTimestamp: "2021-09-21T22:35:20Z" + generation: 2 +spec: + paused: true + progressDeadlineSeconds: 600 + replicas: 3 + revisionHistoryLimit: 10 + selector: + matchLabels: + app: nginx + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + creationTimestamp: null + labels: + app: nginx + spec: + containers: + - image: nginx:latest + imagePullPolicy: Always + name: nginx + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 +status: + availableReplicas: 3 + conditions: + - lastTransitionTime: "2021-09-21T22:35:31Z" + lastUpdateTime: "2021-09-21T22:35:31Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: Available + - lastTransitionTime: "2021-09-21T22:36:25Z" + lastUpdateTime: "2021-09-21T22:36:25Z" + message: Deployment is paused + reason: DeploymentPaused + status: Unknown + type: Progressing + observedGeneration: 2 + readyReplicas: 3 + replicas: 3 + updatedReplicas: 3 \ No newline at end of file diff --git a/resource_customizations/apps/Deployment/actions/testdata/deployment-resume.yaml b/resource_customizations/apps/Deployment/actions/testdata/deployment-resume.yaml new file mode 100644 index 0000000000000..ea8d3b14de51d --- /dev/null +++ b/resource_customizations/apps/Deployment/actions/testdata/deployment-resume.yaml @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "1" + creationTimestamp: "2021-09-21T22:35:20Z" + generation: 3 +spec: + progressDeadlineSeconds: 600 + replicas: 3 + revisionHistoryLimit: 10 + selector: + matchLabels: + app: nginx + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + creationTimestamp: null + labels: + app: nginx + spec: + containers: + - image: nginx:latest + imagePullPolicy: Always + name: nginx + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 +status: + availableReplicas: 3 + conditions: + - lastTransitionTime: "2021-09-21T22:35:31Z" + lastUpdateTime: "2021-09-21T22:35:31Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: Available + - lastTransitionTime: "2021-09-21T22:38:10Z" + lastUpdateTime: "2021-09-21T22:38:10Z" + message: ReplicaSet "nginx-deploy-55649fd747" has successfully progressed. + reason: NewReplicaSetAvailable + status: "True" + type: Progressing + observedGeneration: 3 + readyReplicas: 3 + replicas: 3 + updatedReplicas: 3 \ No newline at end of file diff --git a/resource_customizations/argoproj.io/Rollout/health.lua b/resource_customizations/argoproj.io/Rollout/health.lua index 8bef6a800d0b3..8d352a3ad7f30 100644 --- a/resource_customizations/argoproj.io/Rollout/health.lua +++ b/resource_customizations/argoproj.io/Rollout/health.lua @@ -66,12 +66,36 @@ function isGenerationObserved(obj) return observedGeneration == obj.metadata.generation end +-- isWorkloadGenerationObserved determines if the referenced workload's generation spec has been +-- observed by the controller. This only applies to v1.1 rollout +function isWorkloadGenerationObserved(obj) + if obj.spec.workloadRef == nil or obj.metadata.annotations == nil then + -- rollout is v1.0 or earlier + return true + end + workloadGen = tonumber(obj.metadata.annotations["rollout.argoproj.io/workload-generation"]) + observedWorkloadGen = tonumber(obj.status.workloadObservedGeneration) + return workloadGen == observedWorkloadGen +end + hs = {} -if not isGenerationObserved(obj) then +if not isGenerationObserved(obj) or not isWorkloadGenerationObserved(obj) then hs.status = "Progressing" hs.message = "Waiting for rollout spec update to be observed" return hs -end +end + +-- Argo Rollouts v1.0 has been improved to record a phase/message in status, which Argo CD can blindly surface +if obj.status.phase ~= nil then + if obj.status.phase == "Paused" then + -- Map Rollout's "Paused" status to Argo CD's "Suspended" + hs.status = "Suspended" + else + hs.status = obj.status.phase + end + hs.message = obj.status.message + return hs +end for _, condition in ipairs(obj.status.conditions) do if condition.type == "InvalidSpec" then diff --git a/resource_customizations/argoproj.io/Rollout/health_test.yaml b/resource_customizations/argoproj.io/Rollout/health_test.yaml index de5ae9aacf57c..a9fa6c23360dc 100644 --- a/resource_customizations/argoproj.io/Rollout/health_test.yaml +++ b/resource_customizations/argoproj.io/Rollout/health_test.yaml @@ -7,6 +7,14 @@ tests: status: Progressing message: "Waiting for rollout spec update to be observed" inputPath: testdata/progressing_newGeneration.yaml +- healthStatus: + status: Progressing + message: "Waiting for rollout spec update to be observed" + inputPath: testdata/progressing_newWorkloadGeneration.yaml +- healthStatus: + status: Degraded + message: "InvalidSpec" + inputPath: testdata/degraded_statusPhaseMessage.yaml - healthStatus: status: Healthy message: "" @@ -15,6 +23,14 @@ tests: status: Healthy message: "" inputPath: testdata/healthy_legacy_v0.9_observedGeneration_numeric.yaml +- healthStatus: + status: Healthy + message: "" + inputPath: testdata/healthy_legacy_v1.0_newWorkloadGeneration.yaml +- healthStatus: + status: Healthy + message: "" + inputPath: testdata/healthy_newWorkloadGeneration.yaml - healthStatus: status: Degraded message: "The Rollout \"basic\" is invalid: spec.strategy.strategy: Required value: Rollout has missing field '.spec.strategy.canary or .spec.strategy.blueGreen'" @@ -56,6 +72,10 @@ tests: status: Suspended message: Rollout is paused inputPath: testdata/suspended_userPause.yaml +- healthStatus: + status: Suspended + message: CanaryPauseStep + inputPath: testdata/suspended_v1.0_pausedRollout.yaml - healthStatus: status: Healthy inputPath: testdata/canary/healthy_executedAllStepsPreV0.8.yaml diff --git a/resource_customizations/argoproj.io/Rollout/testdata/degraded_statusPhaseMessage.yaml b/resource_customizations/argoproj.io/Rollout/testdata/degraded_statusPhaseMessage.yaml new file mode 100644 index 0000000000000..02c161dc1f399 --- /dev/null +++ b/resource_customizations/argoproj.io/Rollout/testdata/degraded_statusPhaseMessage.yaml @@ -0,0 +1,50 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + creationTimestamp: "2020-11-13T00:44:55Z" + generation: 1 + name: basic + namespace: argocd-e2e + resourceVersion: "182108" + selfLink: /apis/argoproj.io/v1alpha1/namespaces/argocd-e2e/rollouts/basic + uid: 34e4bbfc-222c-4968-bd60-2b30ae81110d +spec: + replicas: 1 + selector: + matchLabels: + app: basic + strategy: + canary: + steps: + - setWeight: 50 + - pause: {} + template: + metadata: + creationTimestamp: null + labels: + app: basic + spec: + containers: + - image: nginx:1.19-alpine + name: basic + resources: + requests: + cpu: 1m + memory: 16Mi +status: + HPAReplicas: 1 + availableReplicas: 1 + blueGreen: {} + canary: {} + conditions: {} + phase: "Degraded" + message: "InvalidSpec" + currentPodHash: 754cb84d5 + currentStepHash: 757f5f97b + currentStepIndex: 2 + observedGeneration: "8575574967" ## <---- uses legacy observedGeneration hash which are numbers + readyReplicas: 1 + replicas: 1 + selector: app=basic + stableRS: 754cb84d5 + updatedReplicas: 1 diff --git a/resource_customizations/argoproj.io/Rollout/testdata/healthy_legacy_v1.0_newWorkloadGeneration.yaml b/resource_customizations/argoproj.io/Rollout/testdata/healthy_legacy_v1.0_newWorkloadGeneration.yaml new file mode 100644 index 0000000000000..f231ff5ada328 --- /dev/null +++ b/resource_customizations/argoproj.io/Rollout/testdata/healthy_legacy_v1.0_newWorkloadGeneration.yaml @@ -0,0 +1,58 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + annotations: + rollout.argoproj.io/revision: "1" + creationTimestamp: "2021-07-27T12:14:11Z" + generation: 3 + name: rollout-ref-deployment + namespace: default + resourceVersion: "4220" + uid: a3d1d224-ac4f-4f84-9274-e01e1d43b036 +spec: + replicas: 5 + strategy: + canary: + steps: + - setWeight: 20 + - pause: + duration: 10s + workloadRef: + apiVersion: apps/v1 + kind: Deployment + name: rollout-ref-deployment +status: + HPAReplicas: 5 + availableReplicas: 5 + blueGreen: {} + canary: {} + collisionCount: 1 + conditions: + - lastTransitionTime: "2021-07-27T12:14:21Z" + lastUpdateTime: "2021-07-27T12:14:21Z" + message: RolloutCompleted + reason: RolloutCompleted + status: "True" + type: Completed + - lastTransitionTime: "2021-07-27T12:14:11Z" + lastUpdateTime: "2021-07-27T12:14:21Z" + message: ReplicaSet "rollout-ref-deployment-75bbd56864" has successfully progressed. + reason: NewReplicaSetAvailable + status: "True" + type: Progressing + - lastTransitionTime: "2021-07-27T12:14:21Z" + lastUpdateTime: "2021-07-27T12:14:21Z" + message: Rollout has minimum availability + reason: AvailableReason + status: "True" + type: Available + currentPodHash: 75bbd56864 + currentStepHash: 55f5d87bd9 + currentStepIndex: 2 + observedGeneration: "3" + phase: Healthy + readyReplicas: 5 + replicas: 5 + selector: app=rollout-ref-deployment + stableRS: 75bbd56864 + updatedReplicas: 5 diff --git a/resource_customizations/argoproj.io/Rollout/testdata/healthy_newWorkloadGeneration.yaml b/resource_customizations/argoproj.io/Rollout/testdata/healthy_newWorkloadGeneration.yaml new file mode 100644 index 0000000000000..4418a81896ad8 --- /dev/null +++ b/resource_customizations/argoproj.io/Rollout/testdata/healthy_newWorkloadGeneration.yaml @@ -0,0 +1,60 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + annotations: + rollout.argoproj.io/revision: "1" + rollout.argoproj.io/workload-generation: "1" + creationTimestamp: "2021-07-27T12:14:11Z" + generation: 3 + name: rollout-ref-deployment + namespace: default + resourceVersion: "4220" + uid: a3d1d224-ac4f-4f84-9274-e01e1d43b036 +spec: + replicas: 5 + strategy: + canary: + steps: + - setWeight: 20 + - pause: + duration: 10s + workloadRef: + apiVersion: apps/v1 + kind: Deployment + name: rollout-ref-deployment +status: + HPAReplicas: 5 + availableReplicas: 5 + blueGreen: {} + canary: {} + collisionCount: 1 + conditions: + - lastTransitionTime: "2021-07-27T12:14:21Z" + lastUpdateTime: "2021-07-27T12:14:21Z" + message: RolloutCompleted + reason: RolloutCompleted + status: "True" + type: Completed + - lastTransitionTime: "2021-07-27T12:14:11Z" + lastUpdateTime: "2021-07-27T12:14:21Z" + message: ReplicaSet "rollout-ref-deployment-75bbd56864" has successfully progressed. + reason: NewReplicaSetAvailable + status: "True" + type: Progressing + - lastTransitionTime: "2021-07-27T12:14:21Z" + lastUpdateTime: "2021-07-27T12:14:21Z" + message: Rollout has minimum availability + reason: AvailableReason + status: "True" + type: Available + currentPodHash: 75bbd56864 + currentStepHash: 55f5d87bd9 + currentStepIndex: 2 + observedGeneration: "3" + phase: Healthy + readyReplicas: 5 + replicas: 5 + selector: app=rollout-ref-deployment + stableRS: 75bbd56864 + updatedReplicas: 5 + workloadObservedGeneration: "1" diff --git a/resource_customizations/argoproj.io/Rollout/testdata/progressing_newWorkloadGeneration.yaml b/resource_customizations/argoproj.io/Rollout/testdata/progressing_newWorkloadGeneration.yaml new file mode 100644 index 0000000000000..bc5f0f431083c --- /dev/null +++ b/resource_customizations/argoproj.io/Rollout/testdata/progressing_newWorkloadGeneration.yaml @@ -0,0 +1,60 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + annotations: + rollout.argoproj.io/revision: "1" + rollout.argoproj.io/workload-generation: "2" + creationTimestamp: "2021-07-27T12:14:11Z" + generation: 3 + name: rollout-ref-deployment + namespace: default + resourceVersion: "4220" + uid: a3d1d224-ac4f-4f84-9274-e01e1d43b036 +spec: + replicas: 5 + strategy: + canary: + steps: + - setWeight: 20 + - pause: + duration: 10s + workloadRef: + apiVersion: apps/v1 + kind: Deployment + name: rollout-ref-deployment +status: + HPAReplicas: 5 + availableReplicas: 5 + blueGreen: {} + canary: {} + collisionCount: 1 + conditions: + - lastTransitionTime: "2021-07-27T12:14:21Z" + lastUpdateTime: "2021-07-27T12:14:21Z" + message: RolloutCompleted + reason: RolloutCompleted + status: "True" + type: Completed + - lastTransitionTime: "2021-07-27T12:14:11Z" + lastUpdateTime: "2021-07-27T12:14:21Z" + message: ReplicaSet "rollout-ref-deployment-75bbd56864" has successfully progressed. + reason: NewReplicaSetAvailable + status: "True" + type: Progressing + - lastTransitionTime: "2021-07-27T12:14:21Z" + lastUpdateTime: "2021-07-27T12:14:21Z" + message: Rollout has minimum availability + reason: AvailableReason + status: "True" + type: Available + currentPodHash: 75bbd56864 + currentStepHash: 55f5d87bd9 + currentStepIndex: 2 + observedGeneration: "3" + phase: Healthy + readyReplicas: 5 + replicas: 5 + selector: app=rollout-ref-deployment + stableRS: 75bbd56864 + updatedReplicas: 5 + workloadObservedGeneration: "1" diff --git a/resource_customizations/argoproj.io/Rollout/testdata/suspended_v1.0_pausedRollout.yaml b/resource_customizations/argoproj.io/Rollout/testdata/suspended_v1.0_pausedRollout.yaml new file mode 100644 index 0000000000000..9684a91203586 --- /dev/null +++ b/resource_customizations/argoproj.io/Rollout/testdata/suspended_v1.0_pausedRollout.yaml @@ -0,0 +1,97 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + annotations: + rollout.argoproj.io/revision: '2' + creationTimestamp: '2021-06-30T04:40:44Z' + generation: 5 + labels: + app.kubernetes.io/instance: rollouts-demo + name: rollouts-demo + namespace: rollouts-demo + resourceVersion: '4838641' + uid: bf946046-d90e-49b9-863c-76f82bed3b31 +spec: + replicas: 5 + revisionHistoryLimit: 1 + selector: + matchLabels: + app: rollouts-demo + strategy: + canary: + canaryService: rollouts-demo-desired + stableService: rollouts-demo-stable + steps: + - setWeight: 21 + - pause: {} + - setWeight: 40 + - pause: + duration: 10 + - setWeight: 60 + - pause: + duration: 10 + - setWeight: 80 + - pause: + duration: 10 + template: + metadata: + labels: + app: rollouts-demo + spec: + containers: + - image: 'argoproj/rollouts-demo:yellow' + imagePullPolicy: Always + name: rollouts-demo + ports: + - containerPort: 8080 + name: http + protocol: TCP + resources: + requests: + cpu: 5m + memory: 32Mi +status: + HPAReplicas: 6 + availableReplicas: 6 + blueGreen: {} + canary: {} + conditions: + - lastTransitionTime: '2021-06-30T05:01:22Z' + lastUpdateTime: '2021-06-30T05:01:22Z' + message: RolloutCompleted + reason: RolloutCompleted + status: 'False' + type: Completed + - lastTransitionTime: '2021-06-30T05:01:27Z' + lastUpdateTime: '2021-06-30T05:01:27Z' + message: Rollout has minimum availability + reason: AvailableReason + status: 'True' + type: Available + - lastTransitionTime: '2021-07-15T21:08:55Z' + lastUpdateTime: '2021-07-15T21:08:55Z' + message: Rollout is paused + reason: RolloutPaused + status: Unknown + type: Progressing + - lastTransitionTime: '2021-07-15T21:08:55Z' + lastUpdateTime: '2021-07-15T21:08:55Z' + message: Rollout is paused + reason: RolloutPaused + status: 'True' + type: Paused + controllerPause: true + currentPodHash: 8566c77b97 + currentStepHash: 7d5979db69 + currentStepIndex: 1 + message: CanaryPauseStep + observedGeneration: '5' + pauseConditions: + - reason: CanaryPauseStep + startTime: '2021-07-15T21:08:55Z' + phase: Paused + readyReplicas: 6 + replicas: 6 + selector: app=rollouts-demo + stableRS: 77f4f8ff97 + updatedReplicas: 2 diff --git a/resource_customizations/cassandra.rook.io/Cluster/health.lua b/resource_customizations/cassandra.rook.io/Cluster/health.lua new file mode 100644 index 0000000000000..5bf48f6c9c49f --- /dev/null +++ b/resource_customizations/cassandra.rook.io/Cluster/health.lua @@ -0,0 +1,24 @@ +hs = {} +if obj.status ~= nil then + if obj.status.racks ~= nil then + all_racks_good = true + for key, value in pairs(obj.status.racks) do + if all_racks_good and value.members ~= nil and value.readyMembers ~= nil and value.members ~= value.readyMembers then + all_racks_good = false + break + end + end + if all_racks_good then + hs.status = "Healthy" + else + hs.status = "Progressing" + hs.message = "Waiting for Cassandra Cluster" + end + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Cassandra Cluster" +return hs + diff --git a/resource_customizations/cassandra.rook.io/Cluster/health_test.yaml b/resource_customizations/cassandra.rook.io/Cluster/health_test.yaml new file mode 100644 index 0000000000000..2f7b7b5f2bde6 --- /dev/null +++ b/resource_customizations/cassandra.rook.io/Cluster/health_test.yaml @@ -0,0 +1,9 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for Cassandra Cluster" + inputPath: testdata/progressing.yaml +- healthStatus: + status: Healthy + message: "" + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/cassandra.rook.io/Cluster/testdata/healthy.yaml b/resource_customizations/cassandra.rook.io/Cluster/testdata/healthy.yaml new file mode 100644 index 0000000000000..fbfdce3ca4358 --- /dev/null +++ b/resource_customizations/cassandra.rook.io/Cluster/testdata/healthy.yaml @@ -0,0 +1,96 @@ +apiVersion: cassandra.rook.io/v1alpha1 +kind: Cluster +metadata: + name: rook-cassandra + namespace: rook-cassandra +spec: + version: 3.11.6 + repository: my-private-repo.io/cassandra + mode: cassandra + # A key/value list of annotations + annotations: + # key: value + datacenter: + name: us-east-1 + racks: + - name: us-east-1a + members: 3 + storage: + volumeClaimTemplates: + - metadata: + name: rook-cassandra-data + spec: + storageClassName: my-storage-class + resources: + requests: + storage: 200Gi + resources: + requests: + cpu: 8 + memory: 32Gi + limits: + cpu: 8 + memory: 32Gi + - name: us-east-1b + members: 3 + storage: + volumeClaimTemplates: + - metadata: + name: rook-cassandra-data + spec: + storageClassName: my-storage-class + resources: + requests: + storage: 200Gi + resources: + requests: + cpu: 8 + memory: 32Gi + limits: + cpu: 8 + memory: 32Gi + - name: us-east-1c + members: 3 + storage: + volumeClaimTemplates: + - metadata: + name: rook-cassandra-data + spec: + storageClassName: my-storage-class + resources: + requests: + storage: 200Gi + resources: + requests: + cpu: 8 + memory: 32Gi + limits: + cpu: 8 + memory: 32Gi + # A key/value list of annotations + annotations: + # key: value + placement: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: failure-domain.beta.kubernetes.io/region + operator: In + values: + - us-east-1 + - key: failure-domain.beta.kubernetes.io/zone + operator: In + values: + - us-east-1a +status: + racks: + us-east-1a: + members: 3 + readyMembers: 3 + us-east-1b: + members: 3 + readyMembers: 3 + us-east-1c: + members: 3 + readyMembers: 3 \ No newline at end of file diff --git a/resource_customizations/cassandra.rook.io/Cluster/testdata/progressing.yaml b/resource_customizations/cassandra.rook.io/Cluster/testdata/progressing.yaml new file mode 100644 index 0000000000000..98c1a65bfc482 --- /dev/null +++ b/resource_customizations/cassandra.rook.io/Cluster/testdata/progressing.yaml @@ -0,0 +1,96 @@ +apiVersion: cassandra.rook.io/v1alpha1 +kind: Cluster +metadata: + name: rook-cassandra + namespace: rook-cassandra +spec: + version: 3.11.6 + repository: my-private-repo.io/cassandra + mode: cassandra + # A key/value list of annotations + annotations: + # key: value + datacenter: + name: us-east-1 + racks: + - name: us-east-1a + members: 3 + storage: + volumeClaimTemplates: + - metadata: + name: rook-cassandra-data + spec: + storageClassName: my-storage-class + resources: + requests: + storage: 200Gi + resources: + requests: + cpu: 8 + memory: 32Gi + limits: + cpu: 8 + memory: 32Gi + - name: us-east-1b + members: 3 + storage: + volumeClaimTemplates: + - metadata: + name: rook-cassandra-data + spec: + storageClassName: my-storage-class + resources: + requests: + storage: 200Gi + resources: + requests: + cpu: 8 + memory: 32Gi + limits: + cpu: 8 + memory: 32Gi + - name: us-east-1c + members: 3 + storage: + volumeClaimTemplates: + - metadata: + name: rook-cassandra-data + spec: + storageClassName: my-storage-class + resources: + requests: + storage: 200Gi + resources: + requests: + cpu: 8 + memory: 32Gi + limits: + cpu: 8 + memory: 32Gi + # A key/value list of annotations + annotations: + # key: value + placement: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: failure-domain.beta.kubernetes.io/region + operator: In + values: + - us-east-1 + - key: failure-domain.beta.kubernetes.io/zone + operator: In + values: + - us-east-1a +status: + racks: + us-east-1a: + members: 3 + readyMembers: 1 + us-east-1b: + members: 3 + readyMembers: 0 + us-east-1c: + members: 3 + readyMembers: 3 diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/health.lua b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/health.lua new file mode 100644 index 0000000000000..e9d16c7f13188 --- /dev/null +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/health.lua @@ -0,0 +1,28 @@ +hs = {} +hs.status = "Progressing" +hs.message = "Provisioning IAMPolicyMember..." + +if obj.status == nil or obj.status.conditions == nil then + return hs +end + +for i, condition in ipairs(obj.status.conditions) do + -- There should be only Ready status + if condition.type == "Ready" then + + hs.message = condition.message + + if condition.status == "True" then + hs.status = "Healthy" + return hs + end + + if condition.reason == "UpdateFailed" then + hs.status = "Degraded" + return hs + end + + end +end + +return hs diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/health_test.yaml b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/health_test.yaml new file mode 100644 index 0000000000000..e30f8ec323d86 --- /dev/null +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/health_test.yaml @@ -0,0 +1,31 @@ +tests: + - healthStatus: + status: Degraded + message: + 'Update call failed: error setting policy member: error applying changes: + summary: Request "Create IAM Members roles/storage.objects.create serviceAccount:test-sa@test-project.iam.gserviceaccount.com + for \"project \\\"projects/test-project\\\"\"" returned error: Batch request + and retried single request "Create IAM Members roles/storage.objects.create + serviceAccount:test-sa@test-project.iam.gserviceaccount.com + for \"project \\\"projects/test-project\\\"\"" both failed. Final error: + Error applying IAM policy for project "projects/test-project": Error setting + IAM policy for project "projects/test-project": googleapi: Error 400: Role + roles/storage.objects.create is not supported for this resource., badRequest, + detail: ' + inputPath: testdata/degraded_updateFailed.yaml + - healthStatus: + status: Healthy + message: "The resource is up to date" + inputPath: testdata/healthy_upToDate.yaml + - healthStatus: + status: Progressing + message: "Successfully deleted" + inputPath: testdata/progressing_deleted.yaml + - healthStatus: + status: Progressing + message: "Provisioning IAMPolicyMember..." + inputPath: testdata/progressing_noStatus.yaml + - healthStatus: + status: Progressing + message: "Provisioning IAMPolicyMember..." + inputPath: testdata/progressing_statusGeneration.yaml diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/degraded_updateFailed.yaml b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/degraded_updateFailed.yaml new file mode 100644 index 0000000000000..88a79b6103181 --- /dev/null +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/degraded_updateFailed.yaml @@ -0,0 +1,87 @@ +apiVersion: iam.cnrm.cloud.google.com/v1beta1 +kind: IAMPolicyMember +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"iam.cnrm.cloud.google.com/v1beta1","kind":"IAMPolicyMember","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"test-health-iam-sa"},"name":"test-objects-create","namespace":"test-project"},"spec":{"member":"serviceAccount:test-sa@test-project.iam.gserviceaccount.com","resourceRef":{"apiVersion":"resourcemanager.cnrm.cloud.google.com/v1beta1","external":"projects/test-project","kind":"Project"},"role":"roles/storage.objects.create"}} + creationTimestamp: "2021-06-17T15:54:30Z" + finalizers: + - cnrm.cloud.google.com/finalizer + - cnrm.cloud.google.com/deletion-defender + generation: 1 + labels: + app.kubernetes.io/instance: test-health-iam-sa + managedFields: + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:annotations: + .: {} + f:kubectl.kubernetes.io/last-applied-configuration: {} + f:labels: + .: {} + f:app.kubernetes.io/instance: {} + f:spec: + .: {} + f:member: {} + f:resourceRef: + .: {} + f:apiVersion: {} + f:external: {} + f:kind: {} + f:role: {} + manager: argocd-application-controller + operation: Update + time: "2021-06-17T15:54:30Z" + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:finalizers: + .: {} + v:"cnrm.cloud.google.com/deletion-defender": {} + v:"cnrm.cloud.google.com/finalizer": {} + manager: manager + operation: Update + time: "2021-06-17T15:54:31Z" + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:status: + .: {} + f:conditions: {} + f:observedGeneration: {} + manager: cnrm-controller-manager + operation: Update + time: "2021-06-17T15:54:34Z" + name: test-objects-create + namespace: test-project + resourceVersion: "73435624" + selfLink: /apis/iam.cnrm.cloud.google.com/v1beta1/namespaces/test-project/iampolicymembers/test-objects-create + uid: 91c92bb4-c341-41b5-a2ce-efda6e0996a1 +spec: + member: serviceAccount:test-sa@test-project.iam.gserviceaccount.com + resourceRef: + apiVersion: resourcemanager.cnrm.cloud.google.com/v1beta1 + external: projects/test-project + kind: Project + role: roles/storage.objects.create +status: + conditions: + - lastTransitionTime: "2021-06-17T15:54:34Z" + message: + 'Update call failed: error setting policy member: error applying changes: + summary: Request "Create IAM Members roles/storage.objects.create serviceAccount:test-sa@test-project.iam.gserviceaccount.com + for \"project \\\"projects/test-project\\\"\"" returned error: Batch request + and retried single request "Create IAM Members roles/storage.objects.create + serviceAccount:test-sa@test-project.iam.gserviceaccount.com + for \"project \\\"projects/test-project\\\"\"" both failed. Final error: + Error applying IAM policy for project "projects/test-project": Error setting + IAM policy for project "projects/test-project": googleapi: Error 400: Role + roles/storage.objects.create is not supported for this resource., badRequest, + detail: ' + reason: UpdateFailed + status: "False" + type: Ready + observedGeneration: 1 diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/healthy_upToDate.yaml b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/healthy_upToDate.yaml new file mode 100644 index 0000000000000..c50c5d9f8a982 --- /dev/null +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/healthy_upToDate.yaml @@ -0,0 +1,77 @@ +apiVersion: iam.cnrm.cloud.google.com/v1beta1 +kind: IAMPolicyMember +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"iam.cnrm.cloud.google.com/v1beta1","kind":"IAMPolicyMember","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"test-health-iam-sa"},"name":"test-policy-member","namespace":"test-project"},"spec":{"member":"serviceAccount:test-sa@test-project.iam.gserviceaccount.com","resourceRef":{"apiVersion":"resourcemanager.cnrm.cloud.google.com/v1beta1","external":"projects/test-project","kind":"Project"},"role":"roles/storage.objects.create"}} + creationTimestamp: "2021-06-17T15:54:30Z" + finalizers: + - cnrm.cloud.google.com/finalizer + - cnrm.cloud.google.com/deletion-defender + generation: 1 + labels: + app.kubernetes.io/instance: test-health-iam-sa + managedFields: + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:annotations: + .: {} + f:kubectl.kubernetes.io/last-applied-configuration: {} + f:labels: + .: {} + f:app.kubernetes.io/instance: {} + f:spec: + .: {} + f:member: {} + f:resourceRef: + .: {} + f:apiVersion: {} + f:external: {} + f:kind: {} + f:role: {} + manager: argocd-application-controller + operation: Update + time: "2021-06-17T15:54:30Z" + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:finalizers: + .: {} + v:"cnrm.cloud.google.com/deletion-defender": {} + v:"cnrm.cloud.google.com/finalizer": {} + manager: manager + operation: Update + time: "2021-06-17T15:54:31Z" + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:status: + .: {} + f:conditions: {} + f:observedGeneration: {} + manager: cnrm-controller-manager + operation: Update + time: "2021-06-17T15:54:34Z" + name: test-policy-member + namespace: test-project + resourceVersion: "73435624" + selfLink: /apis/iam.cnrm.cloud.google.com/v1beta1/namespaces/test-project/iampolicymembers/test-policy-member + uid: 91c92bb4-c341-41b5-a2ce-efda6e0996a1 +spec: + member: serviceAccount:test-sa@test-project.iam.gserviceaccount.com + resourceRef: + apiVersion: resourcemanager.cnrm.cloud.google.com/v1beta1 + external: projects/test-project + kind: Project + role: roles/storage.objects.create +status: + conditions: + - lastTransitionTime: "2021-05-27T17:55:14Z" + message: The resource is up to date + reason: UpToDate + status: "True" + type: Ready + observedGeneration: 11 diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/progressing_deleted.yaml b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/progressing_deleted.yaml new file mode 100644 index 0000000000000..132d55097d998 --- /dev/null +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/progressing_deleted.yaml @@ -0,0 +1,77 @@ +apiVersion: iam.cnrm.cloud.google.com/v1beta1 +kind: IAMPolicyMember +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"iam.cnrm.cloud.google.com/v1beta1","kind":"IAMPolicyMember","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"test-health-iam-sa"},"name":"test-policy-member","namespace":"test-project"},"spec":{"member":"serviceAccount:test-sa@test-project.iam.gserviceaccount.com","resourceRef":{"apiVersion":"resourcemanager.cnrm.cloud.google.com/v1beta1","external":"projects/test-project","kind":"Project"},"role":"roles/storage.objects.create"}} + creationTimestamp: "2021-06-17T15:54:30Z" + finalizers: + - cnrm.cloud.google.com/finalizer + - cnrm.cloud.google.com/deletion-defender + generation: 1 + labels: + app.kubernetes.io/instance: test-health-iam-sa + managedFields: + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:annotations: + .: {} + f:kubectl.kubernetes.io/last-applied-configuration: {} + f:labels: + .: {} + f:app.kubernetes.io/instance: {} + f:spec: + .: {} + f:member: {} + f:resourceRef: + .: {} + f:apiVersion: {} + f:external: {} + f:kind: {} + f:role: {} + manager: argocd-application-controller + operation: Update + time: "2021-06-17T15:54:30Z" + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:finalizers: + .: {} + v:"cnrm.cloud.google.com/deletion-defender": {} + v:"cnrm.cloud.google.com/finalizer": {} + manager: manager + operation: Update + time: "2021-06-17T15:54:31Z" + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:status: + .: {} + f:conditions: {} + f:observedGeneration: {} + manager: cnrm-controller-manager + operation: Update + time: "2021-06-17T15:54:34Z" + name: test-policy-member + namespace: test-project + resourceVersion: "73435624" + selfLink: /apis/iam.cnrm.cloud.google.com/v1beta1/namespaces/test-project/iampolicymembers/test-policy-member + uid: 91c92bb4-c341-41b5-a2ce-efda6e0996a1 +spec: + member: serviceAccount:test-sa@test-project.iam.gserviceaccount.com + resourceRef: + apiVersion: resourcemanager.cnrm.cloud.google.com/v1beta1 + external: projects/test-project + kind: Project + role: roles/storage.objects.create +status: + conditions: + - lastTransitionTime: "2021-06-18T17:16:40Z" + message: Successfully deleted + reason: Deleted + status: "False" + type: Ready + observedGeneration: 2 diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/progressing_noStatus.yaml b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/progressing_noStatus.yaml new file mode 100644 index 0000000000000..ce20bb8fbcf59 --- /dev/null +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/progressing_noStatus.yaml @@ -0,0 +1,69 @@ +apiVersion: iam.cnrm.cloud.google.com/v1beta1 +kind: IAMPolicyMember +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"iam.cnrm.cloud.google.com/v1beta1","kind":"IAMPolicyMember","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"test-health-iam-sa"},"name":"test-policy-member","namespace":"test-project"},"spec":{"member":"serviceAccount:test-sa@test-project.iam.gserviceaccount.com","resourceRef":{"apiVersion":"resourcemanager.cnrm.cloud.google.com/v1beta1","external":"projects/test-project","kind":"Project"},"role":"roles/storage.objects.create"}} + creationTimestamp: "2021-06-17T15:54:30Z" + finalizers: + - cnrm.cloud.google.com/finalizer + - cnrm.cloud.google.com/deletion-defender + generation: 1 + labels: + app.kubernetes.io/instance: test-health-iam-sa + managedFields: + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:annotations: + .: {} + f:kubectl.kubernetes.io/last-applied-configuration: {} + f:labels: + .: {} + f:app.kubernetes.io/instance: {} + f:spec: + .: {} + f:member: {} + f:resourceRef: + .: {} + f:apiVersion: {} + f:external: {} + f:kind: {} + f:role: {} + manager: argocd-application-controller + operation: Update + time: "2021-06-17T15:54:30Z" + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:finalizers: + .: {} + v:"cnrm.cloud.google.com/deletion-defender": {} + v:"cnrm.cloud.google.com/finalizer": {} + manager: manager + operation: Update + time: "2021-06-17T15:54:31Z" + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:status: + .: {} + f:conditions: {} + f:observedGeneration: {} + manager: cnrm-controller-manager + operation: Update + time: "2021-06-17T15:54:34Z" + name: test-policy-member + namespace: test-project + resourceVersion: "73435624" + selfLink: /apis/iam.cnrm.cloud.google.com/v1beta1/namespaces/test-project/iampolicymembers/test-policy-member + uid: 91c92bb4-c341-41b5-a2ce-efda6e0996a1 +spec: + member: serviceAccount:test-sa@test-project.iam.gserviceaccount.com + resourceRef: + apiVersion: resourcemanager.cnrm.cloud.google.com/v1beta1 + external: projects/test-project + kind: Project + role: roles/storage.objects.create diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/progressing_statusGeneration.yaml b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/progressing_statusGeneration.yaml new file mode 100644 index 0000000000000..9b3bea2081123 --- /dev/null +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/testdata/progressing_statusGeneration.yaml @@ -0,0 +1,71 @@ +apiVersion: iam.cnrm.cloud.google.com/v1beta1 +kind: IAMPolicyMember +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"iam.cnrm.cloud.google.com/v1beta1","kind":"IAMPolicyMember","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"test-health-iam-sa"},"name":"test-policy-member","namespace":"test-project"},"spec":{"member":"serviceAccount:test-sa@test-project.iam.gserviceaccount.com","resourceRef":{"apiVersion":"resourcemanager.cnrm.cloud.google.com/v1beta1","external":"projects/test-project","kind":"Project"},"role":"roles/storage.objects.create"}} + creationTimestamp: "2021-06-17T15:54:30Z" + finalizers: + - cnrm.cloud.google.com/finalizer + - cnrm.cloud.google.com/deletion-defender + generation: 1 + labels: + app.kubernetes.io/instance: test-health-iam-sa + managedFields: + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:annotations: + .: {} + f:kubectl.kubernetes.io/last-applied-configuration: {} + f:labels: + .: {} + f:app.kubernetes.io/instance: {} + f:spec: + .: {} + f:member: {} + f:resourceRef: + .: {} + f:apiVersion: {} + f:external: {} + f:kind: {} + f:role: {} + manager: argocd-application-controller + operation: Update + time: "2021-06-17T15:54:30Z" + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:finalizers: + .: {} + v:"cnrm.cloud.google.com/deletion-defender": {} + v:"cnrm.cloud.google.com/finalizer": {} + manager: manager + operation: Update + time: "2021-06-17T15:54:31Z" + - apiVersion: iam.cnrm.cloud.google.com/v1beta1 + fieldsType: FieldsV1 + fieldsV1: + f:status: + .: {} + f:conditions: {} + f:observedGeneration: {} + manager: cnrm-controller-manager + operation: Update + time: "2021-06-17T15:54:34Z" + name: test-policy-member + namespace: test-project + resourceVersion: "73435624" + selfLink: /apis/iam.cnrm.cloud.google.com/v1beta1/namespaces/test-project/iampolicymembers/test-policy-member + uid: 91c92bb4-c341-41b5-a2ce-efda6e0996a1 +spec: + member: serviceAccount:test-sa@test-project.iam.gserviceaccount.com + resourceRef: + apiVersion: resourcemanager.cnrm.cloud.google.com/v1beta1 + external: projects/test-project + kind: Project + role: roles/storage.objects.create +status: + observedGeneration: 1 diff --git a/resource_customizations/operators.coreos.com/Subscription/health.lua b/resource_customizations/operators.coreos.com/Subscription/health.lua new file mode 100644 index 0000000000000..57f804c7f36fe --- /dev/null +++ b/resource_customizations/operators.coreos.com/Subscription/health.lua @@ -0,0 +1,32 @@ +health_status = {} +if obj.status ~= nil then + if obj.status.conditions ~= nil then + numDegraded = 0 + numPending = 0 + msg = "" + for i, condition in pairs(obj.status.conditions) do + msg = msg .. i .. ": " .. condition.type .. " | " .. condition.status .. "\n" + if condition.type == "InstallPlanPending" and condition.status == "True" then + numPending = numPending + 1 + elseif (condition.type == "CatalogSourcesUnhealthy" or condition.type == "InstallPlanMissing" or condition.type == "InstallPlanFailed" or condition.type == "ResolutionFailed") and condition.status == "True" then + numDegraded = numDegraded + 1 + end + end + if numDegraded == 0 and numPending == 0 then + health_status.status = "Healthy" + health_status.message = msg + return health_status + elseif numPending > 0 and numDegraded == 0 then + health_status.status = "Progressing" + health_status.message = "An install plan for a subscription is pending installation" + return health_status + else + health_status.status = "Degraded" + health_status.message = msg + return health_status + end + end +end +health_status.status = "Progressing" +health_status.message = "An install plan for a subscription is pending installation" +return health_status \ No newline at end of file diff --git a/resource_customizations/operators.coreos.com/Subscription/health_test.yaml b/resource_customizations/operators.coreos.com/Subscription/health_test.yaml new file mode 100644 index 0000000000000..bcd934951a7ac --- /dev/null +++ b/resource_customizations/operators.coreos.com/Subscription/health_test.yaml @@ -0,0 +1,25 @@ +tests: +- healthStatus: + status: Progressing + message: "An install plan for a subscription is pending installation" + inputPath: testdata/install_plan_pending.yaml +- healthStatus: + status: Degraded + message: "1: CatalogSourcesUnhealthy | True\n" + inputPath: testdata/catalog_sources_unhealthy.yaml +- healthStatus: + status: Degraded + message: "1: CatalogSourcesUnhealthy | False\n2: InstallPlanMissing | True\n" + inputPath: testdata/install_plan_missing.yaml +- healthStatus: + status: Degraded + message: "1: CatalogSourcesUnhealthy | False\n2: InstallPlanFailed | True\n" + inputPath: testdata/install_plan_failed.yaml +- healthStatus: + status: Degraded + message: "1: CatalogSourcesUnhealthy | True\n2: ResolutionFailed | True\n" + inputPath: testdata/resolution_failed.yaml +- healthStatus: + status: Healthy + message: "1: CatalogSourcesUnhealthy | False\n" + inputPath: testdata/healthy.yaml \ No newline at end of file diff --git a/resource_customizations/operators.coreos.com/Subscription/testdata/catalog_sources_unhealthy.yaml b/resource_customizations/operators.coreos.com/Subscription/testdata/catalog_sources_unhealthy.yaml new file mode 100644 index 0000000000000..2221bbb7ab430 --- /dev/null +++ b/resource_customizations/operators.coreos.com/Subscription/testdata/catalog_sources_unhealthy.yaml @@ -0,0 +1,58 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' +spec: + channel: stable + name: openshift-gitops-operator + source: redhat-op + sourceNamespace: openshift-marketplace + startingCSV: openshift-gitops-operator.v1.10 +status: + catalogHealth: + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: certified-operators + namespace: openshift-marketplace + resourceVersion: '767930' + uid: dba4fefa-78fa-45c2-9801-d551a086a229 + healthy: true + lastUpdated: '2021-08-25T21:16:59Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: community-operators + namespace: openshift-marketplace + resourceVersion: '769392' + uid: 3e16e348-5b8c-4382-bd71-30fd97836a6c + healthy: true + lastUpdated: '2021-08-25T21:16:59Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-marketplace + namespace: openshift-marketplace + resourceVersion: '766585' + uid: 04003c1c-5414-43e7-ba5e-738addfa2443 + healthy: true + lastUpdated: '2021-08-25T21:16:59Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-operators + namespace: openshift-marketplace + resourceVersion: '766000' + uid: 4cff9c98-14aa-4ebf-a238-2fb8fc48edb6 + healthy: true + lastUpdated: '2021-08-25T21:16:59Z' + conditions: + - lastTransitionTime: '2021-08-25T21:16:59Z' + message: targeted catalogsource openshift-marketplace/redhat-op missing + reason: UnhealthyCatalogSourceFound + status: 'True' + type: CatalogSourcesUnhealthy + lastUpdated: '2021-08-25T21:16:59Z' diff --git a/resource_customizations/operators.coreos.com/Subscription/testdata/healthy.yaml b/resource_customizations/operators.coreos.com/Subscription/testdata/healthy.yaml new file mode 100644 index 0000000000000..1aee8203f43a9 --- /dev/null +++ b/resource_customizations/operators.coreos.com/Subscription/testdata/healthy.yaml @@ -0,0 +1,75 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' + name: openshift-gitops-operator + namespace: openshift-operators +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + startingCSV: openshift-gitops-operator.v1.2.0 +status: + conditions: + - lastTransitionTime: '2021-08-23T16:03:23Z' + message: all available catalogsources are healthy + reason: AllCatalogSourcesHealthy + status: 'False' + type: CatalogSourcesUnhealthy + installplan: + apiVersion: operators.coreos.com/v1alpha1 + kind: InstallPlan + name: install-m7wvs + uuid: 5dfc3b72-b1a7-49b1-868a-c288ccefc667 + lastUpdated: '2021-08-23T16:03:53Z' + installedCSV: openshift-gitops-operator.v1.2.0 + currentCSV: openshift-gitops-operator.v1.2.0 + installPlanRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: InstallPlan + name: install-m7wvs + namespace: openshift-operators + resourceVersion: '49175' + uid: 5dfc3b72-b1a7-49b1-868a-c288ccefc667 + state: AtLatestKnown + catalogHealth: + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: certified-operators + namespace: openshift-marketplace + resourceVersion: '47199' + uid: 1182e96f-bd44-4555-bcae-22ca5545d52f + healthy: true + lastUpdated: '2021-08-23T16:03:23Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: community-operators + namespace: openshift-marketplace + resourceVersion: '45598' + uid: fa39c2ee-af6d-4dbb-a62e-cd20c9e8f19b + healthy: true + lastUpdated: '2021-08-23T16:03:23Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-marketplace + namespace: openshift-marketplace + resourceVersion: '47712' + uid: b4d1171a-ec94-4be9-aa26-237bf4aed0f5 + healthy: true + lastUpdated: '2021-08-23T16:03:23Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-operators + namespace: openshift-marketplace + resourceVersion: '46728' + uid: a1712fa4-1159-4a9b-8e40-fe605ecbb2e5 + healthy: true + lastUpdated: '2021-08-23T16:03:23Z' + installPlanGeneration: 1 diff --git a/resource_customizations/operators.coreos.com/Subscription/testdata/install_plan_failed.yaml b/resource_customizations/operators.coreos.com/Subscription/testdata/install_plan_failed.yaml new file mode 100644 index 0000000000000..347bc1ad68d8a --- /dev/null +++ b/resource_customizations/operators.coreos.com/Subscription/testdata/install_plan_failed.yaml @@ -0,0 +1,86 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' + name: openshift-gitops-operator + namespace: openshift-operators +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + startingCSV: openshift-gitops-operator.v1.2.0 +status: + installplan: + apiVersion: operators.coreos.com/v1alpha1 + kind: InstallPlan + name: install-rzdwt + uuid: 7a772cec-f487-4cf9-8689-7c533c212c82 + lastUpdated: '2021-08-24T03:46:16Z' + installedCSV: openshift-gitops-operator.v1.2.0 + currentCSV: openshift-gitops-operator.v1.2.0 + installPlanRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: InstallPlan + name: install-rzdwt + namespace: openshift-operators + resourceVersion: '50025' + uid: 7a772cec-f487-4cf9-8689-7c533c212c82 + state: AtLatestKnown + catalogHealth: + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: certified-operators + namespace: openshift-marketplace + resourceVersion: '48843' + uid: 6c1dc387-00b4-4bb7-86f3-9e349a55abf0 + healthy: true + lastUpdated: '2021-08-24T02:43:15Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: community-operators + namespace: openshift-marketplace + resourceVersion: '47352' + uid: 7d63bae9-06a9-434b-be50-af60fc73c19d + healthy: true + lastUpdated: '2021-08-24T02:43:15Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-marketplace + namespace: openshift-marketplace + resourceVersion: '48061' + uid: ef6d3590-a326-4ceb-bced-e180c50ff314 + healthy: true + lastUpdated: '2021-08-24T02:43:15Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-operators + namespace: openshift-marketplace + resourceVersion: '46591' + uid: 1a6025d8-c166-4a9a-a608-c051cfc904a8 + healthy: true + lastUpdated: '2021-08-24T02:43:15Z' + conditions: + - lastTransitionTime: '2021-08-24T02:43:15Z' + message: all available catalogsources are healthy + reason: AllCatalogSourcesHealthy + status: 'False' + type: CatalogSourcesUnhealthy + - lastTransitionTime: '2021-08-24T02:53:03Z' + message: >- + api-server resource not found installing CustomResourceDefinition + gitopsservices.pipelines.openshift.io: GroupVersionKind + apiextensions.k8s.io/v1beta1, Kind=CustomResourceDefinition not found on + the cluster. This API may have been deprecated and removed, see + https://kubernetes.io/docs/reference/using-api/deprecation-guide/ for + more information. + reason: InstallComponentFailed + status: 'True' + type: InstallPlanFailed + installPlanGeneration: 1 diff --git a/resource_customizations/operators.coreos.com/Subscription/testdata/install_plan_missing.yaml b/resource_customizations/operators.coreos.com/Subscription/testdata/install_plan_missing.yaml new file mode 100644 index 0000000000000..c3a649093569a --- /dev/null +++ b/resource_customizations/operators.coreos.com/Subscription/testdata/install_plan_missing.yaml @@ -0,0 +1,139 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + selfLink: >- + /apis/operators.coreos.com/v1alpha1/namespaces/openshift-operators/subscriptions/openshift-gitops-operator + resourceVersion: '147969' + name: openshift-gitops-operator + uid: 59318244-d23a-47c1-9f23-38a3ccaaf6f8 + creationTimestamp: '2021-08-30T21:43:17Z' + generation: 1 + managedFields: + - apiVersion: operators.coreos.com/v1alpha1 + fieldsType: FieldsV1 + fieldsV1: + 'f:spec': + .: {} + 'f:channel': {} + 'f:installPlanApproval': {} + 'f:name': {} + 'f:source': {} + 'f:sourceNamespace': {} + 'f:startingCSV': {} + manager: Mozilla + operation: Update + time: '2021-08-30T21:43:17Z' + - apiVersion: operators.coreos.com/v1alpha1 + fieldsType: FieldsV1 + fieldsV1: + 'f:metadata': + 'f:labels': + .: {} + 'f:operators.coreos.com/openshift-gitops-operator.openshift-operators': {} + manager: olm + operation: Update + time: '2021-08-30T21:43:17Z' + - apiVersion: operators.coreos.com/v1alpha1 + fieldsType: FieldsV1 + fieldsV1: + 'f:status': + 'f:installedCSV': {} + 'f:currentCSV': {} + 'f:catalogHealth': {} + 'f:installPlanRef': + .: {} + 'f:apiVersion': {} + 'f:kind': {} + 'f:name': {} + 'f:namespace': {} + 'f:resourceVersion': {} + 'f:uid': {} + 'f:installPlanGeneration': {} + 'f:conditions': {} + .: {} + 'f:installplan': + .: {} + 'f:apiVersion': {} + 'f:kind': {} + 'f:name': {} + 'f:uuid': {} + 'f:lastUpdated': {} + 'f:state': {} + manager: catalog + operation: Update + time: '2021-08-30T21:43:19Z' + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + startingCSV: openshift-gitops-operator.v1.2.0 +status: + installplan: + apiVersion: operators.coreos.com/v1alpha1 + kind: InstallPlan + name: install-jx26v + uuid: 8566ad1f-c3ea-4367-aba5-db021b8cef45 + lastUpdated: '2021-08-30T21:43:30Z' + installedCSV: openshift-gitops-operator.v1.2.0 + currentCSV: openshift-gitops-operator.v1.2.0 + installPlanRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: InstallPlan + name: install-jx26v + namespace: openshift-operators + resourceVersion: '147595' + uid: 8566ad1f-c3ea-4367-aba5-db021b8cef45 + state: AtLatestKnown + catalogHealth: + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: certified-operators + namespace: openshift-marketplace + resourceVersion: '145894' + uid: 12fb6b00-6839-4360-89ea-0ed98cdc94f1 + healthy: true + lastUpdated: '2021-08-30T21:43:17Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: community-operators + namespace: openshift-marketplace + resourceVersion: '145058' + uid: e8913c52-5002-404f-92a4-d7b6eb35ea54 + healthy: true + lastUpdated: '2021-08-30T21:43:17Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-marketplace + namespace: openshift-marketplace + resourceVersion: '143953' + uid: 5bc9368c-50ee-4079-b66c-e32c8f75fa52 + healthy: true + lastUpdated: '2021-08-30T21:43:17Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-operators + namespace: openshift-marketplace + resourceVersion: '143604' + uid: 57a67470-3344-48db-8790-91853b62b650 + healthy: true + lastUpdated: '2021-08-30T21:43:17Z' + conditions: + - lastTransitionTime: '2021-08-30T21:43:17Z' + message: all available catalogsources are healthy + reason: AllCatalogSourcesHealthy + status: 'False' + type: CatalogSourcesUnhealthy + - lastTransitionTime: '2021-08-30T21:43:30Z' + reason: ReferencedInstallPlanNotFound + status: 'True' + type: InstallPlanMissing + installPlanGeneration: 1 diff --git a/resource_customizations/operators.coreos.com/Subscription/testdata/install_plan_pending.yaml b/resource_customizations/operators.coreos.com/Subscription/testdata/install_plan_pending.yaml new file mode 100644 index 0000000000000..c581e32b7666f --- /dev/null +++ b/resource_customizations/operators.coreos.com/Subscription/testdata/install_plan_pending.yaml @@ -0,0 +1,78 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + startingCSV: openshift-gitops-operator.v1.2.0 +status: + catalogHealth: + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: certified-operators + namespace: openshift-marketplace + resourceVersion: '720254' + uid: dba4fefa-78fa-45c2-9801-d551a086a229 + healthy: true + lastUpdated: '2021-08-25T20:55:30Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: community-operators + namespace: openshift-marketplace + resourceVersion: '720253' + uid: 3e16e348-5b8c-4382-bd71-30fd97836a6c + healthy: true + lastUpdated: '2021-08-25T20:55:30Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-marketplace + namespace: openshift-marketplace + resourceVersion: '707692' + uid: 04003c1c-5414-43e7-ba5e-738addfa2443 + healthy: true + lastUpdated: '2021-08-25T20:55:30Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-operators + namespace: openshift-marketplace + resourceVersion: '716643' + uid: 4cff9c98-14aa-4ebf-a238-2fb8fc48edb6 + healthy: true + lastUpdated: '2021-08-25T20:55:30Z' + conditions: + - lastTransitionTime: '2021-08-25T20:55:30Z' + message: all available catalogsources are healthy + reason: AllCatalogSourcesHealthy + status: 'False' + type: CatalogSourcesUnhealthy + - lastTransitionTime: '2021-08-25T20:55:36Z' + reason: Installing + status: 'True' + type: InstallPlanPending + currentCSV: openshift-gitops-operator.v1.2.0 + installPlanGeneration: 2 + installPlanRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: InstallPlan + name: install-gldzx + namespace: openshift-operators + resourceVersion: '736450' + uid: cc3edd24-ae7c-4ddf-b924-aee1611ff4a1 + installplan: + apiVersion: operators.coreos.com/v1alpha1 + kind: InstallPlan + name: install-gldzx + uuid: cc3edd24-ae7c-4ddf-b924-aee1611ff4a1 + lastUpdated: '2021-08-25T20:55:36Z' + state: UpgradePending diff --git a/resource_customizations/operators.coreos.com/Subscription/testdata/resolution_failed.yaml b/resource_customizations/operators.coreos.com/Subscription/testdata/resolution_failed.yaml new file mode 100644 index 0000000000000..7c6e4800a68aa --- /dev/null +++ b/resource_customizations/operators.coreos.com/Subscription/testdata/resolution_failed.yaml @@ -0,0 +1,72 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + creationTimestamp: '2021-08-31T17:39:09Z' + generation: 1 + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' + operators.coreos.com/openshift-gitops123-operator.openshift-operators: '' + name: openshift-gitops123-operator + namespace: openshift-operators + resourceVersion: '83862' + uid: db43fe71-7937-47d4-84e9-382316c1403c +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops123-operator + source: redhat-operators66 + sourceNamespace: openshift-marketplace + startingCSV: openshift-gitops-operator.v1.2.0 +status: + catalogHealth: + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: certified-operators + namespace: openshift-marketplace + resourceVersion: '82603' + uid: 3bca2bd4-5376-4a66-929a-7374540011ab + healthy: true + lastUpdated: '2021-08-31T17:39:09Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: community-operators + namespace: openshift-marketplace + resourceVersion: '81603' + uid: de132714-31c7-441e-9a62-bcd2f3908b75 + healthy: true + lastUpdated: '2021-08-31T17:39:09Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-marketplace + namespace: openshift-marketplace + resourceVersion: '81610' + uid: 36652a07-cd6e-46a5-a53f-74520250b69a + healthy: true + lastUpdated: '2021-08-31T17:39:09Z' + - catalogSourceRef: + apiVersion: operators.coreos.com/v1alpha1 + kind: CatalogSource + name: redhat-operators + namespace: openshift-marketplace + resourceVersion: '81602' + uid: f73d6576-5955-4de2-bf4c-2b4c5c0a9077 + healthy: true + lastUpdated: '2021-08-31T17:39:09Z' + conditions: + - lastTransitionTime: '2021-08-31T17:39:09Z' + message: targeted catalogsource openshift-marketplace/redhat-operators66 missing + reason: UnhealthyCatalogSourceFound + status: 'True' + type: CatalogSourcesUnhealthy + - message: >- + constraints not satisfiable: no operators found from catalog + redhat-operators66 in namespace openshift-marketplace referenced by + subscription openshift-gitops123-operator, subscription + openshift-gitops123-operator exists + reason: ConstraintsNotSatisfiable + status: 'True' + type: ResolutionFailed + lastUpdated: '2021-08-31T17:39:12Z' diff --git a/resource_customizations/platform.confluent.io/Connect/health.lua b/resource_customizations/platform.confluent.io/Connect/health.lua new file mode 100644 index 0000000000000..329585870952b --- /dev/null +++ b/resource_customizations/platform.confluent.io/Connect/health.lua @@ -0,0 +1,19 @@ +hs = {} +if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "RUNNING" then + hs.status = "Healthy" + hs.message = "Connect running" + return hs + end + if obj.status.phase == "PROVISIONING" then + hs.status = "Progressing" + hs.message = "Connect provisioning" + return hs + end + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Connect" +return hs diff --git a/resource_customizations/platform.confluent.io/Connect/health_test.yaml b/resource_customizations/platform.confluent.io/Connect/health_test.yaml new file mode 100644 index 0000000000000..9cff61ac93a0e --- /dev/null +++ b/resource_customizations/platform.confluent.io/Connect/health_test.yaml @@ -0,0 +1,9 @@ +tests: + - healthStatus: + status: Progressing + message: Connect provisioning + inputPath: testdata/progressing.yaml + - healthStatus: + status: Healthy + message: Connect running + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/platform.confluent.io/Connect/testdata/healthy.yaml b/resource_customizations/platform.confluent.io/Connect/testdata/healthy.yaml new file mode 100644 index 0000000000000..92c46d9ed18c5 --- /dev/null +++ b/resource_customizations/platform.confluent.io/Connect/testdata/healthy.yaml @@ -0,0 +1,49 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: Connect +metadata: + finalizers: + - connect.finalizers.platform.confluent.io + generation: 1 + name: connect + namespace: confluent +spec: + dependencies: + kafka: + bootstrapEndpoint: kafka:9071 + replicas: 1 +status: + clusterName: connect + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:03:56Z" + lastTransitionTime: "2021-08-11T10:09:01Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:03:56Z" + lastTransitionTime: "2021-08-11T10:09:01Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + - lastProbeTime: "2021-08-11T10:03:56Z" + lastTransitionTime: "2021-08-11T10:03:56Z" + message: Cluster is not being garbage collected + reason: ClusterNotShrunk + status: "False" + type: platform.confluent.io/garbage-collecting + currentReplicas: 1 + groupId: confluent.connect + internalTopicNames: + - confluent.connect-offsets + - confluent.connect-configs + - confluent.connect-status + kafka: + bootstrapEndpoint: kafka:9071 + operatorVersion: v0.174.13 + phase: RUNNING + readyReplicas: 1 + replicas: 1 + restConfig: + internalEndpoint: http://connect.confluent.svc.cluster.local:8083 diff --git a/resource_customizations/platform.confluent.io/Connect/testdata/progressing.yaml b/resource_customizations/platform.confluent.io/Connect/testdata/progressing.yaml new file mode 100644 index 0000000000000..74022676f6af4 --- /dev/null +++ b/resource_customizations/platform.confluent.io/Connect/testdata/progressing.yaml @@ -0,0 +1,49 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: Connect +metadata: + finalizers: + - connect.finalizers.platform.confluent.io + generation: 1 + name: connect + namespace: confluent +spec: + dependencies: + kafka: + bootstrapEndpoint: kafka:9071 + replicas: 1 +status: + clusterName: connect + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:03:56Z" + lastTransitionTime: "2021-08-11T10:09:01Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:03:56Z" + lastTransitionTime: "2021-08-11T10:09:01Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + - lastProbeTime: "2021-08-11T10:03:56Z" + lastTransitionTime: "2021-08-11T10:03:56Z" + message: Cluster is not being garbage collected + reason: ClusterNotShrunk + status: "False" + type: platform.confluent.io/garbage-collecting + currentReplicas: 1 + groupId: confluent.connect + internalTopicNames: + - confluent.connect-offsets + - confluent.connect-configs + - confluent.connect-status + kafka: + bootstrapEndpoint: kafka:9071 + operatorVersion: v0.174.13 + phase: PROVISIONING + readyReplicas: 1 + replicas: 1 + restConfig: + internalEndpoint: http://connect.confluent.svc.cluster.local:8083 diff --git a/resource_customizations/platform.confluent.io/ControlCenter/health.lua b/resource_customizations/platform.confluent.io/ControlCenter/health.lua new file mode 100644 index 0000000000000..6b54872a2d0a0 --- /dev/null +++ b/resource_customizations/platform.confluent.io/ControlCenter/health.lua @@ -0,0 +1,19 @@ +hs = {} +if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "RUNNING" then + hs.status = "Healthy" + hs.message = "ControlCenter running" + return hs + end + if obj.status.phase == "PROVISIONING" then + hs.status = "Progressing" + hs.message = "ControlCenter provisioning" + return hs + end + end +end + +hs.status = "Progressing" +hs.message = "Waiting for ControlCenter" +return hs diff --git a/resource_customizations/platform.confluent.io/ControlCenter/health_test.yaml b/resource_customizations/platform.confluent.io/ControlCenter/health_test.yaml new file mode 100644 index 0000000000000..b5f017035f090 --- /dev/null +++ b/resource_customizations/platform.confluent.io/ControlCenter/health_test.yaml @@ -0,0 +1,9 @@ +tests: + - healthStatus: + status: Progressing + message: ControlCenter provisioning + inputPath: testdata/progressing.yaml + - healthStatus: + status: Healthy + message: ControlCenter running + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/platform.confluent.io/ControlCenter/testdata/healthy.yaml b/resource_customizations/platform.confluent.io/ControlCenter/testdata/healthy.yaml new file mode 100644 index 0000000000000..ae2e90e5a2927 --- /dev/null +++ b/resource_customizations/platform.confluent.io/ControlCenter/testdata/healthy.yaml @@ -0,0 +1,47 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: ControlCenter +metadata: + finalizers: + - controlcenter.finalizers.platform.confluent.io + generation: 1 + name: controlcenter + namespace: confluent +spec: + dataVolumeCapacity: 10Gi + dependencies: + connect: + - name: connect + url: http://connect:8083 + ksqldb: + - name: ksqldb + url: http://ksqldb:8088 + schemaRegistry: + url: http://schemaregistry:8081 + replicas: 1 +status: + clusterName: controlcenter + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:08:37Z" + lastTransitionTime: "2021-08-11T10:10:37Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:08:37Z" + lastTransitionTime: "2021-08-11T10:10:37Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + controlCenterName: _confluent-controlcenter + currentReplicas: 1 + id: 0 + kafka: + bootstrapEndpoint: kafka.confluent.svc.cluster.local:9071 + operatorVersion: v0.174.13 + phase: RUNNING + readyReplicas: 1 + replicas: 1 + restConfig: + internalEndpoint: http://controlcenter.confluent.svc.cluster.local:9021 diff --git a/resource_customizations/platform.confluent.io/ControlCenter/testdata/progressing.yaml b/resource_customizations/platform.confluent.io/ControlCenter/testdata/progressing.yaml new file mode 100644 index 0000000000000..be3c6ea56bafb --- /dev/null +++ b/resource_customizations/platform.confluent.io/ControlCenter/testdata/progressing.yaml @@ -0,0 +1,46 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: ControlCenter +metadata: + finalizers: + - controlcenter.finalizers.platform.confluent.io + generation: 1 + name: controlcenter + namespace: confluent +spec: + dataVolumeCapacity: 10Gi + dependencies: + connect: + - name: connect + url: http://connect:8083 + ksqldb: + - name: ksqldb + url: http://ksqldb:8088 + schemaRegistry: + url: http://schemaregistry:8081 + replicas: 1 +status: + clusterName: controlcenter + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-02T11:44:51Z" + lastTransitionTime: "2021-08-06T12:50:58Z" + message: Deployment does not have minimum availability. + reason: MinimumReplicasUnavailable + status: "False" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-02T11:44:51Z" + lastTransitionTime: "2021-08-06T12:50:58Z" + message: Kubernetes resources not ready. + reason: KubernetesResourcesNotReady + status: "False" + type: platform.confluent.io/resources-ready + controlCenterName: _confluent-controlcenter + currentReplicas: 1 + id: 0 + kafka: + bootstrapEndpoint: kafka.confluent.svc.cluster.local:9071 + operatorVersion: v0.174.13 + phase: PROVISIONING + replicas: 1 + restConfig: + internalEndpoint: http://controlcenter.confluent.svc.cluster.local:9021 diff --git a/resource_customizations/platform.confluent.io/Kafka/health.lua b/resource_customizations/platform.confluent.io/Kafka/health.lua new file mode 100644 index 0000000000000..00033faf3132b --- /dev/null +++ b/resource_customizations/platform.confluent.io/Kafka/health.lua @@ -0,0 +1,19 @@ +hs = {} +if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "RUNNING" then + hs.status = "Healthy" + hs.message = "Kafka running" + return hs + end + if obj.status.phase == "PROVISIONING" then + hs.status = "Progressing" + hs.message = "Kafka provisioning" + return hs + end + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Kafka" +return hs diff --git a/resource_customizations/platform.confluent.io/Kafka/health_test.yaml b/resource_customizations/platform.confluent.io/Kafka/health_test.yaml new file mode 100644 index 0000000000000..1fb857ab1159a --- /dev/null +++ b/resource_customizations/platform.confluent.io/Kafka/health_test.yaml @@ -0,0 +1,9 @@ +tests: + - healthStatus: + status: Progressing + message: Kafka provisioning + inputPath: testdata/progressing.yaml + - healthStatus: + status: Healthy + message: Kafka running + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/platform.confluent.io/Kafka/testdata/healthy.yaml b/resource_customizations/platform.confluent.io/Kafka/testdata/healthy.yaml new file mode 100644 index 0000000000000..3388533f78cba --- /dev/null +++ b/resource_customizations/platform.confluent.io/Kafka/testdata/healthy.yaml @@ -0,0 +1,63 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: Kafka +metadata: + generation: 1 + name: kafka + namespace: confluent +spec: + dataVolumeCapacity: 10Gi + metricReporter: + enabled: true + replicas: 3 +status: + clusterName: kafka + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:06:58Z" + lastTransitionTime: "2021-08-11T10:08:42Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:06:58Z" + lastTransitionTime: "2021-08-11T10:08:42Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + - lastProbeTime: "2021-08-11T10:06:58Z" + lastTransitionTime: "2021-08-11T10:06:58Z" + message: Cluster is not rolling, ignore=false + reason: ClusterNotRolling + status: "False" + type: platform.confluent.io/rolling + - lastProbeTime: "2021-08-11T10:06:58Z" + lastTransitionTime: "2021-08-11T10:06:58Z" + message: Cluster is not being garbage collected + reason: ClusterNotShrunk + status: "False" + type: platform.confluent.io/garbage-collecting + currentReplicas: 3 + listeners: + external: + client: |- + bootstrap.servers=kafka.confluent.svc.cluster.local:9092 + security.protocol=PLAINTEXT + internalEndpoint: kafka.confluent.svc.cluster.local:9092 + internal: + client: |- + bootstrap.servers=kafka.confluent.svc.cluster.local:9071 + security.protocol=PLAINTEXT + internalEndpoint: kafka.confluent.svc.cluster.local:9071 + replication: + internalEndpoint: kafka.confluent.svc.cluster.local:9072 + minISR: 2 + operatorVersion: v0.174.13 + phase: RUNNING + readyReplicas: 3 + replicas: 3 + replicationFactor: 3 + services: + kafka-rest: + internalEndpoint: http://kafka.confluent.svc.cluster.local:8090 + zookeeperConnect: zookeeper.confluent.svc.cluster.local:2181/kafka-confluent diff --git a/resource_customizations/platform.confluent.io/Kafka/testdata/progressing.yaml b/resource_customizations/platform.confluent.io/Kafka/testdata/progressing.yaml new file mode 100644 index 0000000000000..c2766820c3105 --- /dev/null +++ b/resource_customizations/platform.confluent.io/Kafka/testdata/progressing.yaml @@ -0,0 +1,63 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: Kafka +metadata: + generation: 1 + name: kafka + namespace: confluent +spec: + dataVolumeCapacity: 10Gi + metricReporter: + enabled: true + replicas: 3 +status: + clusterName: kafka + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:06:58Z" + lastTransitionTime: "2021-08-11T10:08:42Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:06:58Z" + lastTransitionTime: "2021-08-11T10:08:42Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + - lastProbeTime: "2021-08-11T10:06:58Z" + lastTransitionTime: "2021-08-11T10:06:58Z" + message: Cluster is not rolling, ignore=false + reason: ClusterNotRolling + status: "False" + type: platform.confluent.io/rolling + - lastProbeTime: "2021-08-11T10:06:58Z" + lastTransitionTime: "2021-08-11T10:06:58Z" + message: Cluster is not being garbage collected + reason: ClusterNotShrunk + status: "False" + type: platform.confluent.io/garbage-collecting + currentReplicas: 3 + listeners: + external: + client: |- + bootstrap.servers=kafka.confluent.svc.cluster.local:9092 + security.protocol=PLAINTEXT + internalEndpoint: kafka.confluent.svc.cluster.local:9092 + internal: + client: |- + bootstrap.servers=kafka.confluent.svc.cluster.local:9071 + security.protocol=PLAINTEXT + internalEndpoint: kafka.confluent.svc.cluster.local:9071 + replication: + internalEndpoint: kafka.confluent.svc.cluster.local:9072 + minISR: 2 + operatorVersion: v0.174.13 + phase: PROVISIONING + readyReplicas: 3 + replicas: 3 + replicationFactor: 3 + services: + kafka-rest: + internalEndpoint: http://kafka.confluent.svc.cluster.local:8090 + zookeeperConnect: zookeeper.confluent.svc.cluster.local:2181/kafka-confluent diff --git a/resource_customizations/platform.confluent.io/KsqlDB/health.lua b/resource_customizations/platform.confluent.io/KsqlDB/health.lua new file mode 100644 index 0000000000000..cfada56c772a5 --- /dev/null +++ b/resource_customizations/platform.confluent.io/KsqlDB/health.lua @@ -0,0 +1,19 @@ +hs = {} +if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "RUNNING" then + hs.status = "Healthy" + hs.message = "KsqlDB running" + return hs + end + if obj.status.phase == "PROVISIONING" then + hs.status = "Progressing" + hs.message = "KsqlDB provisioning" + return hs + end + end +end + +hs.status = "Progressing" +hs.message = "Waiting for KsqlDB" +return hs diff --git a/resource_customizations/platform.confluent.io/KsqlDB/health_test.yaml b/resource_customizations/platform.confluent.io/KsqlDB/health_test.yaml new file mode 100644 index 0000000000000..70f1a0a39b4c7 --- /dev/null +++ b/resource_customizations/platform.confluent.io/KsqlDB/health_test.yaml @@ -0,0 +1,9 @@ +tests: + - healthStatus: + status: Progressing + message: KsqlDB provisioning + inputPath: testdata/progressing.yaml + - healthStatus: + status: Healthy + message: KsqlDB running + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/platform.confluent.io/KsqlDB/testdata/healthy.yaml b/resource_customizations/platform.confluent.io/KsqlDB/testdata/healthy.yaml new file mode 100644 index 0000000000000..be4a282275510 --- /dev/null +++ b/resource_customizations/platform.confluent.io/KsqlDB/testdata/healthy.yaml @@ -0,0 +1,44 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: KsqlDB +metadata: + generation: 1 + name: ksqldb + namespace: confluent +spec: + dataVolumeCapacity: 10Gi + replicas: 1 +status: + clusterName: ksqldb + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:08:31Z" + lastTransitionTime: "2021-08-11T10:10:17Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:08:31Z" + lastTransitionTime: "2021-08-11T10:10:17Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + - lastProbeTime: "2021-08-11T10:08:31Z" + lastTransitionTime: "2021-08-11T10:08:31Z" + message: Cluster is not being garbage collected + reason: ClusterNotShrunk + status: "False" + type: platform.confluent.io/garbage-collecting + currentReplicas: 1 + internalTopicNames: + - _confluent-ksql-confluent.ksqldb__command_topic + - _confluent-ksql-confluent.ksqldb__configs + kafka: + bootstrapEndpoint: kafka.confluent.svc.cluster.local:9071 + operatorVersion: v0.174.13 + phase: RUNNING + readyReplicas: 1 + replicas: 1 + restConfig: + internalEndpoint: http://ksqldb.confluent.svc.cluster.local:8088 + serviceId: confluent.ksqldb_ diff --git a/resource_customizations/platform.confluent.io/KsqlDB/testdata/progressing.yaml b/resource_customizations/platform.confluent.io/KsqlDB/testdata/progressing.yaml new file mode 100644 index 0000000000000..bcb6076771206 --- /dev/null +++ b/resource_customizations/platform.confluent.io/KsqlDB/testdata/progressing.yaml @@ -0,0 +1,44 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: KsqlDB +metadata: + generation: 1 + name: ksqldb + namespace: confluent +spec: + dataVolumeCapacity: 10Gi + replicas: 1 +status: + clusterName: ksqldb + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:08:31Z" + lastTransitionTime: "2021-08-11T10:10:17Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:08:31Z" + lastTransitionTime: "2021-08-11T10:10:17Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + - lastProbeTime: "2021-08-11T10:08:31Z" + lastTransitionTime: "2021-08-11T10:08:31Z" + message: Cluster is not being garbage collected + reason: ClusterNotShrunk + status: "False" + type: platform.confluent.io/garbage-collecting + currentReplicas: 1 + internalTopicNames: + - _confluent-ksql-confluent.ksqldb__command_topic + - _confluent-ksql-confluent.ksqldb__configs + kafka: + bootstrapEndpoint: kafka.confluent.svc.cluster.local:9071 + operatorVersion: v0.174.13 + phase: PROVISIONING + readyReplicas: 1 + replicas: 1 + restConfig: + internalEndpoint: http://ksqldb.confluent.svc.cluster.local:8088 + serviceId: confluent.ksqldb_ diff --git a/resource_customizations/platform.confluent.io/SchemaRegistry/health.lua b/resource_customizations/platform.confluent.io/SchemaRegistry/health.lua new file mode 100644 index 0000000000000..e5542ba826ed7 --- /dev/null +++ b/resource_customizations/platform.confluent.io/SchemaRegistry/health.lua @@ -0,0 +1,19 @@ +hs = {} +if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "RUNNING" then + hs.status = "Healthy" + hs.message = "SchemaRegistry running" + return hs + end + if obj.status.phase == "PROVISIONING" then + hs.status = "Progressing" + hs.message = "SchemaRegistry provisioning" + return hs + end + end +end + +hs.status = "Progressing" +hs.message = "Waiting for SchemaRegistry" +return hs diff --git a/resource_customizations/platform.confluent.io/SchemaRegistry/health_test.yaml b/resource_customizations/platform.confluent.io/SchemaRegistry/health_test.yaml new file mode 100644 index 0000000000000..2d44f6ac4520b --- /dev/null +++ b/resource_customizations/platform.confluent.io/SchemaRegistry/health_test.yaml @@ -0,0 +1,9 @@ +tests: + - healthStatus: + status: Progressing + message: SchemaRegistry provisioning + inputPath: testdata/progressing.yaml + - healthStatus: + status: Healthy + message: SchemaRegistry running + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/platform.confluent.io/SchemaRegistry/testdata/healthy.yaml b/resource_customizations/platform.confluent.io/SchemaRegistry/testdata/healthy.yaml new file mode 100644 index 0000000000000..8eca77ea1161b --- /dev/null +++ b/resource_customizations/platform.confluent.io/SchemaRegistry/testdata/healthy.yaml @@ -0,0 +1,45 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: SchemaRegistry +metadata: + finalizers: + - schemaregistry.finalizers.platform.confluent.io + generation: 1 + name: schemaregistry + namespace: confluent +spec: + replicas: 1 +status: + clusterName: schemaregistry + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:08:32Z" + lastTransitionTime: "2021-08-11T10:09:41Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:08:32Z" + lastTransitionTime: "2021-08-11T10:09:41Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + - lastProbeTime: "2021-08-11T10:08:32Z" + lastTransitionTime: "2021-08-11T10:08:32Z" + message: Cluster is not being garbage collected + reason: ClusterNotShrunk + status: "False" + type: platform.confluent.io/garbage-collecting + currentReplicas: 1 + groupId: id_schemaregistry_confluent + internalTopicNames: + - _schemas_schemaregistry_confluent + kafka: + bootstrapEndpoint: kafka.confluent.svc.cluster.local:9071 + metricPrefix: schemaregistry_confluent + operatorVersion: v0.174.13 + phase: RUNNING + readyReplicas: 1 + replicas: 1 + restConfig: + internalEndpoint: http://schemaregistry.confluent.svc.cluster.local:8081 diff --git a/resource_customizations/platform.confluent.io/SchemaRegistry/testdata/progressing.yaml b/resource_customizations/platform.confluent.io/SchemaRegistry/testdata/progressing.yaml new file mode 100644 index 0000000000000..a12232bbdae85 --- /dev/null +++ b/resource_customizations/platform.confluent.io/SchemaRegistry/testdata/progressing.yaml @@ -0,0 +1,45 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: SchemaRegistry +metadata: + finalizers: + - schemaregistry.finalizers.platform.confluent.io + generation: 1 + name: schemaregistry + namespace: confluent +spec: + replicas: 1 +status: + clusterName: schemaregistry + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:08:32Z" + lastTransitionTime: "2021-08-11T10:09:41Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:08:32Z" + lastTransitionTime: "2021-08-11T10:09:41Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + - lastProbeTime: "2021-08-11T10:08:32Z" + lastTransitionTime: "2021-08-11T10:08:32Z" + message: Cluster is not being garbage collected + reason: ClusterNotShrunk + status: "False" + type: platform.confluent.io/garbage-collecting + currentReplicas: 1 + groupId: id_schemaregistry_confluent + internalTopicNames: + - _schemas_schemaregistry_confluent + kafka: + bootstrapEndpoint: kafka.confluent.svc.cluster.local:9071 + metricPrefix: schemaregistry_confluent + operatorVersion: v0.174.13 + phase: PROVISIONING + readyReplicas: 1 + replicas: 1 + restConfig: + internalEndpoint: http://schemaregistry.confluent.svc.cluster.local:8081 diff --git a/resource_customizations/platform.confluent.io/Zookeeper/health.lua b/resource_customizations/platform.confluent.io/Zookeeper/health.lua new file mode 100644 index 0000000000000..20c0fa847fe5f --- /dev/null +++ b/resource_customizations/platform.confluent.io/Zookeeper/health.lua @@ -0,0 +1,19 @@ +hs = {} +if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "RUNNING" then + hs.status = "Healthy" + hs.message = "Zookeeper running" + return hs + end + if obj.status.phase == "PROVISIONING" then + hs.status = "Progressing" + hs.message = "Zookeeper provisioning" + return hs + end + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Zookeeper" +return hs diff --git a/resource_customizations/platform.confluent.io/Zookeeper/health_test.yaml b/resource_customizations/platform.confluent.io/Zookeeper/health_test.yaml new file mode 100644 index 0000000000000..7251ec4a06d1a --- /dev/null +++ b/resource_customizations/platform.confluent.io/Zookeeper/health_test.yaml @@ -0,0 +1,9 @@ +tests: + - healthStatus: + status: Progressing + message: Zookeeper provisioning + inputPath: testdata/progressing.yaml + - healthStatus: + status: Healthy + message: Zookeeper running + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/platform.confluent.io/Zookeeper/testdata/healthy.yaml b/resource_customizations/platform.confluent.io/Zookeeper/testdata/healthy.yaml new file mode 100644 index 0000000000000..7bc00dcae2e2b --- /dev/null +++ b/resource_customizations/platform.confluent.io/Zookeeper/testdata/healthy.yaml @@ -0,0 +1,44 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: Zookeeper +metadata: + finalizers: + - zookeeper.finalizers.platform.confluent.io + generation: 1 + name: zookeeper + namespace: confluent +spec: + dataVolumeCapacity: 10Gi + logVolumeCapacity: 10Gi + podTemplate: + annotations: + traffic.sidecar.istio.io/excludeInboundPorts: 2888,3888 + traffic.sidecar.istio.io/excludeOutboundPorts: 2888,3888 + replicas: 3 +status: + clusterName: zookeeper + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:04:53Z" + lastTransitionTime: "2021-08-11T10:06:48Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:04:53Z" + lastTransitionTime: "2021-08-11T10:06:48Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + - lastProbeTime: "2021-08-11T10:04:53Z" + lastTransitionTime: "2021-08-11T10:04:53Z" + message: Cluster is not being garbage collected + reason: ClusterNotShrunk + status: "False" + type: platform.confluent.io/garbage-collecting + currentReplicas: 3 + endpoint: zookeeper.confluent.svc.cluster.local:2181 + operatorVersion: v0.174.13 + phase: RUNNING + readyReplicas: 3 + replicas: 3 diff --git a/resource_customizations/platform.confluent.io/Zookeeper/testdata/progressing.yaml b/resource_customizations/platform.confluent.io/Zookeeper/testdata/progressing.yaml new file mode 100644 index 0000000000000..be0496a442eed --- /dev/null +++ b/resource_customizations/platform.confluent.io/Zookeeper/testdata/progressing.yaml @@ -0,0 +1,44 @@ +apiVersion: platform.confluent.io/v1beta1 +kind: Zookeeper +metadata: + finalizers: + - zookeeper.finalizers.platform.confluent.io + generation: 1 + name: zookeeper + namespace: confluent +spec: + dataVolumeCapacity: 10Gi + logVolumeCapacity: 10Gi + podTemplate: + annotations: + traffic.sidecar.istio.io/excludeInboundPorts: 2888,3888 + traffic.sidecar.istio.io/excludeOutboundPorts: 2888,3888 + replicas: 3 +status: + clusterName: zookeeper + clusterNamespace: confluent + conditions: + - lastProbeTime: "2021-08-11T10:04:53Z" + lastTransitionTime: "2021-08-11T10:06:48Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: platform.confluent.io/statefulset-available + - lastProbeTime: "2021-08-11T10:04:53Z" + lastTransitionTime: "2021-08-11T10:06:48Z" + message: Kubernetes resources ready. + reason: KubernetesResourcesReady + status: "True" + type: platform.confluent.io/resources-ready + - lastProbeTime: "2021-08-11T10:04:53Z" + lastTransitionTime: "2021-08-11T10:04:53Z" + message: Cluster is not being garbage collected + reason: ClusterNotShrunk + status: "False" + type: platform.confluent.io/garbage-collecting + currentReplicas: 3 + endpoint: zookeeper.confluent.svc.cluster.local:2181 + operatorVersion: v0.174.13 + phase: PROVISIONING + readyReplicas: 3 + replicas: 3 diff --git a/resource_customizations/route.openshift.io/Route/health.lua b/resource_customizations/route.openshift.io/Route/health.lua new file mode 100644 index 0000000000000..4a670362e4380 --- /dev/null +++ b/resource_customizations/route.openshift.io/Route/health.lua @@ -0,0 +1,37 @@ +health_status = {} +if obj.status ~= nil then + if obj.status.ingress ~= nil then + numIngressRules = 0 + for _, ingressRules in pairs(obj.status.ingress) do + numIngressRules = numIngressRules + 1 + numTrue = 0 + numFalse = 0 + if obj.status.ingress ~= nil then + for _, condition in pairs(ingressRules.conditions) do + if condition.type == "Admitted" and condition.status == "True" then + numTrue = numTrue + 1 + elseif condition.type == "Admitted" and condition.status == "False" then + numFalse = numFalse + 1 + end + end + end + health_status.status = 'Test' + end + if numTrue == numIngressRules then + health_status.status = "Healthy" + health_status.message = "Route is healthy" + return health_status + elseif numFalse > 0 then + health_status.status = "Degraded" + health_status.message = "Route is degraded" + return health_status + else + health_status.status = "Progressing" + health_status.message = "Route is still getting admitted" + return health_status + end + end +end +health_status.status = "Progressing" +health_status.message = "Route is still getting admitted" +return health_status \ No newline at end of file diff --git a/resource_customizations/route.openshift.io/Route/health_test.yaml b/resource_customizations/route.openshift.io/Route/health_test.yaml new file mode 100644 index 0000000000000..1293d7d6c9a66 --- /dev/null +++ b/resource_customizations/route.openshift.io/Route/health_test.yaml @@ -0,0 +1,13 @@ +tests: +- healthStatus: + status: Progressing + message: 'Route is still getting admitted' + inputPath: testdata/progressing.yaml +- healthStatus: + status: Degraded + message: 'Route is degraded' + inputPath: testdata/degraded.yaml +- healthStatus: + status: Healthy + message: 'Route is healthy' + inputPath: testdata/healthy.yaml \ No newline at end of file diff --git a/resource_customizations/route.openshift.io/Route/testdata/degraded.yaml b/resource_customizations/route.openshift.io/Route/testdata/degraded.yaml new file mode 100644 index 0000000000000..9d6ec91c83489 --- /dev/null +++ b/resource_customizations/route.openshift.io/Route/testdata/degraded.yaml @@ -0,0 +1,43 @@ +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + annotations: + openshift.io/host.generated: 'true' + resourceVersion: '187177' + name: openshift-gitops-server + namespace: openshift-gitops + ownerReferences: + - apiVersion: argoproj.io/v1alpha1 + kind: ArgoCD + name: openshift-gitops + uid: 09443427-36c8-4680-9a4b-602ae8a45b89 + controller: true + blockOwnerDeletion: true + labels: + app.kubernetes.io/managed-by: openshift-gitops + app.kubernetes.io/name: openshift-gitops-server + app.kubernetes.io/part-of: argocd +spec: + host: >- + openshift-gitops-server-openshift-gitops.apps.dev-svc-4.8-083007.devcluster.openshift.com + to: + kind: Service + name: openshift-gitops-server + weight: 100 + port: + targetPort: https + tls: + termination: passthrough + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None +status: + ingress: + - host: >- + openshift-gitops-server-openshift-gitops.apps.dev-svc-4.8-083007.devcluster.openshift.com + routerName: default + conditions: + - type: Admitted + status: 'False' + lastTransitionTime: '2021-08-30T12:13:34Z' + wildcardPolicy: None + routerCanonicalHostname: router-default.apps.dev-svc-4.8-083007.devcluster.openshift.com diff --git a/resource_customizations/route.openshift.io/Route/testdata/healthy.yaml b/resource_customizations/route.openshift.io/Route/testdata/healthy.yaml new file mode 100644 index 0000000000000..a5eabfcc7e93b --- /dev/null +++ b/resource_customizations/route.openshift.io/Route/testdata/healthy.yaml @@ -0,0 +1,43 @@ +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + annotations: + openshift.io/host.generated: 'true' + resourceVersion: '187177' + name: openshift-gitops-server + namespace: openshift-gitops + ownerReferences: + - apiVersion: argoproj.io/v1alpha1 + kind: ArgoCD + name: openshift-gitops + uid: 09443427-36c8-4680-9a4b-602ae8a45b89 + controller: true + blockOwnerDeletion: true + labels: + app.kubernetes.io/managed-by: openshift-gitops + app.kubernetes.io/name: openshift-gitops-server + app.kubernetes.io/part-of: argocd +spec: + host: >- + openshift-gitops-server-openshift-gitops.apps.dev-svc-4.8-083007.devcluster.openshift.com + to: + kind: Service + name: openshift-gitops-server + weight: 100 + port: + targetPort: https + tls: + termination: passthrough + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None +status: + ingress: + - host: >- + openshift-gitops-server-openshift-gitops.apps.dev-svc-4.8-083007.devcluster.openshift.com + routerName: default + conditions: + - type: Admitted + status: 'True' + lastTransitionTime: '2021-08-30T12:13:34Z' + wildcardPolicy: None + routerCanonicalHostname: router-default.apps.dev-svc-4.8-083007.devcluster.openshift.com diff --git a/resource_customizations/route.openshift.io/Route/testdata/progressing.yaml b/resource_customizations/route.openshift.io/Route/testdata/progressing.yaml new file mode 100644 index 0000000000000..c2d219b0a6dfb --- /dev/null +++ b/resource_customizations/route.openshift.io/Route/testdata/progressing.yaml @@ -0,0 +1,43 @@ +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + annotations: + openshift.io/host.generated: 'true' + resourceVersion: '187177' + name: openshift-gitops-server + namespace: openshift-gitops + ownerReferences: + - apiVersion: argoproj.io/v1alpha1 + kind: ArgoCD + name: openshift-gitops + uid: 09443427-36c8-4680-9a4b-602ae8a45b89 + controller: true + blockOwnerDeletion: true + labels: + app.kubernetes.io/managed-by: openshift-gitops + app.kubernetes.io/name: openshift-gitops-server + app.kubernetes.io/part-of: argocd +spec: + host: >- + openshift-gitops-server-openshift-gitops.apps.dev-svc-4.8-083007.devcluster.openshift.com + to: + kind: Service + name: openshift-gitops-server + weight: 100 + port: + targetPort: https + tls: + termination: passthrough + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None +status: + ingress: + - host: >- + openshift-gitops-server-openshift-gitops.apps.dev-svc-4.8-083007.devcluster.openshift.com + routerName: default + conditions: + - type: Admitted + status: 'Unknown' + lastTransitionTime: '2021-08-30T12:13:34Z' + wildcardPolicy: None + routerCanonicalHostname: router-default.apps.dev-svc-4.8-083007.devcluster.openshift.com diff --git a/server/account/account.go b/server/account/account.go index 1e037dc45508b..a06ebc94b7413 100644 --- a/server/account/account.go +++ b/server/account/account.go @@ -3,6 +3,7 @@ package account import ( "errors" "fmt" + "regexp" "sort" "time" @@ -76,6 +77,22 @@ func (s *Server) UpdatePassword(ctx context.Context, q *account.UpdatePasswordRe } } + //Need to validate password complexity with regular expression + passwordPattern, err := s.settingsMgr.GetPasswordPattern() + if err != nil { + return nil, err + } + + validPasswordRegexp, err := regexp.Compile(passwordPattern) + if err != nil { + return nil, err + } + + if !validPasswordRegexp.Match([]byte(q.NewPassword)) { + err := fmt.Errorf("New password does not match the following expression: %s.", passwordPattern) + return nil, err + } + hashedPassword, err := password.HashPassword(q.NewPassword) if err != nil { return nil, err diff --git a/server/application/application.go b/server/application/application.go index 640d8eaf11ded..d621649059477 100644 --- a/server/application/application.go +++ b/server/application/application.go @@ -212,7 +212,7 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq return existing, nil } if q.Upsert == nil || !*q.Upsert { - return nil, status.Errorf(codes.InvalidArgument, "existing application spec is different, use upsert flag to force update") + return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("application", existing.Spec, a.Spec)) } if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, appRBACName(a)); err != nil { return nil, err @@ -249,7 +249,7 @@ func (s *Server) queryRepoServer(ctx context.Context, a *v1alpha1.Application, a if err != nil { return err } - proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr) + proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) if err != nil { if apierr.IsNotFound(err) { return status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", a.Spec.Project) @@ -331,6 +331,7 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan KubeVersion: serverVersion, ApiVersions: argo.APIGroupsToVersions(apiGroups), HelmRepoCreds: helmCreds, + TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)), }) return err }) @@ -414,6 +415,7 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app KustomizeOptions: kustomizeOptions, Repos: helmRepos, NoCache: true, + TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)), }) return err }); err != nil { @@ -800,7 +802,7 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati } func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Application, validate bool) error { - proj, err := argo.GetAppProject(&app.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr) + proj, err := argo.GetAppProject(&app.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) if err != nil { if apierr.IsNotFound(err) { return status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", app.Spec.Project) @@ -843,17 +845,17 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica } if err := argo.ValidateDestination(ctx, &app.Spec.Destination, s.db); err != nil { - return status.Errorf(codes.InvalidArgument, "application destination spec is invalid: %s", err.Error()) + return status.Errorf(codes.InvalidArgument, "application destination spec for %s is invalid: %s", app.Name, err.Error()) } var conditions []appv1.ApplicationCondition if validate { - conditions, err = argo.ValidateRepo(ctx, app, s.repoClientset, s.db, kustomizeOptions, plugins, s.kubectl, proj) + conditions, err = argo.ValidateRepo(ctx, app, s.repoClientset, s.db, kustomizeOptions, plugins, s.kubectl, proj, s.settingsMgr) if err != nil { return err } if len(conditions) > 0 { - return status.Errorf(codes.InvalidArgument, "application spec is invalid: %s", argo.FormatAppConditions(conditions)) + return status.Errorf(codes.InvalidArgument, "application spec for %s is invalid: %s", app.Name, argo.FormatAppConditions(conditions)) } } @@ -862,7 +864,7 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica return err } if len(conditions) > 0 { - return status.Errorf(codes.InvalidArgument, "application spec is invalid: %s", argo.FormatAppConditions(conditions)) + return status.Errorf(codes.InvalidArgument, "application spec for %s is invalid: %s", app.Name, argo.FormatAppConditions(conditions)) } app.Spec = *argo.NormalizeApplicationSpec(&app.Spec) @@ -1095,7 +1097,7 @@ func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMe } // We need to get some information with the project associated to the app, // so we'll know whether GPG signatures are enforced. - proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr) + proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr, s.db, ctx) if err != nil { return nil, err } @@ -1350,7 +1352,7 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR return nil, err } - proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr) + proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr, s.db, ctx) if err != nil { if apierr.IsNotFound(err) { return a, status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", a.Spec.Project) @@ -1793,7 +1795,7 @@ func (s *Server) GetApplicationSyncWindows(ctx context.Context, q *application.A return nil, err } - proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr) + proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr, s.db, ctx) if err != nil { return nil, err } diff --git a/server/application/application_test.go b/server/application/application_test.go index f6e60b9ff3f9a..2308ada40689a 100644 --- a/server/application/application_test.go +++ b/server/application/application_test.go @@ -3,6 +3,7 @@ package application import ( "context" coreerrors "errors" + "fmt" "sync/atomic" "testing" "time" @@ -73,6 +74,14 @@ func fakeAppList() *apiclient.AppList { // return an ApplicationServiceServer which returns fake data func newTestAppServer(objects ...runtime.Object) *Server { + f := func(enf *rbac.Enforcer) { + _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) + enf.SetDefaultRole("role:admin") + } + return newTestAppServerWithEnforcerConfigure(f, objects...) +} + +func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...runtime.Object) *Server { kubeclientset := fake.NewSimpleClientset(&v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: testNamespace, @@ -139,12 +148,11 @@ func newTestAppServer(objects ...runtime.Object) *Server { objects = append(objects, defaultProj, myProj, projWithSyncWindows) fakeAppsClientset := apps.NewSimpleClientset(objects...) - factory := appinformer.NewFilteredSharedInformerFactory(fakeAppsClientset, 0, "", func(options *metav1.ListOptions) {}) + factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(""), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) fakeProjLister := factory.Argoproj().V1alpha1().AppProjects().Lister().AppProjects(testNamespace) enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil) - _ = enforcer.SetBuiltinPolicy(assets.BuiltinPolicyCSV) - enforcer.SetDefaultRole("role:admin") + f(enforcer) enforcer.SetClaimsEnforcerFunc(rbacpolicy.NewRBACPolicyEnforcer(enforcer, fakeProjLister).EnforceClaims) settingsMgr := settings.NewSettingsManager(ctx, kubeclientset, testNamespace) @@ -280,6 +288,49 @@ func TestListApps(t *testing.T) { assert.Equal(t, []string{"abc", "bcd", "def"}, names) } +func TestCoupleAppsListApps(t *testing.T) { + var objects []runtime.Object + ctx := context.Background() + + var groups []string + for i := 0; i < 50; i++ { + groups = append(groups, fmt.Sprintf("group-%d", i)) + } + // nolint:staticcheck + ctx = context.WithValue(ctx, "claims", &jwt.MapClaims{"groups": groups}) + for projectId := 0; projectId < 100; projectId++ { + projectName := fmt.Sprintf("proj-%d", projectId) + for appId := 0; appId < 100; appId++ { + objects = append(objects, newTestApp(func(app *appsv1.Application) { + app.Name = fmt.Sprintf("app-%d-%d", projectId, appId) + app.Spec.Project = projectName + })) + } + } + + f := func(enf *rbac.Enforcer) { + policy := ` +p, role:test, applications, *, proj-10/*, allow +g, group-45, role:test +p, role:test2, applications, *, proj-15/*, allow +g, group-47, role:test2 +p, role:test3, applications, *, proj-20/*, allow +g, group-49, role:test3 +` + _ = enf.SetUserPolicy(policy) + } + appServer := newTestAppServerWithEnforcerConfigure(f, objects...) + + res, err := appServer.List(ctx, &application.ApplicationQuery{}) + + assert.NoError(t, err) + var names []string + for i := range res.Items { + names = append(names, res.Items[i].Name) + } + assert.Equal(t, 300, len(names)) +} + func TestCreateApp(t *testing.T) { testApp := newTestApp() appServer := newTestAppServer() diff --git a/server/cache/cache.go b/server/cache/cache.go index 7bacb51d8559f..dbbeb37833d7d 100644 --- a/server/cache/cache.go +++ b/server/cache/cache.go @@ -41,9 +41,9 @@ func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) var oidcCacheExpiration time.Duration var loginAttemptsExpiration time.Duration - cmd.Flags().DurationVar(&connectionStatusCacheExpiration, "connection-status-cache-expiration", env.ParseDurationFromEnv("ARGOCD_SERVER_CONNECTION_STATUS_CACHE_EXPIRATION", 1*time.Hour, 0, math.MaxInt32), "Cache expiration for cluster/repo connection status") - cmd.Flags().DurationVar(&oidcCacheExpiration, "oidc-cache-expiration", env.ParseDurationFromEnv("ARGOCD_SERVER_OIDC_CACHE_EXPIRATION", 3*time.Minute, 0, math.MaxInt32), "Cache expiration for OIDC state") - cmd.Flags().DurationVar(&loginAttemptsExpiration, "login-attempts-expiration", env.ParseDurationFromEnv("ARGOCD_SERVER_LOGIN_ATTEMPTS_EXPIRATION", 24*time.Hour, 0, math.MaxInt32), "Cache expiration for failed login attempts") + cmd.Flags().DurationVar(&connectionStatusCacheExpiration, "connection-status-cache-expiration", env.ParseDurationFromEnv("ARGOCD_SERVER_CONNECTION_STATUS_CACHE_EXPIRATION", 1*time.Hour, 0, math.MaxInt64), "Cache expiration for cluster/repo connection status") + cmd.Flags().DurationVar(&oidcCacheExpiration, "oidc-cache-expiration", env.ParseDurationFromEnv("ARGOCD_SERVER_OIDC_CACHE_EXPIRATION", 3*time.Minute, 0, math.MaxInt64), "Cache expiration for OIDC state") + cmd.Flags().DurationVar(&loginAttemptsExpiration, "login-attempts-expiration", env.ParseDurationFromEnv("ARGOCD_SERVER_LOGIN_ATTEMPTS_EXPIRATION", 24*time.Hour, 0, math.MaxInt64), "Cache expiration for failed login attempts") fn := appstatecache.AddCacheFlagsToCmd(cmd, opts...) diff --git a/server/cluster/cluster.go b/server/cluster/cluster.go index e304149d0c48b..b09c9e4593587 100644 --- a/server/cluster/cluster.go +++ b/server/cluster/cluster.go @@ -3,6 +3,8 @@ package cluster import ( "time" + "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/argoproj/gitops-engine/pkg/utils/kube" log "github.com/sirupsen/logrus" "golang.org/x/net/context" @@ -38,6 +40,13 @@ func NewServer(db db.ArgoDB, enf *rbac.Enforcer, cache *servercache.Cache, kubec } } +func createRBACObject(project string, server string) string { + if project != "" { + return project + "/" + server + } + return server +} + // List returns list of clusters func (s *Server) List(ctx context.Context, q *cluster.ClusterQuery) (*appv1.ClusterList, error) { clusterList, err := s.db.ListClusters(ctx) @@ -47,7 +56,7 @@ func (s *Server) List(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Clus items := make([]appv1.Cluster, 0) for _, clust := range clusterList.Items { - if s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionGet, clust.Server) { + if s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionGet, createRBACObject(clust.Project, clust.Server)) { items = append(items, clust) } } @@ -64,7 +73,7 @@ func (s *Server) List(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Clus // Create creates a cluster func (s *Server) Create(ctx context.Context, q *cluster.ClusterCreateRequest) (*appv1.Cluster, error) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionCreate, q.Cluster.Server); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionCreate, createRBACObject(q.Cluster.Project, q.Cluster.Server)); err != nil { return nil, err } c := q.Cluster @@ -86,7 +95,7 @@ func (s *Server) Create(ctx context.Context, q *cluster.ClusterCreateRequest) (* } else if q.Upsert { return s.Update(ctx, &cluster.ClusterUpdateRequest{Cluster: c}) } else { - return nil, status.Errorf(codes.InvalidArgument, "existing cluster spec is different; use upsert flag to force update") + return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("cluster", existing, c)) } } err = s.cache.SetClusterInfo(c.Server, &appv1.ClusterInfo{ @@ -104,17 +113,26 @@ func (s *Server) Create(ctx context.Context, q *cluster.ClusterCreateRequest) (* // Get returns a cluster from a query func (s *Server) Get(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Cluster, error) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionGet, q.Server); err != nil { + c, err := s.getClusterWith403IfNotExist(ctx, q) + if err != nil { return nil, err } - c, err := s.getCluster(ctx, q) - if err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionGet, createRBACObject(c.Project, q.Server)); err != nil { return nil, err } + return s.toAPIResponse(c), nil } +func (s *Server) getClusterWith403IfNotExist(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Cluster, error) { + repo, err := s.getCluster(ctx, q) + if err != nil || repo == nil { + return nil, status.Error(codes.PermissionDenied, "permission denied") + } + return repo, nil +} + func (s *Server) getCluster(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Cluster, error) { if q.Server != "" { @@ -155,11 +173,27 @@ var clusterFieldsByPath = map[string]func(updated *appv1.Cluster, existing *appv "shard": func(updated *appv1.Cluster, existing *appv1.Cluster) { updated.Shard = existing.Shard }, + "clusterResources": func(updated *appv1.Cluster, existing *appv1.Cluster) { + updated.ClusterResources = existing.ClusterResources + }, } // Update updates a cluster func (s *Server) Update(ctx context.Context, q *cluster.ClusterUpdateRequest) (*appv1.Cluster, error) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, q.Cluster.Server); err != nil { + c, err := s.getClusterWith403IfNotExist(ctx, &cluster.ClusterQuery{ + Server: q.Cluster.Server, + Name: q.Cluster.Name, + }) + if err != nil { + return nil, err + } + + // verify that user can do update inside project where cluster is located + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, createRBACObject(c.Project, q.Cluster.Server)); err != nil { + return nil, err + } + // verify that user can do update inside project where cluster will be located + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, createRBACObject(q.Cluster.Project, q.Cluster.Server)); err != nil { return nil, err } @@ -202,24 +236,28 @@ func (s *Server) Update(ctx context.Context, q *cluster.ClusterUpdateRequest) (* // Delete deletes a cluster by name func (s *Server) Delete(ctx context.Context, q *cluster.ClusterQuery) (*cluster.ClusterResponse, error) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionDelete, q.Server); err != nil { + c, err := s.getClusterWith403IfNotExist(ctx, q) + if err != nil { return nil, err } - err := s.db.DeleteCluster(ctx, q.Server) + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionDelete, createRBACObject(c.Project, c.Server)); err != nil { + return nil, err + } + err = s.db.DeleteCluster(ctx, q.Server) return &cluster.ClusterResponse{}, err } // RotateAuth rotates the bearer token used for a cluster func (s *Server) RotateAuth(ctx context.Context, q *cluster.ClusterQuery) (*cluster.ClusterResponse, error) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, q.Server); err != nil { + clust, err := s.getClusterWith403IfNotExist(ctx, q) + if err != nil { return nil, err } - logCtx := log.WithField("cluster", q.Server) - logCtx.Info("Rotating auth") - clust, err := s.db.GetCluster(ctx, q.Server) - if err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, createRBACObject(clust.Project, q.Server)); err != nil { return nil, err } + logCtx := log.WithField("cluster", q.Server) + logCtx.Info("Rotating auth") restCfg := clust.RESTConfig() if restCfg.BearerToken == "" { return nil, status.Errorf(codes.InvalidArgument, "Cluster '%s' does not use bearer token authentication", q.Server) @@ -290,11 +328,11 @@ func (s *Server) toAPIResponse(clust *appv1.Cluster) *appv1.Cluster { // InvalidateCache invalidates cluster cache func (s *Server) InvalidateCache(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Cluster, error) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, q.Server); err != nil { + cls, err := s.getClusterWith403IfNotExist(ctx, q) + if err != nil { return nil, err } - cls, err := s.db.GetCluster(ctx, q.Server) - if err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, createRBACObject(cls.Project, q.Server)); err != nil { return nil, err } now := v1.Now() diff --git a/server/cluster/cluster_test.go b/server/cluster/cluster_test.go index b073b281e9396..42b64307a5722 100644 --- a/server/cluster/cluster_test.go +++ b/server/cluster/cluster_test.go @@ -5,6 +5,8 @@ import ( "testing" "time" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -44,6 +46,21 @@ func newNoopEnforcer() *rbac.Enforcer { func TestUpdateCluster_NoFieldsPaths(t *testing.T) { db := &dbmocks.ArgoDB{} var updated *v1alpha1.Cluster + + clusters := []v1alpha1.Cluster{ + { + Name: "minikube", + Server: "https://127.0.0.1", + Namespaces: []string{"default", "kube-system"}, + }, + } + + clusterList := v1alpha1.ClusterList{ + ListMeta: v1.ListMeta{}, + Items: clusters, + } + + db.On("ListClusters", mock.Anything).Return(&clusterList, nil) db.On("UpdateCluster", mock.Anything, mock.MatchedBy(func(c *v1alpha1.Cluster) bool { updated = c return true diff --git a/server/logout/logout.go b/server/logout/logout.go index 8446d4b5b19f0..54ee0cd1b658d 100644 --- a/server/logout/logout.go +++ b/server/logout/logout.go @@ -20,12 +20,13 @@ import ( ) //NewHandler creates handler serving to do api/logout endpoint -func NewHandler(appClientset versioned.Interface, settingsMrg *settings.SettingsManager, sessionMgr *session.SessionManager, rootPath, namespace string) *Handler { +func NewHandler(appClientset versioned.Interface, settingsMrg *settings.SettingsManager, sessionMgr *session.SessionManager, rootPath, baseHRef, namespace string) *Handler { return &Handler{ appClientset: appClientset, namespace: namespace, settingsMgr: settingsMrg, rootPath: rootPath, + baseHRef: baseHRef, verifyToken: sessionMgr.VerifyToken, revokeToken: sessionMgr.RevokeToken, } @@ -38,6 +39,7 @@ type Handler struct { rootPath string verifyToken func(tokenString string) (jwt.Claims, string, error) revokeToken func(ctx context.Context, id string, expiringAt time.Duration) error + baseHRef string } var ( @@ -67,7 +69,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 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 - argoURL = fmt.Sprintf("http://%s", r.Host) + strings.TrimRight(strings.TrimLeft(h.rootPath, "/"), "/") + host := strings.TrimRight(r.Host, "/") + argoURL = fmt.Sprintf("http://%s", host) + "/" + strings.TrimRight(strings.TrimLeft(h.rootPath, "/"), "/") } logoutRedirectURL := strings.TrimRight(strings.TrimLeft(argoURL, "/"), "/") @@ -84,11 +87,13 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if !strings.HasPrefix(cookie.Name, common.AuthCookieName) { continue } + argocdCookie := http.Cookie{ Name: cookie.Name, Value: "", } - argocdCookie.Path = fmt.Sprintf("/%s", strings.TrimRight(strings.TrimLeft(h.rootPath, "/"), "/")) + + argocdCookie.Path = fmt.Sprintf("/%s", strings.TrimRight(strings.TrimLeft(h.baseHRef, "/"), "/")) w.Header().Add("Set-Cookie", argocdCookie.String()) } diff --git a/server/logout/logout_test.go b/server/logout/logout_test.go index 68a2ee7e69fc0..1d6776f8b4173 100644 --- a/server/logout/logout_test.go +++ b/server/logout/logout_test.go @@ -26,6 +26,7 @@ var ( validJWTPattern = regexp.MustCompile(`[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+`) baseURL = "http://localhost:4000" rootPath = "argocd" + baseHRef = "argocd" baseLogoutURL = "http://localhost:4000/logout" baseLogoutURLwithToken = "http://localhost:4000/logout?id_token_hint={{token}}" baseLogoutURLwithRedirectURL = "http://localhost:4000/logout?post_logout_redirect_uri={{logoutRedirectURL}}" @@ -35,7 +36,7 @@ var ( nonOidcToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2MDU1NzQyMTIsImlzcyI6ImFyZ29jZCIsIm5iZiI6MTYwNTU3NDIxMiwic3ViIjoiYWRtaW4ifQ.zDJ4piwWnwsHON-oPusHMXWINlnrRDTQykYogT7afeE" expectedNonOIDCLogoutURL = "http://localhost:4000" 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 + expectedOIDCLogoutURLWithRootPath = "https://dev-5695098.okta.com/oauth2/v1/logout?id_token_hint=" + oidcToken + "&post_logout_redirect_uri=" + baseURL + "/" + rootPath ) func TestConstructLogoutURL(t *testing.T) { @@ -214,21 +215,21 @@ func TestHandlerConstructLogoutURL(t *testing.T) { sessionManager := session.NewSessionManager(settingsManagerWithOIDCConfig, test.NewFakeProjLister(), "", session.NewUserStateStorage(nil)) - oidcHandler := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfig, sessionManager, rootPath, "default") + oidcHandler := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfig, sessionManager, rootPath, baseHRef, "default") oidcHandler.verifyToken = func(tokenString string) (jwt.Claims, string, error) { if !validJWTPattern.MatchString(tokenString) { return nil, "", errors.New("invalid jwt") } return &jwt.StandardClaims{Issuer: "okta"}, "", nil } - nonoidcHandler := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithoutOIDCConfig, sessionManager, "", "default") + nonoidcHandler := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithoutOIDCConfig, sessionManager, "", baseHRef, "default") nonoidcHandler.verifyToken = func(tokenString string) (jwt.Claims, string, error) { if !validJWTPattern.MatchString(tokenString) { return nil, "", errors.New("invalid jwt") } return &jwt.StandardClaims{Issuer: session.SessionManagerClaimsIssuer}, "", nil } - oidcHandlerWithoutLogoutURL := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfigButNoLogoutURL, sessionManager, "", "default") + oidcHandlerWithoutLogoutURL := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfigButNoLogoutURL, sessionManager, "", baseHRef, "default") oidcHandlerWithoutLogoutURL.verifyToken = func(tokenString string) (jwt.Claims, string, error) { if !validJWTPattern.MatchString(tokenString) { return nil, "", errors.New("invalid jwt") @@ -236,7 +237,7 @@ func TestHandlerConstructLogoutURL(t *testing.T) { return &jwt.StandardClaims{Issuer: "okta"}, "", nil } - oidcHandlerWithoutBaseURL := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfigButNoURL, sessionManager, "argocd", "default") + oidcHandlerWithoutBaseURL := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfigButNoURL, sessionManager, "argocd", baseHRef, "default") oidcHandlerWithoutBaseURL.verifyToken = func(tokenString string) (jwt.Claims, string, error) { if !validJWTPattern.MatchString(tokenString) { return nil, "", errors.New("invalid jwt") diff --git a/server/metrics/metrics.go b/server/metrics/metrics.go index e823555674238..0a940a318a78a 100644 --- a/server/metrics/metrics.go +++ b/server/metrics/metrics.go @@ -35,7 +35,7 @@ var ( ) // NewMetricsServer returns a new prometheus server which collects api server metrics -func NewMetricsServer(port int) *MetricsServer { +func NewMetricsServer(host string, port int) *MetricsServer { mux := http.NewServeMux() registry := prometheus.NewRegistry() mux.Handle("/metrics", promhttp.HandlerFor(prometheus.Gatherers{ @@ -48,7 +48,7 @@ func NewMetricsServer(port int) *MetricsServer { return &MetricsServer{ Server: &http.Server{ - Addr: fmt.Sprintf("0.0.0.0:%d", port), + Addr: fmt.Sprintf("%s:%d", host, port), Handler: mux, }, redisRequestCounter: redisRequestCounter, diff --git a/server/project/project.go b/server/project/project.go index ec94fe4a86465..69559cc9a7302 100644 --- a/server/project/project.go +++ b/server/project/project.go @@ -6,6 +6,8 @@ import ( "reflect" "strings" + "github.com/argoproj/argo-cd/v2/util/db" + "github.com/argoproj/pkg/sync" "github.com/dgrijalva/jwt-go/v4" "github.com/google/uuid" @@ -48,14 +50,15 @@ type Server struct { sessionMgr *session.SessionManager projInformer cache.SharedIndexInformer settingsMgr *settings.SettingsManager + db db.ArgoDB } // 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) *Server { + 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} + projInformer: projInformer, settingsMgr: settingsMgr, db: db} } func validateProject(proj *v1alpha1.AppProject) error { @@ -208,7 +211,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.Errorf(codes.InvalidArgument, "existing project spec is different, use upsert flag to force update") + return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("project", existing.Spec, q.GetProject().Spec)) } return existing, nil } @@ -235,6 +238,26 @@ func (s *Server) List(ctx context.Context, q *project.ProjectQuery) (*v1alpha1.A return list, err } +// Get returns a project with scoped resources +func (s *Server) GetDetailedProject(ctx context.Context, q *project.ProjectQuery) (*project.DetailedProjectsResponse, error) { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionGet, q.Name); err != nil { + return nil, err + } + proj, repositories, clusters, err := argo.GetAppProjectWithScopedResources(q.Name, listersv1alpha1.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) + if err != nil { + return nil, err + } + proj.NormalizeJWTTokens() + globalProjects := argo.GetGlobalProjects(proj, listersv1alpha1.NewAppProjectLister(s.projInformer.GetIndexer()), s.settingsMgr) + + return &project.DetailedProjectsResponse{ + GlobalProjects: globalProjects, + Project: proj, + Repositories: repositories, + Clusters: clusters, + }, err +} + // Get returns a project by name func (s *Server) Get(ctx context.Context, q *project.ProjectQuery) (*v1alpha1.AppProject, error) { if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionGet, q.Name); err != nil { diff --git a/server/project/project.proto b/server/project/project.proto index 37aa158491af9..1b8a01d537821 100644 --- a/server/project/project.proto +++ b/server/project/project.proto @@ -65,6 +65,13 @@ message GlobalProjectsResponse { repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.AppProject items = 1; } +message DetailedProjectsResponse { + repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.AppProject globalProjects = 1; + github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.AppProject project = 2; + repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repositories = 3; + repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Cluster clusters = 4; +} + // ProjectService service ProjectService { @@ -94,6 +101,11 @@ service ProjectService { option (google.api.http).get = "/api/v1/projects"; } + // GetDetailedProject returns a project that include project, global project and scoped resources by name + rpc GetDetailedProject(ProjectQuery) returns (DetailedProjectsResponse) { + option (google.api.http).get = "/api/v1/projects/{name}/detailed"; + } + // Get returns a project by name rpc Get(ProjectQuery) returns (github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.AppProject) { option (google.api.http).get = "/api/v1/projects/{name}"; diff --git a/server/project/project_test.go b/server/project/project_test.go index a87bef3cc3ff7..1a332a6c0b66e 100644 --- a/server/project/project_test.go +++ b/server/project/project_test.go @@ -6,6 +6,8 @@ import ( "strings" "testing" + "github.com/argoproj/argo-cd/v2/util/db" + "github.com/argoproj/pkg/sync" "github.com/dgrijalva/jwt-go/v4" "github.com/google/uuid" @@ -74,7 +76,7 @@ func TestProjectServer(t *testing.T) { ctx := context.Background() fakeAppsClientset := apps.NewSimpleClientset() - factory := informer.NewFilteredSharedInformerFactory(fakeAppsClientset, 0, "", func(options *metav1.ListOptions) {}) + factory := informer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, informer.WithNamespace(""), informer.WithTweakListOptions(func(options *metav1.ListOptions) {})) projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer() go projInformer.Run(ctx.Done()) if !k8scache.WaitForCacheSync(ctx.Done(), projInformer.HasSynced) { @@ -87,8 +89,8 @@ func TestProjectServer(t *testing.T) { roleName := "roleName" role1 := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} projectWithRole.Spec.Roles = append(projectWithRole.Spec.Roles, role1) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB) err := projectServer.NormalizeProjs() assert.NoError(t, err) @@ -103,7 +105,8 @@ func TestProjectServer(t *testing.T) { enforcer.SetDefaultRole("role:projects") _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.Destinations = nil @@ -117,7 +120,8 @@ func TestProjectServer(t *testing.T) { enforcer.SetDefaultRole("role:projects") _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.SourceRepos = nil @@ -131,7 +135,8 @@ func TestProjectServer(t *testing.T) { enforcer.SetDefaultRole("role:projects") _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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{{}} @@ -145,7 +150,8 @@ func TestProjectServer(t *testing.T) { enforcer.SetDefaultRole("role:projects") _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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{{}} @@ -163,7 +169,8 @@ func TestProjectServer(t *testing.T) { Spec: v1alpha1.ApplicationSpec{Project: "test", Destination: v1alpha1.ApplicationDestination{Namespace: "ns3", Server: "https://server3"}}, } - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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:] @@ -179,7 +186,8 @@ func TestProjectServer(t *testing.T) { Spec: v1alpha1.ApplicationSpec{Project: "test", Destination: v1alpha1.ApplicationDestination{Namespace: "ns1", Server: "https://server1"}}, } - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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:] @@ -197,7 +205,8 @@ func TestProjectServer(t *testing.T) { Spec: v1alpha1.ApplicationSpec{Project: "test"}, } - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.SourceRepos = []string{} @@ -213,7 +222,8 @@ func TestProjectServer(t *testing.T) { Spec: v1alpha1.ApplicationSpec{Project: "test", Source: v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argo-cd.git"}}, } - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) updatedProj := existingProj.DeepCopy() updatedProj.Spec.SourceRepos = []string{} @@ -232,8 +242,8 @@ func TestProjectServer(t *testing.T) { ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: v1alpha1.ApplicationSpec{Project: "test", Source: v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argo-cd.git"}}, } - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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/*"} @@ -258,7 +268,9 @@ func TestProjectServer(t *testing.T) { }}, } - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + + 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{ @@ -272,7 +284,8 @@ func TestProjectServer(t *testing.T) { }) t.Run("TestDeleteProjectSuccessful", func(t *testing.T) { - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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"}) @@ -284,7 +297,8 @@ func TestProjectServer(t *testing.T) { ObjectMeta: v1.ObjectMeta{Name: "default", Namespace: "default"}, Spec: v1alpha1.AppProjectSpec{}, } - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&defaultProj), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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) @@ -297,7 +311,8 @@ func TestProjectServer(t *testing.T) { Spec: v1alpha1.ApplicationSpec{Project: "test"}, } - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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"}) @@ -322,7 +337,9 @@ func TestProjectServer(t *testing.T) { sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewUserStateStorage(nil)) projectWithRole := existingProj.DeepCopy() projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}} - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr) + + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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") }) @@ -331,7 +348,8 @@ func TestProjectServer(t *testing.T) { sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewUserStateStorage(nil)) projectWithRole := existingProj.DeepCopy() projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName, Groups: []string{"my-group"}}} - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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.NoError(t, err) }) @@ -344,7 +362,8 @@ func TestProjectServer(t *testing.T) { clientset := apps.NewSimpleClientset(projectWithRole) sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", session.NewUserStateStorage(nil)) - projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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}) assert.NoError(t, err) claims, _, err := sessionMgr.Parse(tokenResponse.Token) @@ -364,7 +383,8 @@ func TestProjectServer(t *testing.T) { clientset := apps.NewSimpleClientset(projectWithRole) sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", session.NewUserStateStorage(nil)) - projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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}) assert.NoError(t, err) claims, _, err := sessionMgr.Parse(tokenResponse.Token) @@ -384,7 +404,8 @@ func TestProjectServer(t *testing.T) { clientset := apps.NewSimpleClientset(projectWithRole) sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", session.NewUserStateStorage(nil)) - projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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}) assert.NoError(t, err) @@ -412,8 +433,8 @@ func TestProjectServer(t *testing.T) { secondIssuedAt := issuedAt + 1 token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}} projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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") }) @@ -425,8 +446,8 @@ func TestProjectServer(t *testing.T) { secondIssuedAt := issuedAt + 1 token := v1alpha1.ProjectRole{Name: tokenName, Groups: []string{"my-group"}, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}} projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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.NoError(t, err) }) @@ -441,8 +462,8 @@ p, role:admin, projects, update, *, allow`) secondIssuedAt := issuedAt + 1 token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}} projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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.NoError(t, err) projWithoutToken, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name}) @@ -465,8 +486,8 @@ p, role:admin, projects, update, *, allow`) secondId := uniqueId.String() token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt, ID: id}, {IssuedAt: secondIssuedAt, ID: secondId}}} projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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}) assert.NoError(t, err) projWithoutToken, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name}) @@ -484,7 +505,8 @@ p, role:admin, projects, update, *, allow`) tokenName := "testToken" token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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}) assert.Nil(t, err) projWithTwoTokens, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name}) @@ -498,8 +520,8 @@ p, role:admin, projects, update, *, allow`) proj := existingProj.DeepCopy() wildSourceRepo := "*" proj.Spec.SourceRepos = append(proj.Spec.SourceRepos, wildSourceRepo) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj), enforcer, sync.NewKeyLock(), nil, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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) assert.Nil(t, err) @@ -517,8 +539,8 @@ p, role:admin, projects, update, *, allow`) policy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect) role.Policies = append(role.Policies, policy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, policyEnf, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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) assert.Nil(t, err) @@ -539,8 +561,8 @@ p, role:admin, projects, update, *, allow`) role.Policies = append(role.Policies, policy) role.Policies = append(role.Policies, policy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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) @@ -559,8 +581,8 @@ p, role:admin, projects, update, *, allow`) policy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, otherProject, object, effect) role.Policies = append(role.Policies, policy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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/*' or 'test/'") @@ -578,8 +600,8 @@ p, role:admin, projects, update, *, allow`) invalidPolicy := fmt.Sprintf(policyTemplate, otherProject, roleName, action, projWithRole.Name, object, effect) role.Policies = append(role.Policies, invalidPolicy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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'") @@ -597,8 +619,8 @@ p, role:admin, projects, update, *, allow`) invalidPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, otherToken, action, projWithRole.Name, object, effect) role.Policies = append(role.Policies, invalidPolicy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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'") @@ -615,8 +637,8 @@ p, role:admin, projects, update, *, allow`) invalidPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect) role.Policies = append(role.Policies, invalidPolicy) projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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'") @@ -634,8 +656,8 @@ p, role:admin, projects, update, *, allow`) 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) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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) assert.Nil(t, err) @@ -649,8 +671,8 @@ p, role:admin, projects, update, *, allow`) projectWithSyncWindows.Spec.SyncWindows = v1alpha1.SyncWindows{} win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"} projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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}) assert.NoError(t, err) assert.Equal(t, 1, len(res.Windows)) @@ -662,8 +684,8 @@ p, role:admin, projects, update, *, allow`) projectWithSyncWindows.Spec.SyncWindows = v1alpha1.SyncWindows{} win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"} projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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) @@ -680,8 +702,8 @@ p, role:admin, projects, update, *, allow`) projectWithSyncWindows := existingProj.DeepCopy() win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"} projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win) - - projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr) + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + 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") }) diff --git a/server/rbacpolicy/rbacpolicy.go b/server/rbacpolicy/rbacpolicy.go index e7b2683f4b3db..7fff88962238b 100644 --- a/server/rbacpolicy/rbacpolicy.go +++ b/server/rbacpolicy/rbacpolicy.go @@ -113,10 +113,13 @@ func (p *RBACPolicyEnforcer) EnforceClaims(claims jwt.Claims, rvals ...interface runtimePolicy = proj.ProjectPoliciesString() } + // NOTE: This calls prevent multiple creation of the wrapped enforcer + enforcer := p.enf.CreateEnforcerWithRuntimePolicy(runtimePolicy) + // Check the subject. This is typically the 'admin' case. - // NOTE: the call to EnforceRuntimePolicy will also consider the default role + // NOTE: the call to EnforceWithCustomEnforcer will also consider the default role vals := append([]interface{}{subject}, rvals[1:]...) - if p.enf.EnforceRuntimePolicy(runtimePolicy, vals...) { + if p.enf.EnforceWithCustomEnforcer(enforcer, vals...) { return true } @@ -126,10 +129,19 @@ func (p *RBACPolicyEnforcer) EnforceClaims(claims jwt.Claims, rvals ...interface } // Finally check if any of the user's groups grant them permissions groups := jwtutil.GetScopeValues(mapClaims, scopes) - for _, group := range groups { - vals := append([]interface{}{group}, rvals[1:]...) - if p.enf.EnforceRuntimePolicy(runtimePolicy, vals...) { - return true + + // Get groups to reduce the amount to checking groups + groupingPolicies := enforcer.GetGroupingPolicy() + for gidx := range groups { + for gpidx := range groupingPolicies { + // Prefilter user groups by groups defined in the model + if groupingPolicies[gpidx][0] == groups[gidx] { + vals := append([]interface{}{groups[gidx]}, rvals[1:]...) + if p.enf.EnforceWithCustomEnforcer(enforcer, vals...) { + return true + } + break + } } } logCtx := log.WithField("claims", claims).WithField("rval", rvals) diff --git a/server/repocreds/repocreds.go b/server/repocreds/repocreds.go index ee38096fedbcc..add37e5a38c93 100644 --- a/server/repocreds/repocreds.go +++ b/server/repocreds/repocreds.go @@ -3,6 +3,8 @@ package repocreds import ( "reflect" + "github.com/argoproj/argo-cd/v2/util/argo" + "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -91,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.Errorf(codes.InvalidArgument, "existing repository credentials spec is different; use upsert flag to force update") + 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 b1fc053cbc34f..65d45b56c4af5 100644 --- a/server/repository/repository.go +++ b/server/repository/repository.go @@ -4,6 +4,8 @@ import ( "fmt" "reflect" + "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/argoproj/gitops-engine/pkg/utils/text" log "github.com/sirupsen/logrus" @@ -51,6 +53,21 @@ func NewServer( } } +func (s *Server) getRepo(ctx context.Context, url string) (*appsv1.Repository, error) { + repo, err := s.db.GetRepository(ctx, url) + if err != nil { + return nil, status.Error(codes.PermissionDenied, "permission denied") + } + return repo, nil +} + +func createRBACObject(project string, repo string) string { + if project != "" { + return project + "/" + repo + } + return repo +} + // 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. @@ -94,14 +111,15 @@ 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) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, q.Repo); err != nil { + repo, err := s.getRepo(ctx, q.Repo) + if err != nil { return nil, err } - repo, err := s.db.GetRepository(ctx, q.Repo) - if err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil { return nil, err } + // For backwards compatibility, if we have no repo type set assume a default rType := repo.Type if rType == "" { @@ -119,6 +137,7 @@ func (s *Server) Get(ctx context.Context, q *repositorypkg.RepoQuery) (*appsv1.R GithubAppInstallationId: repo.GithubAppInstallationId, GitHubAppEnterpriseBaseURL: repo.GitHubAppEnterpriseBaseURL, Proxy: repo.Proxy, + Project: repo.Project, } item.ConnectionState = s.getConnectionState(ctx, item.Repo, q.ForceRefresh) @@ -134,7 +153,7 @@ func (s *Server) ListRepositories(ctx context.Context, q *repositorypkg.RepoQuer } items := appsv1.Repositories{} for _, repo := range repos { - if s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, repo.Repo) { + if s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)) { // For backwards compatibility, if we have no repo type set assume a default rType := repo.Type if rType == "" { @@ -150,6 +169,7 @@ func (s *Server) ListRepositories(ctx context.Context, q *repositorypkg.RepoQuer EnableLFS: repo.EnableLFS, EnableOCI: repo.EnableOCI, Proxy: repo.Proxy, + Project: repo.Project, }) } } @@ -164,11 +184,12 @@ func (s *Server) ListRepositories(ctx context.Context, q *repositorypkg.RepoQuer } func (s *Server) ListRefs(ctx context.Context, q *repositorypkg.RepoQuery) (*apiclient.Refs, error) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, q.Repo); err != nil { + repo, err := s.getRepo(ctx, q.Repo) + if err != nil { return nil, err } - repo, err := s.db.GetRepository(ctx, q.Repo) - if err != nil { + + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil { return nil, err } @@ -185,11 +206,12 @@ func (s *Server) ListRefs(ctx context.Context, q *repositorypkg.RepoQuery) (*api // ListApps returns list of apps in the repo func (s *Server) ListApps(ctx context.Context, q *repositorypkg.RepoAppsQuery) (*repositorypkg.RepoAppsResponse, error) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, q.Repo); err != nil { + repo, err := s.getRepo(ctx, q.Repo) + if err != nil { return nil, err } - repo, err := s.db.GetRepository(ctx, q.Repo) - if err != nil { + + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil { return nil, err } @@ -218,11 +240,11 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta if q.Source == nil { return nil, status.Errorf(codes.InvalidArgument, "missing payload in request") } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, q.Source.RepoURL); err != nil { + repo, err := s.getRepo(ctx, q.Source.RepoURL) + if err != nil { return nil, err } - repo, err := s.db.GetRepository(ctx, q.Source.RepoURL) - if err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil { return nil, err } conn, repoClient, err := s.repoClientset.NewRepoServerClient() @@ -253,11 +275,11 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta // GetHelmCharts returns list of helm charts in the specified repository func (s *Server) GetHelmCharts(ctx context.Context, q *repositorypkg.RepoQuery) (*apiclient.HelmChartsResponse, error) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, q.Repo); err != nil { + repo, err := s.getRepo(ctx, q.Repo) + if err != nil { return nil, err } - repo, err := s.db.GetRepository(ctx, q.Repo) - if err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil { return nil, err } conn, repoClient, err := s.repoClientset.NewRepoServerClient() @@ -279,7 +301,8 @@ func (s *Server) CreateRepository(ctx context.Context, q *repositorypkg.RepoCrea if q.Repo == nil { return nil, status.Errorf(codes.InvalidArgument, "missing payload in request") } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionCreate, q.Repo.Repo); err != nil { + + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionCreate, createRBACObject(q.Repo.Project, q.Repo.Repo)); err != nil { return nil, err } @@ -319,9 +342,10 @@ func (s *Server) CreateRepository(ctx context.Context, q *repositorypkg.RepoCrea if reflect.DeepEqual(existing, r) { repo, err = existing, nil } else if q.Upsert { + r.Project = q.Repo.Project return s.UpdateRepository(ctx, &repositorypkg.RepoUpdateRequest{Repo: r}) } else { - return nil, status.Errorf(codes.InvalidArgument, "existing repository spec is different; use upsert flag to force update") + return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("repository", existing, r)) } } if err != nil { @@ -341,10 +365,21 @@ func (s *Server) UpdateRepository(ctx context.Context, q *repositorypkg.RepoUpda if q.Repo == nil { return nil, status.Errorf(codes.InvalidArgument, "missing payload in request") } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionUpdate, q.Repo.Repo); err != nil { + + repo, err := s.getRepo(ctx, q.Repo.Repo) + if err != nil { + return nil, err + } + + // verify that user can do update inside project where repository is located + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionUpdate, createRBACObject(repo.Project, repo.Repo)); err != nil { + return nil, err + } + // verify that user can do update inside project where repository will be located + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionUpdate, createRBACObject(q.Repo.Project, q.Repo.Repo)); err != nil { return nil, err } - _, err := s.db.UpdateRepository(ctx, q.Repo) + _, err = s.db.UpdateRepository(ctx, q.Repo) return &appsv1.Repository{Repo: q.Repo.Repo, Type: q.Repo.Type, Name: q.Repo.Name}, err } @@ -356,7 +391,12 @@ 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) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionDelete, q.Repo); err != nil { + repo, err := s.getRepo(ctx, q.Repo) + if err != nil { + return nil, err + } + + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionDelete, createRBACObject(repo.Project, repo.Repo)); err != nil { return nil, err } @@ -365,14 +405,14 @@ func (s *Server) DeleteRepository(ctx context.Context, q *repositorypkg.RepoQuer log.Errorf("error invalidating cache: %v", err) } - err := s.db.DeleteRepository(ctx, q.Repo) + err = s.db.DeleteRepository(ctx, q.Repo) return &repositorypkg.RepoResponse{}, err } // 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) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionCreate, q.Repo); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionCreate, createRBACObject(q.Project, q.Repo)); err != nil { return nil, err } @@ -394,13 +434,10 @@ func (s *Server) ValidateAccess(ctx context.Context, q *repositorypkg.RepoAccess Proxy: q.Proxy, } - var repoCreds *appsv1.RepoCreds - var err error - // If repo does not have credentials, check if there are credentials stored // for it and if yes, copy them if !repo.HasCredentials() { - repoCreds, err = s.db.GetRepositoryCredentials(ctx, q.Repo) + repoCreds, err := s.db.GetRepositoryCredentials(ctx, q.Repo) if err != nil { return nil, err } @@ -408,7 +445,7 @@ func (s *Server) ValidateAccess(ctx context.Context, q *repositorypkg.RepoAccess repo.CopyCredentialsFrom(repoCreds) } } - err = s.testRepo(ctx, repo) + err := s.testRepo(ctx, repo) if err != nil { return nil, err } diff --git a/server/repository/repository.proto b/server/repository/repository.proto index 537030ceecefe..87688d0b7e4c8 100644 --- a/server/repository/repository.proto +++ b/server/repository/repository.proto @@ -76,6 +76,8 @@ message RepoAccessQuery { string githubAppEnterpriseBaseUrl = 15; // HTTP/HTTPS proxy to access the repository string proxy = 16; + // Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity + string project = 17; } message RepoResponse {} diff --git a/server/repository/repository_test.go b/server/repository/repository_test.go new file mode 100644 index 0000000000000..02988635915e9 --- /dev/null +++ b/server/repository/repository_test.go @@ -0,0 +1,204 @@ +package repository + +import ( + "context" + "errors" + "testing" + "time" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + + "github.com/argoproj/argo-cd/v2/server/cache" + + "github.com/stretchr/testify/mock" + + "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + + "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository" + + "github.com/argoproj/argo-cd/v2/util/db" + + "k8s.io/client-go/kubernetes/fake" + + "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/util/assets" + "github.com/argoproj/argo-cd/v2/util/rbac" + "github.com/argoproj/argo-cd/v2/util/settings" + + "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks" + + "github.com/stretchr/testify/assert" + + "github.com/dgrijalva/jwt-go/v4" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + cacheutil "github.com/argoproj/argo-cd/v2/util/cache" + appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" + dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" +) + +const testNamespace = "default" + +func Test_createRBACObject(t *testing.T) { + object := createRBACObject("test-prj", "test-repo") + assert.Equal(t, "test-prj/test-repo", object) + objectWithoutPrj := createRBACObject("", "test-repo") + assert.Equal(t, "test-repo", objectWithoutPrj) +} + +func TestRepositoryServer(t *testing.T) { + kubeclientset := fake.NewSimpleClientset(&corev1.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Namespace: testNamespace, + Name: "argocd-cm", + Labels: map[string]string{ + "app.kubernetes.io/part-of": "argocd", + }, + }, + }, &corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Name: "argocd-secret", + Namespace: testNamespace, + }, + Data: map[string][]byte{ + "admin.password": []byte("test"), + "server.secretkey": []byte("test"), + }, + }) + settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace) + enforcer := newEnforcer(kubeclientset) + + argoDB := db.NewDB("default", settingsMgr, kubeclientset) + + t.Run("Test_getRepo", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + + s := NewServer(&repoServerClientset, argoDB, enforcer, nil, settingsMgr) + url := "https://test" + repo, _ := s.getRepo(context.TODO(), url) + assert.Equal(t, repo.Repo, url) + }) + + t.Run("Test_validateAccess", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + + s := NewServer(&repoServerClientset, argoDB, enforcer, nil, settingsMgr) + url := "https://test" + _, err := s.ValidateAccess(context.TODO(), &repository.RepoAccessQuery{ + Repo: url, + }) + assert.Nil(t, err) + }) + + t.Run("Test_Get", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + + s := NewServer(&repoServerClientset, argoDB, enforcer, newFixtures().Cache, settingsMgr) + url := "https://test" + repo, err := s.Get(context.TODO(), &repository.RepoQuery{ + Repo: url, + }) + assert.Nil(t, err) + assert.Equal(t, repo.Repo, url) + }) + + t.Run("Test_GetWithNotExistRepoShouldReturn403", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + + db := &dbmocks.ArgoDB{} + db.On("GetRepository", context.TODO(), "test").Return(nil, errors.New("not found")) + + s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr) + repo, err := s.Get(context.TODO(), &repository.RepoQuery{ + Repo: "test", + }) + assert.Nil(t, repo) + assert.Equal(t, err.Error(), "rpc error: code = PermissionDenied desc = permission denied") + }) + + t.Run("Test_CreateRepositoryWithoutUpsert", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + + db := &dbmocks.ArgoDB{} + db.On("GetRepository", context.TODO(), "test").Return(nil, errors.New("not found")) + db.On("CreateRepository", context.TODO(), mock.Anything).Return(&apiclient.TestRepositoryResponse{}).Return(&v1alpha1.Repository{ + Repo: "repo", + Project: "proj", + }, nil) + + s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr) + repo, err := s.CreateRepository(context.TODO(), &repository.RepoCreateRequest{ + Repo: &v1alpha1.Repository{ + Repo: "test", + Username: "test", + }, + }) + assert.Nil(t, err) + assert.Equal(t, repo.Repo, "repo") + }) + + t.Run("Test_CreateRepositoryWithUpsert", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + + db := &dbmocks.ArgoDB{} + db.On("GetRepository", context.TODO(), "test").Return(&v1alpha1.Repository{ + Repo: "test", + Username: "test", + }, nil) + db.On("CreateRepository", context.TODO(), mock.Anything).Return(nil, status.Errorf(codes.AlreadyExists, "repository already exists")) + db.On("UpdateRepository", context.TODO(), mock.Anything).Return(nil, nil) + + s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr) + repo, err := s.CreateRepository(context.TODO(), &repository.RepoCreateRequest{ + Repo: &v1alpha1.Repository{ + Repo: "test", + Username: "test", + }, + Upsert: true, + }) + + assert.Nil(t, err) + assert.Equal(t, repo.Repo, "test") + }) + +} + +type fixtures struct { + *cache.Cache +} + +func newFixtures() *fixtures { + return &fixtures{cache.NewCache( + appstatecache.NewCache( + cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)), + 1*time.Minute, + ), + 1*time.Minute, + 1*time.Minute, + 1*time.Minute, + )} +} + +func newEnforcer(kubeclientset *fake.Clientset) *rbac.Enforcer { + enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil) + _ = enforcer.SetBuiltinPolicy(assets.BuiltinPolicyCSV) + enforcer.SetDefaultRole("role:admin") + enforcer.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool { + return true + }) + return enforcer +} diff --git a/server/server.go b/server/server.go index 3aab936f11a77..1dcea29cd3aef 100644 --- a/server/server.go +++ b/server/server.go @@ -4,17 +4,16 @@ import ( "context" "crypto/tls" "fmt" - "io/ioutil" + "io/fs" "math" "net" "net/http" "net/url" "os" "os/exec" - "path" - "path/filepath" "regexp" "strings" + gosync "sync" "time" // nolint:staticcheck @@ -82,6 +81,7 @@ import ( "github.com/argoproj/argo-cd/v2/server/session" "github.com/argoproj/argo-cd/v2/server/settings" "github.com/argoproj/argo-cd/v2/server/version" + "github.com/argoproj/argo-cd/v2/ui" "github.com/argoproj/argo-cd/v2/util/assets" cacheutil "github.com/argoproj/argo-cd/v2/util/cache" "github.com/argoproj/argo-cd/v2/util/db" @@ -161,17 +161,21 @@ type ArgoCDServer struct { // stopCh is the channel which when closed, will shutdown the Argo CD server stopCh chan struct{} userStateStorage util_session.UserStateStorage + indexDataInit gosync.Once + indexData []byte + indexDataErr error + staticAssets http.FileSystem } type ArgoCDServerOpts struct { DisableAuth bool EnableGZip bool Insecure bool + StaticAssetsDir string ListenPort int MetricsPort int Namespace string DexServerAddr string - StaticAssetsDir string BaseHRef string RootPath string KubeClientset kubernetes.Interface @@ -181,6 +185,7 @@ type ArgoCDServerOpts struct { RedisClient *redis.Client TLSConfigCustomizer tlsutil.ConfigCustomizer XFrameOptions string + ListenHost string } // initializeDefaultProject creates the default project if it does not already exist @@ -212,7 +217,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer { err = initializeDefaultProject(opts) errors.CheckError(err) - factory := appinformer.NewFilteredSharedInformerFactory(opts.AppClientset, 0, opts.Namespace, func(options *metav1.ListOptions) {}) + factory := appinformer.NewSharedInformerFactoryWithOptions(opts.AppClientset, 0, appinformer.WithNamespace(opts.Namespace), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer() projLister := factory.Argoproj().V1alpha1().AppProjects().Lister().AppProjects(opts.Namespace) @@ -230,6 +235,11 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer { policyEnf := rbacpolicy.NewRBACPolicyEnforcer(enf, projLister) enf.SetClaimsEnforcerFunc(policyEnf.EnforceClaims) + var staticFS fs.FS = io.NewSubDirFS("dist/app", ui.Embedded) + if opts.StaticAssetsDir != "" { + staticFS = io.NewComposableFS(staticFS, os.DirFS(opts.StaticAssetsDir)) + } + return &ArgoCDServer{ ArgoCDServerOpts: opts, log: log.NewEntry(log.StandardLogger()), @@ -242,6 +252,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer { appLister: appLister, policyEnforcer: policyEnf, userStateStorage: userStateStorage, + staticAssets: http.FS(staticFS), } } @@ -290,7 +301,7 @@ func (a *ArgoCDServer) Run(ctx context.Context, port int, metricsPort int) { httpsS.Handler = &bug21955Workaround{handler: httpsS.Handler} } - metricsServ := metrics.NewMetricsServer(metricsPort) + metricsServ := metrics.NewMetricsServer(a.ListenHost, metricsPort) if a.RedisClient != nil { cacheutil.CollectMetrics(a.RedisClient, metricsServ) } @@ -299,7 +310,7 @@ func (a *ArgoCDServer) Run(ctx context.Context, port int, metricsPort int) { var conn net.Listener var realErr error _ = wait.ExponentialBackoff(backoff, func() (bool, error) { - conn, realErr = net.Listen("tcp", fmt.Sprintf(":%d", port)) + conn, realErr = net.Listen("tcp", fmt.Sprintf("%s:%d", a.ListenHost, port)) if realErr != nil { a.log.Warnf("failed listen: %v", realErr) return false, nil @@ -361,6 +372,13 @@ func (a *ArgoCDServer) Run(ctx context.Context, port int, metricsPort int) { a.stopCh = make(chan struct{}) <-a.stopCh errors.CheckError(conn.Close()) + if err := metricsServ.Shutdown(ctx); err != nil { + log.Fatalf("Failed to gracefully shutdown metrics server: %v", err) + } +} + +func (a *ArgoCDServer) Initialized() bool { + return a.projInformer.HasSynced() && a.appInformer.HasSynced() } // checkServeErr checks the error from a .Serve() call to decide if it was a graceful shutdown @@ -523,7 +541,8 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server { grpc_util.PayloadStreamServerInterceptor(a.log, true, func(ctx netCtx.Context, fullMethodName string, servingObject interface{}) bool { return !sensitiveMethods[fullMethodName] }), - grpc_util.ErrorCodeStreamServerInterceptor(), + grpc_util.ErrorCodeK8sStreamServerInterceptor(), + grpc_util.ErrorCodeGitStreamServerInterceptor(), grpc_util.PanicLoggerStreamServerInterceptor(a.log), ))) sOpts = append(sOpts, grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( @@ -535,7 +554,8 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server { grpc_util.PayloadUnaryServerInterceptor(a.log, true, func(ctx netCtx.Context, fullMethodName string, servingObject interface{}) bool { return !sensitiveMethods[fullMethodName] }), - grpc_util.ErrorCodeUnaryServerInterceptor(), + grpc_util.ErrorCodeK8sUnaryServerInterceptor(), + grpc_util.ErrorCodeGitUnaryServerInterceptor(), grpc_util.PanicLoggerUnaryServerInterceptor(a.log), ))) grpcS := grpc.NewServer(sOpts...) @@ -564,7 +584,7 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server { projectLock, a.settingsMgr, a.projInformer) - projectService := project.NewServer(a.Namespace, a.KubeClientset, a.AppClientset, a.enf, projectLock, a.sessionMgr, a.policyEnforcer, a.projInformer, a.settingsMgr) + projectService := project.NewServer(a.Namespace, a.KubeClientset, a.AppClientset, a.enf, projectLock, a.sessionMgr, a.policyEnforcer, a.projInformer, a.settingsMgr, db) settingsService := settings.NewServer(a.settingsMgr, a, a.DisableAuth) accountService := account.NewServer(a.sessionMgr, a.settingsMgr, a.enf) certificateService := certificate.NewServer(a.RepoClientset, db, a.enf) @@ -615,7 +635,7 @@ func (a *ArgoCDServer) translateGrpcCookieHeader(ctx context.Context, w http.Res } func (a *ArgoCDServer) setTokenCookie(token string, w http.ResponseWriter) error { - cookiePath := fmt.Sprintf("path=/%s", strings.TrimRight(strings.TrimLeft(a.ArgoCDServerOpts.RootPath, "/"), "/")) + cookiePath := fmt.Sprintf("path=/%s", strings.TrimRight(strings.TrimLeft(a.ArgoCDServerOpts.BaseHRef, "/"), "/")) flags := []string{cookiePath, "SameSite=lax", "httpOnly"} if !a.Insecure { flags = append(flags, "Secure") @@ -664,7 +684,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl handler: mux, urlToHandler: map[string]http.Handler{ "/api/badge": badge.NewHandler(a.AppClientset, a.settingsMgr, a.Namespace), - common.LogoutEndpoint: logout.NewHandler(a.AppClientset, a.settingsMgr, a.sessionMgr, a.ArgoCDServerOpts.RootPath, a.Namespace), + common.LogoutEndpoint: logout.NewHandler(a.AppClientset, a.settingsMgr, a.sessionMgr, a.ArgoCDServerOpts.RootPath, a.ArgoCDServerOpts.BaseHRef, a.Namespace), }, contentTypeToHandler: map[string]http.Handler{ "application/grpc-web+proto": grpcWebHandler, @@ -733,14 +753,19 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl // Serve cli binaries directly from API server registerDownloadHandlers(mux, "/download") + // Serve extensions + var extensionsApiPath = "/extensions/" + var extensionsSharedPath = "/tmp/extensions/" + + extHandler := http.StripPrefix(extensionsApiPath, http.FileServer(http.Dir(extensionsSharedPath))) + mux.HandleFunc(extensionsApiPath, extHandler.ServeHTTP) + // Serve UI static assets - if a.StaticAssetsDir != "" { - var assetsHandler http.Handler = http.HandlerFunc(a.newStaticAssetsHandler(a.StaticAssetsDir, a.BaseHRef)) - if a.ArgoCDServerOpts.EnableGZip { - assetsHandler = compressHandler(assetsHandler) - } - mux.Handle("/", assetsHandler) + var assetsHandler http.Handler = http.HandlerFunc(a.newStaticAssetsHandler()) + if a.ArgoCDServerOpts.EnableGZip { + assetsHandler = compressHandler(assetsHandler) } + mux.Handle("/", assetsHandler) return &httpS } @@ -810,55 +835,38 @@ func registerDownloadHandlers(mux *http.ServeMux, base string) { } } -func indexFilePath(srcPath string, baseHRef string) (string, error) { - if baseHRef == "/" { - return srcPath, nil - } - filePath := path.Join(os.TempDir(), fmt.Sprintf("index_%s.html", strings.Replace(strings.Trim(baseHRef, "/"), "/", "_", -1))) - f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) - if err != nil { - if os.IsExist(err) { - return filePath, nil +func (s *ArgoCDServer) getIndexData() ([]byte, error) { + s.indexDataInit.Do(func() { + data, err := ui.Embedded.ReadFile("dist/app/index.html") + if err != nil { + s.indexDataErr = err + return } - return "", err - } - defer io.Close(f) - - data, err := ioutil.ReadFile(srcPath) - if err != nil { - return "", err - } - if baseHRef != "/" { - data = []byte(baseHRefRegex.ReplaceAllString(string(data), fmt.Sprintf(``, strings.Trim(baseHRef, "/")))) - } - _, err = f.Write(data) - if err != nil { - return "", err - } + if s.BaseHRef == "/" || s.BaseHRef == "" { + s.indexData = data + } else { + s.indexData = []byte(baseHRefRegex.ReplaceAllString(string(data), fmt.Sprintf(``, strings.Trim(s.BaseHRef, "/")))) + } + }) - return filePath, nil + return s.indexData, s.indexDataErr } -func fileExists(dir, filename string) bool { - // We make sure that the resulting path is within the directory we intend to - // serve content from. path.Join() will normalize the path. - abs, err := filepath.Abs(dir) +func (server *ArgoCDServer) uiAssetExists(filename string) bool { + f, err := server.staticAssets.Open(strings.Trim(filename, "/")) if err != nil { return false } - fp := path.Join(abs, filename) - if !strings.HasPrefix(fp, abs) { - return false - } - info, err := os.Stat(fp) - if os.IsNotExist(err) { + defer io.Close(f) + stat, err := f.Stat() + if err != nil { return false } - return !info.IsDir() + return !stat.IsDir() } // newStaticAssetsHandler returns an HTTP handler to serve UI static assets -func (server *ArgoCDServer) newStaticAssetsHandler(dir string, baseHRef string) func(http.ResponseWriter, *http.Request) { +func (server *ArgoCDServer) newStaticAssetsHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { acceptHTML := false for _, acceptType := range strings.Split(r.Header.Get("Accept"), ",") { @@ -868,7 +876,7 @@ func (server *ArgoCDServer) newStaticAssetsHandler(dir string, baseHRef string) } } - fileRequest := r.URL.Path != "/index.html" && fileExists(dir, r.URL.Path) + fileRequest := r.URL.Path != "/index.html" && server.uiAssetExists(r.URL.Path) // Set X-Frame-Options according to configuration if server.XFrameOptions != "" { @@ -881,14 +889,19 @@ func (server *ArgoCDServer) newStaticAssetsHandler(dir string, baseHRef string) for k, v := range noCacheHeaders { w.Header().Set(k, v) } - indexHtmlPath, err := indexFilePath(path.Join(dir, "index.html"), baseHRef) + data, err := server.getIndexData() if err != nil { - http.Error(w, fmt.Sprintf("Unable to access index.html: %v", err), http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusInternalServerError) return } - http.ServeFile(w, r, indexHtmlPath) + + modTime, err := time.Parse(common.GetVersion().BuildDate, time.RFC3339) + if err != nil { + modTime = time.Now() + } + http.ServeContent(w, r, "index.html", modTime, io.NewByteReadSeeker(data)) } else { - http.ServeFile(w, r, path.Join(dir, r.URL.Path)) + http.FileServer(server.staticAssets).ServeHTTP(w, r) } } } diff --git a/server/server_test.go b/server/server_test.go index e5253ebf04126..69a465d67c018 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -37,13 +37,12 @@ func fakeServer() (*ArgoCDServer, func()) { redis, closer := test.NewInMemoryRedis() argoCDOpts := ArgoCDServerOpts{ - Namespace: test.FakeArgoCDNamespace, - KubeClientset: kubeclientset, - AppClientset: appClientSet, - Insecure: true, - DisableAuth: true, - StaticAssetsDir: "../test/testdata/static", - XFrameOptions: "sameorigin", + Namespace: test.FakeArgoCDNamespace, + KubeClientset: kubeclientset, + AppClientset: appClientSet, + Insecure: true, + DisableAuth: true, + XFrameOptions: "sameorigin", Cache: servercache.NewCache( appstatecache.NewCache( cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)), @@ -546,24 +545,3 @@ func TestInitializeDefaultProject_ProjectAlreadyInitialized(t *testing.T) { assert.Equal(t, proj.Spec, existingDefaultProject.Spec) } - -func TestFileExists(t *testing.T) { - t.Run("File exists and path is within dir", func(t *testing.T) { - exists := fileExists(".", "server.go") - assert.True(t, exists) - exists = fileExists(".", "account/account.go") - assert.True(t, exists) - }) - t.Run("File does not exist", func(t *testing.T) { - exists := fileExists(".", "notexist.go") - assert.False(t, exists) - }) - t.Run("Dir does not exist", func(t *testing.T) { - exists := fileExists("/notexisting", "server.go") - assert.False(t, exists) - }) - t.Run("File outside of dir", func(t *testing.T) { - exists := fileExists(".", "../reposerver/server.go") - assert.False(t, exists) - }) -} diff --git a/server/settings/settings.go b/server/settings/settings.go index 7a2a7a267aca4..108a3241ccaea 100644 --- a/server/settings/settings.go +++ b/server/settings/settings.go @@ -79,6 +79,11 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin kustomizeVersions = append(kustomizeVersions, kustomizeSettings.Versions[i].Name) } + trackingMethod, err := s.mgr.GetTrackingMethod() + if err != nil { + return nil, err + } + set := settingspkg.Settings{ URL: argoCDSettings.URL, AppLabelKey: appInstanceLabelKey, @@ -99,6 +104,8 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin UserLoginsDisabled: userLoginsDisabled, KustomizeVersions: kustomizeVersions, UiCssURL: argoCDSettings.UiCssURL, + PasswordPattern: argoCDSettings.PasswordPattern, + TrackingMethod: trackingMethod, } if sessionmgr.LoggedIn(ctx) || s.disableAuth { @@ -113,6 +120,7 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin set.ConfigManagementPlugins = tools set.UiBannerContent = argoCDSettings.UiBannerContent set.UiBannerURL = argoCDSettings.UiBannerURL + set.UiBannerPermanent = argoCDSettings.UiBannerPermanent } if argoCDSettings.DexConfig != "" { var cfg settingspkg.DexConfig diff --git a/server/settings/settings.proto b/server/settings/settings.proto index 4f688e4edfd8d..91820d218b59b 100644 --- a/server/settings/settings.proto +++ b/server/settings/settings.proto @@ -33,6 +33,9 @@ message Settings { string uiCssURL = 14; string uiBannerContent = 15; string uiBannerURL = 16; + string passwordPattern = 17; + string trackingMethod = 18; + bool uiBannerPermanent = 19; } message GoogleAnalyticsConfig { diff --git a/test/container/Dockerfile b/test/container/Dockerfile index b9e5dbed34cdc..351a8656eee21 100644 --- a/test/container/Dockerfile +++ b/test/container/Dockerfile @@ -74,7 +74,7 @@ COPY --from=node /usr/local/bin/node /usr/local/bin COPY --from=node /opt/yarn-v1.22.4 /opt/yarn-v1.22.4 # Entrypoint is required for container's user management -COPY ./test/container/uid_entrypoint.sh /usr/local/bin +COPY ./test/container/entrypoint.sh /usr/local/bin ARG UID @@ -104,4 +104,4 @@ RUN useradd -l -u ${UID} -d /home/user -s /bin/bash user && \ ln -s /opt/yarn-v1.22.4/bin/yarnpkg /usr/local/bin/yarnpkg && \ mkdir -p /var/lib/registry && chmod -R 777 /var/lib/registry -ENTRYPOINT ["/usr/local/bin/uid_entrypoint.sh"] +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/test/container/Procfile b/test/container/Procfile index 689782d407175..bfcf1d631ce11 100644 --- a/test/container/Procfile +++ b/test/container/Procfile @@ -1,6 +1,6 @@ controller: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-application-controller go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}" -api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-server go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --staticassets ui/dist/app" -dex: sh -c "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.27.0 serve /dex.yaml" +api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-server go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080}" +dex: sh -c "test $ARGOCD_IN_CI = true && exit 0; ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.30.0 serve /dex.yaml" redis: sh -c "/usr/local/bin/redis-server --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}" repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_BINARY_NAME=argocd-repo-server go run ./cmd/main.go --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/container/uid_entrypoint.sh b/test/container/entrypoint.sh similarity index 100% rename from test/container/uid_entrypoint.sh rename to test/container/entrypoint.sh diff --git a/test/e2e/accounts_test.go b/test/e2e/accounts_test.go index af623763d377e..b5f9a4160555a 100644 --- a/test/e2e/accounts_test.go +++ b/test/e2e/accounts_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/argoproj/argo-cd/v2/pkg/apiclient/account" + "github.com/argoproj/pkg/errors" "github.com/stretchr/testify/assert" "google.golang.org/grpc/codes" @@ -12,10 +14,31 @@ import ( argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" "github.com/argoproj/argo-cd/v2/pkg/apiclient/session" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + accountFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/account" "github.com/argoproj/argo-cd/v2/util/io" ) func TestCreateAndUseAccount(t *testing.T) { + ctx := accountFixture.Given(t) + ctx. + Name("test"). + When(). + Create(). + Then(). + And(func(account *account.Account, err error) { + assert.Equal(t, account.Name, ctx.GetName()) + assert.Equal(t, account.Capabilities, []string{"login"}) + }). + When(). + Login(). + Then(). + CurrentUser(func(user *session.GetUserInfoResponse, err error) { + assert.Equal(t, user.LoggedIn, true) + assert.Equal(t, user.Username, ctx.GetName()) + }) +} + +func TestCreateAndUseAccountCLI(t *testing.T) { EnsureCleanState(t) output, err := RunCli("account", "list") diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index 10d10014fcd4d..decb308dabcf4 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -35,6 +35,8 @@ import ( . "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" + projectFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/project" + repoFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos" . "github.com/argoproj/argo-cd/v2/util/argo" . "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/io" @@ -44,7 +46,7 @@ import ( const ( guestbookPath = "guestbook" guestbookPathLocal = "./testdata/guestbook_local" - globalWithNoNameSpace = "global-with-no-namesapce" + globalWithNoNameSpace = "global-with-no-namespace" guestbookWithNamespace = "guestbook-with-namespace" ) @@ -141,6 +143,26 @@ func TestAppCreation(t *testing.T) { }) } +func TestDeleteAppResource(t *testing.T) { + ctx := Given(t) + + ctx. + Path(guestbookPath). + When(). + Create(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(_ *Application) { + // app should be listed + if _, err := RunCli("app", "delete-resource", Name(), "--kind", "Service", "--resource-name", "guestbook-ui"); err != nil { + assert.NoError(t, err) + } + }). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(HealthIs(health.HealthStatusMissing)) +} + // demonstrate that we cannot use a standard sync when an immutable field is changed, we must use "force" func TestImmutableChange(t *testing.T) { SkipOnEnv(t, "OPENSHIFT") @@ -859,6 +881,70 @@ func TestPermissions(t *testing.T) { assert.True(t, sourceErrorExist) } +func TestPermissionWithScopedRepo(t *testing.T) { + projName := "argo-project" + projectFixture. + Given(t). + Name(projName). + Destination("*,*"). + When(). + Create() + + repoFixture.Given(t, true). + When(). + Path(RepoURL(RepoURLTypeFile)). + Project(projName). + Create() + + GivenWithSameState(t). + Project(projName). + RepoURLType(RepoURLTypeFile). + Path("two-nice-pods"). + When(). + PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Prune=false"}}]`). + Create(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + DeleteFile("pod-1.yaml"). + Refresh(RefreshTypeHard). + IgnoreErrors(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeOutOfSync)) +} + +func TestPermissionDeniedWithScopedRepo(t *testing.T) { + projName := "argo-project" + projectFixture. + Given(t). + Name(projName). + Destination("*,*"). + When(). + Create() + + repoFixture.Given(t, true). + When(). + Path(RepoURL(RepoURLTypeFile)). + Create() + + GivenWithSameState(t). + Project(projName). + RepoURLType(RepoURLTypeFile). + Path("two-nice-pods"). + When(). + PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Prune=false"}}]`). + IgnoreErrors(). + Create(). + 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 func TestSyncOptionPruneFalse(t *testing.T) { Given(t). diff --git a/test/e2e/cluster_test.go b/test/e2e/cluster_test.go index 56c147021c300..fb6371c38ce78 100644 --- a/test/e2e/cluster_test.go +++ b/test/e2e/cluster_test.go @@ -2,32 +2,145 @@ package e2e import ( "fmt" + "strings" "testing" "github.com/stretchr/testify/assert" + "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" + accountFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/account" + clusterFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/cluster" . "github.com/argoproj/argo-cd/v2/util/errors" ) func TestClusterList(t *testing.T) { SkipIfAlreadyRun(t) defer RecordTestRun(t) - output := FailOnErr(RunCli("cluster", "list")).(string) - assert.Equal(t, fmt.Sprintf(`SERVER NAME VERSION STATUS MESSAGE -https://kubernetes.default.svc in-cluster %v Successful `, GetVersions().ServerVersion), output) + + clusterFixture. + Given(t). + Project(ProjectName). + When(). + List(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.Equal(t, fmt.Sprintf(`SERVER NAME VERSION STATUS MESSAGE PROJECT +https://kubernetes.default.svc in-cluster %v Successful `, GetVersions().ServerVersion), output) + }) +} + +func TestClusterAdd(t *testing.T) { + clusterFixture. + Given(t). + Project(ProjectName). + Upsert(true). + Server(KubernetesInternalAPIServerAddr). + When(). + Create(). + List(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.Equal(t, fmt.Sprintf(`SERVER NAME VERSION STATUS MESSAGE PROJECT +https://kubernetes.default.svc test-cluster-add %v Successful %s`, GetVersions().ServerVersion, ProjectName), output) + }) +} + +func TestClusterAddPermissionDenied(t *testing.T) { + accountFixture.Given(t). + Name("test"). + When(). + Create(). + Login(). + SetPermissions([]fixture.ACL{}, "org-admin") + + clusterFixture. + GivenWithSameState(t). + Project(ProjectName). + Upsert(true). + Server(KubernetesInternalAPIServerAddr). + When(). + IgnoreErrors(). + Create(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.True(t, strings.Contains(err.Error(), "PermissionDenied desc = permission denied: clusters, create")) + }) +} + +func TestClusterAddAllowed(t *testing.T) { + accountFixture.Given(t). + Name("test"). + When(). + Create(). + Login(). + SetPermissions([]fixture.ACL{ + { + Resource: "clusters", + Action: "create", + Scope: ProjectName + "/*", + }, + { + Resource: "clusters", + Action: "get", + Scope: ProjectName + "/*", + }, + }, "org-admin") + + clusterFixture. + GivenWithSameState(t). + Project(ProjectName). + Upsert(true). + Server(KubernetesInternalAPIServerAddr). + When(). + Create(). + List(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.Equal(t, fmt.Sprintf(`SERVER NAME VERSION STATUS MESSAGE PROJECT +https://kubernetes.default.svc test-cluster-add-allowed %v Successful argo-project`, GetVersions().ServerVersion), output) + }) +} + +func TestClusterListDenied(t *testing.T) { + accountFixture.Given(t). + Name("test"). + When(). + Create(). + Login(). + SetPermissions([]fixture.ACL{ + { + Resource: "clusters", + Action: "create", + Scope: ProjectName + "/*", + }, + }, "org-admin") + + clusterFixture. + GivenWithSameState(t). + Project(ProjectName). + Upsert(true). + Server(KubernetesInternalAPIServerAddr). + When(). + Create(). + List(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.Equal(t, output, "SERVER NAME VERSION STATUS MESSAGE PROJECT") + }) } func TestClusterGet(t *testing.T) { SkipIfAlreadyRun(t) + EnsureCleanState(t) defer RecordTestRun(t) output := FailOnErr(RunCli("cluster", "get", "https://kubernetes.default.svc")).(string) - assert.Contains(t, output, fmt.Sprintf(` -name: in-cluster -server: https://kubernetes.default.svc -serverVersion: "%v"`, GetVersions().ServerVersion)) - + assert.Contains(t, output, "name: in-cluster") + assert.Contains(t, output, "server: https://kubernetes.default.svc") + assert.Contains(t, output, fmt.Sprintf(`serverVersion: "%v"`, GetVersions().ServerVersion)) assert.Contains(t, output, `config: tlsClientConfig: insecure: false`) diff --git a/test/e2e/deployment_test.go b/test/e2e/deployment_test.go index bb12e5efbbd9f..acc913c5fe7fc 100644 --- a/test/e2e/deployment_test.go +++ b/test/e2e/deployment_test.go @@ -1,12 +1,18 @@ package e2e import ( + "fmt" "testing" + "github.com/stretchr/testify/assert" + + "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/argoproj/gitops-engine/pkg/health" . "github.com/argoproj/gitops-engine/pkg/sync/common" . "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" ) @@ -31,3 +37,73 @@ func TestDeployment(t *testing.T) { ]`). Sync() } + +func TestDeploymentWithAnnotationTrackingMode(t *testing.T) { + ctx := Given(t) + + SetTrackingMethod(string(argo.TrackingMethodAnnotation)) + ctx. + Path("deployment"). + When(). + Create(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + When(). + Then(). + And(func(app *Application) { + out, err := RunCli("app", "manifests", app.Name) + assert.NoError(t, err) + assert.Contains(t, out, fmt.Sprintf(`annotations: + argocd.argoproj.io/tracking-id: %s:apps/Deployment:%s/nginx-deployment +`, Name(), DeploymentNamespace())) + }) +} + +func TestDeploymentWithLabelTrackingMode(t *testing.T) { + ctx := Given(t) + SetTrackingMethod(string(argo.TrackingMethodLabel)) + ctx. + Path("deployment"). + When(). + Create(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + When(). + Then(). + And(func(app *Application) { + out, err := RunCli("app", "manifests", app.Name) + assert.NoError(t, err) + assert.Contains(t, out, fmt.Sprintf(`labels: + app: nginx + app.kubernetes.io/instance: %s +`, Name())) + }) +} + +func TestDeploymentWithoutTrackingMode(t *testing.T) { + Given(t). + Path("deployment"). + When(). + Create(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + When(). + Then(). + And(func(app *Application) { + out, err := RunCli("app", "manifests", app.Name) + assert.NoError(t, err) + assert.Contains(t, out, fmt.Sprintf(`labels: + app: nginx + app.kubernetes.io/instance: %s +`, Name())) + }) +} diff --git a/test/e2e/fixture/account/actions.go b/test/e2e/fixture/account/actions.go new file mode 100644 index 0000000000000..caf6f18c4fab9 --- /dev/null +++ b/test/e2e/fixture/account/actions.go @@ -0,0 +1,54 @@ +package project + +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 +} + +func (a *Actions) IgnoreErrors() *Actions { + a.ignoreErrors = true + return a +} + +func (a *Actions) DoNotIgnoreErrors() *Actions { + a.ignoreErrors = false + return a +} + +func (a *Actions) prepareSetPasswordArgs(account string) []string { + a.context.t.Helper() + return []string{ + "account", "update-password", "--account", account, "--current-password", fixture.AdminPassword, "--new-password", fixture.DefaultTestUserPassword, "--plaintext", + } +} + +func (a *Actions) Create() *Actions { + fixture.SetAccounts(map[string][]string{ + a.context.name: {"login"}, + }) + _, _ = fixture.RunCli(a.prepareSetPasswordArgs(a.context.name)...) + return a +} + +func (a *Actions) SetPermissions(permissions []fixture.ACL, roleName string) *Actions { + fixture.SetPermissions(permissions, a.context.name, roleName) + return a +} + +func (a *Actions) Login() *Actions { + fixture.LoginAs(a.context.name) + return a +} + +func (a *Actions) Then() *Consequences { + a.context.t.Helper() + return &Consequences{a.context, a} +} diff --git a/test/e2e/fixture/account/consequences.go b/test/e2e/fixture/account/consequences.go new file mode 100644 index 0000000000000..20b0677f78821 --- /dev/null +++ b/test/e2e/fixture/account/consequences.go @@ -0,0 +1,60 @@ +package project + +import ( + "context" + "errors" + + "github.com/argoproj/argo-cd/v2/pkg/apiclient/session" + + "github.com/argoproj/argo-cd/v2/pkg/apiclient/account" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + . "github.com/argoproj/argo-cd/v2/util/errors" + "github.com/argoproj/argo-cd/v2/util/io" +) + +// this implements the "then" part of given/when/then +type Consequences struct { + context *Context + actions *Actions +} + +func (c *Consequences) And(block func(account *account.Account, err error)) *Consequences { + c.context.t.Helper() + block(c.get()) + return c +} + +func (c *Consequences) CurrentUser(block func(user *session.GetUserInfoResponse, err error)) *Consequences { + c.context.t.Helper() + block(c.getCurrentUser()) + return c +} + +func (c *Consequences) get() (*account.Account, error) { + _, accountClient, _ := fixture.ArgoCDClientset.NewAccountClient() + accList, err := accountClient.ListAccounts(context.Background(), &account.ListAccountRequest{}) + if err != nil { + return nil, err + } + for _, acc := range accList.Items { + if acc.Name == c.context.name { + return acc, nil + } + } + return nil, errors.New("account not found") +} + +func (c *Consequences) getCurrentUser() (*session.GetUserInfoResponse, error) { + closer, client, err := fixture.ArgoCDClientset.NewSessionClient() + CheckError(err) + defer io.Close(closer) + return client.GetUserInfo(context.Background(), &session.GetUserInfoRequest{}) +} + +func (c *Consequences) Given() *Context { + return c.context +} + +func (c *Consequences) When() *Actions { + return c.actions +} diff --git a/test/e2e/fixture/account/context.go b/test/e2e/fixture/account/context.go new file mode 100644 index 0000000000000..99f3adb92d7fd --- /dev/null +++ b/test/e2e/fixture/account/context.go @@ -0,0 +1,45 @@ +package project + +import ( + "testing" + "time" + + "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) + // ARGOCE_E2E_DEFAULT_TIMEOUT can be used to override the default timeout + // for any context. + timeout := env.ParseNumFromEnv("ARGOCD_E2E_DEFAULT_TIMEOUT", 10, 0, 180) + return &Context{t: t, name: fixture.Name(), timeout: timeout} +} + +func (c *Context) GetName() string { + return c.name +} + +func (c *Context) Name(name string) *Context { + c.name = name + return c +} + +func (c *Context) And(block func()) *Context { + block() + return c +} + +func (c *Context) When() *Actions { + // in case any settings have changed, pause for 1s, not great, but fine + time.Sleep(1 * time.Second) + return &Actions{context: c} +} diff --git a/test/e2e/fixture/app/context.go b/test/e2e/fixture/app/context.go index c2db065f3d9e1..ff5eb49ba3fb6 100644 --- a/test/e2e/fixture/app/context.go +++ b/test/e2e/fixture/app/context.go @@ -40,6 +40,10 @@ type Context struct { 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", 10, 0, 180) @@ -128,6 +132,11 @@ func (c *Context) HTTPSCredentialsUserPassAdded() *Context { return c } +func (c *Context) HelmHTTPSCredentialsUserPassAdded() *Context { + repos.AddHelmHTTPSCredentialsTLSClientCert() + return c +} + func (c *Context) HelmoOCICredentialsWithoutUserPassAdded() *Context { repos.AddHelmoOCICredentialsWithoutUserPass() return c diff --git a/test/e2e/fixture/app/expectation.go b/test/e2e/fixture/app/expectation.go index 3c92aca31846d..e8581a011768b 100644 --- a/test/e2e/fixture/app/expectation.go +++ b/test/e2e/fixture/app/expectation.go @@ -106,6 +106,7 @@ func ResourceSyncStatusWithNamespaceIs(kind, resource, namespace string, expecte return simple(actual == expected, fmt.Sprintf("resource '%s/%s' sync status should be %s, is %s", kind, resource, expected, actual)) } } + func ResourceHealthIs(kind, resource string, expected health.HealthStatusCode) Expectation { return func(c *Consequences) (state, string) { actual := c.resource(kind, resource, "").Health.Status diff --git a/test/e2e/fixture/cluster/actions.go b/test/e2e/fixture/cluster/actions.go new file mode 100644 index 0000000000000..25de5e01f8c42 --- /dev/null +++ b/test/e2e/fixture/cluster/actions.go @@ -0,0 +1,86 @@ +package cluster + +import ( + "context" + "errors" + "fmt" + "log" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + + clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster" + "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 + lastOutput string + lastError error + ignoreErrors bool +} + +func (a *Actions) IgnoreErrors() *Actions { + a.ignoreErrors = true + return a +} + +func (a *Actions) DoNotIgnoreErrors() *Actions { + a.ignoreErrors = false + return a +} + +func (a *Actions) Create(args ...string) *Actions { + _, clusterClient, _ := fixture.ArgoCDClientset.NewClusterClient() + + _, err := clusterClient.Create(context.Background(), &clusterpkg.ClusterCreateRequest{ + Cluster: &v1alpha1.Cluster{ + Server: a.context.server, + Name: a.context.name, + Config: v1alpha1.ClusterConfig{}, + ConnectionState: v1alpha1.ConnectionState{}, + ServerVersion: "", + Namespaces: nil, + RefreshRequestedAt: nil, + Info: v1alpha1.ClusterInfo{}, + Shard: nil, + ClusterResources: false, + Project: a.context.project, + }, + Upsert: a.context.upsert, + }) + + if err != nil { + if !a.ignoreErrors { + log.Fatalf(fmt.Sprintf("Failed to upsert cluster %v", err.Error())) + } + a.lastError = errors.New(err.Error()) + } + + return a +} + +func (a *Actions) List() *Actions { + a.context.t.Helper() + a.runCli("cluster", "list") + return a +} + +func (a *Actions) Get() *Actions { + a.context.t.Helper() + a.runCli("cluster", "get", a.context.server) + return a +} + +func (a *Actions) Then() *Consequences { + a.context.t.Helper() + return &Consequences{a.context, a} +} + +func (a *Actions) runCli(args ...string) { + a.context.t.Helper() + a.lastOutput, a.lastError = fixture.RunCli(args...) +} diff --git a/test/e2e/fixture/cluster/consequences.go b/test/e2e/fixture/cluster/consequences.go new file mode 100644 index 0000000000000..5e35f942cc9db --- /dev/null +++ b/test/e2e/fixture/cluster/consequences.go @@ -0,0 +1,58 @@ +package cluster + +import ( + "context" + "fmt" + + clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" +) + +// this implements the "then" part of given/when/then +type Consequences struct { + context *Context + actions *Actions +} + +func (c *Consequences) Expect() *Consequences { + return c +} + +func (c *Consequences) And(block func(cluster *v1alpha1.Cluster, err error)) *Consequences { + c.context.t.Helper() + block(c.cluster()) + 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 +} + +func (c *Consequences) cluster() (*v1alpha1.Cluster, error) { + app, err := c.get() + return app, err +} + +func (c *Consequences) get() (*v1alpha1.Cluster, error) { + _, clusterClient, _ := fixture.ArgoCDClientset.NewClusterClient() + + cluster, _ := clusterClient.List(context.Background(), &clusterpkg.ClusterQuery{}) + for i := range cluster.Items { + if cluster.Items[i].Server == c.context.server { + return &cluster.Items[i], nil + } + } + + return nil, fmt.Errorf("cluster not found") +} + +func (c *Consequences) Given() *Context { + return c.context +} + +func (c *Consequences) When() *Actions { + return c.actions +} diff --git a/test/e2e/fixture/cluster/context.go b/test/e2e/fixture/cluster/context.go new file mode 100644 index 0000000000000..8bc2cf4b1357b --- /dev/null +++ b/test/e2e/fixture/cluster/context.go @@ -0,0 +1,67 @@ +package cluster + +import ( + "testing" + "time" + + "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 + project string + server string + upsert bool +} + +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", 10, 0, 180) + return &Context{t: t, name: fixture.Name(), timeout: timeout, project: "default"} +} + +func (c *Context) GetName() string { + return c.name +} + +func (c *Context) Name(name string) *Context { + c.name = name + return c +} + +func (c *Context) Server(server string) *Context { + c.server = server + return c +} + +func (c *Context) And(block func()) *Context { + block() + return c +} + +func (c *Context) When() *Actions { + // in case any settings have changed, pause for 1s, not great, but fine + time.Sleep(1 * time.Second) + return &Actions{context: c} +} + +func (c *Context) Project(project string) *Context { + c.project = project + return c +} + +func (c *Context) Upsert(upsert bool) *Context { + c.upsert = upsert + return c +} diff --git a/test/e2e/fixture/fixture.go b/test/e2e/fixture/fixture.go index 62254b5d6f76a..95789cdbed710 100644 --- a/test/e2e/fixture/fixture.go +++ b/test/e2e/fixture/fixture.go @@ -38,11 +38,12 @@ import ( ) const ( - defaultApiServer = "localhost:8080" - defaultAdminPassword = "password" - defaultAdminUsername = "admin" - testingLabel = "e2e.argoproj.io" - ArgoCDNamespace = "argocd-e2e" + defaultApiServer = "localhost:8080" + defaultAdminPassword = "password" + defaultAdminUsername = "admin" + DefaultTestUserPassword = "password" + testingLabel = "e2e.argoproj.io" + ArgoCDNamespace = "argocd-e2e" // ensure all repos are in one directory tree, so we can easily clean them up TmpDir = "/tmp/argo-e2e" @@ -51,6 +52,8 @@ const ( submoduleParentDir = "submoduleParent.git" GuestbookPath = "guestbook" + + ProjectName = "argo-project" ) const ( @@ -67,7 +70,7 @@ var ( AppClientset appclientset.Interface ArgoCDClientset argocdclient.Client adminUsername string - adminPassword string + AdminPassword string apiServerAddress string token string plainText bool @@ -76,6 +79,12 @@ var ( type RepoURLType string +type ACL struct { + Resource string + Action string + Scope string +} + const ( RepoURLTypeFile = "file" RepoURLTypeHTTPS = "https" @@ -86,6 +95,7 @@ const ( RepoURLTypeSSHSubmodule = "ssh-sub" RepoURLTypeSSHSubmoduleParent = "ssh-par" RepoURLTypeHelm = "helm" + RepoURLTypeHelmParent = "helm-par" RepoURLTypeHelmOCI = "helm-oci" GitUsername = "admin" GitPassword = "password" @@ -143,7 +153,7 @@ func init() { apiServerAddress = GetEnvWithDefault(argocdclient.EnvArgoCDServer, defaultApiServer) adminUsername = GetEnvWithDefault(EnvAdminUsername, defaultAdminUsername) - adminPassword = GetEnvWithDefault(EnvAdminPassword, defaultAdminPassword) + AdminPassword = GetEnvWithDefault(EnvAdminPassword, defaultAdminPassword) tlsTestResult, err := grpcutil.TestTLS(apiServerAddress) CheckError(err) @@ -151,24 +161,10 @@ func init() { ArgoCDClientset, err = argocdclient.NewClient(&argocdclient.ClientOptions{Insecure: true, ServerAddr: apiServerAddress, PlainText: !tlsTestResult.TLS}) CheckError(err) - closer, client, err := ArgoCDClientset.NewSessionClient() - CheckError(err) - defer io.Close(closer) - - sessionResponse, err := client.Create(context.Background(), &sessionpkg.SessionCreateRequest{Username: adminUsername, Password: adminPassword}) - CheckError(err) - - ArgoCDClientset, err = argocdclient.NewClient(&argocdclient.ClientOptions{ - Insecure: true, - ServerAddr: apiServerAddress, - AuthToken: sessionResponse.Token, - PlainText: !tlsTestResult.TLS, - }) - CheckError(err) - - token = sessionResponse.Token plainText = !tlsTestResult.TLS + LoginAs(adminUsername) + log.WithFields(log.Fields{"apiServerAddress": apiServerAddress}).Info("initialized") // Preload a list of tests that should be skipped @@ -193,6 +189,32 @@ func init() { } +func loginAs(username, password string) { + closer, client, err := ArgoCDClientset.NewSessionClient() + CheckError(err) + defer io.Close(closer) + + sessionResponse, err := client.Create(context.Background(), &sessionpkg.SessionCreateRequest{Username: username, Password: password}) + CheckError(err) + token = sessionResponse.Token + + ArgoCDClientset, err = argocdclient.NewClient(&argocdclient.ClientOptions{ + Insecure: true, + ServerAddr: apiServerAddress, + AuthToken: token, + PlainText: plainText, + }) + CheckError(err) +} + +func LoginAs(username string) { + password := DefaultTestUserPassword + if username == "admin" { + password = AdminPassword + } + loginAs(username, password) +} + func Name() string { return name } @@ -246,6 +268,9 @@ func RepoURL(urlType RepoURLType) string { // Default - file based Git repository case RepoURLTypeHelm: return GetEnvWithDefault(EnvRepoURLTypeHelm, "https://localhost:9444/argo-e2e/testdata.git/helm-repo/local") + // When Helm Repo has sub repos, this is the parent repo URL + case RepoURLTypeHelmParent: + return GetEnvWithDefault(EnvRepoURLTypeHelm, "https://localhost:9444/argo-e2e/testdata.git/helm-repo") case RepoURLTypeHelmOCI: return HelmOCIRegistryURL default: @@ -277,6 +302,11 @@ func updateSettingConfigMap(updater func(cm *corev1.ConfigMap) error) { updateGenericConfigMap(common.ArgoCDConfigMapName, updater) } +// Convenience wrapper for updating argocd-cm-rbac +func updateRBACConfigMap(updater func(cm *corev1.ConfigMap) error) { + updateGenericConfigMap(common.ArgoCDRBACConfigMapName, updater) +} + // Updates a given config map in argocd-e2e namespace func updateGenericConfigMap(name string, updater func(cm *corev1.ConfigMap) error) { cm, err := KubeClientset.CoreV1().ConfigMaps(TestNamespace()).Get(context.Background(), name, v1.GetOptions{}) @@ -306,6 +336,13 @@ func SetResourceOverrides(overrides map[string]v1alpha1.ResourceOverride) { SetResourceOverridesSplitKeys(overrides) } +func SetTrackingMethod(trackingMethod string) { + updateSettingConfigMap(func(cm *corev1.ConfigMap) error { + cm.Data["application.resourceTrackingMethod"] = trackingMethod + return nil + }) +} + func SetResourceOverridesSplitKeys(overrides map[string]v1alpha1.ResourceOverride) { updateSettingConfigMap(func(cm *corev1.ConfigMap) error { for k, v := range overrides { @@ -353,6 +390,21 @@ func SetAccounts(accounts map[string][]string) { }) } +func SetPermissions(permissions []ACL, username string, roleName string) { + updateRBACConfigMap(func(cm *corev1.ConfigMap) error { + var aclstr string + + for _, permission := range permissions { + aclstr += fmt.Sprintf("p, role:%s, %s, %s, %s, allow \n", roleName, permission.Resource, permission.Action, permission.Scope) + } + + aclstr += fmt.Sprintf("g, %s, role:%s", username, roleName) + cm.Data["policy.csv"] = aclstr + + return nil + }) +} + func SetConfigManagementPlugins(plugin ...v1alpha1.ConfigManagementPlugin) { updateSettingConfigMap(func(cm *corev1.ConfigMap) error { yamlBytes, err := yaml.Marshal(plugin) @@ -433,6 +485,9 @@ func EnsureCleanState(t *testing.T) { // kubectl delete secrets -l argocd.argoproj.io/secret-type=repo-creds CheckError(KubeClientset.CoreV1().Secrets(TestNamespace()).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{LabelSelector: common.LabelKeySecretType + "=" + common.LabelValueSecretTypeRepoCreds})) + // kubectl delete secrets -l argocd.argoproj.io/secret-type=cluster + CheckError(KubeClientset.CoreV1().Secrets(TestNamespace()).DeleteCollection(context.Background(), + v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{LabelSelector: common.LabelKeySecretType + "=" + common.LabelValueSecretTypeCluster})) // kubectl delete secrets -l e2e.argoproj.io=true CheckError(KubeClientset.CoreV1().Secrets(TestNamespace()).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{LabelSelector: testingLabel + "=true"})) @@ -446,6 +501,15 @@ func EnsureCleanState(t *testing.T) { return nil }) + // reset rbac + updateRBACConfigMap(func(cm *corev1.ConfigMap) error { + cm.Data = map[string]string{} + return nil + }) + + // We can switch user and as result in previous state we will have non-admin user, this case should be reset + LoginAs(adminUsername) + // reset gpg-keys config map updateGenericConfigMap(common.ArgoCDGPGKeysConfigMapName, func(cm *corev1.ConfigMap) error { cm.Data = map[string]string{} diff --git a/test/e2e/fixture/project/actions.go b/test/e2e/fixture/project/actions.go new file mode 100644 index 0000000000000..38ede63736f7c --- /dev/null +++ b/test/e2e/fixture/project/actions.go @@ -0,0 +1,75 @@ +package project + +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 + lastOutput string + lastError error + ignoreErrors bool +} + +func (a *Actions) IgnoreErrors() *Actions { + a.ignoreErrors = true + return a +} + +func (a *Actions) DoNotIgnoreErrors() *Actions { + a.ignoreErrors = false + return a +} + +func (a *Actions) Create(args ...string) *Actions { + args = a.prepareCreateArgs(args) + + // are you adding new context values? if you only use them for this func, then use args instead + a.runCli(args...) + + return a +} + +func (a *Actions) Name(name string) *Actions { + a.context.name = name + return a +} + +func (a *Actions) prepareCreateArgs(args []string) []string { + a.context.t.Helper() + args = append([]string{ + "proj", "create", a.context.name, + }, args...) + + if a.context.destination != "" { + args = append(args, "--dest", a.context.destination) + } + + return args +} + +func (a *Actions) Delete() *Actions { + a.context.t.Helper() + a.runCli("proj", "delete", a.context.name) + return a +} + +func (a *Actions) And(block func()) *Actions { + a.context.t.Helper() + block() + return a +} + +func (a *Actions) Then() *Consequences { + a.context.t.Helper() + return &Consequences{a.context, a} +} + +func (a *Actions) runCli(args ...string) { + a.context.t.Helper() + a.lastOutput, a.lastError = fixture.RunCli(args...) +} diff --git a/test/e2e/fixture/project/consequences.go b/test/e2e/fixture/project/consequences.go new file mode 100644 index 0000000000000..e7b2033c4608d --- /dev/null +++ b/test/e2e/fixture/project/consequences.go @@ -0,0 +1,47 @@ +package project + +import ( + "context" + + "github.com/argoproj/argo-cd/v2/pkg/apiclient/project" + + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" +) + +// this implements the "then" part of given/when/then +type Consequences struct { + context *Context + actions *Actions +} + +func (c *Consequences) Expect() *Consequences { + return c +} + +func (c *Consequences) And(block func(app *project.DetailedProjectsResponse, err error)) *Consequences { + c.context.t.Helper() + block(c.detailedProject()) + return c +} + +func (c *Consequences) detailedProject() (*project.DetailedProjectsResponse, error) { + prj, err := c.get() + return prj, err +} + +func (c *Consequences) get() (*project.DetailedProjectsResponse, error) { + _, projectClient, _ := fixture.ArgoCDClientset.NewProjectClient() + prj, err := projectClient.GetDetailedProject(context.Background(), &project.ProjectQuery{ + Name: c.context.name, + }) + + return prj, err +} + +func (c *Consequences) Given() *Context { + return c.context +} + +func (c *Consequences) When() *Actions { + return c.actions +} diff --git a/test/e2e/fixture/project/context.go b/test/e2e/fixture/project/context.go new file mode 100644 index 0000000000000..360e81155d500 --- /dev/null +++ b/test/e2e/fixture/project/context.go @@ -0,0 +1,55 @@ +package project + +import ( + "testing" + "time" + + "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 + destination 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", 10, 0, 180) + return &Context{t: t, name: fixture.Name(), timeout: timeout} +} + +func (c *Context) GetName() string { + return c.name +} + +func (c *Context) Name(name string) *Context { + c.name = name + return c +} + +func (c *Context) Destination(destination string) *Context { + c.destination = destination + return c +} + +func (c *Context) And(block func()) *Context { + block() + return c +} + +func (c *Context) When() *Actions { + // in case any settings have changed, pause for 1s, not great, but fine + time.Sleep(1 * time.Second) + return &Actions{context: c} +} diff --git a/test/e2e/fixture/repos/actions.go b/test/e2e/fixture/repos/actions.go new file mode 100644 index 0000000000000..800548413183d --- /dev/null +++ b/test/e2e/fixture/repos/actions.go @@ -0,0 +1,84 @@ +package repos + +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 + lastOutput string + lastError error + ignoreErrors bool +} + +func (a *Actions) IgnoreErrors() *Actions { + a.ignoreErrors = true + return a +} + +func (a *Actions) DoNotIgnoreErrors() *Actions { + a.ignoreErrors = false + return a +} + +func (a *Actions) Create(args ...string) *Actions { + args = a.prepareCreateArgs(args) + + // are you adding new context values? if you only use them for this func, then use args instead + a.runCli(args...) + + return a +} + +func (a *Actions) prepareCreateArgs(args []string) []string { + a.context.t.Helper() + args = append([]string{ + "repo", "add", a.context.path, + }, args...) + if a.context.project != "" { + args = append(args, "--project", a.context.project) + } + return args +} + +func (a *Actions) Delete() *Actions { + a.context.t.Helper() + a.runCli("repo", "rm", a.context.path) + return a +} + +func (a *Actions) List() *Actions { + a.context.t.Helper() + a.runCli("repo", "list") + return a +} + +func (a *Actions) Get() *Actions { + a.context.t.Helper() + a.runCli("repo", "get", a.context.path) + return a +} + +func (a *Actions) Path(path string) *Actions { + a.context.path = path + return a +} + +func (a *Actions) Project(project string) *Actions { + a.context.project = project + return a +} + +func (a *Actions) Then() *Consequences { + a.context.t.Helper() + return &Consequences{a.context, a} +} + +func (a *Actions) runCli(args ...string) { + a.context.t.Helper() + a.lastOutput, a.lastError = fixture.RunCli(args...) +} diff --git a/test/e2e/fixture/repos/consequences.go b/test/e2e/fixture/repos/consequences.go new file mode 100644 index 0000000000000..794d78d483cd7 --- /dev/null +++ b/test/e2e/fixture/repos/consequences.go @@ -0,0 +1,58 @@ +package repos + +import ( + "context" + "fmt" + + repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" +) + +// this implements the "then" part of given/when/then +type Consequences struct { + context *Context + actions *Actions +} + +func (c *Consequences) Expect() *Consequences { + return c +} + +func (c *Consequences) And(block func(repository *v1alpha1.Repository, err error)) *Consequences { + c.context.t.Helper() + block(c.repo()) + 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 +} + +func (c *Consequences) repo() (*v1alpha1.Repository, error) { + app, err := c.get() + return app, err +} + +func (c *Consequences) get() (*v1alpha1.Repository, error) { + _, repoClient, _ := fixture.ArgoCDClientset.NewRepoClient() + + repo, _ := repoClient.List(context.Background(), &repositorypkg.RepoQuery{}) + for i := range repo.Items { + if repo.Items[i].Repo == c.context.path { + return repo.Items[i], nil + } + } + + return nil, fmt.Errorf("repo not found") +} + +func (c *Consequences) Given() *Context { + return c.context +} + +func (c *Consequences) When() *Actions { + return c.actions +} diff --git a/test/e2e/fixture/repos/context.go b/test/e2e/fixture/repos/context.go new file mode 100644 index 0000000000000..28064e721c5f4 --- /dev/null +++ b/test/e2e/fixture/repos/context.go @@ -0,0 +1,60 @@ +package repos + +import ( + "testing" + "time" + + "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 + path string + repoURLType fixture.RepoURLType + // seconds + timeout int + name string + project string +} + +func Given(t *testing.T, sameState bool) *Context { + if !sameState { + fixture.EnsureCleanState(t) + } + // ARGOCE_E2E_DEFAULT_TIMEOUT can be used to override the default timeout + // for any context. + timeout := env.ParseNumFromEnv("ARGOCD_E2E_DEFAULT_TIMEOUT", 10, 0, 180) + return &Context{t: t, repoURLType: fixture.RepoURLTypeFile, name: fixture.Name(), timeout: timeout, project: "default"} +} + +func (c *Context) RepoURLType(urlType fixture.RepoURLType) *Context { + c.repoURLType = urlType + return c +} + +func (c *Context) GetName() string { + return c.name +} + +func (c *Context) Name(name string) *Context { + c.name = name + return c +} + +func (c *Context) And(block func()) *Context { + block() + return c +} + +func (c *Context) When() *Actions { + // in case any settings have changed, pause for 1s, not great, but fine + time.Sleep(1 * time.Second) + return &Actions{context: c} +} + +func (c *Context) Project(project string) *Context { + c.project = project + return c +} diff --git a/test/e2e/fixture/repos/repos.go b/test/e2e/fixture/repos/repos.go index e6fd1801790f6..a6cb71041a91b 100644 --- a/test/e2e/fixture/repos/repos.go +++ b/test/e2e/fixture/repos/repos.go @@ -116,6 +116,25 @@ func AddHTTPSCredentialsTLSClientCert() { errors.FailOnErr(fixture.RunCli(args...)) } +// AddHelmHTTPSCredentialsTLSClientCert adds credentials for Helm repos to context +func AddHelmHTTPSCredentialsTLSClientCert() { + certPath, err := filepath.Abs("../fixture/certs/argocd-test-client.crt") + errors.CheckError(err) + keyPath, err := filepath.Abs("../fixture/certs/argocd-test-client.key") + errors.CheckError(err) + args := []string{ + "repocreds", + "add", + fixture.RepoURL(fixture.RepoURLTypeHelmParent), + "--username", fixture.GitUsername, + "--password", fixture.GitPassword, + "--tls-client-cert-path", certPath, + "--tls-client-cert-key-path", keyPath, + "--type", "helm", + } + errors.FailOnErr(fixture.RunCli(args...)) +} + // AddHelmoOCICredentialsWithoutUserPass adds credentials for Helm OIC repo to context func AddHelmoOCICredentialsWithoutUserPass() { args := []string{"repocreds", "add", fixture.RepoURL(fixture.RepoURLTypeHelmOCI), diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 910e32ccfe2f7..d97ee14e21a22 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -350,6 +350,21 @@ func TestHelmWithDependencies(t *testing.T) { testHelmWithDependencies(t, "helm-with-dependencies", false) } +func TestHelmWithMultipleDependencies(t *testing.T) { + SkipOnEnv(t, "HELM") + + Given(t).Path("helm-with-multiple-dependencies"). + CustomCACertAdded(). + // these are slow tests + Timeout(30). + HelmHTTPSCredentialsUserPassAdded(). + When(). + Create(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)) +} + func TestHelm2WithDependencies(t *testing.T) { SkipOnEnv(t, "HELM", "HELM2") testHelmWithDependencies(t, "helm2-with-dependencies", false) diff --git a/test/e2e/project_management_test.go b/test/e2e/project_management_test.go index eb11bdfb63256..089267ca6ccde 100644 --- a/test/e2e/project_management_test.go +++ b/test/e2e/project_management_test.go @@ -66,7 +66,7 @@ func TestProjectCreation(t *testing.T) { assert.Equal(t, "https://github.com/argoproj/argo-cd.git", proj.Spec.SourceRepos[0]) assert.NotNil(t, proj.Spec.OrphanedResources) - assert.True(t, proj.Spec.OrphanedResources.IsWarn()) + assert.False(t, proj.Spec.OrphanedResources.IsWarn()) assertProjHasEvent(t, proj, "create", argo.EventReasonResourceCreated) diff --git a/test/e2e/scoped_repository_test.go b/test/e2e/scoped_repository_test.go new file mode 100644 index 0000000000000..2db45d4acbb4f --- /dev/null +++ b/test/e2e/scoped_repository_test.go @@ -0,0 +1,233 @@ +package e2e + +import ( + "strings" + "testing" + + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + + "github.com/argoproj/argo-cd/v2/pkg/apiclient/project" + + "github.com/stretchr/testify/assert" + + . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + accountFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/account" + projectFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/project" + repoFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos" +) + +func TestCreateRepositoryWithProject(t *testing.T) { + prjConsequence := projectFixture.Given(t). + When(). + Name("argo-project"). + Create(). + Then() + + path := "https://github.com/argoproj/argo-cd.git" + repoFixture.Given(t, true). + When(). + Path(path). + Project("argo-project"). + Create(). + Then(). + And(func(r *Repository, err error) { + assert.Equal(t, r.Repo, path) + assert.Equal(t, r.Project, "argo-project") + + prjConsequence.And(func(projectResponse *project.DetailedProjectsResponse, err error) { + assert.Equal(t, len(projectResponse.Repositories), 1) + assert.Equal(t, projectResponse.Repositories[0].Repo, path) + }) + }) +} + +func TestCreateRepositoryNonAdminUserPermissionDenied(t *testing.T) { + accountFixture.Given(t). + Name("test"). + When(). + Create(). + Login() + + path := "https://github.com/argoproj/argo-cd.git" + repoFixture.Given(t, true). + When(). + Path(path). + Project("argo-project"). + Create(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.True(t, strings.Contains(err.Error(), "PermissionDenied desc = permission denied: repositories, create")) + }) +} + +func TestCreateRepositoryNonAdminUserWithWrongProject(t *testing.T) { + accountFixture.Given(t). + Name("test"). + When(). + Create(). + Login(). + SetPermissions([]fixture.ACL{ + { + Resource: "repositories", + Action: "*", + Scope: "wrong-project/*", + }, + }, "org-admin") + + path := "https://github.com/argoproj/argo-cd.git" + repoFixture.Given(t, true). + When(). + Path(path). + Project("argo-project"). + Create(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.True(t, strings.Contains(err.Error(), "PermissionDenied desc = permission denied: repositories, create")) + }) +} + +func TestDeleteRepositoryRbacAllowed(t *testing.T) { + accountFixture.Given(t). + Name("test"). + When(). + Create(). + Login(). + SetPermissions([]fixture.ACL{ + { + Resource: "repositories", + Action: "create", + Scope: "argo-project/*", + }, + { + Resource: "repositories", + Action: "delete", + Scope: "argo-project/*", + }, + { + Resource: "repositories", + Action: "get", + Scope: "argo-project/*", + }, + }, "org-admin") + + path := "https://github.com/argoproj/argo-cd.git" + repoFixture.Given(t, true). + When(). + Path(path). + Project("argo-project"). + Create(). + Then(). + And(func(r *Repository, err error) { + assert.Equal(t, r.Repo, path) + assert.Equal(t, r.Project, "argo-project") + }). + When(). + Delete(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.True(t, strings.Contains(output, "Repository 'https://github.com/argoproj/argo-cd.git' removed")) + }) +} + +func TestDeleteRepositoryRbacDenied(t *testing.T) { + accountFixture.Given(t). + Name("test"). + When(). + Create(). + Login(). + SetPermissions([]fixture.ACL{ + { + Resource: "repositories", + Action: "create", + Scope: "argo-project/*", + }, + { + Resource: "repositories", + Action: "delete", + Scope: "argo-pr/*", + }, + { + Resource: "repositories", + Action: "get", + Scope: "argo-project/*", + }, + }, "org-admin") + + path := "https://github.com/argoproj/argo-cd.git" + repoFixture.Given(t, true). + When(). + Path(path). + Project("argo-project"). + Create(). + Then(). + And(func(r *Repository, err error) { + assert.Equal(t, r.Repo, path) + assert.Equal(t, r.Project, "argo-project") + }). + When(). + Delete(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.True(t, strings.Contains(err.Error(), "PermissionDenied desc = permission denied: repositories, delete")) + }) +} + +func TestDeleteRepository(t *testing.T) { + path := "https://github.com/argoproj/argo-cd.git" + repoFixture.Given(t, false). + When(). + Path(path). + Project("argo-project"). + Create(). + Then(). + And(func(r *Repository, err error) { + assert.Equal(t, r.Repo, path) + }). + When(). + Delete(). + Then(). + And(func(r *Repository, err error) { + assert.Equal(t, err.Error(), "repo not found") + }) + +} + +func TestListRepoCLIOutput(t *testing.T) { + path := "https://github.com/argoproj/argo-cd.git" + repoFixture.Given(t, false). + When(). + Path(path). + Project("argo-project"). + Create(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.Equal(t, `Repository 'https://github.com/argoproj/argo-cd.git' added`, output) + }). + When(). + List(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.Equal(t, `TYPE NAME REPO INSECURE OCI LFS CREDS STATUS MESSAGE PROJECT +git https://github.com/argoproj/argo-cd.git false false false false Successful argo-project`, output) + }) +} + +func TestGetRepoCLIOutput(t *testing.T) { + path := "https://github.com/argoproj/argo-cd.git" + repoFixture.Given(t, false). + When(). + Path(path). + Project("argo-project"). + Create(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.Equal(t, `Repository 'https://github.com/argoproj/argo-cd.git' added`, output) + }). + When(). + Get(). + Then(). + AndCLIOutput(func(output string, err error) { + assert.Equal(t, `TYPE NAME REPO INSECURE OCI LFS CREDS STATUS MESSAGE PROJECT +git https://github.com/argoproj/argo-cd.git false false false false Successful argo-project`, output) + }) +} diff --git a/test/e2e/testdata/deprecated-extensions/ingress.yaml b/test/e2e/testdata/deprecated-extensions/ingress.yaml index eb6ca119bfca3..c0275a890bfea 100644 --- a/test/e2e/testdata/deprecated-extensions/ingress.yaml +++ b/test/e2e/testdata/deprecated-extensions/ingress.yaml @@ -1,4 +1,4 @@ -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: extensions-ingress diff --git a/test/e2e/testdata/global-with-no-namesapce/console.yaml b/test/e2e/testdata/global-with-no-namespace/console.yaml similarity index 100% rename from test/e2e/testdata/global-with-no-namesapce/console.yaml rename to test/e2e/testdata/global-with-no-namespace/console.yaml diff --git a/test/e2e/testdata/helm-repo/local2/helm-1.0.0.tgz b/test/e2e/testdata/helm-repo/local2/helm-1.0.0.tgz new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/e2e/testdata/helm-repo/local2/helm2-1.0.0.tgz b/test/e2e/testdata/helm-repo/local2/helm2-1.0.0.tgz new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/e2e/testdata/helm-repo/local2/index.yaml b/test/e2e/testdata/helm-repo/local2/index.yaml new file mode 100644 index 0000000000000..b61e5e81238ce --- /dev/null +++ b/test/e2e/testdata/helm-repo/local2/index.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +entries: + helm: + - created: 2019-07-08T23:26:50.723856689Z + urls: + - http://127.0.0.1:9080/argo-e2e/testdata.git/helm-repo/helm-1.0.0.tgz + version: 1.0.0 + helm2: + - created: "2020-03-02T08:16:19.960425-08:00" + name: helm2 + urls: + - http://127.0.0.1:9080/argo-e2e/testdata.git/helm-repo/helm2-1.0.0.tgz + version: 1.0.0 diff --git a/test/e2e/testdata/helm-with-multiple-dependencies/Chart.yaml b/test/e2e/testdata/helm-with-multiple-dependencies/Chart.yaml new file mode 100644 index 0000000000000..a683fefa826be --- /dev/null +++ b/test/e2e/testdata/helm-with-multiple-dependencies/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +name: helm-with-dependencies +version: v1.0.0 +dependencies: + - name: helm + repository: "https://localhost:9444/argo-e2e/testdata.git/helm-repo/local" + version: v1.0.0 + - name: helm2 + repository: "https://localhost:9444/argo-e2e/testdata.git/helm-repo/local2" + version: v1.0.0 \ No newline at end of file diff --git a/test/e2e/testdata/networking/guestbook-ui-svc-ingress.yaml b/test/e2e/testdata/networking/guestbook-ui-svc-ingress.yaml index 839b2bd5eb945..d499de1e9c308 100644 --- a/test/e2e/testdata/networking/guestbook-ui-svc-ingress.yaml +++ b/test/e2e/testdata/networking/guestbook-ui-svc-ingress.yaml @@ -1,5 +1,5 @@ --- -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: guestbook-ui @@ -17,7 +17,7 @@ spec: serviceName: guestbook-ui-internal servicePort: 80 --- -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: guestbook-ui-2 diff --git a/test/remote/Dockerfile b/test/remote/Dockerfile index dce7312767dc2..2f64ccb185ac2 100644 --- a/test/remote/Dockerfile +++ b/test/remote/Dockerfile @@ -36,7 +36,7 @@ COPY ./test/fixture/certs/argocd-e2e-server.key /etc/certs/argocd-test-server.ke COPY ./test/fixture/certs/argocd-test-ca.crt /etc/certs/argocd-test-ca.crt # Entrypoint is required for container's user management -COPY ./test/remote/uid_entrypoint.sh /usr/local/bin +COPY ./test/remote/entrypoint.sh /usr/local/bin COPY ./test/remote/Procfile /Procfile # We need goreman @@ -83,4 +83,4 @@ RUN echo "[http]" >> /tmp/argo-e2e/submodule.git/config && \ RUN echo "[http]" >> /tmp/argo-e2e/submoduleParent.git/config && \ echo " receivepack = true" >> /tmp/argo-e2e/submoduleParent.git/config -ENTRYPOINT ["/usr/local/bin/uid_entrypoint.sh"] +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/test/remote/uid_entrypoint.sh b/test/remote/entrypoint.sh similarity index 100% rename from test/remote/uid_entrypoint.sh rename to test/remote/entrypoint.sh diff --git a/test/testdata.go b/test/testdata.go index 872b732f0830c..4531a11c6eb69 100644 --- a/test/testdata.go +++ b/test/testdata.go @@ -148,7 +148,7 @@ func NewFakeProjListerFromInterface(appProjects appclient.AppProjectInterface) a func NewFakeProjLister(objects ...runtime.Object) applister.AppProjectNamespaceLister { fakeAppClientset := apps.NewSimpleClientset(objects...) - factory := appinformer.NewFilteredSharedInformerFactory(fakeAppClientset, 0, "", func(options *metav1.ListOptions) {}) + factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppClientset, 0, appinformer.WithNamespace(""), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer() cancel := StartInformer(projInformer) defer cancel() diff --git a/tools/cmd-docs/main.go b/tools/cmd-docs/main.go index e6e5b4bdb3322..0183bef47c069 100644 --- a/tools/cmd-docs/main.go +++ b/tools/cmd-docs/main.go @@ -10,7 +10,6 @@ import ( argocddex "github.com/argoproj/argo-cd/v2/cmd/argocd-dex/commands" reposerver "github.com/argoproj/argo-cd/v2/cmd/argocd-repo-server/commands" argocdserver "github.com/argoproj/argo-cd/v2/cmd/argocd-server/commands" - argocdutil "github.com/argoproj/argo-cd/v2/cmd/argocd-util/commands" argocdcli "github.com/argoproj/argo-cd/v2/cmd/argocd/commands" ) @@ -43,8 +42,4 @@ func main() { log.Fatal(err) } - err = doc.GenMarkdownTree(argocdutil.NewCommand(), "./docs/operator-manual/server-commands") - if err != nil { - log.Fatal(err) - } } diff --git a/ui-test/.env b/ui-test/.env new file mode 100644 index 0000000000000..9e4c5b3d72259 --- /dev/null +++ b/ui-test/.env @@ -0,0 +1,40 @@ +# +# Currently defined test environment variables. Uncomment and/or change as desired. +# +############################ +# Test specific variables +############################ +# +# Timeout to wait for an element to appear. The default is 60 sec. +# TEST_TIMEOUT=60000 +# +# Run the tests in headless mode if true, non-headless mode if false +IS_HEADLESS=true +# +# Turn on/off tracing to the console. The default is true. +# ENABLE_CONSOLE_LOG=true +# +############################ +# ArgoCD specific variables +############################ +# +# URL of the ArgoCD UI to test against +ARGOCD_URL=http://localhost:4000 +# +# Git repository where applications reside +GIT_REPO=https://github.com/argoproj/argocd-example-apps +# +# The name to give the app in ArgoCD +APP_NAME=myapp +# +# The project name +APP_PROJECT=default +# +# The source path of the application in the repo +SOURCE_REPO_PATH=helm-guestbook +# +# Destination cluster name +DESTINATION_CLUSTER_NAME=in-cluster +# +# Destination namespace +DESTINATION_NAMESPACE=default diff --git a/ui-test/.gitignore b/ui-test/.gitignore new file mode 100644 index 0000000000000..ca01eb5e477e4 --- /dev/null +++ b/ui-test/.gitignore @@ -0,0 +1,6 @@ +.vscode/ +.idea/ +.DS_Store +out/ +node_modules/ +.env diff --git a/ui-test/.prettierrc b/ui-test/.prettierrc new file mode 100644 index 0000000000000..0abdae70b50de --- /dev/null +++ b/ui-test/.prettierrc @@ -0,0 +1,9 @@ +{ + "bracketSpacing": false, + "jsxSingleQuote": true, + "printWidth": 180, + "singleQuote": true, + "tabWidth": 4, + "jsxBracketSameLine": true, + "quoteProps": "consistent" +} diff --git a/ui-test/package.json b/ui-test/package.json new file mode 100644 index 0000000000000..5b638aab02442 --- /dev/null +++ b/ui-test/package.json @@ -0,0 +1,32 @@ +{ + "name": "ui-test", + "version": "1.0.0", + "description": "UI Testing", + "main": "argocd-ui-test", + "scripts": { + "compile": "npx tsc", + "test": "node out/test001.js", + "pretest": "cp .env out/.env", + "lint": "tslint -p ." + }, + "author": "Keith Chong", + "license": "Apache-2.0", + "dependencies": { + "@types/selenium-webdriver": "^4.0.9", + "assert": "^2.0.0", + "chromedriver": "^86.0.0", + "selenium-webdriver": "^4.0.0-alpha.7" + }, + "devDependencies": { + "@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": "^4.0.3", + "yarn": "^1.22.10" + } +} diff --git a/ui-test/src/Configuration.ts b/ui-test/src/Configuration.ts new file mode 100644 index 0000000000000..33323433f1559 --- /dev/null +++ b/ui-test/src/Configuration.ts @@ -0,0 +1,15 @@ +require('dotenv').config({path: __dirname + '/.env'}); + +export default class Configuration { + // Test specific + public static readonly ENABLE_CONSOLE_LOG: string | undefined = process.env.ENABLE_CONSOLE_LOG; + public static readonly TEST_TIMEOUT: string | undefined = process.env.TEST_TIMEOUT; + // ArgoCD UI specific. These are for single application-based tests, so one can quickly create an app based on the environment variables + public static readonly ARGOCD_URL: string = process.env.ARGOCD_URL ? process.env.ARGOCD_URL : ''; + public static readonly APP_NAME: string = process.env.APP_NAME ? process.env.APP_NAME : ''; + public static readonly APP_PROJECT: string = process.env.APP_PROJECT ? process.env.APP_PROJECT : ''; + public static readonly GIT_REPO: string = process.env.GIT_REPO ? process.env.GIT_REPO : ''; + public static readonly SOURCE_REPO_PATH: string = process.env.SOURCE_REPO_PATH ? process.env.SOURCE_REPO_PATH : ''; + public static readonly DESTINATION_CLUSTER_NAME: string = process.env.DESTINATION_CLUSTER_NAME ? process.env.DESTINATION_CLUSTER_NAME : ''; + public static readonly DESTINATION_NAMESPACE: string = process.env.DESTINATION_NAMESPACE ? process.env.DESTINATION_NAMESPACE : ''; +} diff --git a/ui-test/src/Constants.ts b/ui-test/src/Constants.ts new file mode 100644 index 0000000000000..e868ccb16bbce --- /dev/null +++ b/ui-test/src/Constants.ts @@ -0,0 +1,4 @@ +export const TEST_TIMEOUT: number = 60000; +export const TEST_SLIDING_PANEL_TIMEOUT: number = 5000; +export const TEST_IS_NOT_VISIBLE_TIMEOUT: number = 5000; +export const ENABLE_CONSOLE_LOG: boolean = true; diff --git a/ui-test/src/UiTestUtilities.ts b/ui-test/src/UiTestUtilities.ts new file mode 100644 index 0000000000000..a2f3126ace371 --- /dev/null +++ b/ui-test/src/UiTestUtilities.ts @@ -0,0 +1,134 @@ +import Configuration from './Configuration'; +import {Builder, By, until, WebDriver, WebElement} from 'selenium-webdriver'; +import chrome from 'selenium-webdriver/chrome'; +import * as Const from './Constants'; +import {Navigation} from './navigation'; + +export default class UiTestUtilities { + /** + * Log a message to the console. + * @param message + */ + public static async log(message: string): Promise { + let doLog = Const.ENABLE_CONSOLE_LOG; + // Config override + if (Configuration.ENABLE_CONSOLE_LOG) { + if (Configuration.ENABLE_CONSOLE_LOG === 'false') { + doLog = false; + } else { + doLog = true; + } + } + if (doLog) { + // tslint:disable-next-line:no-console + console.log(message); + } + } + + public static async logError(message: string): Promise { + let doLog = Const.ENABLE_CONSOLE_LOG; + // Config override + if (Configuration.ENABLE_CONSOLE_LOG) { + if (Configuration.ENABLE_CONSOLE_LOG === 'false') { + doLog = false; + } else { + doLog = true; + } + } + if (doLog) { + // tslint:disable-next-line:no-console + console.error(message); + } + } + + /** + * Set up the WebDriver. Initial steps for all tests. Returns the instance of Navigation with the WebDriver. + * From there, navigate the UI. Test cases do no need to reference the instance of WebDriver since Component/Page-specific + * API methods should be called instead. + * + */ + public static async init(): Promise { + const options = new chrome.Options(); + if (process.env.IS_HEADLESS) { + options.addArguments('headless'); + } + options.addArguments('window-size=1400x1200'); + const driver = await new Builder() + .forBrowser('chrome') + .setChromeOptions(options) + .build(); + + UiTestUtilities.log('Environment variables are:'); + UiTestUtilities.log(require('dotenv').config({path: __dirname + '/../.env'})); + + // Navigate to the ArgoCD URL + await driver.get(Configuration.ARGOCD_URL); + + return new Navigation(driver); + } + + /** + * Locate the UI Element for the given locator, and wait until it is visible + * + * @param driver + * @param locator + */ + public static async findUiElement(driver: WebDriver, locator: By): Promise { + try { + let timeout = Const.TEST_TIMEOUT; + if (Configuration.TEST_TIMEOUT) { + timeout = parseInt(Configuration.TEST_TIMEOUT, 10); + } + const element = await driver.wait(until.elementLocated(locator), timeout); + await driver.wait(until.elementIsVisible(element), timeout); + return element; + } catch (err) { + throw err; + } + } + + /** + * Similar to until.methods and used in driver.wait, this will wait until + * the expected attribute is the same as the actual attribute on the element + * + * @param attr + * @param attrValue + */ + public static async untilAttributeIs(element: WebElement, attr: string, attrValue: string): Promise { + const actual = await element.getAttribute(attr); + UiTestUtilities.log('Actual = ' + actual + ', expected = ' + attrValue + ', ' + (actual === attrValue)); + return actual === attrValue; + } + + /** + * Similar to until.methods and used in driver.wait, this function will wait until + * the element (eg. operation state) title attribute no longer is present + * + * @param element + */ + public static async untilOperationStatusDisappears(element: WebElement): Promise { + try { + const opState = await element.getAttribute('title'); + UiTestUtilities.log('Operation State = ' + opState); + return false; + } catch (err) { + UiTestUtilities.log('Status disappeared'); + return true; + } + } + + /** + * For clicking on elements if WebElement.click() doesn't work + * + * @param driver + * @param element + */ + public static async click(driver: WebDriver, element: WebElement): Promise { + try { + // Execute synchronous script + await driver.executeScript('arguments[0].click();', element); + } catch (e) { + throw e; + } + } +} diff --git a/ui-test/src/application-create-panel/application-create-panel.ts b/ui-test/src/application-create-panel/application-create-panel.ts new file mode 100644 index 0000000000000..60f92842a4362 --- /dev/null +++ b/ui-test/src/application-create-panel/application-create-panel.ts @@ -0,0 +1,205 @@ +import {By, until, WebDriver} from 'selenium-webdriver'; +import {Base} from '../base'; +import UiTestUtilities from '../UiTestUtilities'; +import * as Const from '../Constants'; + +const CREATE_APPLICATION_BUTTON_CREATE: By = By.xpath('.//button[@qe-id="applications-list-button-create"]'); +const CREATE_APPLICATION_BUTTON_CANCEL: By = By.xpath('.//button[@qe-id="applications-list-button-cancel"]'); + +const CREATE_APPLICATION_FIELD_APP_NAME: By = By.xpath('.//input[@qeid="application-create-field-app-name"]'); +const CREATE_APPLICATION_FIELD_PROJECT: By = By.xpath('.//input[@qe-id="application-create-field-project"]'); +const CREATE_APPLICATION_FIELD_REPOSITORY_URL: By = By.xpath('.//input[@qe-id="application-create-field-repository-url"]'); +const CREATE_APPLICATION_FIELD_REPOSITORY_PATH: By = By.xpath('.//input[@qe-id="application-create-field-path"]'); + +const CREATE_APPLICATION_DROPDOWN_DESTINATION: By = By.xpath('.//div[@qe-id="application-create-dropdown-destination"]'); +const CREATE_APPLICATION_DROPDOWN_MENU_URL: By = By.xpath('.//li[@qe-id="application-create-dropdown-destination-URL"]'); +const CREATE_APPLICATION_DROPDOWN_MENU_NAME: By = By.xpath('.//li[@qe-id="application-create-dropdown-destination-NAME"]'); + +export const DESTINATION_MENU_NAME: string = 'NAME'; +export const DESTINATION_MENU_URL: string = 'URL'; + +const CREATE_APPLICATION_FIELD_CLUSTER_NAME: By = By.xpath('.//input[@qe-id="application-create-field-cluster-name"]'); +const CREATE_APPLICATION_FIELD_CLUSTER_NAMESPACE: By = By.xpath('.//input[@qeid="application-create-field-namespace"]'); +const CREATE_APPLICATION_FIELD_CLUSTER_URL: By = By.xpath('.//input[@qe-id="application-create-field-cluster-url"]'); + +export class ApplicationCreatePanel extends Base { + public constructor(driver: WebDriver) { + super(driver); + } + + public async setAppName(appName: string): Promise { + try { + const appNameField = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_APP_NAME); + await appNameField.sendKeys(appName); + } catch (err) { + throw new Error(err); + } + } + + public async setProjectName(projectName: string): Promise { + try { + const project = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_PROJECT); + await project.sendKeys(projectName); + } catch (err) { + throw new Error(err); + } + } + + public async setSourceRepoUrl(sourceRepoUrl: string): Promise { + try { + const reposUrl = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_REPOSITORY_URL); + await reposUrl.sendKeys(sourceRepoUrl); + } catch (err) { + throw new Error(err); + } + } + + public async setSourceRepoPath(sourceRepoPath: string): Promise { + try { + const path = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_REPOSITORY_PATH); + await path.sendKeys(sourceRepoPath); + } catch (err) { + throw new Error(err); + } + } + + /** + * Convenience method to select the Destination Cluster URL menu and set the url field with destinationClusterFieldValue + * + * @param destinationClusterFieldValue + */ + public async selectDestinationClusterURLMenu(destinationClusterFieldValue: string): Promise { + try { + const clusterCombo = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_DROPDOWN_DESTINATION); + // click() doesn't work. Use script + await UiTestUtilities.click(this.driver, clusterCombo); + const urlMenu = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_DROPDOWN_MENU_URL); + await urlMenu.click(); + if (destinationClusterFieldValue) { + await this.setDestinationClusterUrl(destinationClusterFieldValue); + } + } catch (err) { + throw new Error(err); + } + } + + /** + * Convenience method to select the Destination Cluster Name menu and set the namefield with destinationClusterFieldValue + * + * @param destinationClusterFieldValue + */ + public async selectDestinationClusterNameMenu(destinationClusterFieldValue: string): Promise { + try { + const clusterCombo = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_DROPDOWN_DESTINATION); + // click() doesn't work. Use script + await UiTestUtilities.click(this.driver, clusterCombo); + const nameMenu = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_DROPDOWN_MENU_NAME); + await nameMenu.click(); + if (destinationClusterFieldValue) { + await this.setDestinationClusterName(destinationClusterFieldValue); + } + } catch (err) { + throw new Error(err); + } + } + + public async setDestinationClusterName(destinationClusterName: string): Promise { + try { + const clusterName = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_CLUSTER_NAME); + await clusterName.sendKeys(destinationClusterName); + // await clusterName.sendKeys('\r'); + } catch (err) { + throw new Error(err); + } + } + + public async setDestinationClusterUrl(destinationClusterUrl: string): Promise { + try { + const clusterUrl = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_CLUSTER_URL); + await clusterUrl.sendKeys(destinationClusterUrl); + } catch (err) { + throw new Error(err); + } + } + + public async setDestinationNamespace(destinationNamespace: string): Promise { + try { + const namespace = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_CLUSTER_NAMESPACE); + await namespace.sendKeys(destinationNamespace); + } catch (err) { + throw new Error(err); + } + } + + /** + * Click the Create button to create the app + */ + public async clickCreateButton(): Promise { + try { + const createButton = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_BUTTON_CREATE); + await createButton.click(); + + // Wait until the Create Application Sliding Panel disappears + await this.driver.wait(until.elementIsNotVisible(createButton), Const.TEST_SLIDING_PANEL_TIMEOUT).catch(e => { + UiTestUtilities.logError('The Create Application Sliding Panel did not disappear'); + throw e; + }); + await this.driver.sleep(1000); + } catch (err) { + throw new Error(err); + } + } + + /** + * Click the Cancel Button. Do not create the app. + */ + public async clickCancelButton(): Promise { + try { + const cancelButton = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_BUTTON_CANCEL); + await cancelButton.click(); + + // Wait until the Create Application Sliding Panel disappears + await this.driver.wait(until.elementIsNotVisible(cancelButton), Const.TEST_SLIDING_PANEL_TIMEOUT).catch(e => { + UiTestUtilities.logError('The Create Application Sliding Panel did not disappear'); + throw e; + }); + } catch (err) { + throw new Error(err); + } + } + + /** + * Convenience method to create an application given the following inputs to the dialog + * + * TODO add Sync Policy and Sync Options and setter methods above + * + * @param appName + * @param projectName + * @param sourceRepoUrl + * @param sourceRepoPath + * @param destinationMenu + * @param destinationClusterName + * @param destinationNamespace + */ + public async createApplication( + appName: string, + projectName: string, + sourceRepoUrl: string, + sourceRepoPath: string, + destinationClusterName: string, + destinationNamespace: string + ): Promise { + UiTestUtilities.log('About to create application'); + try { + await this.setAppName(appName); + await this.setProjectName(projectName); + await this.setSourceRepoUrl(sourceRepoUrl); + await this.setSourceRepoPath(sourceRepoPath); + await this.selectDestinationClusterNameMenu(destinationClusterName); + await this.setDestinationNamespace(destinationNamespace); + await this.clickCreateButton(); + } catch (err) { + throw new Error(err); + } + } +} diff --git a/ui-test/src/applications-list/applications-list.ts b/ui-test/src/applications-list/applications-list.ts new file mode 100644 index 0000000000000..3aa5e42ab2c8d --- /dev/null +++ b/ui-test/src/applications-list/applications-list.ts @@ -0,0 +1,199 @@ +import {By, until, WebDriver} from 'selenium-webdriver'; +import UiTestUtilities from '../UiTestUtilities'; +import * as Const from '../Constants'; +import {Base} from '../base'; +import {ApplicationCreatePanel} from '../application-create-panel/application-create-panel'; +import {ApplicationsSyncPanel, SYNC_PANEL_SYNCHRONIZE_BUTTON} from '../applications-sync-panel/applications-sync-panel'; +import {PopupManager} from '../popup/popup-manager'; + +const NEW_APP_BUTTON: By = By.xpath('.//button[@qe-id="applications-list-button-new-app"]'); +// Uncomment to use: +// const CREATE_APPLICATION_BUTTON: By = By.xpath('.//button[@qe-id="applications-list-button-create-application"]'); + +export class ApplicationsList extends Base { + private applicationCreatePanel: ApplicationCreatePanel; + private applicationsSyncPanel: ApplicationsSyncPanel; + private popupManager: PopupManager; + + public constructor(driver: WebDriver) { + super(driver); + this.applicationCreatePanel = new ApplicationCreatePanel(driver); + this.applicationsSyncPanel = new ApplicationsSyncPanel(driver); + this.popupManager = new PopupManager(driver); + } + + public async clickTile(appName: string): Promise { + try { + const tile = await UiTestUtilities.findUiElement(this.driver, this.getApplicationTileLocator(appName)); + await tile.click(); + } catch (err) { + throw new Error(err); + } + } + + /** + * Click the Add New Button + */ + public async clickNewAppButton(): Promise { + try { + const newAppButton = await UiTestUtilities.findUiElement(this.driver, NEW_APP_BUTTON); + await newAppButton.click(); + } catch (err) { + throw new Error(err); + } + return this.applicationCreatePanel; + } + + /** + * Click the Sync button on the App tile + * + * @param appName + */ + public async clickSyncButtonOnApp(appName: string): Promise { + try { + const syncButton = await UiTestUtilities.findUiElement(this.driver, this.getSyncButtonLocatorForApp(appName)); + await syncButton.click(); + // Wait until the Synchronize sliding panel appears + const synchronizeButton = await this.driver.wait(until.elementLocated(SYNC_PANEL_SYNCHRONIZE_BUTTON), Const.TEST_TIMEOUT); + await this.driver.wait(until.elementIsVisible(synchronizeButton), Const.TEST_TIMEOUT); + } catch (err) { + throw new Error(err); + } + return this.applicationsSyncPanel; + } + + /** + * Delete an application via the Delete button on the App tile + * + * @param appName + */ + public async clickDeleteButtonOnApp(appName: string): Promise { + try { + const deleteButton = await UiTestUtilities.findUiElement(this.driver, this.getDeleteButtonLocatorForApp(appName)); + await deleteButton.click(); + } catch (err) { + throw new Error(err); + } + return this.popupManager; + } + + public async waitUntilOperationStatusDisappearsOnApp(appName: string) { + const opStateElem = await UiTestUtilities.findUiElement(this.driver, this.getApplicationOperationsTitle(appName)); + await this.driver.wait(async () => { + return UiTestUtilities.untilOperationStatusDisappears(opStateElem); + }, Const.TEST_TIMEOUT); + } + + /** + * Click on the Refresh button on the App tile + * + * @param appName + */ + public async clickRefreshButtonOnApp(appName: string): Promise { + try { + const refreshButton = await UiTestUtilities.findUiElement(this.driver, this.getRefreshButtonLocatorForApp(appName)); + await this.driver.wait(until.elementIsVisible(refreshButton), Const.TEST_TIMEOUT); + await refreshButton.click(); + } catch (err) { + throw new Error(err); + } + } + + /** + * Use with wait. Wait for the health status of the app to change to Healthy + * + * @param appName + */ + public async waitForHealthStatusOnApp(appName: string): Promise { + try { + const healthStatusElement = await UiTestUtilities.findUiElement(this.driver, this.getApplicationHealthTitle(appName)); + await this.driver.wait(async () => { + return UiTestUtilities.untilAttributeIs(healthStatusElement, 'title', 'Healthy'); + }, Const.TEST_TIMEOUT); + } catch (err) { + throw new Error(err); + } + } + + /** + * Use with wait. Wait for the sync status of the app to change to Synced + * + * @param appName + */ + public async waitForSyncStatusOnApp(appName: string): Promise { + try { + const statusElement = await UiTestUtilities.findUiElement(this.driver, this.getApplicationSyncTitle(appName)); + await this.driver.wait(async () => { + return UiTestUtilities.untilAttributeIs(statusElement, 'title', 'Synced'); + }, Const.TEST_TIMEOUT); + } catch (err) { + throw new Error(err); + } + } + + /** + * Check that there are no operations associated with the app + * + * @param appName + */ + public async checkNoAdditionalOperations(appName: string): Promise { + // Check if there are no operations still running + UiTestUtilities.log('Checking if there are any additional operations'); + let opStateElem; + let opState; + try { + opStateElem = await this.driver.wait(until.elementLocated(this.getApplicationOperationsTitle(appName)), Const.TEST_IS_NOT_VISIBLE_TIMEOUT); + UiTestUtilities.logError('Unexpected to locate Operation element.'); + opState = await opStateElem.getAttribute('title'); + } catch (e) { + // ignore since we expect to not have any existing operations + } + if (opStateElem) { + throw new Error('Expecting no other operations. Actual: ' + opState); + } + } + + // Locators + + // By.css('#app .applications-tiles .applications-list-" + appName + "''); + + private getApplicationTileLocator(appName: string): By { + return By.xpath('.//div[contains(@class,"qe-applications-list-"' + appName + ')'); + } + + private getSyncButtonLocatorForApp(appName: string): By { + return By.xpath('.//div[contains(@class, "qe-applications-list-' + appName + '")]//div[@class="row"]//ancestor::a[@qe-id="applications-tiles-button-sync"]'); + } + + private getDeleteButtonLocatorForApp(appName: string): By { + return By.xpath('.//div[contains(@class, "qe-applications-list-' + appName + '")]//div[@class="row"]//ancestor::a[@qe-id="applications-tiles-button-delete"]'); + } + + private getRefreshButtonLocatorForApp(appName: string): By { + return By.xpath('.//div[contains(@class, "qe-applications-list-' + appName + '")]//div[@class="row"]//ancestor::a[@qe-id="applications-tiles-button-refresh"]'); + } + + private getApplicationHealthTitle(appName: string): By { + return By.xpath( + './/div[contains(@class, "qe-applications-list-' + + appName + + '")]//div[@class="row"]//div[@qe-id="applications-tiles-health-status"]//i[@qe-id="utils-health-status-title"]' + ); + } + + private getApplicationSyncTitle(appName: string): By { + return By.xpath( + './/div[contains(@class, "qe-applications-list-' + + appName + + '")]//div[@class="row"]//div[@qe-id="applications-tiles-health-status"]//i[@qe-id="utils-sync-status-title"]' + ); + } + + private getApplicationOperationsTitle(appName: string): By { + return By.xpath( + './/div[contains(@class, "qe-applications-list-' + + appName + + '")]//div[@class="row"]//div[@qe-id="applications-tiles-health-status"]//i[@qe-id="utils-operations-status-title"]' + ); + } +} diff --git a/ui-test/src/applications-sync-panel/applications-sync-panel.ts b/ui-test/src/applications-sync-panel/applications-sync-panel.ts new file mode 100644 index 0000000000000..9a5f266de58f3 --- /dev/null +++ b/ui-test/src/applications-sync-panel/applications-sync-panel.ts @@ -0,0 +1,35 @@ +import {By, until, WebDriver} from 'selenium-webdriver'; +import {Base} from '../base'; +import * as Const from '../Constants'; +import UiTestUtilities from '../UiTestUtilities'; + +export const SYNC_PANEL_SYNCHRONIZE_BUTTON: By = By.xpath('.//button[@qe-id="application-sync-panel-button-synchronize"]'); + +export class ApplicationsSyncPanel extends Base { + public constructor(driver: WebDriver) { + super(driver); + } + + /** + * Click the Sync button + */ + public async clickSyncButton() { + try { + // Wait until the Synchronize button appears + const synchronizeButton = await this.driver.wait(until.elementLocated(SYNC_PANEL_SYNCHRONIZE_BUTTON), Const.TEST_TIMEOUT); + await this.driver.wait(until.elementIsVisible(synchronizeButton), Const.TEST_TIMEOUT); + + // Check if the sync button is enabled + await this.driver.wait(until.elementIsEnabled(synchronizeButton), Const.TEST_TIMEOUT); + await synchronizeButton.click(); + + await this.driver.wait(until.elementIsNotVisible(synchronizeButton), Const.TEST_SLIDING_PANEL_TIMEOUT).catch(e => { + UiTestUtilities.logError('The Synchronization Sliding Panel did not disappear'); + throw e; + }); + UiTestUtilities.log('Synchronize sliding panel disappeared'); + } catch (err) { + throw new Error(err); + } + } +} diff --git a/ui-test/src/base.ts b/ui-test/src/base.ts new file mode 100644 index 0000000000000..0982ea8a72cec --- /dev/null +++ b/ui-test/src/base.ts @@ -0,0 +1,9 @@ +import {WebDriver} from 'selenium-webdriver'; + +export abstract class Base { + protected driver: WebDriver; + + public constructor(driver: WebDriver) { + this.driver = driver; + } +} diff --git a/ui-test/src/navigation.ts b/ui-test/src/navigation.ts new file mode 100644 index 0000000000000..57588b86d464d --- /dev/null +++ b/ui-test/src/navigation.ts @@ -0,0 +1,93 @@ +import {By, WebDriver} from 'selenium-webdriver'; +import {ApplicationsList} from './applications-list/applications-list'; +import UiTestUtilities from './UiTestUtilities'; +import {Base} from './base'; + +const NAVBAR_APPLICATIONS_BUTTON: By = By.css('#app .nav-bar .argo-icon-application'); +const NAVBAR_SETTINGS_BUTTON: By = By.css('#app .nav-bar .argo-icon-settings'); +const NAVBAR_USER_INFO_BUTTON: By = By.css('#app .nav-bar .fa-user-circle'); +const NAVBAR_DOCS_BUTTON: By = By.css('#app .nav-bar .argo-icon-docs'); + +export class Navigation extends Base { + private applicationsList: ApplicationsList; + + public constructor(driver: WebDriver) { + super(driver); + this.applicationsList = new ApplicationsList(this.driver); + } + + /** + * Click the Applications Nav Bar Button + * Return: reference to ApplicationsList page + */ + public async clickApplicationsNavBarButton(): Promise { + try { + const navBarButton = await UiTestUtilities.findUiElement(this.driver, NAVBAR_APPLICATIONS_BUTTON); + await navBarButton.click(); + } catch (err) { + throw new Error(err); + } + return this.applicationsList; + } + + /** + * Click the Settings Nav Bar Button + * TODO return settings page + */ + public async clickSettingsNavBarButton() { + try { + const navBarButton = await UiTestUtilities.findUiElement(this.driver, NAVBAR_SETTINGS_BUTTON); + await navBarButton.click(); + } catch (err) { + throw new Error(err); + } + } + + /** + * Click the User Info Nav Bar Button + * TODO return User Info page + */ + public async clickUserInfoNavBarButton() { + try { + const navBarButton = await UiTestUtilities.findUiElement(this.driver, NAVBAR_USER_INFO_BUTTON); + await navBarButton.click(); + } catch (err) { + throw new Error(err); + } + } + + /** + * Click the Documentation Nav Bar Button + * TODO return docs page + */ + public async clickDocsNavBarButton() { + try { + const navBarButton = await UiTestUtilities.findUiElement(this.driver, NAVBAR_DOCS_BUTTON); + await navBarButton.click(); + } catch (err) { + throw new Error(err); + } + } + + /** + * Get the WebDriver. Test cases are not recommended to use this. Use Page/Component objects to perform actions + */ + public getDriver(): WebDriver { + return this.driver; + } + + /** + * Call when test case is finished + */ + public async quit() { + await this.driver.quit(); + } + + /** + * Sleep for t milliseconds. This is not recommended for use by test cases. + * @param t + */ + public async sleep(t: number) { + await this.driver.sleep(t); + } +} diff --git a/ui-test/src/popup/popup-manager.ts b/ui-test/src/popup/popup-manager.ts new file mode 100644 index 0000000000000..c80c2182302fd --- /dev/null +++ b/ui-test/src/popup/popup-manager.ts @@ -0,0 +1,35 @@ +import {By, WebDriver} from 'selenium-webdriver'; +import {Base} from '../base'; +import UiTestUtilities from '../UiTestUtilities'; + +// Popup Confirmation dialog +// Uncomment to use +// const POPUP_OK_BUTTON_CSS: By = By.css('#app .popup-container .qe-argo-popup-ok-button'); +// const POPUP_OK_BUTTON: By = By.xpath('.//button[@qe-id="argo-popup-ok-button"]'); +// const POPUP_CANCEL_BUTTON: By = By.xpath('.//button[@qe-id="argo-popup-cancel-button"]'); + +// Popup Prompt dialog +const DELETE_APP_POPUP_FIELD_CONFIRMATION: By = By.xpath('.//input[@qeid="name-field-delete-confirmation"]'); +const DELETE_APP_PROMPT_POPUP_OK_BUTTON: By = By.xpath('.//button[@qe-id="prompt-popup-ok-button"]'); +const DELETE_APP_PROMPT_POPUP_CANCEL_BUTTON: By = By.xpath('.//button[@qe-id="prompt-popup-cancel-button"]'); + +export class PopupManager extends Base { + public constructor(driver: WebDriver) { + super(driver); + } + + public async setPromptFieldName(appName: string): Promise { + const confirmationField = await UiTestUtilities.findUiElement(this.driver, DELETE_APP_POPUP_FIELD_CONFIRMATION); + await confirmationField.sendKeys(appName); + } + + public async clickPromptOk(): Promise { + const okButton = await UiTestUtilities.findUiElement(this.driver, DELETE_APP_PROMPT_POPUP_OK_BUTTON); + await okButton.click(); + } + + public async clickPromptCancel(): Promise { + const cancelButton = await UiTestUtilities.findUiElement(this.driver, DELETE_APP_PROMPT_POPUP_CANCEL_BUTTON); + await cancelButton.click(); + } +} diff --git a/ui-test/src/test001.ts b/ui-test/src/test001.ts new file mode 100644 index 0000000000000..3db3f4d71413f --- /dev/null +++ b/ui-test/src/test001.ts @@ -0,0 +1,55 @@ +import Configuration from './Configuration'; +import UiTestUtilities from './UiTestUtilities'; +import {trace} from 'console'; +import {ApplicationsList} from './applications-list/applications-list'; +import {ApplicationCreatePanel} from './application-create-panel/application-create-panel'; +import {ApplicationsSyncPanel} from './applications-sync-panel/applications-sync-panel'; +import {PopupManager} from './popup/popup-manager'; + +/** + * General test that + * - creates an app based on the environment variables (see .env), + * - syncs the app + * - waits for the healthy and sync'ed states + * - deletes the app. + * + * This can be run multiple times for different apps + * + */ +async function doTest() { + const navigation = await UiTestUtilities.init(); + try { + const appsList: ApplicationsList = await navigation.clickApplicationsNavBarButton(); + const applicationCreatePanel: ApplicationCreatePanel = await appsList.clickNewAppButton(); + + UiTestUtilities.log('About to create application'); + await applicationCreatePanel.setAppName(Configuration.APP_NAME); + await applicationCreatePanel.setProjectName(Configuration.APP_PROJECT); + await applicationCreatePanel.setSourceRepoUrl(Configuration.GIT_REPO); + await applicationCreatePanel.setSourceRepoPath(Configuration.SOURCE_REPO_PATH); + await applicationCreatePanel.selectDestinationClusterNameMenu(Configuration.DESTINATION_CLUSTER_NAME); + await applicationCreatePanel.setDestinationNamespace(Configuration.DESTINATION_NAMESPACE); + await applicationCreatePanel.clickCreateButton(); + + const appsSyncPanel: ApplicationsSyncPanel = await appsList.clickSyncButtonOnApp(Configuration.APP_NAME); + await appsSyncPanel.clickSyncButton(); + + await appsList.waitForHealthStatusOnApp(Configuration.APP_NAME); + await appsList.waitForSyncStatusOnApp(Configuration.APP_NAME); + await appsList.checkNoAdditionalOperations(Configuration.APP_NAME); + + const popupManager: PopupManager = await appsList.clickDeleteButtonOnApp(Configuration.APP_NAME); + await popupManager.setPromptFieldName(Configuration.APP_NAME); + await popupManager.clickPromptOk(); + // After deleting, wait until the delete operation finishes + await appsList.waitUntilOperationStatusDisappearsOnApp(Configuration.APP_NAME); + + await UiTestUtilities.log('Test passed'); + } catch (e) { + trace('Test failed ', e); + } finally { + await navigation.quit(); + } +} + +doTest(); diff --git a/ui-test/src/test002.ts b/ui-test/src/test002.ts new file mode 100644 index 0000000000000..4e1f41fc05de0 --- /dev/null +++ b/ui-test/src/test002.ts @@ -0,0 +1,27 @@ +import UiTestUtilities from './UiTestUtilities'; +import {trace} from 'console'; +import {ApplicationsList} from './applications-list/applications-list'; +import {ApplicationCreatePanel} from './application-create-panel/application-create-panel'; + +/** + * Test to demo how to visit each page via the navigation bar on the left. + * + */ +async function doTest() { + const navigation = await UiTestUtilities.init(); + try { + await navigation.clickDocsNavBarButton(); + await navigation.clickUserInfoNavBarButton(); + await navigation.clickSettingsNavBarButton(); + const appsList: ApplicationsList = await navigation.clickApplicationsNavBarButton(); + const applicationCreatePanel: ApplicationCreatePanel = await appsList.clickNewAppButton(); + await applicationCreatePanel.clickCancelButton(); + await UiTestUtilities.log('Test passed'); + } catch (e) { + trace('Test failed ', e); + } finally { + await navigation.quit(); + } +} + +doTest(); diff --git a/ui-test/tsconfig.json b/ui-test/tsconfig.json new file mode 100644 index 0000000000000..143f0946f29ef --- /dev/null +++ b/ui-test/tsconfig.json @@ -0,0 +1,74 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "lib": ["es2017"], /* Specify library files to be included in the compilation. */ + "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "out", /* Redirect output structure to the directory. */ + "rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + "noUnusedLocals": true, /* Report errors on unused locals. */ + "noUnusedParameters": true, /* Report errors on unused parameters. */ + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + // "resolveJsonModule": true, /* Include modules imported with '.json' extension */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "exclude": [ + "node_modules", + "out" + ] +} diff --git a/ui-test/tslint.json b/ui-test/tslint.json new file mode 100644 index 0000000000000..2ed387963062c --- /dev/null +++ b/ui-test/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": [ + "tslint:recommended", "tslint-plugin-prettier", "tslint-config-prettier" + ], + "jsRules": {}, + "rules": { + "prettier": true, + "quotemark": [true, "single"], + "no-var-requires": false, + "interface-name": false, + "object-literal-sort-keys": false, + "max-line-length": [true, 200], + "array-type": false, + "max-classes-per-file": false + }, + "rulesDirectory": [] +} diff --git a/ui-test/yarn.lock b/ui-test/yarn.lock new file mode 100644 index 0000000000000..ddc0b2a2d4522 --- /dev/null +++ b/ui-test/yarn.lock @@ -0,0 +1,1529 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@nodelib/fs.scandir@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" + integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== + dependencies: + "@nodelib/fs.stat" "2.0.3" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" + integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" + integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + dependencies: + "@nodelib/fs.scandir" "2.1.3" + fastq "^1.6.0" + +"@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/glob@^7.1.1": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" + integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + +"@types/mocha@^8.0.3": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.0.tgz#3eb56d13a1de1d347ecb1957c6860c911704bc44" + integrity sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ== + +"@types/node@*", "@types/node@^14.14.2": + version "14.14.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.14.tgz#f7fd5f3cc8521301119f63910f0fb965c7d761ae" + integrity sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ== + +"@types/selenium-webdriver@^4.0.9": + version "4.0.10" + resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.0.10.tgz#1aa370a14747b51752e65622999848c4f037b058" + integrity sha512-Xavn3fE+uM2aeIHtefIwpy0zAf2HQOyip/jU7ZR0ailt/B0ww/TJ6yMnfZ5pM0F4+Kx+9AQSnxQio3P5QAl1yQ== + +"@types/yauzl@^2.9.1": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" + integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== + dependencies: + "@types/node" "*" + +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" + 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" + +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-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +assert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" + integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== + dependencies: + 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" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.0" + +axios@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= + +call-bind@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" + integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.0" + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + +chalk@^2.0.0, chalk@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" + integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.1.2" + +chromedriver@^86.0.0: + version "86.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-86.0.0.tgz#4b9504d5bbbcd4c6bd6d6fd1dd8247ab8cdeca67" + integrity sha512-byLJWhAfuYOmzRYPDf4asJgGDbI4gJGHa+i8dnQZGuv+6WW1nW1Fg+8zbBMOfLvGn7sKL41kVdmCEVpQHn9oyg== + dependencies: + "@testim/chrome-version" "^1.0.7" + axios "^0.19.2" + del "^5.1.0" + extract-zip "^2.0.1" + https-proxy-agent "^5.0.0" + mkdirp "^1.0.4" + tcp-port-used "^1.0.1" + +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.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@^2.12.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +debug@4, debug@4.3.1, debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +debug@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +deep-is@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +del@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7" + integrity sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA== + dependencies: + globby "^10.0.1" + graceful-fs "^4.2.2" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.1" + p-map "^3.0.0" + rimraf "^3.0.0" + slash "^3.0.0" + +diff@4.0.2, diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +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@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +es-abstract@^1.18.0-next.1: + version "1.18.0-next.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" + integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es6-object-assign@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" + integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= + +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== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +eslint-plugin-prettier@^2.2.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz#b4312dcf2c1d965379d7f9d5b5f8aaadc6a45904" + integrity sha512-CStQYJgALoQBw3FsBzH0VOVDRnJ/ZimUlpLm226U8qgqYJfPOY/CPK6wyRInMxh73HSKg5wyRwdS4BVYYHwokA== + dependencies: + fast-diff "^1.1.1" + jest-docblock "^21.0.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +extract-zip@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +fast-diff@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-glob@^3.0.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" + integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + +fastq@^1.6.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.9.0.tgz#e16a72f338eaca48e91b5c23593bcc2ef66b7947" + integrity sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w== + 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" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +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: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + 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.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +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.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.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +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" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be" + integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +glob-parent@^5.1.0, glob-parent@~5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.1.6, glob@^7.1.1, glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globby@^10.0.1: + version "10.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" + integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + +graceful-fs@^4.2.2: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +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 "6" + debug "4" + +ignore@^5.1.1: + 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.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + 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" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ip-regex@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.2.0.tgz#a03f5eb661d9a154e3973a03de8b23dd0ad6892e" + integrity sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A== + +is-arguments@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-callable@^1.1.4, is-callable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== + +is-core-module@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-extglob@^2.1.1: + version "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@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-generator-function@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.8.tgz#dfb5c2b120e02b0a8d9d2c6806cd5621aa922f7b" + integrity sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-nan@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.0.tgz#85d1f5482f7051c2019f5673ccebdb06f3b0db03" + integrity sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ== + dependencies: + define-properties "^1.1.3" + +is-negative-zero@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number@^7.0.0: + version "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.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typed-array@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.4.tgz#1f66f34a283a3c94a4335434661ca53fff801120" + integrity sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + has-symbols "^1.0.1" + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + +is2@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/is2/-/is2-2.0.6.tgz#094f887248b49ba7ce278f8c39f85a70927bb5de" + integrity sha512-+Z62OHOjA6k2sUDOKXoZI3EXv7Fb1K52jpTBLbkfx62bcUeSsrTBLhEquCRDKTx0XE5XbHcG/S2vrtE3lnEDsQ== + dependencies: + deep-is "^0.1.3" + ip-regex "^4.1.0" + is-url "^1.2.4" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +jest-docblock@^21.0.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" + integrity sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jszip@^3.5.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9" + integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + set-immediate-shim "~1.0.1" + +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + 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.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +log-symbols@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" + +merge2@^1.2.3, 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== + +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + +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" + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp@^0.5.3: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mocha@^8.2.0: + version "8.2.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.2.1.tgz#f2fa68817ed0e53343d989df65ccd358bc3a4b39" + 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.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +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== + +nanoid@3.1.12: + version "3.1.12" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654" + integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-inspect@^1.8.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" + integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== + +object-is@^1.0.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068" + integrity sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg== + dependencies: + 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.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + 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: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + 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.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + 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" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +prettier@^1.18.2: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve@^1.3.2: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +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.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.0: + 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.1.10" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" + integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== + +safe-buffer@^5.1.0, safe-buffer@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +selenium-webdriver@^4.0.0-alpha.7: + version "4.0.0-alpha.8" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.8.tgz#5cb99f4239b39dbdff6ac796893f873d044dd3bc" + integrity sha512-yPSaiWySZTEbxuuWQMDqdXh3H3N4Aiw/bSUjpkKMPWWCysfPqUncrq6FewBqdxWD1wQKzy5yWaQMGsgTY/0rCQ== + dependencies: + jszip "^3.5.0" + rimraf "^2.7.1" + tmp "^0.1.0" + ws "^7.3.1" + +semver@^5.3.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +serialize-javascript@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-immediate-shim@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + +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.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + 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.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + 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" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" + integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" + integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + 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.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +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" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@7.2.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +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== + dependencies: + debug "4.3.1" + is2 "^2.0.6" + +tmp@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tslib@^1.13.0, tslib@^1.7.1, tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslint-config-prettier@^1.18.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37" + integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg== + +tslint-plugin-prettier@^2.0.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tslint-plugin-prettier/-/tslint-plugin-prettier-2.3.0.tgz#73fe71bf9f03842ac48c104122ca9b1de012ecf4" + integrity sha512-F9e4K03yc9xuvv+A0v1EmjcnDwpz8SpCD8HzqSDe0eyg34cBinwn9JjmnnRrNAs4HdleRQj7qijp+P/JTxt4vA== + dependencies: + eslint-plugin-prettier "^2.2.0" + lines-and-columns "^1.1.6" + tslib "^1.7.1" + +tslint@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" + integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg== + dependencies: + "@babel/code-frame" "^7.0.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^4.0.1" + glob "^7.1.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + mkdirp "^0.5.3" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.13.0" + tsutils "^2.29.0" + +tsutils@^2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + +typescript@^4.0.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util@^0.12.0: + version "0.12.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888" + 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.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which-typed-array@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + +which@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +workerpool@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.2.tgz#e241b43d8d033f1beb52c7851069456039d1d438" + integrity sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q== + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + 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@^7.3.1: + version "7.5.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" + integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== + +y18n@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== + +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + 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.yarnpkg.com/yarn/-/yarn-1.22.10.tgz#c99daa06257c80f8fa2c3f1490724e394c26b18c" + integrity sha512-IanQGI9RRPAN87VGTF7zs2uxkSyQSrSPsju0COgbsKQOOXr5LtcVPeyXWgwVa0ywG3d8dg6kSYKGBuYK021qeA== + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/ui/.gitignore b/ui/.gitignore index 0209917db9fce..f23bcecfc2236 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -1,6 +1,7 @@ node_modules -dist .vscode junit.xml coverage -/yarn-error.log \ No newline at end of file +/yarn-error.log +.yalc +yalc.lock diff --git a/ui/.prettierrc b/ui/.prettierrc index 0abdae70b50de..9e18402b642d4 100644 --- a/ui/.prettierrc +++ b/ui/.prettierrc @@ -5,5 +5,6 @@ "singleQuote": true, "tabWidth": 4, "jsxBracketSameLine": true, - "quoteProps": "consistent" + "quoteProps": "consistent", + "arrowParens": "avoid" } diff --git a/ui/dist/app/gitkeep b/ui/dist/app/gitkeep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ui/embed.go b/ui/embed.go new file mode 100644 index 0000000000000..665621a9c12fd --- /dev/null +++ b/ui/embed.go @@ -0,0 +1,7 @@ +package ui + +import "embed" + +// Embedded contains embedded UI resources +//go:embed dist/app +var Embedded embed.FS diff --git a/ui/jest.config.js b/ui/jest.config.js index d5dfce80cc247..122b76525080e 100644 --- a/ui/jest.config.js +++ b/ui/jest.config.js @@ -5,6 +5,7 @@ module.exports = { collectCoverage: true, transformIgnorePatterns: ['node_modules/(?!(argo-ui)/)'], globals: { + 'window': {localStorage: { getItem: () => '{}', setItem: () => null }}, 'ts-jest': { isolatedModules: true, }, @@ -15,3 +16,20 @@ module.exports = { '.+\\.(css|styl|less|sass|scss)$': 'jest-transform-css', }, }; + +const localStorageMock = (() => { + let store = {}; + return { + getItem: (key) => store[key], + setItem: (key, value) => { + store[key] = value.toString(); + }, + clear: () => { + store = {}; + }, + removeItem: (key) => { + delete store[key]; + } + }; +})(); +global.localStorage = localStorageMock; \ No newline at end of file diff --git a/ui/package.json b/ui/package.json index 25ed4760f8bcd..08800dbc267c8 100644 --- a/ui/package.json +++ b/ui/package.json @@ -5,12 +5,11 @@ "scripts": { "start": "webpack-dev-server --config ./src/app/webpack.config.js --mode development", "docker": "./scripts/build_docker.sh", - "build": "rm -rf dist && webpack --config ./src/app/webpack.config.js", + "build": "find ./dist -type f -not -name gitkeep -delete && webpack --config ./src/app/webpack.config.js", "lint": "tslint -p ./src/app", "test": "jest" }, "dependencies": { - "@reactivex/rxjs": "^6.6.7", "@types/classnames": "^2.2.3", "@types/cookie": "^0.3.1", "@types/dagre": "^0.7.40", @@ -41,6 +40,7 @@ "jest-junit": "^6.4.0", "js-yaml": "^3.13.1", "json-merge-patch": "^0.2.3", + "lodash-es": "^4.17.21", "minimatch": "^3.0.4", "moment": "^2.24.0", "monaco-editor": "^0.15.6", @@ -56,13 +56,13 @@ "react-ga": "^2.6.0", "react-helmet": "^5.2.0", "react-hot-loader": "^3.1.3", - "react-keyhooks": "^0.2.0", "react-moment": "^0.9.2", "react-paginate": "^6.2.1", "react-router": "^4.3.1", "react-router-dom": "^4.2.2", "react-svg-piechart": "^2.1.1", "redoc": "^2.0.0-rc.40", + "rxjs": "^6.6.6", "sass-loader": "^6.0.6", "source-map-loader": "^0.2.3", "style-loader": "^0.20.1", @@ -70,7 +70,7 @@ "superagent-promise": "^1.1.0", "ts-loader": "^6.0.4", "ts-node": "^4.1.0", - "tslint": "^5.16.0", + "tslint": "^6.1.3", "tslint-react": "^3.4.0", "typescript": "^4.0.3", "unidiff": "^1.0.2", @@ -79,8 +79,8 @@ "webpack-dev-server": "^3.11.0" }, "resolutions": { - "@types/react": "16.9.3", - "@types/react-dom": "16.9.3", + "@types/react": "^16.9.3", + "@types/react-dom": "^16.8.2", "normalize-url": "4.3.0" }, "devDependencies": { @@ -88,8 +88,8 @@ "@babel/preset-env": "^7.7.1", "@babel/preset-react": "^7.7.0", "@babel/preset-typescript": "^7.7.2", - "@beyonk/google-fonts-webpack-plugin": "^1.5.0", "@types/jest": "^24.0.13", + "@types/lodash-es": "^4.17.5", "@types/react-test-renderer": "^16.8.3", "add": "^2.0.6", "babel-jest": "^24.9.0", @@ -99,7 +99,7 @@ "jest": "^24.9.0", "jest-transform-css": "^2.0.0", "postcss": "^8.2.10", - "prettier": "^1.18.2", + "prettier": "1.19", "react-test-renderer": "16.8.3", "ts-jest": "^24.1.0", "tslint-config-prettier": "^1.18.0", 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 9ca8303ca5388..c3756c3510277 100644 --- a/ui/src/app/applications/components/__snapshots__/utils.test.tsx.snap +++ b/ui/src/app/applications/components/__snapshots__/utils.test.tsx.snap @@ -80,7 +80,7 @@ exports[`HealthStatusIcon.Missing 1`] = ` qe-id="utils-health-status-title" style={ Object { - "color": "#CCD6DD", + "color": "#f4c030", } } title="Missing" @@ -106,7 +106,7 @@ exports[`HealthStatusIcon.Suspended 1`] = ` qe-id="utils-health-status-title" style={ Object { - "color": "#CCD6DD", + "color": "#766f94", } } title="Suspended" diff --git a/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx b/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx index 1cf9cb5c36d30..95af691ede719 100644 --- a/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx +++ b/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx @@ -6,6 +6,7 @@ import {RevisionHelpIcon, YamlEditor} from '../../../shared/components'; import * as models from '../../../shared/models'; import {services} from '../../../shared/services'; import {ApplicationParameters} from '../application-parameters/application-parameters'; +import {ApplicationRetryOptions} from '../application-retry-options/application-retry-options'; import {ApplicationSyncOptionsField} from '../application-sync-options/application-sync-options'; import {RevisionFormField} from '../revision-form-field/revision-form-field'; @@ -221,6 +222,7 @@ export const ApplicationCreatePanel = (props: {
+
); 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 80eee7e654dbe..d92cd54a10cd1 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 @@ -34,7 +34,7 @@ export const RevisionMetadataRows = (props: {applicationName: string; source: Ap {m.message && (
-
{m.message.split('\n')[0].slice(0, 64)}
+
{m.message?.split('\n')[0].slice(0, 64)}
)}
diff --git a/ui/src/app/applications/components/application-details/application-details.scss b/ui/src/app/applications/components/application-details/application-details.scss index 75e24746145cd..a0d088ef33bdd 100644 --- a/ui/src/app/applications/components/application-details/application-details.scss +++ b/ui/src/app/applications/components/application-details/application-details.scss @@ -1,4 +1,5 @@ @import 'node_modules/argo-ui/src/styles/config'; +@import 'node_modules/foundation-sites/scss/util/util'; $header: 120px; @@ -11,6 +12,9 @@ $header: 120px; @media screen and (max-width: map-get($breakpoints, xlarge)) { top: 150px; } + @media screen and (max-width: map-get($breakpoints, large)) { + top: 146px; + } } .argo-dropdown__content.is-menu { @@ -33,16 +37,11 @@ $header: 120px; font-size: 0.8em; color: darken($argo-status-warning-color, 20%); } - &__orphaned-filter { - position: fixed; - top: $header + 100px; - right: 60px; - } &__refreshing-label { position: fixed; top: $header + 100px; - left: 60px; + left: 310px; background-color: $argo-color-gray-4; border: 1px solid $argo-color-gray-5; border-radius: 5px; @@ -189,4 +188,13 @@ $header: 120px; &__commit-message { line-height: 1.5em; } + + .filters-group__panel { + top: 230px; + } + @include breakpoint(large down) { + .filters-group__panel { + top: 280px; + } + } } 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 41624b5db8849..3b64698501b28 100644 --- a/ui/src/app/applications/components/application-details/application-details.tsx +++ b/ui/src/app/applications/components/application-details/application-details.tsx @@ -1,7 +1,8 @@ -import {Checkbox as ArgoCheckbox, DropDownMenu, NotificationType, SlidingPanel} from 'argo-ui'; +import {DropDownMenu, NotificationType, SlidingPanel} from 'argo-ui'; import * as classNames from 'classnames'; import * as PropTypes from 'prop-types'; import * as React from 'react'; +import * as models from '../../../shared/models'; import {RouteComponentProps} from 'react-router'; import {BehaviorSubject, combineLatest, from, merge, Observable} from 'rxjs'; import {delay, filter, map, mergeMap, repeat, retryWhen} from 'rxjs/operators'; @@ -9,7 +10,6 @@ import {delay, filter, map, mergeMap, repeat, retryWhen} from 'rxjs/operators'; import {DataLoader, EmptyState, ErrorNotification, ObservableQuery, Page, Paginate, Revision, Timestamp} from '../../../shared/components'; import {AppContext, ContextApis} from '../../../shared/context'; import * as appModels from '../../../shared/models'; -import {ApplicationTree} from '../../../shared/models'; import {AppDetailsPreferences, AppsDetailsViewType, services} from '../../../shared/services'; import {ApplicationConditions} from '../application-conditions/application-conditions'; @@ -22,7 +22,8 @@ import {ApplicationSyncPanel} from '../application-sync-panel/application-sync-p import {ResourceDetails} from '../resource-details/resource-details'; import * as AppUtils from '../utils'; import {ApplicationResourceList} from './application-resource-list'; -import {Filters} from './filters'; +import {Filters} from './application-resource-filter'; +import {urlPattern} from '../utils'; require('./application-details.scss'); @@ -32,12 +33,11 @@ interface ApplicationDetailsState { } interface FilterInput { + name: string[]; kind: string[]; health: string[]; sync: string[]; namespace: string[]; - createdWithin: number[]; // number of minutes the resource must be created within - ownership: string[]; } export const NodeInfo = (node?: string): {key: string; container: number} => { @@ -52,7 +52,7 @@ export const NodeInfo = (node?: string): {key: string; container: number} => { export const SelectNode = (fullName: string, containerIndex = 0, tab: string = null, appContext: ContextApis) => { const node = fullName ? `${fullName}/${containerIndex}` : null; - appContext.navigation.goto('.', {node, tab}); + appContext.navigation.goto('.', {node, tab}, {replace: true}); }; export class ApplicationDetails extends React.Component, ApplicationDetailsState> { @@ -121,7 +121,7 @@ export class ApplicationDetails extends React.Component { - this.appContext.apis.navigation.goto('.', {resource: items.join(',')}); + this.appContext.apis.navigation.goto('.', {resource: items.join(',')}, {replace: true}); services.viewPreferences.updatePreferences({appDetails: {...pref, resourceFilter: items}}); }; const clearFilter = () => setFilter([]); @@ -135,20 +135,29 @@ export class ApplicationDetails extends React.Component(); - const visibleOrphans = (pref.orphanedResources && tree.orphanedNodes) || []; - if (visibleOrphans.length > 0) { - application.status.resources.forEach(res => statusByKey.set(AppUtils.nodeKey(res), res)); - } - const orphans = visibleOrphans.map(orphan => statusByKey.get(AppUtils.nodeKey(orphan))); - - const filteredRes = application.status.resources - .filter(res => { - const resNode: ResourceTreeNode = {...res, root: null, info: null, parentRefs: [], resourceVersion: '', uid: ''}; - resNode.root = resNode; - return this.filterTreeNode(tree, resNode, treeFilter); - }) - .concat(orphans); + const orphaned: appModels.ResourceStatus[] = pref.orphanedResources + ? (tree.orphanedNodes || []).map(node => ({ + ...node, + status: null, + health: null + })) + : []; + const filteredRes = application.status.resources.concat(orphaned).filter(res => { + const resNode: ResourceTreeNode = {...res, root: null, info: null, parentRefs: [], resourceVersion: '', uid: ''}; + resNode.root = resNode; + return this.filterTreeNode(resNode, treeFilter); + }); + + const renderCommitMessage = (message: string) => + message.split(/\s/).map(part => + urlPattern.test(part) ? ( + + {part}{' '} + + ) : ( + part + ' ' + ) + ); return (
@@ -156,7 +165,7 @@ export class ApplicationDetails extends React.Component
@@ -164,7 +173,7 @@ export class ApplicationDetails extends React.Component { - this.appContext.apis.navigation.goto('.', {view: 'tree'}); + this.appContext.apis.navigation.goto('.', {view: 'tree'}, {replace: true}); services.viewPreferences.updatePreferences({appDetails: {...pref, view: 'tree'}}); }} /> @@ -172,7 +181,7 @@ export class ApplicationDetails extends React.Component { - this.appContext.apis.navigation.goto('.', {view: 'pods'}); + this.appContext.apis.navigation.goto('.', {view: 'pods'}, {replace: true}); services.viewPreferences.updatePreferences({appDetails: {...pref, view: 'pods'}}); }} /> @@ -180,7 +189,7 @@ export class ApplicationDetails extends React.Component { - this.appContext.apis.navigation.goto('.', {view: 'network'}); + this.appContext.apis.navigation.goto('.', {view: 'network'}, {replace: true}); services.viewPreferences.updatePreferences({appDetails: {...pref, view: 'network'}}); }} /> @@ -188,7 +197,7 @@ export class ApplicationDetails extends React.Component { - this.appContext.apis.navigation.goto('.', {view: 'list'}); + this.appContext.apis.navigation.goto('.', {view: 'list'}, {replace: true}); services.viewPreferences.updatePreferences({appDetails: {...pref, view: 'list'}}); }} /> @@ -206,36 +215,24 @@ export class ApplicationDetails extends React.Component
{refreshing &&

Refreshing

} - - {(tree.orphanedNodes || []).length > 0 && ( -
- { - this.appContext.apis.navigation.goto('.', {orphaned: val}); - services.viewPreferences.updatePreferences({appDetails: {...pref, orphanedResources: val}}); - }} - />{' '} - -
- )} {((pref.view === 'tree' || pref.view === 'network') && ( - this.filterTreeNode(tree, node, treeFilter)} - selectedNodeFullName={this.selectedNodeKey} - onNodeClick={fullName => this.selectNode(fullName)} - nodeMenu={node => - AppUtils.renderResourceMenu(node, application, tree, this.appContext, this.appChanged, () => - this.getApplicationActionMenu(application) - ) - } - tree={tree} - app={application} - showOrphanedResources={pref.orphanedResources} - useNetworkingHierarchy={pref.view === 'network'} - onClearFilter={clearFilter} - /> + + this.filterTreeNode(node, treeFilter)} + selectedNodeFullName={this.selectedNodeKey} + onNodeClick={fullName => this.selectNode(fullName)} + nodeMenu={node => + AppUtils.renderResourceMenu(node, application, tree, this.appContext, this.appChanged, () => + this.getApplicationActionMenu(application, false) + ) + } + tree={tree} + app={application} + showOrphanedResources={pref.orphanedResources} + useNetworkingHierarchy={pref.view === 'network'} + onClearFilter={clearFilter} + /> + )) || (pref.view === 'pods' && ( this.selectNode(fullName)} nodeMenu={node => AppUtils.renderResourceMenu(node, application, tree, this.appContext, this.appChanged, () => - this.getApplicationActionMenu(application) + this.getApplicationActionMenu(application, false) ) } /> )) || (
- {(filteredRes.length > 0 && ( - this.setState({page})} - preferencesKey='application-details'> - {data => ( - this.selectNode(fullName)} - resources={data} - nodeMenu={node => - AppUtils.renderResourceMenu( - {...node, root: node}, - application, - tree, - this.appContext, - this.appChanged, - () => this.getApplicationActionMenu(application) - ) - } - /> - )} - - )) || ( - -

No resources found

-
Try to change filter criteria
-
- )} + + {(filteredRes.length > 0 && ( + this.setState({page})} + preferencesKey='application-details'> + {data => ( + this.selectNode(fullName)} + resources={data} + nodeMenu={node => + AppUtils.renderResourceMenu( + {...node, root: node}, + application, + tree, + this.appContext, + this.appChanged, + () => this.getApplicationActionMenu(application, false) + ) + } + /> + )} + + )) || ( + +

No resources found

+
Try to change filter criteria
+
+ )} +
)}
@@ -287,7 +286,7 @@ export class ApplicationDetails extends React.Component this.updateApp(app)} + updateApp={(app: models.Application, query: {validate?: boolean}) => this.updateApp(app, query)} selectedNode={selectedNode} tab={tab} /> @@ -352,7 +351,7 @@ export class ApplicationDetails extends React.Component
Message:
-
{metadata.message}
+
{renderCommitMessage(metadata.message)}
@@ -371,48 +370,49 @@ export class ApplicationDetails extends React.Component {prop.actionLabel}; return [ { iconClassName: 'fa fa-info-circle', - title: App Details, + title: , action: () => this.selectNode(fullName) }, { iconClassName: 'fa fa-file-medical', - title: App Diff, + title: , action: () => this.selectNode(fullName, 0, 'diff'), disabled: app.status.sync.status === appModels.SyncStatuses.Synced }, { iconClassName: 'fa fa-sync', - title: Sync, + title: , action: () => AppUtils.showDeploy('all', this.appContext) }, { iconClassName: 'fa fa-info-circle', - title: Sync Status, + title: , action: () => this.setOperationStatusVisible(true), disabled: !app.status.operationState }, { iconClassName: 'fa fa-history', - title: History and rollback, + title: , action: () => this.setRollbackPanelVisible(0), disabled: !app.status.operationState }, { iconClassName: 'fa fa-times-circle', - title: Delete, + title: , action: () => this.deleteApplication() }, { iconClassName: classNames('fa fa-redo', {'status-icon--spin': !!refreshing}), title: ( - Refresh{' '} + {' '} (item === 'OutOfSync' ? ['OutOfSync', 'Unknown'] : [item])).reduce((first, second) => first.concat(second), []); - const minutesAgo = (m: number) => { - const d = new Date(); - d.setTime(d.getTime() - m * 60000); - return d; - }; - const createdAt = new Date(node.createdAt); // will be falsely if the node has not been created, and so will not appear - const createdWithin = (n: number) => createdAt.getTime() > minutesAgo(n).getTime(); - const root = node.root || ({} as ResourceTreeNode); const hook = root && root.hook; if ( + (filterInput.name.length === 0 || filterInput.name.indexOf(node.name) > -1) && (filterInput.kind.length === 0 || filterInput.kind.indexOf(node.kind) > -1) && + // include if node's root sync matches filter (syncStatuses.length === 0 || hook || (root.status && syncStatuses.indexOf(root.status) > -1)) && - (filterInput.health.length === 0 || hook || (root.health && filterInput.health.indexOf(root.health.status) > -1)) && - (filterInput.namespace.length === 0 || filterInput.namespace.includes(node.namespace)) && - (filterInput.createdWithin.length === 0 || !!filterInput.createdWithin.find(v => createdWithin(v))) + // include if node or node's root health matches filter + (filterInput.health.length === 0 || + hook || + (root.health && filterInput.health.indexOf(root.health.status) > -1) || + (node.health && filterInput.health.indexOf(node.health.status) > -1)) && + (filterInput.namespace.length === 0 || filterInput.namespace.includes(node.namespace)) ) { return true; } - if (filterInput.ownership.includes('Owned') && ownership !== 'Owners') { - const owned = tree.nodes.filter(n => (node.parentRefs || []).find(r => r.uid === n.uid)); - if (owned.find(n => this.filterTreeNode(tree, n, filterInput, 'Owned'))) { - return true; - } - } - if (filterInput.ownership.includes('Owners') && ownership !== 'Owned') { - const owners = tree.nodes.filter(n => (n.parentRefs || []).find(r => r.uid === node.uid)); - if (owners.find(n => this.filterTreeNode(tree, n, filterInput, 'Owners'))) { - return true; - } - } - return false; } @@ -522,15 +506,15 @@ export class ApplicationDetails extends React.Component(); const kind = new Array(); const health = new Array(); const sync = new Array(); const namespace = new Array(); - const createdWithin = new Array(); - const ownership = new Array(); for (const item of filterInput || []) { const [type, val] = item.split(':'); switch (type) { + case 'name': + name.push(val); + break; case 'kind': kind.push(val); break; @@ -563,27 +549,21 @@ export class ApplicationDetails extends React.Component self.indexOf(value) === index; + +function toOption(label: string) { + return {label}; +} + +export const Filters = (props: { + children?: React.ReactNode; + pref: AppDetailsPreferences; + tree: ApplicationTree; + onSetFilter: (items: string[]) => void; + onClearFilter: () => void; +}) => { + const ctx = React.useContext(Context); + + const {pref, tree, onSetFilter} = props; + + const onClearFilter = () => { + setLoading(true); + props.onClearFilter(); + }; + + const shown = pref.hideFilters; + const setShown = (val: boolean) => services.viewPreferences.updatePreferences({appDetails: {...pref, hideFilters: val}}); + + const resourceFilter = pref.resourceFilter || []; + const removePrefix = (prefix: string) => (v: string) => v.replace(prefix + ':', ''); + + const [groupedFilters, setGroupedFilters] = React.useState<{[key: string]: string}>({}); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const update: {[key: string]: string} = {}; + (resourceFilter || []).forEach(pair => { + const tmp = pair.split(':'); + if (tmp.length === 2) { + const prefix = tmp[0]; + const cur = update[prefix]; + update[prefix] = `${cur ? cur + ',' : ''}${pair}`; + } + }); + setGroupedFilters(update); + setLoading(false); + }, [resourceFilter, loading]); + + const setFilters = (prefix: string, values: string[]) => { + const groups = {...groupedFilters}; + groups[prefix] = values.map(v => `${prefix}:${v}`).join(','); + let strings: string[] = []; + Object.keys(groups).forEach(g => { + strings = strings.concat(groups[g].split(',').filter(f => f !== '')); + }); + onSetFilter(strings); + }; + + const ResourceFilter = (p: {label: string; prefix: string; options: {label: string}[]; field?: boolean; radio?: boolean}) => { + return loading ? ( +
Loading...
+ ) : ( + setFilters(p.prefix, v)} options={p.options} field={!!p.field} radio={!!p.radio} /> + ); + }; + + // we need to include ones that might have been filter in other apps that do not apply to the current app, + // otherwise the user will not be able to clear them from this panel + const alreadyFilteredOn = (prefix: string) => resourceFilter.filter(f => f.startsWith(prefix + ':')).map(removePrefix(prefix)); + + const kinds = tree.nodes + .map(x => x.kind) + .concat(alreadyFilteredOn('kind')) + .filter(uniq) + .sort(); + + const names = tree.nodes + .map(x => x.name) + .concat(alreadyFilteredOn('name')) + .filter(uniq) + .sort(); + + const namespaces = tree.nodes + .map(x => x.namespace) + .filter(x => !!x) + .concat(alreadyFilteredOn('namespace')) + .filter(uniq) + .sort(); + + const selectedFor = (prefix: string) => { + return groupedFilters[prefix] ? groupedFilters[prefix].split(',').map(removePrefix(prefix)) : []; + }; + + return ( + + {ResourceFilter({label: 'NAME', prefix: 'name', options: names.map(toOption), field: true})} + {ResourceFilter({label: 'KINDS', prefix: 'kind', options: kinds.map(toOption), field: true})} + {ResourceFilter({ + label: 'SYNC STATUS', + prefix: 'sync', + options: ['Synced', 'OutOfSync'].map(label => ({ + label, + icon: + })) + })} + {ResourceFilter({ + label: 'HEALTH STATUS', + prefix: 'health', + options: ['Healthy', 'Progressing', 'Degraded', 'Suspended', 'Missing', 'Unknown'].map(label => ({ + label, + icon: + })) + })} + {namespaces.length > 1 && ResourceFilter({label: 'NAMESPACES', prefix: 'namespace', options: (namespaces || []).filter(l => l && l !== '').map(toOption), field: true})} + {(tree.orphanedNodes || []).length > 0 && ( +
+ { + ctx.navigation.goto('.', {orphaned: val}, {replace: true}); + services.viewPreferences.updatePreferences({appDetails: {...pref, orphanedResources: val}}); + }} + />{' '} + +
+ )} +
+ ); +}; diff --git a/ui/src/app/applications/components/application-details/filters.tsx b/ui/src/app/applications/components/application-details/filters.tsx deleted file mode 100644 index 1e10163d36844..0000000000000 --- a/ui/src/app/applications/components/application-details/filters.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import {HelpIcon} from 'argo-ui'; -import * as React from 'react'; -import {useContext} from 'react'; -import {Context} from '../../../shared/context'; -import {ApplicationTree} from '../../../shared/models'; -import {AppDetailsPreferences} from '../../../shared/services'; - -const uniq = (value: string, index: number, self: string[]) => self.indexOf(value) === index; - -export const Filters = ({ - pref, - tree, - onSetFilter, - onClearFilter -}: { - pref: AppDetailsPreferences; - tree: ApplicationTree; - onSetFilter: (items: string[]) => void; - onClearFilter: () => void; -}) => { - const {history, navigation} = useContext(Context); - - const shown = new URLSearchParams(history.location.search).get('showFilters') === 'true'; - const setShown = (showFilters: boolean) => navigation.goto('.', {showFilters}); - - const resourceFilter = pref.resourceFilter || []; - const hasPrefix = (prefix: string) => (v: string) => v.startsWith(prefix + ':'); - const removePrefix = (prefix: string) => (v: string) => v.replace(prefix + ':', ''); - - const anyFiltered = pref.resourceFilter.length > 0; - const isFiltered = (prefix: string, suffix: string) => resourceFilter.includes(`${prefix}:${suffix}`); - const anyPrefixFiltered = (prefix: string) => resourceFilter.find(hasPrefix(prefix)); - const setFilters = (prefix: string, suffixes: string[], v: boolean) => { - const filters = suffixes.map(suffix => `${prefix}:${suffix}`); - const items = resourceFilter.filter(y => !filters.includes(y)); - if (v) { - items.push(...filters); - } - onSetFilter(items); - }; - const enableFilter = (prefix: string, suffix: string) => { - const items = resourceFilter.filter(v => !hasPrefix(prefix)(v)).concat([`${prefix}:${suffix}`]); - onSetFilter(items); - }; - // this is smarter than it looks at first glance, rather than just un-checked known items, - // it instead finds out what is enabled, and then removes them, which will be tolerant to weird or unknown items - const clearFilters = (prefix: string) => { - return setFilters(prefix, resourceFilter.filter(hasPrefix(prefix)).map(removePrefix(prefix)), false); - }; - - // we need to include ones that might have been filter in other apps that do not apply to the current app, - // otherwise the user will not be able to clear them from this panel - const alreadyFilteredOn = (prefix: string) => resourceFilter.filter(hasPrefix(prefix)).map(removePrefix(prefix)); - - const kinds = tree.nodes - .map(x => x.kind) - .concat(alreadyFilteredOn('kind')) - .filter(uniq) - .sort(); - const namespaces = tree.nodes - .map(x => x.namespace) - .concat(alreadyFilteredOn('namespace')) - .filter(uniq) - .sort(); - - const checkbox = (prefix: string, suffix: string, label: string = null) => ( - - ); - const checkboxes = (prefix: string, suffixes: string[]) => suffixes.map(suffix => checkbox(prefix, suffix)); - - const radiobox = (prefix: string, suffix: string, label: string) => ( - - ); - - const clearFilterLink = (prefix: string) => - anyPrefixFiltered(prefix) && ( - - clearFilters(prefix)}>clear - - ); - - return ( - <> - - {shown && ( -
-
-
- OWNERSHIP - - {clearFilterLink('ownership')} -
-
{checkboxes('ownership', ['Owners', 'Owned'])}
-
-
-
SYNC STATUS {clearFilterLink('sync')}
-
{checkboxes('sync', ['Synced', 'OutOfSync'])}
-
-
-
HEALTH STATUS {clearFilterLink('health')}
-
{checkboxes('health', ['Healthy', 'Progressing', 'Degraded', 'Suspended', 'Missing', 'Unknown'])}
-
-
-
- KINDS - - setFilters('kind', kinds, true)}>all {anyPrefixFiltered('kind') && clearFilters('kind')}>clear} - -
-
{checkboxes('kind', kinds)}
-
- {namespaces.length > 1 && ( -
-
NAMESPACE {clearFilterLink('namespace')}
-
{checkboxes('namespace', namespaces)}
-
- )} -
-
- CREATED WITHIN - - {clearFilterLink('createdWithin')} -
-
{[1, 3, 5, 15, 60].map(m => radiobox('createdWithin', String(m), m + 'm'))}
-
-
- )} - - ); -}; 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 140c6e73aa9f1..2b4a8ed8e2844 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 @@ -33,7 +33,7 @@ export const ApplicationFullscreenLogs = (props: RouteComponentProps<{name: stri podName={podName} fullscreen={true} page={{number: parseInt(page, 10) || 0, untilTimes}} - setPage={pageData => appContext.navigation.goto('.', {page: pageData.number, untilTimes: pageData.untilTimes.join(',')})} + setPage={pageData => appContext.navigation.goto('.', {page: pageData.number, untilTimes: pageData.untilTimes.join(',')}, {replace: true})} />
); 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 0ed589c8f20d5..264580c55e16a 100644 --- a/ui/src/app/applications/components/application-parameters/application-parameters.tsx +++ b/ui/src/app/applications/components/application-parameters/application-parameters.tsx @@ -109,7 +109,7 @@ function getParamsEditableItems( export const ApplicationParameters = (props: { application: models.Application; details: models.RepoAppDetails; - save?: (application: models.Application) => Promise; + save?: (application: models.Application, query: {validate?: boolean}) => Promise; noReadonlyMode?: boolean; }) => { const app = props.application; @@ -365,7 +365,7 @@ export const ApplicationParameters = (props: { if (input.spec.source.kustomize && input.spec.source.kustomize.images) { input.spec.source.kustomize.images = input.spec.source.kustomize.images.filter(isDefinedWithVersion); } - await props.save(input); + await props.save(input, {}); setRemovedOverrides(new Array()); }) } 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 a1ff556969e1b..5eb7ac4951740 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 @@ -1,11 +1,10 @@ import {DataLoader, DropDown, DropDownMenu, MenuItem, NotificationType, Tooltip} from 'argo-ui'; import * as PropTypes from 'prop-types'; import * as React from 'react'; -import {Checkbox as ReactCheckbox} from 'react-form'; import Moment from 'react-moment'; -import {EmptyState, ErrorNotification} from '../../../shared/components'; import {AppContext} from '../../../shared/context'; +import {CheckboxField, EmptyState, ErrorNotification} from '../../../shared/components'; import {Application, ApplicationTree, HostResourceInfo, InfoItem, Node, Pod, ResourceName, ResourceNode, ResourceStatus} from '../../../shared/models'; import {PodViewPreferences, services, ViewPreferences} from '../../../shared/services'; @@ -69,13 +68,13 @@ export class PodView extends React.Component {
@@ -140,7 +139,7 @@ export class PodView extends React.Component {
) : null} - {group.info.map(infoItem => ( + {group.info?.map(infoItem => (
{infoItem.value}
))} @@ -170,6 +169,9 @@ export class PodView extends React.Component { preventOverflow: { enabled: true }, + hide: { + enabled: false + }, flip: { enabled: false } @@ -197,7 +199,7 @@ export class PodView extends React.Component { ), action: () => { - this.appContext.apis.navigation.goto('.', {node: pod.fullName, tab: 'logs'}); + this.appContext.apis.navigation.goto('.', {node: pod.fullName, tab: 'logs'}, {replace: true}); } }, { @@ -213,7 +215,7 @@ export class PodView extends React.Component {

Are your sure you want to delete Pod '{pod.name}'?

- +
@@ -304,9 +306,7 @@ export class PodView extends React.Component { } const statusByKey = new Map(); - if (this.props.app) { - this.props.app.status.resources.forEach(res => statusByKey.set(nodeKey(res), res)); - } + this.props.app.status?.resources?.forEach(res => statusByKey.set(nodeKey(res), res)); (tree.nodes || []).forEach((rnode: ResourceTreeNode) => { // make sure each node has not null/undefined parentRefs field rnode.parentRefs = rnode.parentRefs || []; @@ -344,7 +344,7 @@ export class PodView extends React.Component { } as Pod; // Get node name for Pod - rnode.info.forEach(i => { + rnode.info?.forEach(i => { if (i.name === 'Node') { p.spec.nodeName = i.value; } @@ -363,7 +363,10 @@ export class PodView extends React.Component { kind: 'node', name: 'Unschedulable', pods: [p], - info: [{name: 'Kernel Version', value: 'N/A'}, {name: 'OS/Arch', value: 'N/A'}], + info: [ + {name: 'Kernel Version', value: 'N/A'}, + {name: 'OS/Arch', value: 'N/A'} + ], hostResourcesInfo: [] }; } 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 6de3b7232e5e5..d9bc96314cc2d 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 @@ -399,7 +399,17 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) => graph.setNode(EXTERNAL_TRAFFIC_NODE, {height: NODE_HEIGHT, width: 30, type: NODE_TYPES.externalTraffic}); externalRoots.sort(compareNodes).forEach(root => { const loadBalancers = root.networkingInfo.ingress.map(ingress => ingress.hostname || ingress.ip); - processNode(root, root, loadBalancers.map(lb => colorsBySource.get(lb))); + 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, i) => { + processNode(child, root, [colorByService.get(treeNodeKey(child))]); + }); + graph.setNode(treeNodeKey(root), {...root, width: NODE_WIDTH, height: NODE_HEIGHT, root}); + (childrenByParentKey.get(treeNodeKey(root)) || []).forEach(child => { + if (root.namespace === child.namespace) { + graph.setEdge(treeNodeKey(root), treeNodeKey(child), {colors: [colorByService.get(treeNodeKey(child))]}); + } + }); loadBalancers.forEach(key => { const loadBalancerNodeKey = `${EXTERNAL_TRAFFIC_NODE}:${key}`; graph.setNode(loadBalancerNodeKey, { @@ -429,12 +439,15 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) => } else { // Tree view const managedKeys = new Set(props.app.status.resources.map(nodeKey)); + const orphanedKeys = new Set(props.tree.orphanedNodes?.map(nodeKey)); const orphans: ResourceTreeNode[] = []; nodes.forEach(node => { if ((node.parentRefs || []).length === 0 || managedKeys.has(nodeKey(node))) { roots.push(node); } else { - orphans.push(node); + if (orphanedKeys.has(nodeKey(node))) { + orphans.push(node); + } node.parentRefs.forEach(parent => { const children = childrenByParentKey.get(treeNodeKey(parent)) || []; children.push(node); 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 e91e742c91174..7722167a8f445 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 @@ -9,6 +9,15 @@ font-size: 10pt; &__title { font-weight: bold; + .diff__collapse { + position: absolute; + right: 0; + cursor: pointer; + margin-left: auto; + margin-right: 1.5rem; + color: #495763; + font-size: 20px; + } } } } 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 b1c0531f8880d..7fcadeb660982 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 @@ -1,11 +1,12 @@ import {Checkbox, DataLoader} from 'argo-ui'; import * as jsYaml from 'js-yaml'; import * as React from 'react'; -import {Diff, Hunk, parseDiff} from 'react-diff-view'; +import {parseDiff} from 'react-diff-view'; import 'react-diff-view/style/index.css'; import {diffLines, formatLines} from 'unidiff'; import * as models from '../../../shared/models'; import {services} from '../../../shared/services'; +import {IndividualDiffSection} from './individual-diff-section'; require('./application-resources-diff.scss'); @@ -23,7 +24,7 @@ export const ApplicationResourcesDiff = (props: ApplicationResourcesDiffProps) = b: state.predictedLiveState ? jsYaml.safeDump(state.predictedLiveState, {indent: 2}) : '', hook: state.hook, // doubles as sort order - name: (state.group || '') + '/' + state.kind + '/' + state.namespace + '/' + state.name + name: (state.group || '') + '/' + state.kind + '/' + (state.namespace ? state.namespace + '/' : '') + state.name }; }) .filter(i => !i.hook) @@ -74,12 +75,7 @@ ${formatLines(diffLines(i.a, i.b), {context, aname: `a/${name}}`, bname: `b/${i. {files .sort((a: any, b: any) => a.newPath.localeCompare(b.newPath)) .map((file: any) => ( -
- {showPath &&

{file.newPath}

} - - {(hunks: any) => hunks.map((hunk: any) => )} - -
+ ))} ); 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 new file mode 100644 index 0000000000000..a9940cb013b6a --- /dev/null +++ b/ui/src/app/applications/components/application-resources-diff/individual-diff-section.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import {useState} from 'react'; +import {Diff, Hunk} from 'react-diff-view'; +import 'react-diff-view/style/index.css'; + +require('./application-resources-diff.scss'); + +export interface IndividualDiffSectionProps { + file: any; + showPath: boolean; + whiteBox: string; + viewType: string; +} + +export const IndividualDiffSection = (props: IndividualDiffSectionProps) => { + const {file, showPath, whiteBox, viewType} = props; + const [collapsed, setCollapsed] = useState(false); + return ( +
+ {showPath && ( +

+ {file.newPath} + setCollapsed(!collapsed)} /> +

+ )} + {!collapsed && ( + + {(hunks: any) => hunks.map((hunk: any) => )} + + )} +
+ ); +}; diff --git a/ui/src/app/applications/components/application-retry-options/application-retry-options.scss b/ui/src/app/applications/components/application-retry-options/application-retry-options.scss new file mode 100644 index 0000000000000..f300d981c374d --- /dev/null +++ b/ui/src/app/applications/components/application-retry-options/application-retry-options.scss @@ -0,0 +1,16 @@ +.application-retry-options { + margin-bottom: 1em; + label { + font-size: 15px; + } +} + +.application-retry-options-list { + + .application-retry-options-list__item { + margin: 12px 0; + padding-top: 12px; + position: relative; + border: none; + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000000..428936d7b4d77 --- /dev/null +++ b/ui/src/app/applications/components/application-retry-options/application-retry-options.tsx @@ -0,0 +1,111 @@ +import * as React from 'react'; +import {FormApi, NestedForm, Text, Form} from 'react-form'; +import {Checkbox, FormField} from 'argo-ui'; +import {omit} from 'lodash-es'; +import {NumberField} from '../../../shared/components'; +import * as models from '../../../shared/models'; + +require('./application-retry-options.scss'); + +const durationRegex = /^([\d\.]+[HMS])+$/i; +const durationRegexError = 'Should be 1h10m10s/10h10m/10m/10s'; + +const onlyPositiveValidation = { + min: '1', + step: '1' +}; + +function buildFormItem(label: string, propertyPath: string, component: React.ComponentType, formApi: FormApi, componentProps?: Record) { + return ; +} + +const retryOptions: Array<(formApi: FormApi) => React.ReactNode> = [ + formApi => buildFormItem('Limit', 'limit', NumberField, formApi, onlyPositiveValidation), + formApi => buildFormItem('Duration', 'backoff.duration', Text, formApi), + formApi => buildFormItem('Max Duration', 'backoff.maxDuration', Text, formApi), + formApi => buildFormItem('Factor', 'backoff.factor', NumberField, formApi, onlyPositiveValidation) +]; + +const defaultInitialValues = { + limit: '', + backoff: { + duration: '', + maxDuration: '', + factor: '' + } +}; + +export const ApplicationRetryForm = ({initValues, field = 'retryStrategy'}: {initValues?: models.RetryStrategy; field: string}) => { + return ( + +
{ + const backoff = values.backoff || {}; + + if (!values) { + return {}; + } + + return { + 'limit': !values.limit && values.hasOwnProperty('limit') && 'Limit is required', + + 'backoff.duration': + backoff.hasOwnProperty('duration') && ((!backoff.duration && 'Duration is required') || (!durationRegex.test(backoff.duration) && durationRegexError)), + + 'backoff.maxDuration': + backoff.hasOwnProperty('maxDuration') && + ((!backoff.maxDuration && 'Max Duration is required') || (!durationRegex.test(backoff.maxDuration) && durationRegexError)), + + 'backoff.factor': backoff.hasOwnProperty('factor') && !backoff.factor && 'Factor is required' + }; + }}> + {nestedFormApi => { + return ( +
+ {retryOptions.map((render, i) => ( +
+ {render(nestedFormApi)} +
+ ))} +
+ ); + }} +
+
+ ); +}; + +export const ApplicationRetryOptions = ({formApi, initValues, field = 'retryStrategy'}: {formApi: FormApi; field?: string; initValues?: models.RetryStrategy}) => { + const [retry, setRetry] = React.useState(!!initValues); + + const toggleRetry = (value: boolean) => { + if (!value) { + const formState = formApi.getFormState(); + const values = formState.values; + const errors = formState.errors; + + const newValues = omit(values, field); + const newErrors = omit(errors, field); + + formApi.setFormState({ + ...formState, + values: newValues, + errors: newErrors + }); + } + + setRetry(value); + }; + + return ( +
+ toggleRetry(val)} /> + + {retry && } +
+ ); +}; diff --git a/ui/src/app/applications/components/application-retry-view/application-retry-view.scss b/ui/src/app/applications/components/application-retry-view/application-retry-view.scss new file mode 100644 index 0000000000000..96f0b057def54 --- /dev/null +++ b/ui/src/app/applications/components/application-retry-view/application-retry-view.scss @@ -0,0 +1,6 @@ +.application-retry-option-view-list { + .application-retry-option-view-list__item{ + display: inline-block; + margin-right: 10px; + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000000..1e5fe06636792 --- /dev/null +++ b/ui/src/app/applications/components/application-retry-view/application-retry-view.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import * as models from '../../../shared/models'; + +require('./application-retry-view.scss'); + +function buildRetryOptionView(label: string, data: string | number) { + const result = data || 'not installed'; + + return ( +
+ {label} - {result} +
+ ); +} + +const retryOptionsView: Array<(initData: models.RetryStrategy) => React.ReactNode> = [ + initData => buildRetryOptionView('Limit', initData?.limit), + initData => buildRetryOptionView('Duration', initData?.backoff?.duration), + initData => buildRetryOptionView('Max Duration', initData?.backoff?.maxDuration), + initData => buildRetryOptionView('Factor', initData?.backoff.factor) +]; + +export const ApplicationRetryView = ({initValues}: {initValues?: models.RetryStrategy}) => { + const result = !initValues ? 'Retry not installed' : retryOptionsView.map((render, i) => render(initValues)); + return
{result}
; +}; 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 4c7a05856d315..0415ac5b21137 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 @@ -16,6 +16,9 @@ export const RevisionMetadataPanel = (props: {appName: string; type: string; rev preventOverflow: { enabled: false }, + hide: { + enabled: false + }, flip: { enabled: false } @@ -51,7 +54,7 @@ export const RevisionMetadataPanel = (props: {appName: string; type: string; rev )}
Comment:
-
{m.message.split('\n')[0].slice(0, 64)}
+
{m.message?.split('\n')[0].slice(0, 64)}
diff --git a/ui/src/app/applications/components/application-summary/application-summary.scss b/ui/src/app/applications/components/application-summary/application-summary.scss index 2b2333c65e729..2c5d8fbaf7dc8 100644 --- a/ui/src/app/applications/components/application-summary/application-summary.scss +++ b/ui/src/app/applications/components/application-summary/application-summary.scss @@ -62,4 +62,16 @@ .select { padding-bottom: 0; } + + .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-summary/application-summary.tsx b/ui/src/app/applications/components/application-summary/application-summary.tsx index f88ab63476024..da4bd94ca115d 100644 --- a/ui/src/app/applications/components/application-summary/application-summary.tsx +++ b/ui/src/app/applications/components/application-summary/application-summary.tsx @@ -1,34 +1,28 @@ -import {AutocompleteField, DropDownMenu, FormField, FormSelect, HelpIcon, PopupApi} from 'argo-ui'; +import {AutocompleteField, DropDownMenu, ErrorNotification, FormField, FormSelect, HelpIcon, NotificationType} from 'argo-ui'; import * as React from 'react'; import {FormApi, Text} from 'react-form'; import {Cluster, DataLoader, EditablePanel, EditablePanelItem, Expandable, MapInputField, NumberField, Repo, Revision, RevisionHelpIcon} from '../../../shared/components'; import {BadgePanel, Spinner} from '../../../shared/components'; -import {Consumer} from '../../../shared/context'; +import {Consumer, ContextApis} from '../../../shared/context'; import * as models from '../../../shared/models'; import {services} from '../../../shared/services'; import * as moment from 'moment'; import {ApplicationSyncOptionsField} from '../application-sync-options/application-sync-options'; import {RevisionFormField} from '../revision-form-field/revision-form-field'; -import {ComparisonStatusIcon, HealthStatusIcon, syncStatusMessage} from '../utils'; +import {ComparisonStatusIcon, HealthStatusIcon, syncStatusMessage, urlPattern} from '../utils'; +import {ApplicationRetryOptions} from '../application-retry-options/application-retry-options'; +import {ApplicationRetryView} from '../application-retry-view/application-retry-view'; require('./application-summary.scss'); -const urlPattern = new RegExp( - new RegExp( - // tslint:disable-next-line:max-line-length - /^(https?:\/\/(?:www\.|(?!www))[a-z0-9][a-z0-9-]+[a-z0-9]\.[^\s]{2,}|www\.[a-z0-9][a-z0-9-]+[a-z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-z0-9]+\.[^\s]{2,}|www\.[a-z0-9]+\.[^\s]{2,})$/, - 'gi' - ) -); - function swap(array: any[], a: number, b: number) { array = array.slice(); [array[a], array[b]] = [array[b], array[a]]; return array; } -export const ApplicationSummary = (props: {app: models.Application; updateApp: (app: models.Application) => Promise}) => { +export const ApplicationSummary = (props: {app: models.Application; updateApp: (app: models.Application, query: {validate?: boolean}) => Promise}) => { const app = JSON.parse(JSON.stringify(props.app)) as models.Application; const isHelm = app.spec.source.hasOwnProperty('chart'); const initialState = app.spec.destination.server === undefined ? 'NAME' : 'URL'; @@ -246,6 +240,15 @@ export const ApplicationSummary = (props: {app: models.Application; updateApp: ( ) }, + { + title: 'RETRY OPTIONS', + view: , + edit: (formApi: FormApi) => ( +
+ +
+ ) + }, { title: 'STATUS', view: ( @@ -297,7 +300,7 @@ export const ApplicationSummary = (props: {app: models.Application; updateApp: ( }); } - async function setAutoSync(ctx: {popup: PopupApi}, confirmationTitle: string, confirmationText: string, prune: boolean, selfHeal: boolean) { + async function setAutoSync(ctx: ContextApis, confirmationTitle: string, confirmationText: string, prune: boolean, selfHeal: boolean) { const confirmed = await ctx.popup.confirm(confirmationTitle, confirmationText); if (confirmed) { try { @@ -307,21 +310,31 @@ export const ApplicationSummary = (props: {app: models.Application; updateApp: ( updatedApp.spec.syncPolicy = {}; } updatedApp.spec.syncPolicy.automated = {prune, selfHeal}; - await props.updateApp(updatedApp); + await props.updateApp(updatedApp, {validate: false}); + } catch (e) { + ctx.notifications.show({ + content: , + type: NotificationType.Error + }); } finally { setChangeSync(false); } } } - async function unsetAutoSync(ctx: {popup: PopupApi}) { + async function unsetAutoSync(ctx: ContextApis) { const confirmed = await ctx.popup.confirm('Disable Auto-Sync?', 'Are you sure you want to disable automated application synchronization'); if (confirmed) { try { setChangeSync(true); const updatedApp = JSON.parse(JSON.stringify(props.app)) as models.Application; updatedApp.spec.syncPolicy.automated = null; - await props.updateApp(updatedApp); + await props.updateApp(updatedApp, {validate: false}); + } catch (e) { + ctx.notifications.show({ + content: , + type: NotificationType.Error + }); } finally { setChangeSync(false); } diff --git a/ui/src/app/applications/components/application-sync-options/application-sync-options.scss b/ui/src/app/applications/components/application-sync-options/application-sync-options.scss index 9c44fb68b264c..6fc1a0d696258 100644 --- a/ui/src/app/applications/components/application-sync-options/application-sync-options.scss +++ b/ui/src/app/applications/components/application-sync-options/application-sync-options.scss @@ -18,4 +18,15 @@ .fa-exclamation-triangle { color: $argo-status-warning-color; } + + &__warning { + border-left: 5px solid $argo-failed-color-light; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + margin-top: 1em; + padding-left: 1em; + background: white; + box-shadow: 1px 2px 3px $argo-color-gray-5; + color: $argo-color-gray-6; + } } diff --git a/ui/src/app/applications/components/application-sync-options/application-sync-options.tsx b/ui/src/app/applications/components/application-sync-options/application-sync-options.tsx index adc5d668d75b7..829a905c00d96 100644 --- a/ui/src/app/applications/components/application-sync-options/application-sync-options.tsx +++ b/ui/src/app/applications/components/application-sync-options/application-sync-options.tsx @@ -5,7 +5,7 @@ import * as ReactForm from 'react-form'; require('./application-sync-options.scss'); -const REPLACE_WARNING = `The resource will be synced using 'kubectl replace/create' command that is a potentially destructive action.`; +export const REPLACE_WARNING = `The resources will be synced using 'kubectl replace/create' command that is a potentially destructive action and might cause resources recreation.`; export interface ApplicationSyncOptionProps { options: string[]; @@ -59,9 +59,12 @@ function booleanOption(name: string, label: string, defaultVal: boolean, props: /> {' '} {warning && ( - - - + <> + + + + {checked &&
{warning}
} + )} ); @@ -86,7 +89,6 @@ const syncOptions: Array<(props: ApplicationSyncOptionProps) => React.ReactNode> props => booleanOption('CreateNamespace', 'Auto-Create Namespace', false, props, false), props => booleanOption('PruneLast', 'Prune Last', false, props, false), props => booleanOption('ApplyOutOfSyncOnly', 'Apply Out of Sync Only', false, props, false), - props => booleanOption('Replace', 'Replace', false, props, false, REPLACE_WARNING), props => selectOption('PrunePropagationPolicy', 'Prune Propagation Policy', 'foreground', ['foreground', 'background', 'orphan'], props) ]; @@ -104,6 +106,9 @@ export const ApplicationSyncOptions = (props: ApplicationSyncOptionProps) => ( {render(props)} ))} +
+ {booleanOption('Replace', 'Replace', false, props, false, REPLACE_WARNING)} +
); diff --git a/ui/src/app/applications/components/application-sync-panel/application-sync-panel.scss b/ui/src/app/applications/components/application-sync-panel/application-sync-panel.scss index 031266098bae9..6a33f0835a5e3 100644 --- a/ui/src/app/applications/components/application-sync-panel/application-sync-panel.scss +++ b/ui/src/app/applications/components/application-sync-panel/application-sync-panel.scss @@ -2,11 +2,30 @@ margin-top: 0.5em; white-space: nowrap; - label { - max-width: 38em; - overflow: hidden; - text-overflow: ellipsis; + .container { + max-width: 32em; + white-space: nowrap; display: inline-block; - vertical-align: middle; + margin-right: 0.3em; + + label::before, + label::after { + vertical-align: middle; + display: inline-block; + max-width: 50%; + overflow: hidden; + white-space: pre; + } + + label::before { + content: attr(content-start); + text-overflow: ellipsis; + } + + label::after { + content: attr(content-end); + text-overflow: clip; + direction: rtl; // This is to help put the ellipsis in the middle instead of at the end of the resource path + } } -} \ No newline at end of file +} diff --git a/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx b/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx index 533423411f1d6..32615c64700fd 100644 --- a/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx +++ b/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx @@ -1,12 +1,13 @@ -import {ErrorNotification, FormField, NotificationType, SlidingPanel} from 'argo-ui'; +import {ErrorNotification, FormField, NotificationType, SlidingPanel, Tooltip} from 'argo-ui'; import * as React from 'react'; import {Form, FormApi, Text} from 'react-form'; -import {CheckboxField, Spinner} from '../../../shared/components'; +import {ARGO_WARNING_COLOR, CheckboxField, Spinner} from '../../../shared/components'; import {Consumer} from '../../../shared/context'; import * as models from '../../../shared/models'; import {services} from '../../../shared/services'; -import {ApplicationManualSyncFlags, ApplicationSyncOptions, SyncFlags} from '../application-sync-options/application-sync-options'; +import {ApplicationRetryOptions} from '../application-retry-options/application-retry-options'; +import {ApplicationManualSyncFlags, ApplicationSyncOptions, SyncFlags, REPLACE_WARNING} from '../application-sync-options/application-sync-options'; import {ComparisonStatusIcon, nodeKey} from '../utils'; require('./application-sync-panel.scss'); @@ -48,7 +49,7 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app defaultValues={{ revision: application.spec.source.targetRevision || 'HEAD', resources: appResources.map((_, i) => i === syncResIndex || syncResIndex === -1), - syncOptions: application.spec.syncPolicy ? application.spec.syncPolicy.syncOptions : '' + syncOptions: application.spec.syncPolicy ? application.spec.syncPolicy.syncOptions : [] }} validateError={values => ({ resources: values.resources.every((item: boolean) => !item) && 'Select at least one resource' @@ -60,6 +61,19 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app resources = null; } + const replace = params.syncOptions?.findIndex((opt: string) => opt === 'Replace=true') > -1; + if (replace) { + const confirmed = await ctx.popup.confirm('Synchronize using replace?', () => ( +
+ {REPLACE_WARNING} Are you sure you want to continue? +
+ )); + if (!confirmed) { + setPending(false); + return; + } + } + const syncFlags = {...params.syncFlags} as SyncFlags; if (syncFlags.ApplyOnly) { @@ -76,7 +90,8 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app syncFlags.DryRun || false, syncStrategy, resources, - params.syncOptions + params.syncOptions, + params.retryStrategy ); hide(); } catch (e) { @@ -112,9 +127,21 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app }} /> + + + {!formApi.values.resources.every((item: boolean) => item) && (
WARNING: partial synchronization is not recorded in history
@@ -136,9 +172,25 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app .filter(item => !item.hook) .map((item, i) => { const resKey = nodeKey(item); + const contentStart = resKey.substr(0, Math.floor(resKey.length / 2)); + let contentEnd = resKey.substr(-Math.floor(resKey.length / 2)); + // We want the ellipsis to be in the middle of our text, so we use RTL layout to put it there. + // Unfortunately, strong LTR characters get jumbled around, so make sure that the last character isn't strong. + const indexOfFirstLetter = /[a-z]/i.exec(contentEnd).index; + contentEnd = contentEnd.slice(indexOfFirstLetter); + const isLongLabel = resKey.length > 68; return (
- {' '} + + {resKey}
}> +
+ {isLongLabel ? ( +
+ ); diff --git a/ui/src/app/applications/components/applications-list/applications-filter.tsx b/ui/src/app/applications/components/applications-list/applications-filter.tsx index 6d701663cdf9a..4793abf925ac2 100644 --- a/ui/src/app/applications/components/applications-list/applications-filter.tsx +++ b/ui/src/app/applications/components/applications-list/applications-filter.tsx @@ -1,9 +1,9 @@ -import {ActionButton, debounce, useData} from 'argo-ui/v2'; +import {useData} from 'argo-ui/v2'; import * as minimatch from 'minimatch'; import * as React from 'react'; import {Application, ApplicationDestination, Cluster, HealthStatusCode, HealthStatuses, SyncStatusCode, SyncStatuses} from '../../../shared/models'; import {AppsListPreferences, services} from '../../../shared/services'; -import {Filter} from '../filter/filter'; +import {Filter, FiltersGroup} from '../filter/filter'; import * as LabelSelector from '../label-selector'; import {ComparisonStatusIcon, HealthStatusIcon} from '../utils'; @@ -32,11 +32,16 @@ export function getFilterResults(applications: Application[], pref: AppsListPref namespaces: pref.namespacesFilter.length === 0 || pref.namespacesFilter.some(ns => app.spec.destination.namespace && minimatch(app.spec.destination.namespace, ns)), clusters: pref.clustersFilter.length === 0 || - pref.clustersFilter.some( - selector => - (app.spec.destination.server && selector.includes(app.spec.destination.server)) || - (app.spec.destination.name && selector.includes(app.spec.destination.name)) - ), + pref.clustersFilter.some(filterString => { + const match = filterString.match('^(.*) [(](http.*)[)]$'); + if (match?.length === 3) { + const [, name, url] = match; + return url === app.spec.destination.server || name === app.spec.destination.name; + } else { + const inputMatch = filterString.match('^http.*$'); + return (inputMatch && inputMatch[0] === app.spec.destination.server) || (app.spec.destination.name && minimatch(app.spec.destination.name, filterString)); + } + }), labels: pref.labelsFilter.length === 0 || pref.labelsFilter.every(selector => LabelSelector.match(selector, app.metadata.labels)) } })); @@ -54,6 +59,7 @@ interface AppFilterProps { apps: FilteredApp[]; pref: AppsListPreferences; onChange: (newPrefs: AppsListPreferences) => void; + children?: React.ReactNode; } const getCounts = (apps: FilteredApp[], filterType: keyof FilterResult, filter: (app: Application) => string, init?: string[]) => { @@ -84,9 +90,15 @@ const SyncFilter = (props: AppFilterProps) => ( label='SYNC STATUS' selected={props.pref.syncFilter} setSelected={s => props.onChange({...props.pref, syncFilter: s})} - options={getOptions(props.apps, 'sync', app => app.status.sync.status, Object.keys(SyncStatuses), s => ( - - ))} + options={getOptions( + props.apps, + 'sync', + app => app.status.sync.status, + Object.keys(SyncStatuses), + s => ( + + ) + )} /> ); @@ -95,9 +107,15 @@ const HealthFilter = (props: AppFilterProps) => ( label='HEALTH STATUS' selected={props.pref.healthFilter} setSelected={s => props.onChange({...props.pref, healthFilter: s})} - options={getOptions(props.apps, 'health', app => app.status.health.status, Object.keys(HealthStatuses), s => ( - - ))} + options={getOptions( + props.apps, + 'health', + app => app.status.health.status, + Object.keys(HealthStatuses), + s => ( + + ) + )} /> ); @@ -128,7 +146,11 @@ const LabelsFilter = (props: AppFilterProps) => { }; const ProjectFilter = (props: AppFilterProps) => { - const [projects, loading, error] = useData(() => services.projects.list('items.metadata.name'), null, () => null); + const [projects, loading, error] = useData( + () => services.projects.list('items.metadata.name'), + null, + () => null + ); const projectOptions = (projects || []).map(proj => { return {label: proj.metadata.name}; }); @@ -192,42 +214,18 @@ const NamespaceFilter = (props: AppFilterProps) => { }; export const ApplicationsFilter = (props: AppFilterProps) => { - const [hidden, setHidden] = React.useState(false); - - React.useEffect(() => { - const handleResize = () => { - if (window.innerWidth >= 1440) { - setHidden(false); - } - }; + const setShown = (val: boolean) => { + services.viewPreferences.updatePreferences({appList: {...props.pref, hideFilters: !val}}); + }; - window.addEventListener('resize', debounce(handleResize, 1000)); - return () => window.removeEventListener('resize', handleResize); - }); return ( - -
- FILTERS - setHidden(!hidden)} - style={{marginLeft: 'auto', fontSize: '12px', lineHeight: '5px', display: hidden && 'block'}} - /> -
-
- {!hidden && ( - - - -
- - - - -
-
- )} -
-
+ + + + + + + + ); }; diff --git a/ui/src/app/applications/components/applications-list/applications-labels.scss b/ui/src/app/applications/components/applications-list/applications-labels.scss new file mode 100644 index 0000000000000..39def91b41df6 --- /dev/null +++ b/ui/src/app/applications/components/applications-list/applications-labels.scss @@ -0,0 +1,22 @@ +@import 'node_modules/argo-ui/src/styles/config'; + +.application-labels, .application-labels-tooltip { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.application-labels { + .application-labels__item { + background-color: $argo-color-gray-4; + border-radius: 5px; + padding: 0 2px; + margin-right: 2px; + } +} + +.application-labels-tooltip { + display: flex; + flex-direction: column; + align-items: flex-start; +} \ No newline at end of file diff --git a/ui/src/app/applications/components/applications-list/applications-labels.tsx b/ui/src/app/applications/components/applications-list/applications-labels.tsx new file mode 100644 index 0000000000000..80b7a9989dd12 --- /dev/null +++ b/ui/src/app/applications/components/applications-list/applications-labels.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import {Tooltip} from 'argo-ui'; +import {Application} from '../../../shared/models'; + +require('./applications-labels.scss'); + +export const ApplicationsLabels = ({app}: {app: Application}) => { + const labels = ( + <> + {app.spec.source.targetRevision || 'HEAD'} + {Object.keys(app.metadata.labels || {}).map(label => ( + {`${label}=${app.metadata.labels[label]}`} + ))} + + ); + + return ( + {labels}}> +
{labels}
+
+ ); +}; diff --git a/ui/src/app/applications/components/applications-list/applications-list.scss b/ui/src/app/applications/components/applications-list/applications-list.scss index 5ecf2faadfd0f..75051ad0ac932 100644 --- a/ui/src/app/applications/components/applications-list/applications-list.scss +++ b/ui/src/app/applications/components/applications-list/applications-list.scss @@ -32,82 +32,34 @@ color: $argo-color-gray-7; // healthy statuses - - &--comparison-Synced { - border-left-color: $argo-success-color; - } - &--health-Healthy { border-left-color: $argo-success-color; } - // having this higher up ensures that progressing takes precedence - &--comparison-OutOfSync { - border-left-color: $argo-status-warning-color; - } - // intermediate statuses - &--health-Progressing { border-left-color: $argo-running-color; } - // failed statuses - - &--health-Degraded { - border-left-color: $argo-failed-color; + &--health-Suspended { + border-left-color: $argo-suspended-color; } - &--comparison-Error { + // failed statuses + &--health-Degraded { border-left-color: $argo-failed-color; } - &--actions { - padding-top: 1em; - } - } - - &__filters { - display: flex; - &__title { - margin-bottom: 1em; - font-size: 13px; - color: $argo-color-gray-6; - display: flex; - align-items: center; - } - - .filter { - margin-right: 15px; - height: max-content; - min-width: 200px; - max-width: 200px; + &--health-Unknown { + border-left-color: $argo-color-gray-4; } - &__text-filters { - align-self: start; - display: flex; - flex-wrap: wrap; - } - } - - @include breakpoint(xxlarge up) { - .filter { - width: 100%; - margin-right: 0; + &--health-Missing { + border-left-color: $argo-status-warning-color; } - &__filters { - flex-wrap: wrap; - padding-bottom: 6em; - - &__title .action-button { - display: none; - } - - &__text-filters { - width: 100%; - } + &--actions { + padding-top: 1em; } } @@ -213,4 +165,25 @@ display: inline-block; width: 28px; } + + .filters-group__panel { + top: 120px; + } + @include breakpoint(medium down) { + .filters-group__panel { + top: 200px; + } + } + + ul { + margin: 0; + } + + .chart-group { + margin: 0 0.8em; + } + + .chart { + justify-content: space-evenly; + } } diff --git a/ui/src/app/applications/components/applications-list/applications-list.tsx b/ui/src/app/applications/components/applications-list/applications-list.tsx index 6f71a2cf0dcc8..eba209a7fc763 100644 --- a/ui/src/app/applications/components/applications-list/applications-list.tsx +++ b/ui/src/app/applications/components/applications-list/applications-list.tsx @@ -1,7 +1,7 @@ import {Autocomplete, ErrorNotification, MockupList, NotificationType, SlidingPanel, Toolbar} from 'argo-ui'; import * as classNames from 'classnames'; import * as React from 'react'; -import {Key, KeybindingContext, KeybindingProvider} from 'react-keyhooks'; +import {Key, KeybindingContext, KeybindingProvider} from 'argo-ui/v2'; import {RouteComponentProps} from 'react-router'; import {combineLatest, from, merge, Observable} from 'rxjs'; import {bufferTime, delay, filter, map, mergeMap, repeat, retryWhen} from 'rxjs/operators'; @@ -14,6 +14,7 @@ import {ApplicationSyncPanel} from '../application-sync-panel/application-sync-p import {ApplicationsSyncPanel} from '../applications-sync-panel/applications-sync-panel'; import * as AppUtils from '../utils'; import {ApplicationsFilter, FilteredApp, getFilterResults} from './applications-filter'; +import {ApplicationsStatusBar} from './applications-status-bar'; import {ApplicationsSummary} from './applications-summary'; import {ApplicationsTable} from './applications-table'; import {ApplicationTiles} from './applications-tiles'; @@ -167,22 +168,28 @@ const SearchBar = (props: {content: string; ctx: ContextApis; apps: models.Appli const {useKeybinding} = React.useContext(KeybindingContext); const [isFocused, setFocus] = React.useState(false); - useKeybinding(Key.SLASH, () => { - if (searchBar.current && !appInput) { - searchBar.current.querySelector('input').focus(); - setFocus(true); - return true; + useKeybinding({ + keys: Key.SLASH, + action: () => { + if (searchBar.current && !appInput) { + searchBar.current.querySelector('input').focus(); + setFocus(true); + return true; + } + return false; } - return false; }); - useKeybinding(Key.ESCAPE, () => { - if (searchBar.current && !appInput && isFocused) { - searchBar.current.querySelector('input').blur(); - setFocus(false); - return true; + useKeybinding({ + keys: Key.ESCAPE, + action: () => { + if (searchBar.current && !appInput && isFocused) { + searchBar.current.querySelector('input').blur(); + setFocus(false); + return true; + } + return false; } - return false; }); return ( @@ -295,14 +302,18 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { function onFilterPrefChanged(ctx: ContextApis, newPref: AppsListPreferences) { services.viewPreferences.updatePreferences({appList: newPref}); - ctx.navigation.goto('.', { - proj: newPref.projectsFilter.join(','), - sync: newPref.syncFilter.join(','), - health: newPref.healthFilter.join(','), - namespace: newPref.namespacesFilter.join(','), - cluster: newPref.clustersFilter.join(','), - labels: newPref.labelsFilter.map(encodeURIComponent).join(',') - }); + ctx.navigation.goto( + '.', + { + proj: newPref.projectsFilter.join(','), + sync: newPref.syncFilter.join(','), + health: newPref.healthFilter.join(','), + namespace: newPref.namespacesFilter.join(','), + cluster: newPref.clustersFilter.join(','), + labels: newPref.labelsFilter.map(encodeURIComponent).join(',') + }, + {replace: true} + ); } return ( @@ -332,7 +343,7 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { className={classNames('fa fa-th', {selected: pref.appList.view === 'tiles'})} title='Tiles' onClick={() => { - ctx.navigation.goto('.', {view: 'tiles'}); + ctx.navigation.goto('.', {view: 'tiles'}, {replace: true}); services.viewPreferences.updatePreferences({appList: {...pref.appList, view: 'tiles'}}); }} /> @@ -340,7 +351,7 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { className={classNames('fa fa-th-list', {selected: pref.appList.view === 'list'})} title='List' onClick={() => { - ctx.navigation.goto('.', {view: 'list'}); + ctx.navigation.goto('.', {view: 'list'}, {replace: true}); services.viewPreferences.updatePreferences({appList: {...pref.appList, view: 'list'}}); }} /> @@ -348,7 +359,7 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { className={classNames('fa fa-chart-pie', {selected: pref.appList.view === 'summary'})} title='Summary' onClick={() => { - ctx.navigation.goto('.', {view: 'summary'}); + ctx.navigation.goto('.', {view: 'summary'}, {replace: true}); services.viewPreferences.updatePreferences({appList: {...pref.appList, view: 'summary'}}); }} /> @@ -361,12 +372,12 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { title: 'New App', iconClassName: 'fa fa-plus', qeId: 'applications-list-button-new-app', - action: () => ctx.navigation.goto('.', {new: '{}'}) + action: () => ctx.navigation.goto('.', {new: '{}'}, {replace: true}) }, { title: 'Sync Apps', iconClassName: 'fa fa-sync', - action: () => ctx.navigation.goto('.', {syncApps: true}) + action: () => ctx.navigation.goto('.', {syncApps: true}, {replace: true}) } ] } @@ -377,33 +388,23 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { {pref => { const {filteredApps, filterResults} = filterApps(applications, pref, pref.search); - return applications.length === 0 && (pref.labelsFilter || []).length === 0 ? ( - -

No applications yet

-
Create new application to start managing resources in your cluster
- -
- ) : ( -
-
- onFilterPrefChanged(ctx, newPrefs)} pref={pref} /> - {syncAppsInput && ( - ctx.navigation.goto('.', {syncApps: null})} - apps={filteredApps} - /> - )} -
-
+ const appsView = + applications.length === 0 && (pref.labelsFilter || []).length === 0 ? ( + +

No applications yet

+
Create new application to start managing resources in your cluster
+ +
+ ) : ( + onFilterPrefChanged(ctx, newPrefs)} pref={pref}> {(pref.view === 'summary' && ) || ( 1 && } preferencesKey='applications-list' page={pref.page} emptyState={() => ( @@ -427,14 +428,14 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { (pref.view === 'tiles' && ( ctx.navigation.goto('.', {syncApp: appName})} + syncApplication={appName => ctx.navigation.goto('.', {syncApp: appName}, {replace: true})} refreshApplication={refreshApp} deleteApplication={appName => AppUtils.deleteApplication(appName, ctx)} /> )) || ( ctx.navigation.goto('.', {syncApp: appName})} + syncApplication={appName => ctx.navigation.goto('.', {syncApp: appName}, {replace: true})} refreshApplication={refreshApp} deleteApplication={appName => AppUtils.deleteApplication(appName, ctx)} /> @@ -442,8 +443,18 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { } )} -
-
+ + ); + return ( + <> + {appsView} + ctx.navigation.goto('.', {syncApps: null}, {replace: true})} + apps={filteredApps} + /> + ); }}
@@ -464,7 +475,7 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { key='syncPanel' application={app} selectedResource={'all'} - hide={() => ctx.navigation.goto('.', {syncApp: null})} + hide={() => ctx.navigation.goto('.', {syncApp: null}, {replace: true})} /> )} @@ -472,7 +483,7 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { ctx.navigation.goto('.', {new: null})} + onClose={() => ctx.navigation.goto('.', {new: null}, {replace: true})} header={
{' '} @@ -500,7 +511,7 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { setAppCreatePending(true); try { await services.applications.create(app); - ctx.navigation.goto('.', {new: null}); + ctx.navigation.goto('.', {new: null}, {replace: true}); } catch (e) { ctx.notifications.show({ content: , diff --git a/ui/src/app/applications/components/applications-list/applications-source.scss b/ui/src/app/applications/components/applications-list/applications-source.scss new file mode 100644 index 0000000000000..d066fbc31c1b9 --- /dev/null +++ b/ui/src/app/applications/components/applications-list/applications-source.scss @@ -0,0 +1,5 @@ +.application-source { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} \ No newline at end of file diff --git a/ui/src/app/applications/components/applications-list/applications-source.tsx b/ui/src/app/applications/components/applications-list/applications-source.tsx new file mode 100644 index 0000000000000..a7bba5a66f48a --- /dev/null +++ b/ui/src/app/applications/components/applications-list/applications-source.tsx @@ -0,0 +1,14 @@ +import {Tooltip} from 'argo-ui'; +import * as React from 'react'; +import {ApplicationSource as ApplicationSourceType} from '../../../shared/models'; + +require('./applications-source.scss'); + +export const ApplicationsSource = ({source}: {source: ApplicationSourceType}) => { + const sourceString = `${source.repoURL}/${source.path || source.chart}`; + return ( + +
{sourceString}
+
+ ); +}; diff --git a/ui/src/app/applications/components/applications-list/applications-status-bar.scss b/ui/src/app/applications/components/applications-list/applications-status-bar.scss new file mode 100644 index 0000000000000..ed94f335ff7a6 --- /dev/null +++ b/ui/src/app/applications/components/applications-list/applications-status-bar.scss @@ -0,0 +1,32 @@ +@import 'node_modules/argo-ui/src/styles/config'; + +.status-bar { + $height: 16px; + $border-width: 2px; + margin: 0px; + width: 100%; + height: $height; + display: flex; + border-radius: 25px; + border: $border-width solid white; + + &__segment { + &__fill { + height: $height - (2 * $border-width); + } + } + + &__segment:first-child { + border-top-left-radius: 25px; + border-bottom-left-radius: 25px; + } + + &__segment:last-child { + border-top-right-radius: 25px; + border-bottom-right-radius: 25px; + } + + &__segment:not(:first-child) { + border-left: 3px solid white; + } +} 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 new file mode 100644 index 0000000000000..9d61598752084 --- /dev/null +++ b/ui/src/app/applications/components/applications-list/applications-status-bar.tsx @@ -0,0 +1,79 @@ +import {Tooltip} from 'argo-ui/v2'; +import * as React from 'react'; +import {COLORS} from '../../../shared/components'; +import {Consumer} from '../../../shared/context'; +import * as models from '../../../shared/models'; + +require('./applications-status-bar.scss'); + +export interface ApplicationsStatusBarProps { + applications: models.Application[]; +} + +export const ApplicationsStatusBar = ({applications}: ApplicationsStatusBarProps) => { + const readings = [ + { + name: 'Healthy', + value: applications.filter(app => app.status.health.status === 'Healthy').length, + color: COLORS.health.healthy + }, + { + name: 'Progressing', + value: applications.filter(app => app.status.health.status === 'Progressing').length, + color: COLORS.health.progressing + }, + { + name: 'Degraded', + value: applications.filter(app => app.status.health.status === 'Degraded').length, + color: COLORS.health.degraded + }, + { + name: 'Suspended', + value: applications.filter(app => app.status.health.status === 'Suspended').length, + color: COLORS.health.suspended + }, + { + name: 'Missing', + value: applications.filter(app => app.status.health.status === 'Missing').length, + color: COLORS.health.missing + }, + { + name: 'Unknown', + value: applications.filter(app => app.status.health.status === 'Unknown').length, + color: COLORS.health.unknown + } + ]; + + // will sort readings by value greatest to lowest, then by name + readings.sort((a, b) => (a.value < b.value ? 1 : a.value === b.value ? (a.name > b.name ? 1 : -1) : -1)); + + const totalItems = readings.reduce((total, i) => { + return total + i.value; + }, 0); + + return ( + + {ctx => ( + <> + {totalItems > 1 && ( +
+ {readings && + readings.length > 1 && + readings.map((item, i) => { + if (item.value > 0) { + return ( +
+ +
+ +
+ ); + } + })} +
+ )} + + )} + + ); +}; 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 f60be26aa258e..9fc78169ea700 100644 --- a/ui/src/app/applications/components/applications-list/applications-summary.tsx +++ b/ui/src/app/applications/components/applications-list/applications-summary.tsx @@ -3,18 +3,21 @@ const PieChart = require('react-svg-piechart').default; import {COLORS} from '../../../shared/components'; import * as models from '../../../shared/models'; +import {HealthStatusCode, SyncStatusCode} from '../../../shared/models'; +import {ComparisonStatusIcon, HealthStatusIcon} from '../utils'; const healthColors = new Map(); -healthColors.set('Healthy', COLORS.health.healthy); +healthColors.set('Unknown', COLORS.health.unknown); healthColors.set('Progressing', COLORS.health.progressing); +healthColors.set('Suspended', COLORS.health.suspended); +healthColors.set('Healthy', COLORS.health.healthy); healthColors.set('Degraded', COLORS.health.degraded); healthColors.set('Missing', COLORS.health.missing); -healthColors.set('Unknown', COLORS.health.unknown); const syncColors = new Map(); +syncColors.set('Unknown', COLORS.sync.unknown); syncColors.set('Synced', COLORS.sync.synced); syncColors.set('OutOfSync', COLORS.sync.out_of_sync); -syncColors.set('Unknown', COLORS.sync.unknown); export const ApplicationsSummary = ({applications}: {applications: models.Application[]}) => { const sync = new Map(); @@ -73,27 +76,39 @@ export const ApplicationsSummary = ({applications}: {applications: models.Applic ))}
- {charts.map(chart => ( - -
-
-
-

{chart.title}

- -
-
-
    - {Array.from(chart.legend.keys()).map(key => ( -
  • - {key} -
  • - ))} -
-
-
-
-
- ))} +
+
+ {charts.map(chart => { + const getLegendValue = (key: string) => { + const index = chart.data.findIndex((data: {title: string}) => data.title === key); + return index > -1 ? chart.data[index].value : 0; + }; + return ( + +
+
+
+

{chart.title}

+ +
+
+
    + {Array.from(chart.legend.keys()).map(key => ( +
  • + {chart.title === 'Health' && } + {chart.title === 'Sync' && } + {` ${key} (${getLegendValue(key)})`} +
  • + ))} +
+
+
+
+
+ ); + })} +
+
); diff --git a/ui/src/app/applications/components/applications-list/applications-table.scss b/ui/src/app/applications/components/applications-list/applications-table.scss index a8c7f9f000366..ce2e723c2a014 100644 --- a/ui/src/app/applications/components/applications-list/applications-table.scss +++ b/ui/src/app/applications/components/applications-list/applications-table.scss @@ -1,22 +1,4 @@ -@import 'node_modules/argo-ui/src/styles/config'; - .applications-table { - &__meta { - position: absolute; - right: 5px; - top: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 40%; - span { - background-color: $argo-color-gray-4; - border-radius: 5px; - padding: 0 2px; - margin-right: 2px; - } - } - .argo-table-list__row { line-height: 26px; padding-top: 4px; @@ -32,4 +14,18 @@ } } } + + .applications-table-source { + display: flex; + justify-content: space-between; + + .applications-table-source__link { + flex: 1; + min-width: 0; + } + + .applications-table-source__labels { + max-width: 40%; + } + } } 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 f7b3c80257744..d8ced3906c901 100644 --- a/ui/src/app/applications/components/applications-list/applications-table.tsx +++ b/ui/src/app/applications/components/applications-list/applications-table.tsx @@ -1,12 +1,14 @@ import {DropDownMenu} from 'argo-ui'; import * as React from 'react'; -import {Key, KeybindingContext, useNav} from 'react-keyhooks'; +import {Key, KeybindingContext, useNav} from 'argo-ui/v2'; import {Cluster} from '../../../shared/components'; import {Consumer} from '../../../shared/context'; import * as models from '../../../shared/models'; import {ApplicationURLs} from '../application-urls'; import * as AppUtils from '../utils'; import {OperationState} from '../utils'; +import {ApplicationsLabels} from './applications-labels'; +import {ApplicationsSource} from './applications-source'; require('./applications-table.scss'); export const ApplicationsTable = (props: { @@ -19,11 +21,14 @@ export const ApplicationsTable = (props: { const {useKeybinding} = React.useContext(KeybindingContext); - useKeybinding(Key.DOWN, () => navApp(1)); - useKeybinding(Key.UP, () => navApp(-1)); - useKeybinding(Key.ESCAPE, () => { - reset(); - return selectedApp > -1 ? true : false; + useKeybinding({keys: Key.DOWN, action: () => navApp(1)}); + useKeybinding({keys: Key.UP, action: () => navApp(-1)}); + useKeybinding({ + keys: Key.ESCAPE, + action: () => { + reset(); + return selectedApp > -1 ? true : false; + } }); return ( @@ -34,8 +39,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.name}`, {}, {event: e})}>
@@ -52,13 +56,12 @@ export const ApplicationsTable = (props: {
Source:
-
- {app.spec.source.repoURL}/{app.spec.source.path || app.spec.source.chart} -
- {app.spec.source.targetRevision || 'HEAD'} - {Object.keys(app.metadata.labels || {}).map(label => ( - {`${label}=${app.metadata.labels[label]}`} - ))} +
+
+ +
+
+
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 bb16778f88f80..fcc1d246950f8 100644 --- a/ui/src/app/applications/components/applications-list/applications-tiles.tsx +++ b/ui/src/app/applications/components/applications-list/applications-tiles.tsx @@ -1,7 +1,7 @@ import {Tooltip} from 'argo-ui'; import * as classNames from 'classnames'; import * as React from 'react'; -import {Key, KeybindingContext, NumKey, NumKeyToNumber, NumPadKey, useNav} from 'react-keyhooks'; +import {Key, KeybindingContext, NumKey, NumKeyToNumber, NumPadKey, useNav} from 'argo-ui/v2'; import {Cluster} from '../../../shared/components'; import {Consumer, Context} from '../../../shared/context'; import * as models from '../../../shared/models'; @@ -55,34 +55,46 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat const {useKeybinding} = React.useContext(KeybindingContext); - useKeybinding(Key.RIGHT, () => navApp(1)); - useKeybinding(Key.LEFT, () => navApp(-1)); - useKeybinding(Key.DOWN, () => navApp(appsPerRow)); - useKeybinding(Key.UP, () => navApp(-1 * appsPerRow)); + useKeybinding({keys: Key.RIGHT, action: () => navApp(1)}); + useKeybinding({keys: Key.LEFT, action: () => navApp(-1)}); + useKeybinding({keys: Key.DOWN, action: () => navApp(appsPerRow)}); + useKeybinding({keys: Key.UP, action: () => navApp(-1 * appsPerRow)}); - useKeybinding(Key.ENTER, () => { - if (selectedApp > -1) { - ctxh.navigation.goto(`/applications/${applications[selectedApp].metadata.name}`); - return true; + useKeybinding({ + keys: Key.ENTER, + action: () => { + if (selectedApp > -1) { + ctxh.navigation.goto(`/applications/${applications[selectedApp].metadata.name}`); + return true; + } + return false; } - return false; }); - useKeybinding(Key.ESCAPE, () => { - if (selectedApp > -1) { - reset(); - return true; + useKeybinding({ + keys: Key.ESCAPE, + action: () => { + if (selectedApp > -1) { + reset(); + return true; + } + return false; } - return false; }); - useKeybinding(Object.values(NumKey) as NumKey[], n => { - reset(); - return navApp(NumKeyToNumber(n)); + useKeybinding({ + keys: Object.values(NumKey) as NumKey[], + action: n => { + reset(); + return navApp(NumKeyToNumber(n)); + } }); - useKeybinding(Object.values(NumPadKey) as NumPadKey[], n => { - reset(); - return navApp(NumKeyToNumber(n)); + useKeybinding({ + keys: Object.values(NumPadKey) as NumPadKey[], + action: n => { + reset(); + return navApp(NumKeyToNumber(n)); + } }); return ( @@ -93,9 +105,9 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
+ className={`argo-table-list__row applications-list__entry applications-list__entry--health-${app.status.health.status} ${ + selectedApp === i ? 'applications-tiles__selected' : '' + }`}>
ctx.navigation.goto(`/applications/${app.metadata.name}`, {}, {event: e})}>
@@ -119,6 +131,7 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
{Object.keys(app.metadata.labels || {}) @@ -155,7 +168,7 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat Repository:
- + {app.spec.source.repoURL}
@@ -217,7 +230,7 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat refreshApplication(app.metadata.name); }}> {' '} - Refresh + Refresh   - Delete + Delete
diff --git a/ui/src/app/applications/components/applications-sync-panel/applications-sync-panel.tsx b/ui/src/app/applications/components/applications-sync-panel/applications-sync-panel.tsx index 5f50de44b5ebf..18d17b21bfaac 100644 --- a/ui/src/app/applications/components/applications-sync-panel/applications-sync-panel.tsx +++ b/ui/src/app/applications/components/applications-sync-panel/applications-sync-panel.tsx @@ -5,6 +5,7 @@ import {CheckboxField, ProgressPopup} from '../../../shared/components'; import {Consumer} from '../../../shared/context'; import * as models from '../../../shared/models'; import {services} from '../../../shared/services'; +import {ApplicationRetryOptions} from '../application-retry-options/application-retry-options'; import {ApplicationManualSyncFlags, ApplicationSyncOptions, SyncFlags} from '../application-sync-options/application-sync-options'; import {ComparisonStatusIcon, HealthStatusIcon, OperationPhaseIcon} from '../utils'; @@ -59,7 +60,8 @@ export const ApplicationsSyncPanel = ({show, apps, hide}: {show: boolean; apps: syncFlags.DryRun || false, syncStrategy, null, - params.syncOptions + params.syncOptions, + params.retryStrategy ) .catch(e => { ctx.notifications.show({ @@ -94,6 +96,9 @@ export const ApplicationsSyncPanel = ({show, apps, hide}: {show: boolean; apps: }} />
+ + +