Compare commits

..

4 Commits

Author SHA1 Message Date
Michael Crenshaw
2abaa0f7dd clarify
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2022-11-30 18:17:58 -05:00
Michael Crenshaw
9a5bc59816 clarify
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2022-11-30 18:17:23 -05:00
Michael Crenshaw
4f16c15854 remove non-vendor-neutral link
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2022-11-30 18:16:03 -05:00
Michael Crenshaw
d059048921 docs: add vendor resources section
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2022-11-30 17:24:08 -05:00
375 changed files with 10449 additions and 57570 deletions

View File

@@ -1,32 +0,0 @@
---
name: Argo CD Release
about: Used by our Release Champion to track progress of a minor release
title: 'Argo CD Release vX.X'
labels: 'release'
assignees: ''
---
Target RC1 date: ___. __, ____
Target GA date: ___. __, ____
- [ ] Create new section in the [Release Planning doc](https://docs.google.com/document/d/1trJIomcgXcfvLw0aYnERrFWfPjQOfYMDJOCh1S8nMBc/edit?usp=sharing)
- [ ] Schedule a Release Planning meeting roughly two weeks before the scheduled Release freeze date by adding it to the community calendar (or delegate this task to someone with write access to the community calendar)
- [ ] Include Zoom link in the invite
- [ ] Post in #argo-cd and #argo-contributors one week before the meeting
- [ ] Post again one hour before the meeting
- [ ] At the meeting, remove issues/PRs from the project's column for that release which have not been “claimed” by at least one Approver (add it to the next column if Approver requests that)
- [ ] 1wk before feature freeze post in #argo-contributors that PRs must be merged by DD-MM-YYYY to be included in the release - ask approvers to drop items from milestone they cant merge
- [ ] At least two days before RC1 date, draft RC blog post and submit it for review (or delegate this task)
- [ ] Cut RC1 (or delegate this task to an Approver and coordinate timing)
- [ ] Create new release branch
- [ ] Add the release branch to ReadTheDocs
- [ ] Confirm that tweet and blog post are ready
- [ ] Trigger the release
- [ ] After the release is finished, publish tweet and blog post
- [ ] Post in #argo-cd and #argo-announcements with lots of emojis announcing the release and requesting help testing
- [ ] Monitor support channels for issues, cherry-picking bugfixes and docs fixes as appropriate (or delegate this task to an Approver and coordinate timing)
- [ ] At release date, evaluate if any bugs justify delaying the release. If not, cut the release (or delegate this task to an Approver and coordinate timing)
- [ ] If unreleased changes are on the release branch for {current minor version minus 3}, cut a final patch release for that series (or delegate this task to an Approver and coordinate timing)
- [ ] After the release, post in #argo-cd that the {current minor version minus 3} has reached EOL (example: https://cloud-native.slack.com/archives/C01TSERG0KZ/p1667336234059729)
- [ ] (For the next release champion) Review the [items scheduled for the next release](https://github.com/orgs/argoproj/projects/25). If any item does not have an assignee who can commit to finish the feature, move it to the next release.
- [ ] (For the next release champion) Schedule a time mid-way through the release cycle to review items again.

View File

@@ -27,9 +27,9 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Download all Go modules
@@ -45,13 +45,13 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Restore go build cache
uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -69,9 +69,9 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Run golangci-lint
@@ -92,11 +92,11 @@ jobs:
- name: Create checkout directory
run: mkdir -p ~/go/src/github.com/argoproj
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
- name: Create symlink in GOPATH
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Install required packages
@@ -116,17 +116,13 @@ jobs:
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Restore go build cache
uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
- name: Install all tools required for building & testing
run: |
make install-test-tools-local
# We install kustomize in the dist directory
- name: Add dist to PATH
run: |
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
- name: Setup git username and email
run: |
git config --global user.name "John Doe"
@@ -137,12 +133,12 @@ jobs:
- name: Run all unit tests
run: make test-local
- name: Generate code coverage artifacts
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
with:
name: code-coverage
path: coverage.out
- name: Generate test results artifacts
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
with:
name: test-results
path: test-results/
@@ -159,11 +155,11 @@ jobs:
- name: Create checkout directory
run: mkdir -p ~/go/src/github.com/argoproj
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
- name: Create symlink in GOPATH
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Install required packages
@@ -183,17 +179,13 @@ jobs:
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Restore go build cache
uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
- name: Install all tools required for building & testing
run: |
make install-test-tools-local
# We install kustomize in the dist directory
- name: Add dist to PATH
run: |
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
- name: Setup git username and email
run: |
git config --global user.name "John Doe"
@@ -204,7 +196,7 @@ jobs:
- name: Run all unit tests
run: make test-race-local
- name: Generate test results artifacts
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
with:
name: race-results
path: test-results/
@@ -214,9 +206,9 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Create symlink in GOPATH
@@ -240,10 +232,6 @@ jobs:
make install-codegen-tools-local
make install-go-tools-local
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
# We install kustomize in the dist directory
- name: Add dist to PATH
run: |
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
- name: Run codegen
run: |
set -x
@@ -262,14 +250,14 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
- name: Setup NodeJS
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 # v3.5.1
with:
node-version: '12.18.4'
- name: Restore node dependency cache
id: cache-dependencies
uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
@@ -299,12 +287,12 @@ jobs:
sonar_secret: ${{ secrets.SONAR_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
with:
fetch-depth: 0
- name: Restore node dependency cache
id: cache-dependencies
uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
@@ -315,11 +303,11 @@ jobs:
run: |
mkdir -p test-results
- name: Get code coverage artifiact
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # v3.0.1
with:
name: code-coverage
- name: Get test result artifact
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # v3.0.1
with:
name: test-results
path: test-results
@@ -378,9 +366,9 @@ jobs:
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: GH actions workaround - Kill XSP4 process
@@ -398,7 +386,7 @@ jobs:
sudo chown runner $HOME/.kube/config
kubectl version
- name: Restore go build cache
uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -426,7 +414,7 @@ jobs:
run: |
docker pull ghcr.io/dexidp/dex:v2.35.3
docker pull argoproj/argo-cd-ci-builder:v1.0.0
docker pull redis:7.0.7-alpine
docker pull redis:7.0.5-alpine
- name: Create target directory for binaries in the build-process
run: |
mkdir -p dist
@@ -454,7 +442,7 @@ jobs:
set -x
make test-e2e-local
- name: Upload e2e-server logs
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
with:
name: e2e-server-k8s${{ matrix.k3s-version }}.log
path: /tmp/e2e-server.log

View File

@@ -29,7 +29,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@@ -29,10 +29,10 @@ jobs:
env:
GOPATH: /home/runner/work/argo-cd/argo-cd
steps:
- uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
- uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
with:
go-version: ${{ env.GOLANG_VERSION }}
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
with:
path: src/github.com/argoproj/argo-cd
@@ -54,7 +54,7 @@ jobs:
# build
- uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
- uses: docker/setup-buildx-action@15c905b16b06416d2086efa066dd8e3a35cc7f98 # v2.4.0
- uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # v2.2.1
- run: |
IMAGE_PLATFORMS=linux/amd64
if [[ "${{ github.event_name }}" == "push" || "${{ contains(github.event.pull_request.labels.*.name, 'test-arm-image') }}" == "true" ]]
@@ -62,7 +62,7 @@ jobs:
IMAGE_PLATFORMS=linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
fi
echo "Building image for platforms: $IMAGE_PLATFORMS"
docker buildx build --platform $IMAGE_PLATFORMS --sbom=false --provenance=false --push="${{ github.event_name == 'push' }}" \
docker buildx build --platform $IMAGE_PLATFORMS --push="${{ github.event_name == 'push' }}" \
-t ghcr.io/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }} \
-t quay.io/argoproj/argocd:latest .
working-directory: ./src/github.com/argoproj/argo-cd
@@ -71,18 +71,11 @@ jobs:
- name: Install cosign
uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1
with:
cosign-release: 'v1.13.1'
- name: Install crane to get digest of image
uses: imjasonh/setup-crane@e82f1b9a8007d399333baba4d75915558e9fb6a4
- name: Get digest of image
run: |
echo "IMAGE_DIGEST=$(crane digest quay.io/argoproj/argocd:latest)" >> $GITHUB_ENV
cosign-release: 'v1.13.0'
- name: Sign Argo CD latest image
run: |
cosign sign --key env://COSIGN_PRIVATE_KEY quay.io/argoproj/argocd@${{ env.IMAGE_DIGEST }}
cosign sign --key env://COSIGN_PRIVATE_KEY quay.io/argoproj/argocd:latest
# Displays the public key to share.
cosign public-key --key env://COSIGN_PRIVATE_KEY
env:

View File

@@ -12,7 +12,7 @@ on:
- "!release-v0*"
env:
GOLANG_VERSION: '1.18'
GOLANG_VERSION: '1.18'
permissions:
contents: read
@@ -43,7 +43,7 @@ jobs:
GIT_EMAIL: argoproj@gmail.com
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
@@ -147,7 +147,7 @@ jobs:
echo "RELEASE_NOTES=${RELEASE_NOTES}" >> $GITHUB_ENV
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
with:
go-version: ${{ env.GOLANG_VERSION }}
@@ -177,10 +177,6 @@ jobs:
run: |
set -ue
make install-codegen-tools-local
# We install kustomize in the dist directory
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
make manifests-local VERSION=${TARGET_VERSION}
git diff
git commit manifests/ -m "Bump version to ${TARGET_VERSION}"
@@ -205,13 +201,13 @@ jobs:
if: ${{ env.DRY_RUN != 'true' }}
- uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
- uses: docker/setup-buildx-action@15c905b16b06416d2086efa066dd8e3a35cc7f98 # v2.4.0
- uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # v2.2.1
- name: Build and push Docker image for release
run: |
set -ue
git clean -fd
mkdir -p dist/
docker buildx build --platform linux/amd64,linux/arm64,linux/s390x,linux/ppc64le --sbom=false --provenance=false --push -t ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} -t argoproj/argocd:v${TARGET_VERSION} .
docker buildx build --platform linux/amd64,linux/arm64,linux/s390x,linux/ppc64le --push -t ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} -t argoproj/argocd:v${TARGET_VERSION} .
make release-cli
make checksums
chmod +x ./dist/argocd-linux-amd64
@@ -221,18 +217,11 @@ jobs:
- name: Install cosign
uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1
with:
cosign-release: 'v1.13.1'
- name: Install crane to get digest of image
uses: imjasonh/setup-crane@e82f1b9a8007d399333baba4d75915558e9fb6a4
- name: Get digest of image
run: |
echo "IMAGE_DIGEST=$(crane digest quay.io/argoproj/argocd:v${TARGET_VERSION})" >> $GITHUB_ENV
cosign-release: 'v1.13.0'
- name: Sign Argo CD container images and assets
run: |
cosign sign --key env://COSIGN_PRIVATE_KEY ${IMAGE_NAMESPACE}/argocd@${{ env.IMAGE_DIGEST }}
cosign sign --key env://COSIGN_PRIVATE_KEY ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
cosign sign-blob --key env://COSIGN_PRIVATE_KEY ./dist/argocd-${TARGET_VERSION}-checksums.txt > ./dist/argocd-${TARGET_VERSION}-checksums.sig
# Retrieves the public key to release as an asset
cosign public-key --key env://COSIGN_PRIVATE_KEY > ./dist/argocd-cosign.pub
@@ -275,7 +264,7 @@ jobs:
SIGS_BOM_VERSION: v0.2.1
# comma delimited list of project relative folders to inspect for package
# managers (gomod, yarn, npm).
PROJECT_FOLDERS: ".,./ui"
PROJECT_FOLDERS: ".,./ui"
# full qualified name of the docker image to be inspected
DOCKER_IMAGE: ${{env.IMAGE_NAMESPACE}}/argocd:v${{env.TARGET_VERSION}}
run: |
@@ -306,7 +295,7 @@ jobs:
if: ${{ env.DRY_RUN != 'true' }}
- name: Create GitHub release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5 # v0.1.14
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build reports

View File

@@ -43,7 +43,6 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
### Blogs and Presentations
1. [Awesome-Argo: A Curated List of Awesome Projects and Resources Related to Argo](https://github.com/terrytangyuan/awesome-argo)
1. [Unveil the Secret Ingredients of Continuous Delivery at Enterprise Scale with Argo CD](https://blog.akuity.io/unveil-the-secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argo-cd-7c5b4057ee49)
1. [GitOps Without Pipelines With ArgoCD Image Updater](https://youtu.be/avPUQin9kzU)
1. [Combining Argo CD (GitOps), Crossplane (Control Plane), And KubeVela (OAM)](https://youtu.be/eEcgn_gU3SM)
1. [How to Apply GitOps to Everything - Combining Argo CD and Crossplane](https://youtu.be/yrj4lmScKHQ)
@@ -51,10 +50,8 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
1. [Automation of Everything - How To Combine Argo Events, Workflows & Pipelines, CD, and Rollouts](https://youtu.be/XNXJtxkUKeY)
1. [Environments Based On Pull Requests (PRs): Using Argo CD To Apply GitOps Principles On Previews](https://youtu.be/cpAaI8p4R60)
1. [Argo CD: Applying GitOps Principles To Manage Production Environment In Kubernetes](https://youtu.be/vpWQeoaiRM4)
1. [Creating Temporary Preview Environments Based On Pull Requests With Argo CD And Codefresh](https://codefresh.io/continuous-deployment/creating-temporary-preview-environments-based-pull-requests-argo-cd-codefresh/)
1. [Tutorial: Everything You Need To Become A GitOps Ninja](https://www.youtube.com/watch?v=r50tRQjisxw) 90m tutorial on GitOps and Argo CD.
1. [Comparison of Argo CD, Spinnaker, Jenkins X, and Tekton](https://www.inovex.de/blog/spinnaker-vs-argo-cd-vs-tekton-vs-jenkins-x/)
1. [Simplify and Automate Deployments Using GitOps with IBM Multicloud Manager 3.1.2](https://www.ibm.com/cloud/blog/simplify-and-automate-deployments-using-gitops-with-ibm-multicloud-manager-3-1-2)
1. [GitOps for Kubeflow using Argo CD](https://v0-6.kubeflow.org/docs/use-cases/gitops-for-kubeflow/)
1. [GitOps Toolsets on Kubernetes with CircleCI and Argo CD](https://www.digitalocean.com/community/tutorials/webinar-series-gitops-tool-sets-on-kubernetes-with-circleci-and-argo-cd)
1. [CI/CD in Light Speed with K8s and Argo CD](https://www.youtube.com/watch?v=OdzH82VpMwI&feature=youtu.be)
@@ -63,7 +60,6 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
1. [Introduction to Argo CD : Kubernetes DevOps CI/CD](https://www.youtube.com/watch?v=2WSJF7d8dUg&feature=youtu.be)
1. [GitOps Deployment and Kubernetes - using Argo CD](https://medium.com/riskified-technology/gitops-deployment-and-kubernetes-f1ab289efa4b)
1. [Deploy Argo CD with Ingress and TLS in Three Steps: No YAML Yak Shaving Required](https://itnext.io/deploy-argo-cd-with-ingress-and-tls-in-three-steps-no-yaml-yak-shaving-required-bc536d401491)
1. [GitOps Continuous Delivery with Argo and Codefresh](https://codefresh.io/events/cncf-member-webinar-gitops-continuous-delivery-argo-codefresh/)
1. [Stay up to date with Argo CD and Renovate](https://mjpitz.com/blog/2020/12/03/renovate-your-gitops/)
1. [Setting up Argo CD with Helm](https://www.arthurkoziel.com/setting-up-argocd-with-helm/)
1. [Applied GitOps with Argo CD](https://thenewstack.io/applied-gitops-with-argocd/)
@@ -73,3 +69,10 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
1. [Getting Started with ArgoCD for GitOps Deployments](https://youtu.be/AvLuplh1skA)
1. [Using Argo CD & Datree for Stable Kubernetes CI/CD Deployments](https://youtu.be/17894DTru2Y)
### Vendor-sponsored community resources
The following links are to resources hosted by Argo CD vendors (companies which use Argo CD as part of their software offerings).
These resources are vendor-neutral (overwhelmingly _about_ Argo CD, but may briefly mention the vendor) and do not require providing information for marketing purposes.
1. [Unveil the Secret Ingredients of Continuous Delivery at Enterprise Scale with Argo CD](https://blog.akuity.io/unveil-the-secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argo-cd-7c5b4057ee49)
1. [Argo CD Sandbox (demo instance)](https://argocd.argo.opsmx.net/) (requires logging into the demo instance with a Google account)

View File

@@ -44,7 +44,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Chargetrip](https://chargetrip.com)
1. [Chime](https://www.chime.com)
1. [Cisco ET&I](https://eti.cisco.com/)
1. [Cloud Scale](https://cloudscaleinc.com/)
1. [Cobalt](https://www.cobalt.io/)
1. [Codefresh](https://www.codefresh.io/)
1. [Codility](https://www.codility.com/)
@@ -83,7 +82,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Garner](https://www.garnercorp.com)
1. [Generali Deutschland AG](https://www.generali.de/)
1. [Gepardec](https://gepardec.com/)
1. [GetYourGuide](https://www.getyourguide.com/)
1. [Gitpod](https://www.gitpod.io)
1. [Gllue](https://gllue.com)
1. [gloat](https://gloat.com/)
@@ -105,7 +103,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Ibotta](https://home.ibotta.com)
1. [IITS-Consulting](https://iits-consulting.de)
1. [imaware](https://imaware.health)
1. [Indeed](https://indeed.com)
1. [Index Exchange](https://www.indexexchange.com/)
1. [InsideBoard](https://www.insideboard.com)
1. [Intuit](https://www.intuit.com/)
@@ -138,7 +135,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Max Kelsen](https://www.maxkelsen.com/)
1. [MeDirect](https://medirect.com.mt/)
1. [Meican](https://meican.com/)
1. [Mercedes-Benz Tech Innovation](https://www.mercedes-benz-techinnovation.com/)
1. [Metanet](http://www.metanet.co.kr/en/)
1. [MindSpore](https://mindspore.cn)
1. [Mirantis](https://mirantis.com/)
@@ -169,7 +165,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Packlink](https://www.packlink.com/)
1. [Pandosearch](https://www.pandosearch.com/en/home)
1. [PagerDuty](https://www.pagerduty.com/)
1. [Patreon](https://www.patreon.com/)
1. [PayPay](https://paypay.ne.jp/)
1. [Peloton Interactive](https://www.onepeloton.com/)
1. [Pigment](https://www.gopigment.com/)
@@ -229,7 +224,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Toss](https://toss.im/en)
1. [Trendyol](https://www.trendyol.com/)
1. [tru.ID](https://tru.id)
1. [Trusting Social](https://trustingsocial.com/)
1. [Twilio SendGrid](https://sendgrid.com)
1. [tZERO](https://www.tzero.com/)
1. [UBIO](https://ub.io/)
@@ -238,11 +232,9 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Unifonic Inc](https://www.unifonic.com/)
1. [Universidad Mesoamericana](https://www.umes.edu.gt/)
1. [Viaduct](https://www.viaduct.ai/)
1. [Vinted](https://vinted.com/)
1. [Virtuo](https://www.govirtuo.com/)
1. [VISITS Technologies](https://visits.world/en)
1. [Volvo Cars](https://www.volvocars.com/)
1. [Voyager Digital](https://www.investvoyager.com/)
1. [VSHN - The DevOps Company](https://vshn.ch/)
1. [Walkbase](https://www.walkbase.com/)
1. [Webstores](https://www.webstores.nl)

View File

@@ -1 +1 @@
2.6.2
2.5.0

View File

@@ -25,7 +25,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
@@ -70,8 +69,6 @@ type ApplicationSetReconciler struct {
KubeClientset kubernetes.Interface
utils.Policy
utils.Renderer
EnableProgressiveSyncs bool
}
// +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets,verbs=get;list;watch;create;update;patch;delete
@@ -137,27 +134,6 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
return ctrl.Result{RequeueAfter: ReconcileRequeueOnValidationError}, nil
}
// appMap is a name->app collection of Applications in this ApplicationSet.
appMap := map[string]argov1alpha1.Application{}
// appSyncMap tracks which apps will be synced during this reconciliation.
appSyncMap := map[string]bool{}
if r.EnableProgressiveSyncs && applicationSetInfo.Spec.Strategy != nil {
applications, err := r.getCurrentApplications(ctx, applicationSetInfo)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err)
}
for _, app := range applications {
appMap[app.Name] = app
}
appSyncMap, err = r.performProgressiveSyncs(ctx, applicationSetInfo, applications, desiredApplications, appMap)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to perform progressive sync reconciliation for application set: %w", err)
}
}
var validApps []argov1alpha1.Application
for i := range desiredApplications {
if validateErrors[i] == nil {
@@ -186,26 +162,6 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
)
}
if r.EnableProgressiveSyncs {
// trigger appropriate application syncs if RollingSync strategy is enabled
if progressiveSyncsStrategyEnabled(&applicationSetInfo, "RollingSync") {
validApps, err = r.syncValidApplications(ctx, &applicationSetInfo, appSyncMap, appMap, validApps)
if err != nil {
_ = r.setApplicationSetStatusCondition(ctx,
&applicationSetInfo,
argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionErrorOccurred,
Message: err.Error(),
Reason: argov1alpha1.ApplicationSetReasonSyncApplicationError,
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
}, parametersGenerated,
)
return ctrl.Result{}, err
}
}
}
if r.Policy.Update() {
err = r.createOrUpdateInCluster(ctx, applicationSetInfo, validApps)
if err != nil {
@@ -572,11 +528,6 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
// Copy only the Application/ObjectMeta fields that are significant, from the generatedApp
found.Spec = generatedApp.Spec
// allow setting the Operation field to trigger a sync operation on an Application
if generatedApp.Operation != nil {
found.Operation = generatedApp.Operation
}
// Preserve specially treated argo cd annotations:
// * https://github.com/argoproj/applicationset/issues/180
// * https://github.com/argoproj/argo-cd/issues/10500
@@ -775,541 +726,4 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte
return nil
}
func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context, appset argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, desiredApplications []argov1alpha1.Application, appMap map[string]argov1alpha1.Application) (map[string]bool, error) {
appDependencyList, appStepMap, err := r.buildAppDependencyList(ctx, appset, desiredApplications)
if err != nil {
return nil, fmt.Errorf("failed to build app dependency list: %w", err)
}
_, err = r.updateApplicationSetApplicationStatus(ctx, &appset, applications, appStepMap)
if err != nil {
return nil, fmt.Errorf("failed to update applicationset app status: %w", err)
}
log.Infof("ApplicationSet %v step list:", appset.Name)
for i, step := range appDependencyList {
log.Infof("step %v: %+v", i+1, step)
}
appSyncMap, err := r.buildAppSyncMap(ctx, appset, appDependencyList, appMap)
if err != nil {
return nil, fmt.Errorf("failed to build app sync map: %w", err)
}
log.Infof("Application allowed to sync before maxUpdate?: %+v", appSyncMap)
_, err = r.updateApplicationSetApplicationStatusProgress(ctx, &appset, appSyncMap, appStepMap, appMap)
if err != nil {
return nil, fmt.Errorf("failed to update applicationset application status progress: %w", err)
}
_, err = r.updateApplicationSetApplicationStatusConditions(ctx, &appset)
if err != nil {
return nil, fmt.Errorf("failed to update applicationset application status conditions: %w", err)
}
return appSyncMap, nil
}
// this list tracks which Applications belong to each RollingUpdate step
func (r *ApplicationSetReconciler) buildAppDependencyList(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([][]string, map[string]int, error) {
if applicationSet.Spec.Strategy == nil || applicationSet.Spec.Strategy.Type == "" || applicationSet.Spec.Strategy.Type == "AllAtOnce" {
return [][]string{}, map[string]int{}, nil
}
steps := []argov1alpha1.ApplicationSetRolloutStep{}
if progressiveSyncsStrategyEnabled(&applicationSet, "RollingSync") {
steps = applicationSet.Spec.Strategy.RollingSync.Steps
}
appDependencyList := make([][]string, 0)
for range steps {
appDependencyList = append(appDependencyList, make([]string, 0))
}
appStepMap := map[string]int{}
// use applicationLabelSelectors to filter generated Applications into steps and status by name
for _, app := range applications {
for i, step := range steps {
selected := true // default to true, assuming the current Application is a match for the given step matchExpression
allNotInMatched := true // needed to support correct AND behavior between multiple NotIn MatchExpressions
notInUsed := false // since we default to allNotInMatched == true, track whether a NotIn expression was actually used
for _, matchExpression := range step.MatchExpressions {
if matchExpression.Operator == "In" {
if val, ok := app.Labels[matchExpression.Key]; ok {
valueMatched := labelMatchedExpression(val, matchExpression)
if !valueMatched { // none of the matchExpression values was a match with the Application'ss labels
selected = false
break
}
} else {
selected = false // no matching label key with In means this Application will not be included in the current step
break
}
} else if matchExpression.Operator == "NotIn" {
notInUsed = true // a NotIn selector was used in this matchExpression
if val, ok := app.Labels[matchExpression.Key]; ok {
valueMatched := labelMatchedExpression(val, matchExpression)
if !valueMatched { // none of the matchExpression values was a match with the Application's labels
allNotInMatched = false
}
} else {
allNotInMatched = false // no matching label key with NotIn means this Application may still be included in the current step
}
} else { // handle invalid operator selection
log.Warnf("skipping AppSet rollingUpdate step Application selection for %q, invalid matchExpression operator provided: %q ", applicationSet.Name, matchExpression.Operator)
selected = false
break
}
}
if notInUsed && allNotInMatched { // check if all NotIn Expressions matched, if so exclude this Application
selected = false
}
if selected {
appDependencyList[i] = append(appDependencyList[i], app.Name)
if val, ok := appStepMap[app.Name]; ok {
log.Warnf("AppSet '%v' has a invalid matchExpression that selects Application '%v' label twice, in steps %v and %v", applicationSet.Name, app.Name, val+1, i+1)
} else {
appStepMap[app.Name] = i
}
}
}
}
return appDependencyList, appStepMap, nil
}
func labelMatchedExpression(val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool {
valueMatched := false
for _, value := range matchExpression.Values {
if val == value {
valueMatched = true
break
}
}
return valueMatched
}
// this map is used to determine which stage of Applications are ready to be updated in the reconciler loop
func (r *ApplicationSetReconciler) buildAppSyncMap(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, appDependencyList [][]string, appMap map[string]argov1alpha1.Application) (map[string]bool, error) {
appSyncMap := map[string]bool{}
syncEnabled := true
// healthy stages and the first non-healthy stage should have sync enabled
// every stage after should have sync disabled
for i := range appDependencyList {
// set the syncEnabled boolean for every Application in the current step
for _, appName := range appDependencyList[i] {
appSyncMap[appName] = syncEnabled
}
// detect if we need to halt before progressing to the next step
for _, appName := range appDependencyList[i] {
idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, appName)
if idx == -1 {
// no Application status found, likely because the Application is being newly created
syncEnabled = false
break
}
appStatus := applicationSet.Status.ApplicationStatus[idx]
if app, ok := appMap[appName]; ok {
syncEnabled = appSyncEnabledForNextStep(&applicationSet, app, appStatus)
if !syncEnabled {
break
}
} else {
// application name not found in the list of applications managed by this ApplicationSet, maybe because it's being deleted
syncEnabled = false
break
}
}
}
return appSyncMap, nil
}
func appSyncEnabledForNextStep(appset *argov1alpha1.ApplicationSet, app argov1alpha1.Application, appStatus argov1alpha1.ApplicationSetApplicationStatus) bool {
if progressiveSyncsStrategyEnabled(appset, "RollingSync") {
// we still need to complete the current step if the Application is not yet Healthy or there are still pending Application changes
return isApplicationHealthy(app) && appStatus.Status == "Healthy"
}
return true
}
func progressiveSyncsStrategyEnabled(appset *argov1alpha1.ApplicationSet, strategyType string) bool {
if appset.Spec.Strategy == nil || appset.Spec.Strategy.Type != strategyType {
return false
}
if strategyType == "RollingSync" && appset.Spec.Strategy.RollingSync == nil {
return false
}
return true
}
func isApplicationHealthy(app argov1alpha1.Application) bool {
healthStatusString, syncStatusString, operationPhaseString := statusStrings(app)
if healthStatusString == "Healthy" && syncStatusString != "OutOfSync" && (operationPhaseString == "Succeeded" || operationPhaseString == "") {
return true
}
return false
}
func statusStrings(app argov1alpha1.Application) (string, string, string) {
healthStatusString := string(app.Status.Health.Status)
syncStatusString := string(app.Status.Sync.Status)
operationPhaseString := ""
if app.Status.OperationState != nil {
operationPhaseString = string(app.Status.OperationState.Phase)
}
return healthStatusString, syncStatusString, operationPhaseString
}
// check the status of each Application's status and promote Applications to the next status if needed
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, appStepMap map[string]int) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
now := metav1.Now()
appStatuses := make([]argov1alpha1.ApplicationSetApplicationStatus, 0, len(applications))
for _, app := range applications {
healthStatusString, syncStatusString, operationPhaseString := statusStrings(app)
idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, app.Name)
currentAppStatus := argov1alpha1.ApplicationSetApplicationStatus{}
if idx == -1 {
// AppStatus not found, set default status of "Waiting"
currentAppStatus = argov1alpha1.ApplicationSetApplicationStatus{
Application: app.Name,
LastTransitionTime: &now,
Message: "No Application status found, defaulting status to Waiting.",
Status: "Waiting",
Step: fmt.Sprint(appStepMap[app.Name] + 1),
}
} else {
// we have an existing AppStatus
currentAppStatus = applicationSet.Status.ApplicationStatus[idx]
}
appOutdated := false
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
appOutdated = syncStatusString == "OutOfSync"
}
if appOutdated && currentAppStatus.Status != "Waiting" && currentAppStatus.Status != "Pending" {
log.Infof("Application %v is outdated, updating its ApplicationSet status to Waiting", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = "Waiting"
currentAppStatus.Message = "Application has pending changes, setting status to Waiting."
currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1)
}
if currentAppStatus.Status == "Pending" {
if operationPhaseString == "Succeeded" && app.Status.OperationState.StartedAt.After(currentAppStatus.LastTransitionTime.Time) {
log.Infof("Application %v has completed a sync successfully, updating its ApplicationSet status to Progressing", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = "Progressing"
currentAppStatus.Message = "Application resource completed a sync successfully, updating status from Pending to Progressing."
currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1)
} else if operationPhaseString == "Running" || healthStatusString == "Progressing" {
log.Infof("Application %v has entered Progressing status, updating its ApplicationSet status to Progressing", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = "Progressing"
currentAppStatus.Message = "Application resource became Progressing, updating status from Pending to Progressing."
currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1)
}
}
if currentAppStatus.Status == "Waiting" && isApplicationHealthy(app) {
log.Infof("Application %v is already synced and healthy, updating its ApplicationSet status to Healthy", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = healthStatusString
currentAppStatus.Message = "Application resource is already Healthy, updating status from Waiting to Healthy."
currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1)
}
if currentAppStatus.Status == "Progressing" && isApplicationHealthy(app) {
log.Infof("Application %v has completed Progressing status, updating its ApplicationSet status to Healthy", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = healthStatusString
currentAppStatus.Message = "Application resource became Healthy, updating status from Progressing to Healthy."
currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1)
}
appStatuses = append(appStatuses, currentAppStatus)
}
err := r.setAppSetApplicationStatus(ctx, applicationSet, appStatuses)
if err != nil {
return nil, fmt.Errorf("failed to set AppSet application statuses: %w", err)
}
return appStatuses, nil
}
// check Applications that are in Waiting status and promote them to Pending if needed
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appStepMap map[string]int, appMap map[string]argov1alpha1.Application) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
now := metav1.Now()
appStatuses := make([]argov1alpha1.ApplicationSetApplicationStatus, 0, len(applicationSet.Status.ApplicationStatus))
// if we have no RollingUpdate steps, clear out the existing ApplicationStatus entries
if applicationSet.Spec.Strategy != nil && applicationSet.Spec.Strategy.Type != "" && applicationSet.Spec.Strategy.Type != "AllAtOnce" {
updateCountMap := []int{}
totalCountMap := []int{}
length := 0
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
length = len(applicationSet.Spec.Strategy.RollingSync.Steps)
}
for s := 0; s < length; s++ {
updateCountMap = append(updateCountMap, 0)
totalCountMap = append(totalCountMap, 0)
}
// populate updateCountMap with counts of existing Pending and Progressing Applications
for _, appStatus := range applicationSet.Status.ApplicationStatus {
totalCountMap[appStepMap[appStatus.Application]] += 1
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
if appStatus.Status == "Pending" || appStatus.Status == "Progressing" {
updateCountMap[appStepMap[appStatus.Application]] += 1
}
}
}
for _, appStatus := range applicationSet.Status.ApplicationStatus {
maxUpdateAllowed := true
maxUpdate := &intstr.IntOrString{}
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
maxUpdate = applicationSet.Spec.Strategy.RollingSync.Steps[appStepMap[appStatus.Application]].MaxUpdate
}
// by default allow all applications to update if maxUpdate is unset
if maxUpdate != nil {
maxUpdateVal, err := intstr.GetScaledValueFromIntOrPercent(maxUpdate, totalCountMap[appStepMap[appStatus.Application]], false)
if err != nil {
log.Warnf("AppSet '%v' has a invalid maxUpdate value '%+v', ignoring maxUpdate logic for this step: %v", applicationSet.Name, maxUpdate, err)
}
// ensure that percentage values greater than 0% always result in at least 1 Application being selected
if maxUpdate.Type == intstr.String && maxUpdate.StrVal != "0%" && maxUpdateVal < 1 {
maxUpdateVal = 1
}
if updateCountMap[appStepMap[appStatus.Application]] >= maxUpdateVal {
maxUpdateAllowed = false
log.Infof("Application %v is not allowed to update yet, %v/%v Applications already updating in step %v in AppSet %v", appStatus.Application, updateCountMap[appStepMap[appStatus.Application]], maxUpdateVal, appStepMap[appStatus.Application]+1, applicationSet.Name)
}
}
if appStatus.Status == "Waiting" && appSyncMap[appStatus.Application] && maxUpdateAllowed {
log.Infof("Application %v moved to Pending status, watching for the Application to start Progressing", appStatus.Application)
appStatus.LastTransitionTime = &now
appStatus.Status = "Pending"
appStatus.Message = "Application moved to Pending status, watching for the Application resource to start Progressing."
appStatus.Step = fmt.Sprint(appStepMap[appStatus.Application] + 1)
updateCountMap[appStepMap[appStatus.Application]] += 1
}
appStatuses = append(appStatuses, appStatus)
}
}
err := r.setAppSetApplicationStatus(ctx, applicationSet, appStatuses)
if err != nil {
return nil, fmt.Errorf("failed to set AppSet app status: %w", err)
}
return appStatuses, nil
}
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditions(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet) ([]argov1alpha1.ApplicationSetCondition, error) {
appSetProgressing := false
for _, appStatus := range applicationSet.Status.ApplicationStatus {
if appStatus.Status != "Healthy" {
appSetProgressing = true
break
}
}
appSetConditionProgressing := false
for _, appSetCondition := range applicationSet.Status.Conditions {
if appSetCondition.Type == argov1alpha1.ApplicationSetConditionRolloutProgressing && appSetCondition.Status == argov1alpha1.ApplicationSetConditionStatusTrue {
appSetConditionProgressing = true
break
}
}
if appSetProgressing && !appSetConditionProgressing {
_ = r.setApplicationSetStatusCondition(ctx,
applicationSet,
argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "ApplicationSet Rollout Rollout started",
Reason: argov1alpha1.ApplicationSetReasonApplicationSetModified,
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
}, false,
)
} else if !appSetProgressing && appSetConditionProgressing {
_ = r.setApplicationSetStatusCondition(ctx,
applicationSet,
argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "ApplicationSet Rollout Rollout complete",
Reason: argov1alpha1.ApplicationSetReasonApplicationSetRolloutComplete,
Status: argov1alpha1.ApplicationSetConditionStatusFalse,
}, false,
)
}
return applicationSet.Status.Conditions, nil
}
func findApplicationStatusIndex(appStatuses []argov1alpha1.ApplicationSetApplicationStatus, application string) int {
for i := range appStatuses {
if appStatuses[i].Application == application {
return i
}
}
return -1
}
// setApplicationSetApplicationStatus updates the ApplicatonSet's status field
// with any new/changed Application statuses.
func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, applicationStatuses []argov1alpha1.ApplicationSetApplicationStatus) error {
needToUpdateStatus := false
for i := range applicationStatuses {
appStatus := applicationStatuses[i]
idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, appStatus.Application)
if idx == -1 {
needToUpdateStatus = true
break
}
currentStatus := applicationSet.Status.ApplicationStatus[idx]
if currentStatus.Message != appStatus.Message || currentStatus.Status != appStatus.Status {
needToUpdateStatus = true
break
}
}
if needToUpdateStatus {
// fetch updated Application Set object before updating it
namespacedName := types.NamespacedName{Namespace: applicationSet.Namespace, Name: applicationSet.Name}
if err := r.Get(ctx, namespacedName, applicationSet); err != nil {
if client.IgnoreNotFound(err) != nil {
return nil
}
return fmt.Errorf("error fetching updated application set: %v", err)
}
for i := range applicationStatuses {
applicationSet.Status.SetApplicationStatus(applicationStatuses[i])
}
// Update the newly fetched object with new set of ApplicationStatus
err := r.Client.Status().Update(ctx, applicationSet)
if err != nil {
log.Errorf("unable to set application set status: %v", err)
return fmt.Errorf("unable to set application set status: %v", err)
}
if err := r.Get(ctx, namespacedName, applicationSet); err != nil {
if client.IgnoreNotFound(err) != nil {
return nil
}
return fmt.Errorf("error fetching updated application set: %v", err)
}
}
return nil
}
func (r *ApplicationSetReconciler) syncValidApplications(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appMap map[string]argov1alpha1.Application, validApps []argov1alpha1.Application) ([]argov1alpha1.Application, error) {
rolloutApps := []argov1alpha1.Application{}
for i := range validApps {
pruneEnabled := false
// ensure that Applications generated with RollingSync do not have an automated sync policy, since the AppSet controller will handle triggering the sync operation instead
if validApps[i].Spec.SyncPolicy != nil && validApps[i].Spec.SyncPolicy.Automated != nil {
pruneEnabled = validApps[i].Spec.SyncPolicy.Automated.Prune
validApps[i].Spec.SyncPolicy.Automated = nil
}
appSetStatusPending := false
idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, validApps[i].Name)
if idx > -1 && applicationSet.Status.ApplicationStatus[idx].Status == "Pending" {
// only trigger a sync for Applications that are in Pending status, since this is governed by maxUpdate
appSetStatusPending = true
}
// check appSyncMap to determine which Applications are ready to be updated and which should be skipped
if appSyncMap[validApps[i].Name] && appMap[validApps[i].Name].Status.Sync.Status == "OutOfSync" && appSetStatusPending {
log.Infof("triggering sync for application: %v, prune enabled: %v", validApps[i].Name, pruneEnabled)
validApps[i], _ = syncApplication(validApps[i], pruneEnabled)
}
rolloutApps = append(rolloutApps, validApps[i])
}
return rolloutApps, nil
}
// used by the RollingSync Progressive Sync strategy to trigger a sync of a particular Application resource
func syncApplication(application argov1alpha1.Application, prune bool) (argov1alpha1.Application, error) {
operation := argov1alpha1.Operation{
InitiatedBy: argov1alpha1.OperationInitiator{
Username: "applicationset-controller",
Automated: true,
},
Info: []*argov1alpha1.Info{
{
Name: "Reason",
Value: "ApplicationSet RollingSync triggered a sync of this Application resource.",
},
},
Sync: &argov1alpha1.SyncOperation{},
}
if application.Spec.SyncPolicy != nil {
if application.Spec.SyncPolicy.Retry != nil {
operation.Retry = *application.Spec.SyncPolicy.Retry
}
if application.Spec.SyncPolicy.SyncOptions != nil {
operation.Sync.SyncOptions = application.Spec.SyncPolicy.SyncOptions
}
operation.Sync.Prune = prune
}
application.Operation = &operation
return application, nil
}
var _ handler.EventHandler = &clusterSecretEventHandler{}

File diff suppressed because it is too large Load Diff

View File

@@ -23,8 +23,6 @@ spec:
template:
metadata:
name: 'myapp-{{ .branch }}-{{ .number }}'
labels:
key1: '{{ index .labels 0 }}'
spec:
source:
repoURL: 'https://github.com/myorg/myrepo.git'

View File

@@ -85,7 +85,6 @@ func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoproj
"total": len(allPaths),
"repoURL": appSetGenerator.Git.RepoURL,
"revision": appSetGenerator.Git.Revision,
"pathParamPrefix": appSetGenerator.Git.PathParamPrefix,
}).Info("applications result from the repo service")
requestedApps := g.filterApps(appSetGenerator.Git.Directories, allPaths)
@@ -122,7 +121,7 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al
for _, path := range allPaths {
// A JSON / YAML file path can contain multiple sets of parameters (ie it is an array)
paramsArray, err := g.generateParamsFromGitFile(path, allFiles[path], useGoTemplate, appSetGenerator.Git.PathParamPrefix)
paramsArray, err := g.generateParamsFromGitFile(path, allFiles[path], useGoTemplate)
if err != nil {
return nil, fmt.Errorf("unable to process file '%s': %v", path, err)
}
@@ -134,7 +133,7 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al
return res, nil
}
func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []byte, useGoTemplate bool, pathParamPrefix string) ([]map[string]interface{}, error) {
func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []byte, useGoTemplate bool) ([]map[string]interface{}, error) {
objectsFound := []map[string]interface{}{}
// First, we attempt to parse as an array
@@ -168,11 +167,7 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
paramPath["basenameNormalized"] = utils.SanitizeName(path.Base(paramPath["path"].(string)))
paramPath["filenameNormalized"] = utils.SanitizeName(path.Base(paramPath["filename"].(string)))
paramPath["segments"] = strings.Split(paramPath["path"].(string), "/")
if pathParamPrefix != "" {
params[pathParamPrefix] = map[string]interface{}{"path": paramPath}
} else {
params["path"] = paramPath
}
params["path"] = paramPath
} else {
flat, err := flatten.Flatten(objectFound, "", flatten.DotStyle)
if err != nil {
@@ -181,18 +176,14 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
for k, v := range flat {
params[k] = fmt.Sprintf("%v", v)
}
pathParamName := "path"
if pathParamPrefix != "" {
pathParamName = pathParamPrefix+"."+pathParamName
}
params[pathParamName] = path.Dir(filePath)
params[pathParamName+".basename"] = path.Base(params[pathParamName].(string))
params[pathParamName+".filename"] = path.Base(filePath)
params[pathParamName+".basenameNormalized"] = utils.SanitizeName(path.Base(params[pathParamName].(string)))
params[pathParamName+".filenameNormalized"] = utils.SanitizeName(path.Base(params[pathParamName+".filename"].(string)))
for k, v := range strings.Split(params[pathParamName].(string), "/") {
params["path"] = path.Dir(filePath)
params["path.basename"] = path.Base(params["path"].(string))
params["path.filename"] = path.Base(filePath)
params["path.basenameNormalized"] = utils.SanitizeName(path.Base(params["path"].(string)))
params["path.filenameNormalized"] = utils.SanitizeName(path.Base(params["path.filename"].(string)))
for k, v := range strings.Split(params["path"].(string), "/") {
if len(v) > 0 {
params[pathParamName+"["+strconv.Itoa(k)+"]"] = v
params["path["+strconv.Itoa(k)+"]"] = v
}
}
}
@@ -201,6 +192,7 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
}
return res, nil
}
func (g *GitGenerator) filterApps(Directories []argoprojiov1alpha1.GitDirectoryGeneratorItem, allPaths []string) []string {
@@ -231,7 +223,9 @@ func (g *GitGenerator) filterApps(Directories []argoprojiov1alpha1.GitDirectoryG
return res
}
func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool) []map[string]interface{} {
func (g *GitGenerator) generateParamsFromApps(requestedApps []string, _ *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool) []map[string]interface{} {
// TODO: At some point, the applicationSetGenerator param should be used
res := make([]map[string]interface{}, len(requestedApps))
for i, a := range requestedApps {
@@ -243,22 +237,14 @@ func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGene
paramPath["basename"] = path.Base(a)
paramPath["basenameNormalized"] = utils.SanitizeName(path.Base(a))
paramPath["segments"] = strings.Split(paramPath["path"].(string), "/")
if appSetGenerator.Git.PathParamPrefix != "" {
params[appSetGenerator.Git.PathParamPrefix] = map[string]interface{}{"path": paramPath}
} else {
params["path"] = paramPath
}
params["path"] = paramPath
} else {
pathParamName := "path"
if appSetGenerator.Git.PathParamPrefix != "" {
pathParamName = appSetGenerator.Git.PathParamPrefix+"."+pathParamName
}
params[pathParamName] = a
params[pathParamName+".basename"] = path.Base(a)
params[pathParamName+".basenameNormalized"] = utils.SanitizeName(path.Base(a))
for k, v := range strings.Split(params[pathParamName].(string), "/") {
params["path"] = a
params["path.basename"] = path.Base(a)
params["path.basenameNormalized"] = utils.SanitizeName(path.Base(a))
for k, v := range strings.Split(params["path"].(string), "/") {
if len(v) > 0 {
params[pathParamName+"["+strconv.Itoa(k)+"]"] = v
params["path["+strconv.Itoa(k)+"]"] = v
}
}
}

View File

@@ -51,7 +51,7 @@ func Test_generateParamsFromGitFile(t *testing.T) {
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`), false, "")
`), false)
if err != nil {
t.Fatal(err)
}
@@ -69,33 +69,11 @@ foo:
}, params)
}
func Test_generatePrefixedParamsFromGitFile(t *testing.T) {
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`), false, "myRepo")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, []map[string]interface{}{
{
"foo.bar": "baz",
"myRepo.path": "path/dir",
"myRepo.path.basename": "dir",
"myRepo.path.filename": "file_name.yaml",
"myRepo.path.basenameNormalized": "dir",
"myRepo.path.filenameNormalized": "file-name.yaml",
"myRepo.path[0]": "path",
"myRepo.path[1]": "dir",
},
}, params)
}
func Test_generateParamsFromGitFileGoTemplate(t *testing.T) {
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`), true, "")
`), true)
if err != nil {
t.Fatal(err)
}
@@ -119,46 +97,15 @@ foo:
}, params)
}
func Test_generatePrefixedParamsFromGitFileGoTemplate(t *testing.T) {
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`), true, "myRepo")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, []map[string]interface{}{
{
"foo": map[string]interface{}{
"bar": "baz",
},
"myRepo": map[string]interface{}{
"path": map[string]interface{}{
"path": "path/dir",
"basename": "dir",
"filename": "file_name.yaml",
"basenameNormalized": "dir",
"filenameNormalized": "file-name.yaml",
"segments": []string{
"path",
"dir",
},
},
},
},
}, params)
}
func TestGitGenerateParamsFromDirectories(t *testing.T) {
cases := []struct {
name string
directories []argoprojiov1alpha1.GitDirectoryGeneratorItem
pathParamPrefix string
repoApps []string
repoError error
expected []map[string]interface{}
expectedError error
name string
directories []argoprojiov1alpha1.GitDirectoryGeneratorItem
repoApps []string
repoError error
expected []map[string]interface{}
expectedError error
}{
{
name: "happy flow - created apps",
@@ -177,24 +124,6 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
},
expectedError: nil,
},
{
name: "It prefixes path parameters with PathParamPrefix",
directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
pathParamPrefix: "myRepo",
repoApps: []string{
"app1",
"app2",
"app_3",
"p1/app4",
},
repoError: nil,
expected: []map[string]interface{}{
{"myRepo.path": "app1", "myRepo.path.basename": "app1", "myRepo.path.basenameNormalized": "app1", "myRepo.path[0]": "app1"},
{"myRepo.path": "app2", "myRepo.path.basename": "app2", "myRepo.path.basenameNormalized": "app2", "myRepo.path[0]": "app2"},
{"myRepo.path": "app_3", "myRepo.path.basename": "app_3", "myRepo.path.basenameNormalized": "app-3", "myRepo.path[0]": "app_3"},
},
expectedError: nil,
},
{
name: "It filters application according to the paths",
directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "p1/*"}, {Path: "p1/*/*"}},
@@ -283,10 +212,9 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
Spec: argoprojiov1alpha1.ApplicationSetSpec{
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
Git: &argoprojiov1alpha1.GitGenerator{
RepoURL: "RepoURL",
Revision: "Revision",
Directories: testCaseCopy.directories,
PathParamPrefix: testCaseCopy.pathParamPrefix,
RepoURL: "RepoURL",
Revision: "Revision",
Directories: testCaseCopy.directories,
},
}},
},
@@ -309,13 +237,12 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
cases := []struct {
name string
directories []argoprojiov1alpha1.GitDirectoryGeneratorItem
pathParamPrefix string
repoApps []string
repoError error
expected []map[string]interface{}
expectedError error
name string
directories []argoprojiov1alpha1.GitDirectoryGeneratorItem
repoApps []string
repoError error
expected []map[string]interface{}
expectedError error
}{
{
name: "happy flow - created apps",
@@ -361,57 +288,6 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
},
expectedError: nil,
},
{
name: "It prefixes path parameters with PathParamPrefix",
directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
pathParamPrefix: "myRepo",
repoApps: []string{
"app1",
"app2",
"app_3",
"p1/app4",
},
repoError: nil,
expected: []map[string]interface{}{
{
"myRepo": map[string]interface{}{
"path": map[string]interface{}{
"path": "app1",
"basename": "app1",
"basenameNormalized": "app1",
"segments": []string{
"app1",
},
},
},
},
{
"myRepo": map[string]interface{}{
"path": map[string]interface{}{
"path": "app2",
"basename": "app2",
"basenameNormalized": "app2",
"segments": []string{
"app2",
},
},
},
},
{
"myRepo": map[string]interface{}{
"path": map[string]interface{}{
"path": "app_3",
"basename": "app_3",
"basenameNormalized": "app-3",
"segments": []string{
"app_3",
},
},
},
},
},
expectedError: nil,
},
{
name: "It filters application according to the paths",
directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "p1/*"}, {Path: "p1/*/*"}},
@@ -579,10 +455,9 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
GoTemplate: true,
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
Git: &argoprojiov1alpha1.GitGenerator{
RepoURL: "RepoURL",
Revision: "Revision",
Directories: testCaseCopy.directories,
PathParamPrefix: testCaseCopy.pathParamPrefix,
RepoURL: "RepoURL",
Revision: "Revision",
Directories: testCaseCopy.directories,
},
}},
},

View File

@@ -144,10 +144,9 @@ func (m *MatrixGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap
for _, r := range appSetGenerator.Matrix.Generators {
base := &argoprojiov1alpha1.ApplicationSetGenerator{
List: r.List,
Clusters: r.Clusters,
Git: r.Git,
PullRequest: r.PullRequest,
List: r.List,
Clusters: r.Clusters,
Git: r.Git,
}
generators := GetRelevantGenerators(base, m.supportedGenerators)

View File

@@ -399,8 +399,6 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url"}`)}},
}
pullRequestGenerator := &argoprojiov1alpha1.PullRequestGenerator{}
testCases := []struct {
name string
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
@@ -433,31 +431,6 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
gitGetRequeueAfter: time.Duration(1),
expected: time.Duration(1),
},
{
name: "returns the minimal time for pull request",
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
{
Git: gitGenerator,
},
{
PullRequest: pullRequestGenerator,
},
},
gitGetRequeueAfter: time.Duration(15 * time.Second),
expected: time.Duration(15 * time.Second),
},
{
name: "returns the default time if no requeueAfterSeconds is provided",
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
{
Git: gitGenerator,
},
{
PullRequest: pullRequestGenerator,
},
},
expected: time.Duration(30 * time.Minute),
},
}
for _, testCase := range testCases {
@@ -468,18 +441,16 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
for _, g := range testCaseCopy.baseGenerators {
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
Git: g.Git,
List: g.List,
PullRequest: g.PullRequest,
Git: g.Git,
List: g.List,
}
mock.On("GetRequeueAfter", &gitGeneratorSpec).Return(testCaseCopy.gitGetRequeueAfter, nil)
}
var matrixGenerator = NewMatrixGenerator(
map[string]Generator{
"Git": mock,
"List": &ListGenerator{},
"PullRequest": &PullRequestGenerator{},
"Git": mock,
"List": &ListGenerator{},
},
)

View File

@@ -90,19 +90,13 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
shortSHALength = len(pull.HeadSHA)
}
paramMap := map[string]interface{}{
params = append(params, map[string]interface{}{
"number": strconv.Itoa(pull.Number),
"branch": pull.Branch,
"branch_slug": slug.Make(pull.Branch),
"head_sha": pull.HeadSHA,
"head_short_sha": pull.HeadSHA[:shortSHALength],
}
// PR lables will only be supported for Go Template appsets, since fasttemplate will be deprecated.
if applicationSetInfo != nil && applicationSetInfo.Spec.GoTemplate {
paramMap["labels"] = pull.Labels
}
params = append(params, paramMap)
})
}
return params, nil
}

View File

@@ -17,10 +17,9 @@ import (
func TestPullRequestGithubGenerateParams(t *testing.T) {
ctx := context.Background()
cases := []struct {
selectFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
expected []map[string]interface{}
expectedErr error
applicationSet argoprojiov1alpha1.ApplicationSet
selectFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
expected []map[string]interface{}
expectedErr error
}{
{
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
@@ -108,71 +107,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
expected: nil,
expectedErr: fmt.Errorf("error listing repos: fake error"),
},
{
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
return pullrequest.NewFakeService(
ctx,
[]*pullrequest.PullRequest{
&pullrequest.PullRequest{
Number: 1,
Branch: "branch1",
HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958",
Labels: []string{"preview"},
},
},
nil,
)
},
expected: []map[string]interface{}{
{
"number": "1",
"branch": "branch1",
"branch_slug": "branch1",
"head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958",
"head_short_sha": "089d92cb",
"labels": []string{"preview"},
},
},
expectedErr: nil,
applicationSet: argoprojiov1alpha1.ApplicationSet{
Spec: argoprojiov1alpha1.ApplicationSetSpec{
// Application set is using Go Template.
GoTemplate: true,
},
},
},
{
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
return pullrequest.NewFakeService(
ctx,
[]*pullrequest.PullRequest{
&pullrequest.PullRequest{
Number: 1,
Branch: "branch1",
HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958",
Labels: []string{"preview"},
},
},
nil,
)
},
expected: []map[string]interface{}{
{
"number": "1",
"branch": "branch1",
"branch_slug": "branch1",
"head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958",
"head_short_sha": "089d92cb",
},
},
expectedErr: nil,
applicationSet: argoprojiov1alpha1.ApplicationSet{
Spec: argoprojiov1alpha1.ApplicationSetSpec{
// Application set is using fasttemplate.
GoTemplate: false,
},
},
},
}
for _, c := range cases {
@@ -183,7 +117,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{},
}
got, gotErr := gen.GenerateParams(&generatorConfig, &c.applicationSet)
got, gotErr := gen.GenerateParams(&generatorConfig, nil)
assert.Equal(t, c.expectedErr, gotErr)
assert.ElementsMatch(t, c.expected, got)
}

View File

@@ -122,15 +122,6 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
if err != nil {
return nil, fmt.Errorf("error initializing Azure Devops service: %v", err)
}
} else if providerConfig.Bitbucket != nil {
appPassword, err := g.getSecretRef(ctx, providerConfig.Bitbucket.AppPasswordRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Bitbucket cloud appPassword: %v", err)
}
provider, err = scm_provider.NewBitBucketCloudProvider(ctx, providerConfig.Bitbucket.Owner, providerConfig.Bitbucket.User, appPassword, providerConfig.Bitbucket.AllBranches)
if err != nil {
return nil, fmt.Errorf("error initializing Bitbucket cloud service: %v", err)
}
} else {
return nil, fmt.Errorf("no SCM provider implementation configured")
}

View File

@@ -20,12 +20,10 @@ func Client(g github_app_auth.Authentication, url string) (*github.Client, error
url = g.EnterpriseBaseURL
}
var client *github.Client
httpClient := http.Client{Transport: rt}
if url == "" {
httpClient := http.Client{Transport: rt}
client = github.NewClient(&httpClient)
} else {
rt.BaseURL = url
httpClient := http.Client{Transport: rt}
client, err = github.NewEnterpriseClient(url, url, &httpClient)
if err != nil {
return nil, fmt.Errorf("failed to create github enterprise client: %w", err)

View File

@@ -69,7 +69,6 @@ func (b *BitbucketService) List(_ context.Context) ([]*PullRequest, error) {
Number: pull.ID,
Branch: pull.FromRef.DisplayID, // ID: refs/heads/main DisplayID: main
HeadSHA: pull.FromRef.LatestCommit, // This is not defined in the official docs, but works in practice
Labels: []string{}, // Not supported by library
})
}

View File

@@ -122,19 +122,16 @@ func TestListPullRequestPagination(t *testing.T) {
Number: 101,
Branch: "feature-101",
HeadSHA: "ab3cf2e4d1517c83e720d2585b9402dbef71f992",
Labels: []string{},
}, *pullRequests[0])
assert.Equal(t, PullRequest{
Number: 102,
Branch: "feature-102",
HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992",
Labels: []string{},
}, *pullRequests[1])
assert.Equal(t, PullRequest{
Number: 200,
Branch: "feature-200",
HeadSHA: "cb3cf2e4d1517c83e720d2585b9402dbef71f992",
Labels: []string{},
}, *pullRequests[2])
}
@@ -287,13 +284,11 @@ func TestListPullRequestBranchMatch(t *testing.T) {
Number: 101,
Branch: "feature-101",
HeadSHA: "ab3cf2e4d1517c83e720d2585b9402dbef71f992",
Labels: []string{},
}, *pullRequests[0])
assert.Equal(t, PullRequest{
Number: 102,
Branch: "feature-102",
HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992",
Labels: []string{},
}, *pullRequests[1])
regexp = `.*2$`
@@ -310,7 +305,6 @@ func TestListPullRequestBranchMatch(t *testing.T) {
Number: 102,
Branch: "feature-102",
HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992",
Labels: []string{},
}, *pullRequests[0])
regexp = `[\d{2}`

View File

@@ -57,17 +57,7 @@ func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) {
Number: int(pr.Index),
Branch: pr.Head.Ref,
HeadSHA: pr.Head.Sha,
Labels: getGiteaPRLabelNames(pr.Labels),
})
}
return list, nil
}
// Get the Gitea pull request label names.
func getGiteaPRLabelNames(giteaLabels []*gitea.Label) []string {
var labelNames []string
for _, giteaLabel := range giteaLabels {
labelNames = append(labelNames, giteaLabel.Name)
}
return labelNames
}

View File

@@ -8,7 +8,6 @@ import (
"net/http/httptest"
"testing"
"code.gitea.io/sdk/gitea"
"github.com/stretchr/testify/assert"
)
@@ -258,32 +257,3 @@ func TestGiteaList(t *testing.T) {
assert.Equal(t, prs[0].Branch, "test")
assert.Equal(t, prs[0].HeadSHA, "7bbaf62d92ddfafd9cc8b340c619abaec32bc09f")
}
func TestGetGiteaPRLabelNames(t *testing.T) {
Tests := []struct {
Name string
PullLabels []*gitea.Label
ExpectedResult []string
}{
{
Name: "PR has labels",
PullLabels: []*gitea.Label{
&gitea.Label{Name: "label1"},
&gitea.Label{Name: "label2"},
&gitea.Label{Name: "label3"},
},
ExpectedResult: []string{"label1", "label2", "label3"},
},
{
Name: "PR does not have labels",
PullLabels: []*gitea.Label{},
ExpectedResult: nil,
},
}
for _, test := range Tests {
t.Run(test.Name, func(t *testing.T) {
labels := getGiteaPRLabelNames(test.PullLabels)
assert.Equal(t, test.ExpectedResult, labels)
})
}
}

View File

@@ -68,7 +68,6 @@ func (g *GithubService) List(ctx context.Context) ([]*PullRequest, error) {
Number: *pull.Number,
Branch: *pull.Head.Ref,
HeadSHA: *pull.Head.SHA,
Labels: getGithubPRLabelNames(pull.Labels),
})
}
if resp.NextPage == 0 {
@@ -98,12 +97,3 @@ func containLabels(expectedLabels []string, gotLabels []*github.Label) bool {
}
return true
}
// Get the Github pull request label names.
func getGithubPRLabelNames(gitHubLabels []*github.Label) []string {
var labelNames []string
for _, gitHubLabel := range gitHubLabels {
labelNames = append(labelNames, *gitHubLabel.Name)
}
return labelNames
}

View File

@@ -4,7 +4,6 @@ import (
"testing"
"github.com/google/go-github/v35/github"
"github.com/stretchr/testify/assert"
)
func toPtr(s string) *string {
@@ -58,32 +57,3 @@ func TestContainLabels(t *testing.T) {
})
}
}
func TestGetGitHubPRLabelNames(t *testing.T) {
Tests := []struct {
Name string
PullLabels []*github.Label
ExpectedResult []string
}{
{
Name: "PR has labels",
PullLabels: []*github.Label{
&github.Label{Name: toPtr("label1")},
&github.Label{Name: toPtr("label2")},
&github.Label{Name: toPtr("label3")},
},
ExpectedResult: []string{"label1", "label2", "label3"},
},
{
Name: "PR does not have labels",
PullLabels: []*github.Label{},
ExpectedResult: nil,
},
}
for _, test := range Tests {
t.Run(test.Name, func(t *testing.T) {
labels := getGithubPRLabelNames(test.PullLabels)
assert.Equal(t, test.ExpectedResult, labels)
})
}
}

View File

@@ -72,7 +72,6 @@ func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) {
Number: mr.IID,
Branch: mr.SourceBranch,
HeadSHA: mr.SHA,
Labels: mr.Labels,
})
}
if resp.NextPage == 0 {

View File

@@ -12,8 +12,6 @@ type PullRequest struct {
Branch string
// HeadSHA is the SHA of the HEAD from which the pull request originated.
HeadSHA string
// Labels of the pull request.
Labels []string
}
type PullRequestService interface {

View File

@@ -11,7 +11,6 @@ var Policies = map[string]Policy{
"sync": &SyncPolicy{},
"create-only": &CreateOnlyPolicy{},
"create-update": &CreateUpdatePolicy{},
"create-delete": &CreateDeletePolicy{},
}
type SyncPolicy struct{}
@@ -43,13 +42,3 @@ func (p *CreateOnlyPolicy) Update() bool {
func (p *CreateOnlyPolicy) Delete() bool {
return false
}
type CreateDeletePolicy struct{}
func (p *CreateDeletePolicy) Update() bool {
return false
}
func (p *CreateDeletePolicy) Delete() bool {
return true
}

View File

@@ -41,7 +41,7 @@ func TestRenderTemplateParams(t *testing.T) {
Namespace: "default",
},
Spec: argoappsv1.ApplicationSpec{
Source: &argoappsv1.ApplicationSource{
Source: argoappsv1.ApplicationSource{
Path: "",
RepoURL: "",
TargetRevision: "",
@@ -220,7 +220,7 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) {
Namespace: "default",
},
Spec: argoappsv1.ApplicationSpec{
Source: &argoappsv1.ApplicationSource{
Source: argoappsv1.ApplicationSource{
Path: "",
RepoURL: "",
TargetRevision: "",
@@ -511,7 +511,7 @@ func TestRenderTemplateParamsFinalizers(t *testing.T) {
emptyApplication := &argoappsv1.Application{
Spec: argoappsv1.ApplicationSpec{
Source: &argoappsv1.ApplicationSource{
Source: argoappsv1.ApplicationSource{
Path: "",
RepoURL: "",
TargetRevision: "",

4
argocd-cosign.pub Normal file
View File

@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEesHEB7vX5Y2RxXypjMy1nI1z7iRG
JI9/gt/sYqzpsa65aaNP4npM43DDxoIy/MQBo9s/mxGxmA+8UXeDpVC9vw==
-----END PUBLIC KEY-----

View File

@@ -735,42 +735,6 @@
}
}
},
"/api/v1/applications/{name}/links": {
"get": {
"tags": [
"ApplicationService"
],
"summary": "ListLinks returns the list of all application deep links",
"operationId": "ApplicationService_ListLinks",
"parameters": [
{
"type": "string",
"name": "name",
"in": "path",
"required": true
},
{
"type": "string",
"name": "namespace",
"in": "query"
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/applicationLinksResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
}
}
},
"/api/v1/applications/{name}/logs": {
"get": {
"tags": [
@@ -1421,67 +1385,6 @@
}
}
},
"/api/v1/applications/{name}/resource/links": {
"get": {
"tags": [
"ApplicationService"
],
"summary": "ListResourceLinks returns the list of all resource deep links",
"operationId": "ApplicationService_ListResourceLinks",
"parameters": [
{
"type": "string",
"name": "name",
"in": "path",
"required": true
},
{
"type": "string",
"name": "namespace",
"in": "query"
},
{
"type": "string",
"name": "resourceName",
"in": "query"
},
{
"type": "string",
"name": "version",
"in": "query"
},
{
"type": "string",
"name": "group",
"in": "query"
},
{
"type": "string",
"name": "kind",
"in": "query"
},
{
"type": "string",
"name": "appNamespace",
"in": "query"
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/applicationLinksResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
}
}
},
"/api/v1/applications/{name}/revisions/{revision}/metadata": {
"get": {
"tags": [
@@ -2657,37 +2560,6 @@
}
}
},
"/api/v1/projects/{name}/links": {
"get": {
"tags": [
"ProjectService"
],
"summary": "ListLinks returns all deep links for the particular project",
"operationId": "ProjectService_ListLinks",
"parameters": [
{
"type": "string",
"name": "name",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/applicationLinksResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
}
}
},
"/api/v1/projects/{name}/syncwindows": {
"get": {
"tags": [
@@ -3424,18 +3296,6 @@
"description": "Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity.",
"name": "project",
"in": "query"
},
{
"type": "string",
"description": "Google Cloud Platform service account key.",
"name": "gcpServiceAccountKey",
"in": "query"
},
{
"type": "boolean",
"description": "Whether to force HTTP basic auth.",
"name": "forceHttpBasicAuth",
"in": "query"
}
],
"responses": {
@@ -3594,29 +3454,6 @@
}
}
},
"/api/v1/settings/plugins": {
"get": {
"tags": [
"SettingsService"
],
"summary": "Get returns Argo CD plugins",
"operationId": "SettingsService_GetPlugins",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/clusterSettingsPluginsResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
}
}
},
"/api/v1/stream/applications": {
"get": {
"tags": [
@@ -4063,34 +3900,6 @@
}
}
},
"applicationLinkInfo": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"iconClass": {
"type": "string"
},
"title": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"applicationLinksResponse": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/applicationLinkInfo"
}
}
}
},
"applicationLogEntry": {
"type": "object",
"properties": {
@@ -4371,17 +4180,6 @@
}
}
},
"clusterSettingsPluginsResponse": {
"type": "object",
"properties": {
"plugins": {
"type": "array",
"items": {
"$ref": "#/definitions/clusterPlugin"
}
}
}
},
"gpgkeyGnuPGPublicKeyCreateResponse": {
"type": "object",
"title": "Response to a public key creation request",
@@ -4402,24 +4200,6 @@
"type": "object",
"title": "Generic (empty) response for GPG public key CRUD requests"
},
"intstrIntOrString": {
"description": "+protobuf=true\n+protobuf.options.(gogoproto.goproto_stringer)=false\n+k8s:openapi-gen=true",
"type": "object",
"title": "IntOrString is a type that can hold an int32 or a string. When used in\nJSON or YAML marshalling and unmarshalling, it produces or consumes the\ninner type. This allows you to have, for example, a JSON field that can\naccept a name or number.\nTODO: Rename to Int32OrString",
"properties": {
"intVal": {
"type": "integer",
"format": "int32"
},
"strVal": {
"type": "string"
},
"type": {
"type": "string",
"format": "int64"
}
}
},
"notificationService": {
"type": "object",
"properties": {
@@ -5658,23 +5438,6 @@
}
}
},
"v1alpha1ApplicationMatchExpression": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"operator": {
"type": "string"
},
"values": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"v1alpha1ApplicationSet": {
"type": "object",
"title": "ApplicationSet is a set of Application resources\n+genclient\n+genclient:noStatus\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+kubebuilder:resource:path=applicationsets,shortName=appset;appsets\n+kubebuilder:subresource:status",
@@ -5690,31 +5453,6 @@
}
}
},
"v1alpha1ApplicationSetApplicationStatus": {
"type": "object",
"title": "ApplicationSetApplicationStatus contains details about each Application managed by the ApplicationSet",
"properties": {
"application": {
"type": "string",
"title": "Application contains the name of the Application resource"
},
"lastTransitionTime": {
"$ref": "#/definitions/v1Time"
},
"message": {
"type": "string",
"title": "Message contains human-readable message indicating details about the status"
},
"status": {
"type": "string",
"title": "Status contains the AppSet's perceived status of the managed Application resource: (Waiting, Pending, Progressing, Healthy)"
},
"step": {
"type": "string",
"title": "Step tracks which step this Application should be updated in"
}
}
},
"v1alpha1ApplicationSetCondition": {
"type": "object",
"title": "ApplicationSetCondition contains details about an applicationset condition, which is usally an error or warning",
@@ -5821,31 +5559,6 @@
}
}
},
"v1alpha1ApplicationSetRolloutStep": {
"type": "object",
"properties": {
"matchExpressions": {
"type": "array",
"items": {
"$ref": "#/definitions/v1alpha1ApplicationMatchExpression"
}
},
"maxUpdate": {
"$ref": "#/definitions/intstrIntOrString"
}
}
},
"v1alpha1ApplicationSetRolloutStrategy": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"$ref": "#/definitions/v1alpha1ApplicationSetRolloutStep"
}
}
}
},
"v1alpha1ApplicationSetSpec": {
"description": "ApplicationSetSpec represents a class of application set state.",
"type": "object",
@@ -5859,9 +5572,6 @@
"goTemplate": {
"type": "boolean"
},
"strategy": {
"$ref": "#/definitions/v1alpha1ApplicationSetStrategy"
},
"syncPolicy": {
"$ref": "#/definitions/v1alpha1ApplicationSetSyncPolicy"
},
@@ -5874,12 +5584,6 @@
"type": "object",
"title": "ApplicationSetStatus defines the observed state of ApplicationSet",
"properties": {
"applicationStatus": {
"type": "array",
"items": {
"$ref": "#/definitions/v1alpha1ApplicationSetApplicationStatus"
}
},
"conditions": {
"type": "array",
"title": "INSERT ADDITIONAL STATUS FIELD - define observed state of cluster\nImportant: Run \"make\" to regenerate code after modifying this file",
@@ -5889,18 +5593,6 @@
}
}
},
"v1alpha1ApplicationSetStrategy": {
"description": "ApplicationSetStrategy configures how generated Applications are updated in sequence.",
"type": "object",
"properties": {
"rollingSync": {
"$ref": "#/definitions/v1alpha1ApplicationSetRolloutStrategy"
},
"type": {
"type": "string"
}
}
},
"v1alpha1ApplicationSetSyncPolicy": {
"description": "ApplicationSetSyncPolicy configures how generated Applications will relate to their\nApplicationSet.",
"type": "object",
@@ -5977,10 +5669,6 @@
"plugin": {
"$ref": "#/definitions/v1alpha1ApplicationSourcePlugin"
},
"ref": {
"description": "Ref is reference to another source within sources field. This field will not be used if used with a `source` tag.",
"type": "string"
},
"repoURL": {
"type": "string",
"title": "RepoURL is the URL to the repository (Git or Helm) that contains the application manifests"
@@ -6218,13 +5906,6 @@
"source": {
"$ref": "#/definitions/v1alpha1ApplicationSource"
},
"sources": {
"type": "array",
"title": "Sources is a reference to the location of the application's manifests or chart",
"items": {
"$ref": "#/definitions/v1alpha1ApplicationSource"
}
},
"syncPolicy": {
"$ref": "#/definitions/v1alpha1SyncPolicy"
}
@@ -6275,13 +5956,6 @@
"type": "string",
"title": "SourceType specifies the type of this application"
},
"sourceTypes": {
"type": "array",
"title": "SourceTypes specifies the type of the sources included in the application",
"items": {
"type": "string"
}
},
"summary": {
"$ref": "#/definitions/v1alpha1ApplicationSummary"
},
@@ -6579,13 +6253,6 @@
},
"source": {
"$ref": "#/definitions/v1alpha1ApplicationSource"
},
"sources": {
"type": "array",
"title": "Sources is a reference to the application's multiple sources used for comparison",
"items": {
"$ref": "#/definitions/v1alpha1ApplicationSource"
}
}
}
},
@@ -6720,9 +6387,6 @@
"$ref": "#/definitions/v1alpha1GitFileGeneratorItem"
}
},
"pathParamPrefix": {
"type": "string"
},
"repoURL": {
"type": "string"
},
@@ -7348,14 +7012,6 @@
"type": "boolean",
"title": "EnableOCI specifies whether helm-oci support should be enabled for this repo"
},
"forceHttpBasicAuth": {
"type": "boolean",
"title": "ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections"
},
"gcpServiceAccountKey": {
"type": "string",
"title": "GCPServiceAccountKey specifies the service account key in JSON format to be used for getting credentials to Google Cloud Source repos"
},
"githubAppEnterpriseBaseUrl": {
"type": "string",
"title": "GithubAppEnterpriseBaseURL specifies the GitHub API URL for GitHub app authentication. If empty will default to https://api.github.com"
@@ -7378,10 +7034,6 @@
"type": "string",
"title": "Password for authenticating at the repo server"
},
"proxy": {
"type": "string",
"title": "Proxy specifies the HTTP/HTTPS proxy used to access repos at the repo server"
},
"sshPrivateKey": {
"type": "string",
"title": "SSHPrivateKey contains the private key data for authenticating at the repo server using SSH (only Git repos)"
@@ -7438,14 +7090,6 @@
"type": "boolean",
"title": "EnableOCI specifies whether helm-oci support should be enabled for this repo"
},
"forceHttpBasicAuth": {
"type": "boolean",
"title": "ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections"
},
"gcpServiceAccountKey": {
"type": "string",
"title": "GCPServiceAccountKey specifies the service account key in JSON format to be used for getting credentials to Google Cloud Source repos"
},
"githubAppEnterpriseBaseUrl": {
"type": "string",
"title": "GithubAppEnterpriseBaseURL specifies the base URL of GitHub Enterprise installation. If empty will default to https://api.github.com"
@@ -7936,22 +7580,8 @@
"type": "string",
"title": "Revision holds the revision the sync was performed against"
},
"revisions": {
"type": "array",
"title": "Revisions holds the revision of each source in sources field the sync was performed against",
"items": {
"type": "string"
}
},
"source": {
"$ref": "#/definitions/v1alpha1ApplicationSource"
},
"sources": {
"type": "array",
"title": "Sources is a reference to the application sources used for the sync operation",
"items": {
"$ref": "#/definitions/v1alpha1ApplicationSource"
}
}
}
},
@@ -8252,23 +7882,9 @@
"description": "Revision is the revision (Git) or chart version (Helm) which to sync the application to\nIf omitted, will use the revision specified in app spec.",
"type": "string"
},
"revisions": {
"description": "Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to\nIf omitted, will use the revision specified in app spec.",
"type": "array",
"items": {
"type": "string"
}
},
"source": {
"$ref": "#/definitions/v1alpha1ApplicationSource"
},
"sources": {
"type": "array",
"title": "Sources overrides the source definition set in the application.\nThis is typically set in a Rollback operation and is nil during a Sync operation",
"items": {
"$ref": "#/definitions/v1alpha1ApplicationSource"
}
},
"syncOptions": {
"type": "array",
"title": "SyncOptions provide per-sync sync-options, e.g. Validate=false",
@@ -8314,22 +7930,8 @@
"type": "string",
"title": "Revision holds the revision this sync operation was performed to"
},
"revisions": {
"type": "array",
"title": "Revisions holds the revision this sync operation was performed for respective indexed source in sources field",
"items": {
"type": "string"
}
},
"source": {
"$ref": "#/definitions/v1alpha1ApplicationSource"
},
"sources": {
"type": "array",
"title": "Source records the application source information of the sync, used for comparing auto-sync",
"items": {
"$ref": "#/definitions/v1alpha1ApplicationSource"
}
}
}
},
@@ -8384,13 +7986,6 @@
"type": "string",
"title": "Revision contains information about the revision the comparison has been performed to"
},
"revisions": {
"type": "array",
"title": "Revisions contains information about the revisions of multiple sources the comparison has been performed to",
"items": {
"type": "string"
}
},
"status": {
"type": "string",
"title": "Status is the sync state of the comparison"

View File

@@ -46,17 +46,16 @@ func getSubmoduleEnabled() bool {
func NewCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
metricsAddr string
probeBindAddr string
webhookAddr string
enableLeaderElection bool
namespace string
argocdRepoServer string
policy string
debugLog bool
dryRun bool
enableProgressiveSyncs bool
clientConfig clientcmd.ClientConfig
metricsAddr string
probeBindAddr string
webhookAddr string
enableLeaderElection bool
namespace string
argocdRepoServer string
policy string
debugLog bool
dryRun bool
)
scheme := runtime.NewScheme()
_ = clientgoscheme.AddToScheme(scheme)
@@ -90,7 +89,7 @@ func NewCommand() *cobra.Command {
policyObj, exists := utils.Policies[policy]
if !exists {
log.Info("Policy value can be: sync, create-only, create-update, create-delete")
log.Info("Policy value can be: sync, create-only, create-update")
os.Exit(1)
}
@@ -169,16 +168,15 @@ func NewCommand() *cobra.Command {
go func() { errors.CheckError(askPassServer.Run(askpass.SocketPath)) }()
if err = (&controllers.ApplicationSetReconciler{
Generators: topLevelGenerators,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("applicationset-controller"),
Renderer: &utils.Render{},
Policy: policyObj,
ArgoAppClientset: appSetConfig,
KubeClientset: k8sClient,
ArgoDB: argoCDDB,
EnableProgressiveSyncs: enableProgressiveSyncs,
Generators: topLevelGenerators,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("applicationset-controller"),
Renderer: &utils.Render{},
Policy: policyObj,
ArgoAppClientset: appSetConfig,
KubeClientset: k8sClient,
ArgoDB: argoCDDB,
}).SetupWithManager(mgr); err != nil {
log.Error(err, "unable to create controller", "controller", "ApplicationSet")
os.Exit(1)
@@ -202,12 +200,11 @@ func NewCommand() *cobra.Command {
"Enabling this will ensure there is only one active controller manager.")
command.Flags().StringVar(&namespace, "namespace", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACE", ""), "Argo CD repo namespace (default: argocd)")
command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER", common.DefaultRepoServerAddr), "Argo CD repo server address")
command.Flags().StringVar(&policy, "policy", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_POLICY", "sync"), "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion), 'create-delete' (no update)")
command.Flags().StringVar(&policy, "policy", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_POLICY", "sync"), "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion)")
command.Flags().BoolVar(&debugLog, "debug", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG", false), "Print debug logs. Takes precedence over loglevel")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
command.Flags().BoolVar(&dryRun, "dry-run", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DRY_RUN", false), "Enable dry run mode")
command.Flags().BoolVar(&enableProgressiveSyncs, "enable-progressive-syncs", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS", false), "Enable use of the experimental progressive syncs feature.")
return &command
}

View File

@@ -71,7 +71,6 @@ func NewCommand() *cobra.Command {
dexServerStrictTLS bool
staticAssetsDir string
applicationNamespaces []string
enableProxyExtension bool
)
var command = &cobra.Command{
Use: cliName,
@@ -185,7 +184,6 @@ func NewCommand() *cobra.Command {
RedisClient: redisClient,
StaticAssetsDir: staticAssetsDir,
ApplicationNamespaces: applicationNamespaces,
EnableProxyExtension: enableProxyExtension,
}
stats.RegisterStackDumper()
@@ -237,7 +235,6 @@ func NewCommand() *cobra.Command {
command.Flags().BoolVar(&dexServerPlaintext, "dex-server-plaintext", env.ParseBoolFromEnv("ARGOCD_SERVER_DEX_SERVER_PLAINTEXT", false), "Use a plaintext client (non-TLS) to connect to dex server")
command.Flags().BoolVar(&dexServerStrictTLS, "dex-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_SERVER_DEX_SERVER_STRICT_TLS", false), "Perform strict validation of TLS certificates when connecting to dex server")
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces where application resources can be managed in")
command.Flags().BoolVar(&enableProxyExtension, "enable-proxy-extension", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_PROXY_EXTENSION", false), "Enable Proxy Extension feature")
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command)
cacheSrc = servercache.AddCacheFlagsToCmd(command, func(client *redis.Client) {
redisClient = client

View File

@@ -401,12 +401,7 @@ func reconcileApplications(
return nil, err
}
sources := make([]v1alpha1.ApplicationSource, 0)
revisions := make([]string, 0)
sources = append(sources, app.Spec.GetSource())
revisions = append(revisions, app.Spec.GetSource().TargetRevision)
res := appStateManager.CompareAppState(&app, proj, revisions, sources, false, false, nil, false)
res := appStateManager.CompareAppState(&app, proj, app.Spec.Source.TargetRevision, app.Spec.Source, false, false, nil)
items = append(items, appReconcileResult{
Name: app.Name,
Conditions: app.Status.Conditions,

View File

@@ -80,7 +80,6 @@ func TestGetReconcileResults_Refresh(t *testing.T) {
Namespace: "default",
},
Spec: v1alpha1.ApplicationSpec{
Source: &v1alpha1.ApplicationSource{},
Project: "default",
Destination: v1alpha1.ApplicationDestination{
Server: v1alpha1.KubernetesInternalAPIServerAddr,

View File

@@ -206,7 +206,7 @@ var validatorsByGroup = map[string]settingValidator{
}
ssoProvider = "Dex"
} else if general.OIDCConfigRAW != "" {
if err := settings.ValidateOIDCConfig(general.OIDCConfigRAW); err != nil {
if _, err := settings.UnmarshalOIDCConfig(general.OIDCConfigRAW); err != nil {
return "", fmt.Errorf("invalid oidc.config: %v", err)
}
ssoProvider = "OIDC"

View File

@@ -33,6 +33,7 @@ import (
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
argocommon "github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/controller"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
@@ -149,6 +150,9 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
c.HelpFunc()(c, args)
os.Exit(1)
}
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
if appNamespace != "" {
app.Namespace = appNamespace
}
@@ -290,6 +294,10 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
})
errors.CheckError(err)
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
pConn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie()
defer argoio.Close(pConn)
proj, err := projIf.Get(ctx, &projectpkg.ProjectQuery{Name: app.Spec.Project})
@@ -432,16 +440,15 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
}
func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *argoappv1.SyncWindows) {
source := app.Spec.GetSource()
fmt.Printf(printOpFmtStr, "Name:", app.QualifiedName())
fmt.Printf(printOpFmtStr, "Project:", app.Spec.GetProject())
fmt.Printf(printOpFmtStr, "Server:", getServer(app))
fmt.Printf(printOpFmtStr, "Namespace:", app.Spec.Destination.Namespace)
fmt.Printf(printOpFmtStr, "URL:", appURL)
fmt.Printf(printOpFmtStr, "Repo:", source.RepoURL)
fmt.Printf(printOpFmtStr, "Target:", source.TargetRevision)
fmt.Printf(printOpFmtStr, "Path:", source.Path)
printAppSourceDetails(&source)
fmt.Printf(printOpFmtStr, "Repo:", app.Spec.Source.RepoURL)
fmt.Printf(printOpFmtStr, "Target:", app.Spec.Source.TargetRevision)
fmt.Printf(printOpFmtStr, "Path:", app.Spec.Source.Path)
printAppSourceDetails(&app.Spec.Source)
var wds []string
var status string
var allow, deny, inactiveAllows bool
@@ -495,11 +502,11 @@ func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *ar
syncStatusStr := string(app.Status.Sync.Status)
switch app.Status.Sync.Status {
case argoappv1.SyncStatusCodeSynced:
syncStatusStr += fmt.Sprintf(" to %s", app.Spec.GetSource().TargetRevision)
syncStatusStr += fmt.Sprintf(" to %s", app.Spec.Source.TargetRevision)
case argoappv1.SyncStatusCodeOutOfSync:
syncStatusStr += fmt.Sprintf(" from %s", app.Spec.GetSource().TargetRevision)
syncStatusStr += fmt.Sprintf(" from %s", app.Spec.Source.TargetRevision)
}
if !git.IsCommitSHA(app.Spec.GetSource().TargetRevision) && !git.IsTruncatedCommitSHA(app.Spec.GetSource().TargetRevision) && len(app.Status.Sync.Revision) > 7 {
if !git.IsCommitSHA(app.Spec.Source.TargetRevision) && !git.IsTruncatedCommitSHA(app.Spec.Source.TargetRevision) && len(app.Status.Sync.Revision) > 7 {
syncStatusStr += fmt.Sprintf(" (%s)", app.Status.Sync.Revision[0:7])
}
fmt.Printf(printOpFmtStr, "Sync Status:", syncStatusStr)
@@ -568,8 +575,8 @@ func truncateString(str string, num int) string {
// printParams prints parameters and overrides
func printParams(app *argoappv1.Application) {
if app.Spec.GetSource().Helm != nil {
printHelmParams(app.Spec.GetSource().Helm)
if app.Spec.Source.Helm != nil {
printHelmParams(app.Spec.Source.Helm)
}
}
@@ -617,6 +624,10 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName, AppNamespace: &appNs})
errors.CheckError(err)
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
visited := cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts)
if visited == 0 {
log.Error("Please set at least one option to update")
@@ -681,8 +692,11 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName, AppNamespace: &appNs})
errors.CheckError(err)
source := app.Spec.GetSource()
updated, nothingToUnset := unset(&source, opts)
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
updated, nothingToUnset := unset(&app.Spec.Source, opts)
if nothingToUnset {
c.HelpFunc()(c, args)
os.Exit(1)
@@ -828,19 +842,18 @@ func getLocalObjects(ctx context.Context, app *argoappv1.Application, local, loc
func getLocalObjectsString(ctx context.Context, app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions,
configManagementPlugins []*argoappv1.ConfigManagementPlugin, trackingMethod string) []string {
source := app.Spec.GetSource()
res, err := repository.GenerateManifests(ctx, local, localRepoRoot, source.TargetRevision, &repoapiclient.ManifestRequest{
Repo: &argoappv1.Repository{Repo: source.RepoURL},
res, err := repository.GenerateManifests(ctx, local, localRepoRoot, app.Spec.Source.TargetRevision, &repoapiclient.ManifestRequest{
Repo: &argoappv1.Repository{Repo: app.Spec.Source.RepoURL},
AppLabelKey: appLabelKey,
AppName: app.Name,
Namespace: app.Spec.Destination.Namespace,
ApplicationSource: &source,
ApplicationSource: &app.Spec.Source,
KustomizeOptions: kustomizeOptions,
KubeVersion: kubeVersion,
ApiVersions: apiVersions,
Plugins: configManagementPlugins,
TrackingMethod: trackingMethod,
}, true, &git.NoopCredsStore{}, resource.MustParse("0"), nil)
}, true, &git.NoopCredsStore{}, resource.MustParse("0"))
errors.CheckError(err)
return res.Manifests
@@ -917,6 +930,10 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
})
errors.CheckError(err)
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName, AppNamespace: &appNs})
errors.CheckError(err)
conn, settingsIf := clientset.NewSettingsClientOrDie()
@@ -947,7 +964,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
diffOption.serversideRes = res
} else {
fmt.Fprintf(os.Stderr, "Warning: local diff without --server-side-generate is deprecated and does not work with plugins. Server-side generation will be the default in v2.7.")
fmt.Fprintf(os.Stderr, "Warning: local diff without --server-side-generate is deprecated and does not work with plugins. Server-side generation will be the default in v2.6.")
conn, clusterIf := clientset.NewClusterClientOrDie()
defer argoio.Close(conn)
cluster, err := clusterIf.Get(ctx, &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server})
@@ -1230,7 +1247,7 @@ func printApplicationTable(apps []argoappv1.Application, output *string) {
formatConditionsSummary(app),
}
if *output == "wide" {
vals = append(vals, app.Spec.GetSource().RepoURL, app.Spec.GetSource().Path, app.Spec.GetSource().TargetRevision)
vals = append(vals, app.Spec.Source.RepoURL, app.Spec.Source.Path, app.Spec.Source.TargetRevision)
}
_, _ = fmt.Fprintf(w, fmtStr, vals...)
}
@@ -1281,6 +1298,17 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
if cluster != "" {
appList = argo.FilterByCluster(appList, cluster)
}
var appsWithDeprecatedPlugins []string
for _, app := range appList {
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
appsWithDeprecatedPlugins = append(appsWithDeprecatedPlugins, app.Name)
}
}
if len(appsWithDeprecatedPlugins) > 0 {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
log.Warnf("The following Applications use deprecated plugins: %s", strings.Join(appsWithDeprecatedPlugins, ", "))
}
switch output {
case "yaml", "json":
@@ -1597,19 +1625,17 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
var localObjsStrings []string
diffOption := &DifferenceOption{}
app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{
Name: &appName,
AppNamespace: &appNs,
})
errors.CheckError(err)
if app.Spec.HasMultipleSources() {
log.Fatal("argocd cli does not work on multi-source app")
return
}
if local != "" {
app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{
Name: &appName,
AppNamespace: &appNs,
})
errors.CheckError(err)
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
if app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.Automated != nil && !dryRun {
log.Fatal("Cannot use local sync when Automatic Sync Policy is enabled except with --dry-run")
}
@@ -1683,6 +1709,16 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
}
}
if diffChanges {
app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{
Name: &appName,
AppNamespace: &appNs,
})
errors.CheckError(err)
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{
ApplicationName: &appName,
AppNamespace: &appNs,
@@ -2051,9 +2087,8 @@ func setParameterOverrides(app *argoappv1.Application, parameters []string) {
if len(parameters) == 0 {
return
}
source := app.Spec.GetSource()
var sourceType argoappv1.ApplicationSourceType
if st, _ := source.ExplicitType(); st != nil {
if st, _ := app.Spec.Source.ExplicitType(); st != nil {
sourceType = *st
} else if app.Status.SourceType != "" {
sourceType = app.Status.SourceType
@@ -2065,8 +2100,8 @@ func setParameterOverrides(app *argoappv1.Application, parameters []string) {
switch sourceType {
case argoappv1.ApplicationSourceTypeHelm:
if source.Helm == nil {
source.Helm = &argoappv1.ApplicationSourceHelm{}
if app.Spec.Source.Helm == nil {
app.Spec.Source.Helm = &argoappv1.ApplicationSourceHelm{}
}
for _, p := range parameters {
newParam, err := argoappv1.NewHelmParameter(p, false)
@@ -2074,7 +2109,7 @@ func setParameterOverrides(app *argoappv1.Application, parameters []string) {
log.Error(err)
continue
}
source.Helm.AddParameter(*newParam)
app.Spec.Source.Helm.AddParameter(*newParam)
}
default:
log.Fatalf("Parameters can only be set against Helm applications")
@@ -2126,6 +2161,10 @@ func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra
})
errors.CheckError(err)
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
if output == "id" {
printApplicationHistoryIds(app.Status.History)
} else {
@@ -2186,6 +2225,10 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr
})
errors.CheckError(err)
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
depInfo, err := findRevisionHistory(app, int64(depID))
errors.CheckError(err)
@@ -2269,6 +2312,10 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob
app, err := appIf.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName})
errors.CheckError(err)
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
settingsConn, settingsIf := clientset.NewSettingsClientOrDie()
defer argoio.Close(settingsConn)
argoSettings, err := settingsIf.Get(context.Background(), &settingspkg.SettingsQuery{})
@@ -2368,6 +2415,10 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
})
errors.CheckError(err)
if app.Spec.Source.Plugin != nil && app.Spec.Source.Plugin.Name != "" {
log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning)
}
appData, err := json.Marshal(app.Spec)
errors.CheckError(err)
appData, err = yaml.JSONToYAML(appData)

View File

@@ -498,7 +498,7 @@ func TestPrintAppSummaryTable(t *testing.T) {
},
Project: "default",
Destination: v1alpha1.ApplicationDestination{Server: "local", Namespace: "argocd"},
Source: &v1alpha1.ApplicationSource{
Source: v1alpha1.ApplicationSource{
RepoURL: "test",
TargetRevision: "master",
Path: "/test",
@@ -604,7 +604,7 @@ func TestPrintParams(t *testing.T) {
output, _ := captureOutput(func() error {
app := &v1alpha1.Application{
Spec: v1alpha1.ApplicationSpec{
Source: &v1alpha1.ApplicationSource{
Source: v1alpha1.ApplicationSource{
Helm: &v1alpha1.ApplicationSourceHelm{
Parameters: []v1alpha1.HelmParameter{
{
@@ -985,7 +985,7 @@ func TestPrintApplicationTableWide(t *testing.T) {
Server: "http://localhost:8080",
Namespace: "default",
},
Source: &v1alpha1.ApplicationSource{
Source: v1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps",
Path: "guestbook",
TargetRevision: "123",
@@ -1261,7 +1261,7 @@ func testApp(name, project string, labels map[string]string, annotations map[str
Finalizers: finalizers,
},
Spec: argoappv1.ApplicationSpec{
Source: &argoappv1.ApplicationSource{
Source: argoappv1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
},
Project: project,

View File

@@ -95,7 +95,7 @@ func NewApplicationSetGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.
fmt.Println()
}
if showParams {
printHelmParams(appSet.Spec.Template.Spec.GetSource().Helm)
printHelmParams(appSet.Spec.Template.Spec.Source.Helm)
}
default:
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
@@ -317,7 +317,7 @@ func printApplicationSetTable(apps []arogappsetv1.ApplicationSet, output *string
conditions,
}
if *output == "wide" {
vals = append(vals, app.Spec.Template.Spec.GetSource().RepoURL, app.Spec.Template.Spec.GetSource().Path, app.Spec.Template.Spec.GetSource().TargetRevision)
vals = append(vals, app.Spec.Template.Spec.Source.RepoURL, app.Spec.Template.Spec.Source.Path, app.Spec.Template.Spec.Source.TargetRevision)
}
_, _ = fmt.Fprintf(w, fmtStr, vals...)
}
@@ -333,15 +333,14 @@ func getServerForAppSet(appSet *arogappsetv1.ApplicationSet) string {
}
func printAppSetSummaryTable(appSet *arogappsetv1.ApplicationSet) {
source := appSet.Spec.Template.Spec.GetSource()
fmt.Printf(printOpFmtStr, "Name:", appSet.Name)
fmt.Printf(printOpFmtStr, "Project:", appSet.Spec.Template.Spec.GetProject())
fmt.Printf(printOpFmtStr, "Server:", getServerForAppSet(appSet))
fmt.Printf(printOpFmtStr, "Namespace:", appSet.Spec.Template.Spec.Destination.Namespace)
fmt.Printf(printOpFmtStr, "Repo:", source.RepoURL)
fmt.Printf(printOpFmtStr, "Target:", source.TargetRevision)
fmt.Printf(printOpFmtStr, "Path:", source.Path)
printAppSourceDetails(&source)
fmt.Printf(printOpFmtStr, "Repo:", appSet.Spec.Template.Spec.Source.RepoURL)
fmt.Printf(printOpFmtStr, "Target:", appSet.Spec.Template.Spec.Source.TargetRevision)
fmt.Printf(printOpFmtStr, "Path:", appSet.Spec.Template.Spec.Source.Path)
printAppSourceDetails(&appSet.Spec.Template.Spec.Source)
var syncPolicy string
if appSet.Spec.SyncPolicy != nil && appSet.Spec.Template.Spec.SyncPolicy.Automated != nil {

View File

@@ -202,18 +202,17 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions,
}
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr}), 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: *address,
RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr},
EnableProxyExtension: false,
EnableGZip: false,
Namespace: namespace,
ListenPort: *port,
AppClientset: appClientset,
DisableAuth: true,
RedisClient: redis.NewClient(&redis.Options{Addr: mr.Addr()}),
Cache: servercache.NewCache(appstateCache, 0, 0, 0),
KubeClientset: kubeClientset,
Insecure: true,
ListenHost: *address,
RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr},
})
srv.Init(ctx)

View File

@@ -70,9 +70,6 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
# Add a private Git repository on GitHub Enterprise via GitHub App
argocd repo add https://ghe.example.com/repos/repo --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 a private Git repository on Google Cloud Sources via GCP service account credentials
argocd repo add https://source.developers.google.com/p/my-google-cloud-project/r/my-repo --gcp-service-account-key-path service-account-key.json
`
var command = &cobra.Command{
@@ -138,17 +135,6 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
}
}
if repoOpts.GCPServiceAccountKeyPath != "" {
if git.IsHTTPSURL(repoOpts.Repo.Repo) {
gcpServiceAccountKey, err := os.ReadFile(repoOpts.GCPServiceAccountKeyPath)
errors.CheckError(err)
repoOpts.Repo.GCPServiceAccountKey = string(gcpServiceAccountKey)
} else {
err := fmt.Errorf("--gcp-service-account-key-path is only supported for HTTPS repositories")
errors.CheckError(err)
}
}
// Set repository connection properties only when creating repository, not
// when creating repository credentials.
// InsecureIgnoreHostKey is deprecated and only here for backwards compat
@@ -160,7 +146,6 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
repoOpts.Repo.GithubAppInstallationId = repoOpts.GithubAppInstallationId
repoOpts.Repo.GitHubAppEnterpriseBaseURL = repoOpts.GitHubAppEnterpriseBaseURL
repoOpts.Repo.Proxy = repoOpts.Proxy
repoOpts.Repo.ForceHttpBasicAuth = repoOpts.ForceHttpBasicAuth
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
errors.CheckError(fmt.Errorf("Must specify --name for repos of type 'helm'"))
@@ -199,8 +184,6 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
GithubAppEnterpriseBaseUrl: repoOpts.Repo.GitHubAppEnterpriseBaseURL,
Proxy: repoOpts.Proxy,
Project: repoOpts.Repo.Project,
GcpServiceAccountKey: repoOpts.Repo.GCPServiceAccountKey,
ForceHttpBasicAuth: repoOpts.Repo.ForceHttpBasicAuth,
}
_, err := repoIf.ValidateAccess(ctx, &repoAccessReq)
errors.CheckError(err)

View File

@@ -39,13 +39,12 @@ func NewRepoCredsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
// NewRepoCredsAddCommand returns a new instance of an `argocd repocreds add` command
func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
repo appsv1.RepoCreds
upsert bool
sshPrivateKeyPath string
tlsClientCertPath string
tlsClientCertKeyPath string
githubAppPrivateKeyPath string
gcpServiceAccountKeyPath string
repo appsv1.RepoCreds
upsert bool
sshPrivateKeyPath string
tlsClientCertPath string
tlsClientCertKeyPath string
githubAppPrivateKeyPath string
)
// For better readability and easier formatting
@@ -63,9 +62,6 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
# 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
# Add credentials with GCP credentials for all repositories under https://source.developers.google.com/p/my-google-cloud-project/r/
argocd repocreds add https://source.developers.google.com/p/my-google-cloud-project/r/ --gcp-service-account-key-path service-account-key.json
`
var command = &cobra.Command{
@@ -131,18 +127,6 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
}
}
// Specifying gcpServiceAccountKeyPath is only valid for HTTPS repositories
if gcpServiceAccountKeyPath != "" {
if git.IsHTTPSURL(repo.URL) {
gcpServiceAccountKey, err := os.ReadFile(gcpServiceAccountKeyPath)
errors.CheckError(err)
repo.GCPServiceAccountKey = string(gcpServiceAccountKey)
} else {
err := fmt.Errorf("--gcp-service-account-key-path is only supported for HTTPS repositories")
errors.CheckError(err)
}
}
conn, repoIf := headless.NewClientOrDie(clientOpts, c).NewRepoCredsClientOrDie()
defer io.Close(conn)
@@ -174,8 +158,6 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
command.Flags().BoolVar(&upsert, "upsert", false, "Override an existing repository with the same name even if the spec differs")
command.Flags().BoolVar(&repo.EnableOCI, "enable-oci", false, "Specifies whether helm-oci support should be enabled for this repo")
command.Flags().StringVar(&repo.Type, "type", common.DefaultRepoType, "type of the repository, \"git\" or \"helm\"")
command.Flags().StringVar(&gcpServiceAccountKeyPath, "gcp-service-account-key-path", "", "service account key for the Google Cloud Platform")
command.Flags().BoolVar(&repo.ForceHttpBasicAuth, "force-http-basic-auth", false, "whether to force basic auth when connecting via HTTP")
return command
}

View File

@@ -138,26 +138,22 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
}
flags.Visit(func(f *pflag.Flag) {
visited++
source := spec.GetSourcePtr()
if source == nil {
source = &argoappv1.ApplicationSource{}
}
switch f.Name {
case "repo":
source.RepoURL = appOpts.repoURL
spec.Source.RepoURL = appOpts.repoURL
case "path":
source.Path = appOpts.appPath
spec.Source.Path = appOpts.appPath
case "helm-chart":
source.Chart = appOpts.chart
spec.Source.Chart = appOpts.chart
case "revision":
source.TargetRevision = appOpts.revision
spec.Source.TargetRevision = appOpts.revision
case "revision-history-limit":
i := int64(appOpts.revisionHistoryLimit)
spec.RevisionHistoryLimit = &i
case "values":
setHelmOpt(source, helmOpts{valueFiles: appOpts.valuesFiles})
setHelmOpt(&spec.Source, helmOpts{valueFiles: appOpts.valuesFiles})
case "ignore-missing-value-files":
setHelmOpt(source, helmOpts{ignoreMissingValueFiles: appOpts.ignoreMissingValueFiles})
setHelmOpt(&spec.Source, helmOpts{ignoreMissingValueFiles: appOpts.ignoreMissingValueFiles})
case "values-literal-file":
var data []byte
@@ -169,41 +165,41 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
data, err = config.ReadRemoteFile(appOpts.values)
}
errors.CheckError(err)
setHelmOpt(source, helmOpts{values: string(data)})
setHelmOpt(&spec.Source, helmOpts{values: string(data)})
case "release-name":
setHelmOpt(source, helmOpts{releaseName: appOpts.releaseName})
setHelmOpt(&spec.Source, helmOpts{releaseName: appOpts.releaseName})
case "helm-version":
setHelmOpt(source, helmOpts{version: appOpts.helmVersion})
setHelmOpt(&spec.Source, helmOpts{version: appOpts.helmVersion})
case "helm-pass-credentials":
setHelmOpt(source, helmOpts{passCredentials: appOpts.helmPassCredentials})
setHelmOpt(&spec.Source, helmOpts{passCredentials: appOpts.helmPassCredentials})
case "helm-set":
setHelmOpt(source, helmOpts{helmSets: appOpts.helmSets})
setHelmOpt(&spec.Source, helmOpts{helmSets: appOpts.helmSets})
case "helm-set-string":
setHelmOpt(source, helmOpts{helmSetStrings: appOpts.helmSetStrings})
setHelmOpt(&spec.Source, helmOpts{helmSetStrings: appOpts.helmSetStrings})
case "helm-set-file":
setHelmOpt(source, helmOpts{helmSetFiles: appOpts.helmSetFiles})
setHelmOpt(&spec.Source, helmOpts{helmSetFiles: appOpts.helmSetFiles})
case "helm-skip-crds":
setHelmOpt(source, helmOpts{skipCrds: appOpts.helmSkipCrds})
setHelmOpt(&spec.Source, helmOpts{skipCrds: appOpts.helmSkipCrds})
case "directory-recurse":
if source.Directory != nil {
source.Directory.Recurse = appOpts.directoryRecurse
if spec.Source.Directory != nil {
spec.Source.Directory.Recurse = appOpts.directoryRecurse
} else {
source.Directory = &argoappv1.ApplicationSourceDirectory{Recurse: appOpts.directoryRecurse}
spec.Source.Directory = &argoappv1.ApplicationSourceDirectory{Recurse: appOpts.directoryRecurse}
}
case "directory-exclude":
if source.Directory != nil {
source.Directory.Exclude = appOpts.directoryExclude
if spec.Source.Directory != nil {
spec.Source.Directory.Exclude = appOpts.directoryExclude
} else {
source.Directory = &argoappv1.ApplicationSourceDirectory{Exclude: appOpts.directoryExclude}
spec.Source.Directory = &argoappv1.ApplicationSourceDirectory{Exclude: appOpts.directoryExclude}
}
case "directory-include":
if source.Directory != nil {
source.Directory.Include = appOpts.directoryInclude
if spec.Source.Directory != nil {
spec.Source.Directory.Include = appOpts.directoryInclude
} else {
source.Directory = &argoappv1.ApplicationSourceDirectory{Include: appOpts.directoryInclude}
spec.Source.Directory = &argoappv1.ApplicationSourceDirectory{Include: appOpts.directoryInclude}
}
case "config-management-plugin":
source.Plugin = &argoappv1.ApplicationSourcePlugin{Name: appOpts.configManagementPlugin}
spec.Source.Plugin = &argoappv1.ApplicationSourcePlugin{Name: appOpts.configManagementPlugin}
case "dest-name":
spec.Destination.Name = appOpts.destName
case "dest-server":
@@ -213,37 +209,37 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
case "project":
spec.Project = appOpts.project
case "nameprefix":
setKustomizeOpt(source, kustomizeOpts{namePrefix: appOpts.namePrefix})
setKustomizeOpt(&spec.Source, kustomizeOpts{namePrefix: appOpts.namePrefix})
case "namesuffix":
setKustomizeOpt(source, kustomizeOpts{nameSuffix: appOpts.nameSuffix})
setKustomizeOpt(&spec.Source, kustomizeOpts{nameSuffix: appOpts.nameSuffix})
case "kustomize-image":
setKustomizeOpt(source, kustomizeOpts{images: appOpts.kustomizeImages})
setKustomizeOpt(&spec.Source, kustomizeOpts{images: appOpts.kustomizeImages})
case "kustomize-version":
setKustomizeOpt(source, kustomizeOpts{version: appOpts.kustomizeVersion})
setKustomizeOpt(&spec.Source, kustomizeOpts{version: appOpts.kustomizeVersion})
case "kustomize-common-label":
parsedLabels, err := label.Parse(appOpts.kustomizeCommonLabels)
errors.CheckError(err)
setKustomizeOpt(source, kustomizeOpts{commonLabels: parsedLabels})
setKustomizeOpt(&spec.Source, kustomizeOpts{commonLabels: parsedLabels})
case "kustomize-common-annotation":
parsedAnnotations, err := label.Parse(appOpts.kustomizeCommonAnnotations)
errors.CheckError(err)
setKustomizeOpt(source, kustomizeOpts{commonAnnotations: parsedAnnotations})
setKustomizeOpt(&spec.Source, kustomizeOpts{commonAnnotations: parsedAnnotations})
case "kustomize-force-common-label":
setKustomizeOpt(source, kustomizeOpts{forceCommonLabels: appOpts.kustomizeForceCommonLabels})
setKustomizeOpt(&spec.Source, kustomizeOpts{forceCommonLabels: appOpts.kustomizeForceCommonLabels})
case "kustomize-force-common-annotation":
setKustomizeOpt(source, kustomizeOpts{forceCommonAnnotations: appOpts.kustomizeForceCommonAnnotations})
setKustomizeOpt(&spec.Source, kustomizeOpts{forceCommonAnnotations: appOpts.kustomizeForceCommonAnnotations})
case "jsonnet-tla-str":
setJsonnetOpt(source, appOpts.jsonnetTlaStr, false)
setJsonnetOpt(&spec.Source, appOpts.jsonnetTlaStr, false)
case "jsonnet-tla-code":
setJsonnetOpt(source, appOpts.jsonnetTlaCode, true)
setJsonnetOpt(&spec.Source, appOpts.jsonnetTlaCode, true)
case "jsonnet-ext-var-str":
setJsonnetOptExtVar(source, appOpts.jsonnetExtVarStr, false)
setJsonnetOptExtVar(&spec.Source, appOpts.jsonnetExtVarStr, false)
case "jsonnet-ext-var-code":
setJsonnetOptExtVar(source, appOpts.jsonnetExtVarCode, true)
setJsonnetOptExtVar(&spec.Source, appOpts.jsonnetExtVarCode, true)
case "jsonnet-libs":
setJsonnetOptLibs(source, appOpts.jsonnetLibs)
setJsonnetOptLibs(&spec.Source, appOpts.jsonnetLibs)
case "plugin-env":
setPluginOptEnvs(source, appOpts.pluginEnvs)
setPluginOptEnvs(&spec.Source, appOpts.pluginEnvs)
case "sync-policy":
switch appOpts.syncPolicy {
case "none":
@@ -300,7 +296,6 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
log.Fatalf("Invalid sync-retry-limit [%d]", appOpts.retryLimit)
}
}
spec.Source = source
})
if flags.Changed("auto-prune") {
if spec.SyncPolicy == nil || spec.SyncPolicy.Automated == nil {
@@ -478,9 +473,8 @@ func SetParameterOverrides(app *argoappv1.Application, parameters []string) {
if len(parameters) == 0 {
return
}
source := app.Spec.GetSource()
var sourceType argoappv1.ApplicationSourceType
if st, _ := source.ExplicitType(); st != nil {
if st, _ := app.Spec.Source.ExplicitType(); st != nil {
sourceType = *st
} else if app.Status.SourceType != "" {
sourceType = app.Status.SourceType
@@ -492,8 +486,8 @@ func SetParameterOverrides(app *argoappv1.Application, parameters []string) {
switch sourceType {
case argoappv1.ApplicationSourceTypeHelm:
if source.Helm == nil {
source.Helm = &argoappv1.ApplicationSourceHelm{}
if app.Spec.Source.Helm == nil {
app.Spec.Source.Helm = &argoappv1.ApplicationSourceHelm{}
}
for _, p := range parameters {
newParam, err := argoappv1.NewHelmParameter(p, false)
@@ -501,7 +495,7 @@ func SetParameterOverrides(app *argoappv1.Application, parameters []string) {
log.Error(err)
continue
}
source.Helm.AddParameter(*newParam)
app.Spec.Source.Helm.AddParameter(*newParam)
}
default:
log.Fatalf("Parameters can only be set against Helm applications")
@@ -586,9 +580,6 @@ func constructAppsBaseOnName(appName string, labels, annotations, args []string,
Name: appName,
Namespace: appNs,
},
Spec: argoappv1.ApplicationSpec{
Source: &argoappv1.ApplicationSource{},
},
}
SetAppSpecOptions(flags, &app.Spec, &appOpts)
SetParameterOverrides(app, appOpts.Parameters)

View File

@@ -149,9 +149,7 @@ func (f *appOptionsFixture) SetFlag(key, value string) error {
func newAppOptionsFixture() *appOptionsFixture {
fixture := &appOptionsFixture{
spec: &v1alpha1.ApplicationSpec{
Source: &v1alpha1.ApplicationSource{},
},
spec: &v1alpha1.ApplicationSpec{},
command: &cobra.Command{},
options: &AppOptions{},
}

View File

@@ -61,6 +61,6 @@ func readAppset(yml []byte, appsets *[]*argoprojiov1alpha1.ApplicationSet) error
*appsets = append(*appsets, &appset)
}
// we reach here if there is no error found while reading the Application Set
return nil
return fmt.Errorf("error reading app set: %w", err)
}

View File

@@ -22,8 +22,6 @@ type RepoOptions struct {
GithubAppPrivateKeyPath string
GitHubAppEnterpriseBaseURL string
Proxy string
GCPServiceAccountKeyPath string
ForceHttpBasicAuth bool
}
func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
@@ -44,6 +42,4 @@ func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
command.Flags().StringVar(&opts.GithubAppPrivateKeyPath, "github-app-private-key-path", "", "private key of the GitHub Application")
command.Flags().StringVar(&opts.GitHubAppEnterpriseBaseURL, "github-app-enterprise-base-url", "", "base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3")
command.Flags().StringVar(&opts.Proxy, "proxy", "", "use proxy to access repository")
command.Flags().StringVar(&opts.GCPServiceAccountKeyPath, "gcp-service-account-key-path", "", "service account key for the Google Cloud Platform")
command.Flags().BoolVar(&opts.ForceHttpBasicAuth, "force-http-basic-auth", false, "whether to force use of basic auth when connecting repository via HTTP")
}

View File

@@ -318,7 +318,6 @@ func (m *ManifestResponse) GetSourceType() string {
type RepositoryResponse struct {
IsSupported bool `protobuf:"varint,1,opt,name=isSupported,proto3" json:"isSupported,omitempty"`
IsDiscoveryEnabled bool `protobuf:"varint,2,opt,name=isDiscoveryEnabled,proto3" json:"isDiscoveryEnabled,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -364,13 +363,6 @@ func (m *RepositoryResponse) GetIsSupported() bool {
return false
}
func (m *RepositoryResponse) GetIsDiscoveryEnabled() bool {
if m != nil {
return m.IsDiscoveryEnabled
}
return false
}
// ParametersAnnouncementResponse contains a list of announcements. This list represents all the parameters which a CMP
// is able to accept.
type ParametersAnnouncementResponse struct {
@@ -480,43 +472,42 @@ func init() {
func init() { proto.RegisterFile("cmpserver/plugin/plugin.proto", fileDescriptor_b21875a7079a06ed) }
var fileDescriptor_b21875a7079a06ed = []byte{
// 576 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xdd, 0x6e, 0x12, 0x4f,
0x14, 0xc0, 0xbb, 0x85, 0xb6, 0x70, 0x68, 0xf2, 0x27, 0x93, 0x7f, 0x74, 0x25, 0x2d, 0xe2, 0x5e,
0x18, 0x6e, 0x84, 0x04, 0xbd, 0x35, 0xb1, 0x55, 0x6c, 0xa3, 0xc1, 0x90, 0xa9, 0x37, 0x7a, 0x37,
0x1d, 0x0e, 0x30, 0x76, 0x77, 0x66, 0x9c, 0x99, 0xdd, 0x04, 0xbd, 0xf1, 0x3d, 0x7c, 0x00, 0x5f,
0xc5, 0x4b, 0x1f, 0xc1, 0xf4, 0x49, 0x0c, 0xb3, 0xbb, 0x40, 0x6c, 0x8b, 0x57, 0x7b, 0x3e, 0x7f,
0x7b, 0xbe, 0x32, 0x70, 0xcc, 0x13, 0x6d, 0xd1, 0x64, 0x68, 0xfa, 0x3a, 0x4e, 0x67, 0x42, 0x16,
0x9f, 0x9e, 0x36, 0xca, 0x29, 0xb2, 0x9f, 0x6b, 0xad, 0xe1, 0x4c, 0xb8, 0x79, 0x7a, 0xd9, 0xe3,
0x2a, 0xe9, 0x33, 0x33, 0x53, 0xda, 0xa8, 0x4f, 0x5e, 0x78, 0xc2, 0x27, 0xfd, 0x6c, 0xd0, 0x37,
0xa8, 0x55, 0x81, 0xf1, 0xa2, 0x70, 0xca, 0x2c, 0x36, 0xc4, 0x1c, 0x17, 0x7d, 0x0b, 0xa0, 0x79,
0xa2, 0xf5, 0x85, 0x33, 0xc8, 0x12, 0x8a, 0x9f, 0x53, 0xb4, 0x8e, 0x3c, 0x87, 0x5a, 0x82, 0x8e,
0x4d, 0x98, 0x63, 0x61, 0xd0, 0x09, 0xba, 0x8d, 0xc1, 0xc3, 0x5e, 0x51, 0xc4, 0x88, 0x49, 0x31,
0x45, 0xeb, 0x8a, 0xd0, 0x51, 0x11, 0x76, 0xbe, 0x43, 0x57, 0x29, 0x24, 0x82, 0xea, 0x54, 0xc4,
0x18, 0xee, 0xfa, 0xd4, 0xc3, 0x32, 0xf5, 0xb5, 0x88, 0xf1, 0x7c, 0x87, 0x7a, 0xdf, 0x69, 0x1d,
0x0e, 0x4c, 0x8e, 0x88, 0x7e, 0x04, 0x70, 0xff, 0x0e, 0x2c, 0x09, 0xe1, 0x80, 0x69, 0xfd, 0x8e,
0x25, 0xe8, 0x0b, 0xa9, 0xd3, 0x52, 0x25, 0x6d, 0x00, 0xa6, 0x35, 0xc5, 0x78, 0xcc, 0xdc, 0xdc,
0xff, 0xaa, 0x4e, 0x37, 0x2c, 0xa4, 0x05, 0x35, 0x3e, 0x47, 0x7e, 0x65, 0xd3, 0x24, 0xac, 0x78,
0xef, 0x4a, 0x27, 0x04, 0xaa, 0x56, 0x7c, 0xc1, 0xb0, 0xda, 0x09, 0xba, 0x15, 0xea, 0x65, 0x12,
0x41, 0x05, 0x65, 0x16, 0xee, 0x75, 0x2a, 0xdd, 0xc6, 0xa0, 0x59, 0xd6, 0x3c, 0x94, 0xd9, 0x50,
0x3a, 0xb3, 0xa0, 0x4b, 0x67, 0xf4, 0x0c, 0x6a, 0xa5, 0x61, 0xc9, 0x90, 0xeb, 0xb2, 0xbc, 0x4c,
0xfe, 0x87, 0xbd, 0x8c, 0xc5, 0x29, 0x16, 0xe5, 0xe4, 0x4a, 0x34, 0x86, 0xe6, 0xba, 0x3d, 0xab,
0x95, 0xb4, 0x48, 0x8e, 0xa0, 0x9e, 0x14, 0x36, 0x1b, 0x06, 0x9d, 0x4a, 0xb7, 0x4e, 0xd7, 0x86,
0x65, 0x6f, 0x56, 0xa5, 0x86, 0xe3, 0xfb, 0x85, 0x2e, 0x61, 0x1b, 0x96, 0x68, 0x0a, 0x84, 0xae,
0x16, 0xb9, 0x62, 0x76, 0xa0, 0x21, 0xec, 0x45, 0xaa, 0xb5, 0x32, 0x0e, 0x27, 0xbe, 0xb0, 0x1a,
0xdd, 0x34, 0x91, 0x1e, 0x10, 0x61, 0x5f, 0x09, 0xcb, 0x55, 0x86, 0x66, 0x31, 0x94, 0xec, 0x32,
0xc6, 0x89, 0xe7, 0xd7, 0xe8, 0x2d, 0x9e, 0xe8, 0x2b, 0xb4, 0xc7, 0xcc, 0xb0, 0x04, 0x1d, 0x1a,
0x7b, 0x22, 0xa5, 0x4a, 0x25, 0xc7, 0x04, 0xe5, 0xba, 0x8f, 0x0f, 0x70, 0x4f, 0x97, 0x11, 0x9b,
0x01, 0x79, 0x53, 0x8d, 0xc1, 0xa3, 0xde, 0xc6, 0xc5, 0x8d, 0x6f, 0x8b, 0xa4, 0x77, 0x00, 0xa2,
0x23, 0xa8, 0x2e, 0x2f, 0x66, 0x39, 0x54, 0x3e, 0x4f, 0xe5, 0x95, 0x6f, 0xe8, 0x90, 0xe6, 0xca,
0xe0, 0xfb, 0x2e, 0x1c, 0xbf, 0x54, 0x72, 0x2a, 0x66, 0x23, 0x26, 0xd9, 0xcc, 0xe7, 0x8c, 0xfd,
0xce, 0x2e, 0xd0, 0x64, 0x82, 0x23, 0x79, 0x03, 0xcd, 0x33, 0x94, 0x68, 0x98, 0xc3, 0x72, 0xfc,
0x24, 0x2c, 0xf7, 0xfa, 0xf7, 0xc9, 0xb7, 0xc2, 0x9b, 0x07, 0x9e, 0xb7, 0x18, 0xed, 0x74, 0x03,
0xf2, 0x16, 0xfe, 0x1b, 0x31, 0xc7, 0xe7, 0xeb, 0xa9, 0x6f, 0x41, 0xb5, 0x4a, 0xcf, 0xcd, 0x1d,
0x79, 0x18, 0x83, 0x07, 0x67, 0xe8, 0x6e, 0x1f, 0xec, 0x16, 0xec, 0xe3, 0xd2, 0xb3, 0x7d, 0x25,
0xcb, 0x5f, 0x9c, 0xbe, 0xf8, 0x79, 0xdd, 0x0e, 0x7e, 0x5d, 0xb7, 0x83, 0xdf, 0xd7, 0xed, 0xe0,
0xe3, 0xe0, 0x1f, 0x4f, 0xc5, 0xfa, 0xc1, 0x61, 0x5a, 0xf0, 0x58, 0xa0, 0x74, 0x97, 0xfb, 0xfe,
0x79, 0x78, 0xfa, 0x27, 0x00, 0x00, 0xff, 0xff, 0x23, 0x88, 0x8e, 0xd3, 0x8e, 0x04, 0x00, 0x00,
// 558 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0xc1, 0x6e, 0xd3, 0x4c,
0x10, 0xae, 0x9b, 0xb4, 0x4d, 0x26, 0x95, 0xfe, 0x68, 0xf5, 0x0b, 0x4c, 0xd4, 0x86, 0xe0, 0x03,
0xca, 0x85, 0x44, 0x32, 0x88, 0x1b, 0x12, 0x2d, 0x2a, 0xad, 0x40, 0x41, 0xd1, 0x96, 0x0b, 0xdc,
0xb6, 0xce, 0x24, 0x59, 0x6a, 0xef, 0x2e, 0xeb, 0xb5, 0xa5, 0xc0, 0x85, 0xf7, 0xe0, 0x01, 0x78,
0x15, 0x8e, 0x3c, 0x02, 0xca, 0x93, 0x20, 0xaf, 0xed, 0xd8, 0xa2, 0x6d, 0x38, 0x79, 0xe6, 0x9b,
0x99, 0x6f, 0xbf, 0x9d, 0x99, 0x35, 0x1c, 0x07, 0x91, 0x8a, 0x51, 0xa7, 0xa8, 0xc7, 0x2a, 0x4c,
0x16, 0x5c, 0x14, 0x9f, 0x91, 0xd2, 0xd2, 0x48, 0xb2, 0x9f, 0x7b, 0xbd, 0xb3, 0x05, 0x37, 0xcb,
0xe4, 0x6a, 0x14, 0xc8, 0x68, 0xcc, 0xf4, 0x42, 0x2a, 0x2d, 0x3f, 0x59, 0xe3, 0x49, 0x30, 0x1b,
0xa7, 0xfe, 0x58, 0xa3, 0x92, 0x05, 0x8d, 0x35, 0xb9, 0x91, 0x7a, 0x55, 0x33, 0x73, 0x3a, 0xef,
0x9b, 0x03, 0xdd, 0x13, 0xa5, 0x2e, 0x8d, 0x46, 0x16, 0x51, 0xfc, 0x9c, 0x60, 0x6c, 0xc8, 0x0b,
0x68, 0x45, 0x68, 0xd8, 0x8c, 0x19, 0xe6, 0x3a, 0x03, 0x67, 0xd8, 0xf1, 0x1f, 0x8e, 0x0a, 0x11,
0x13, 0x26, 0xf8, 0x1c, 0x63, 0x53, 0xa4, 0x4e, 0x8a, 0xb4, 0x8b, 0x1d, 0xba, 0x29, 0x21, 0x1e,
0x34, 0xe7, 0x3c, 0x44, 0x77, 0xd7, 0x96, 0x1e, 0x96, 0xa5, 0xaf, 0x79, 0x88, 0x17, 0x3b, 0xd4,
0xc6, 0x4e, 0xdb, 0x70, 0xa0, 0x73, 0x0a, 0xef, 0x87, 0x03, 0xf7, 0xef, 0xa0, 0x25, 0x2e, 0x1c,
0x30, 0xa5, 0xde, 0xb1, 0x08, 0xad, 0x90, 0x36, 0x2d, 0x5d, 0xd2, 0x07, 0x60, 0x4a, 0x51, 0x0c,
0xa7, 0xcc, 0x2c, 0xed, 0x51, 0x6d, 0x5a, 0x43, 0x48, 0x0f, 0x5a, 0xc1, 0x12, 0x83, 0xeb, 0x38,
0x89, 0xdc, 0x86, 0x8d, 0x6e, 0x7c, 0x42, 0xa0, 0x19, 0xf3, 0x2f, 0xe8, 0x36, 0x07, 0xce, 0xb0,
0x41, 0xad, 0x4d, 0x3c, 0x68, 0xa0, 0x48, 0xdd, 0xbd, 0x41, 0x63, 0xd8, 0xf1, 0xbb, 0xa5, 0xe6,
0x33, 0x91, 0x9e, 0x09, 0xa3, 0x57, 0x34, 0x0b, 0x7a, 0xcf, 0xa0, 0x55, 0x02, 0x19, 0x87, 0xa8,
0x64, 0x59, 0x9b, 0xfc, 0x0f, 0x7b, 0x29, 0x0b, 0x13, 0x2c, 0xe4, 0xe4, 0x8e, 0x37, 0x85, 0x6e,
0x75, 0xbd, 0x58, 0x49, 0x11, 0x23, 0x39, 0x82, 0x76, 0x54, 0x60, 0xb1, 0xeb, 0x0c, 0x1a, 0xc3,
0x36, 0xad, 0x80, 0xec, 0x6e, 0xb1, 0x4c, 0x74, 0x80, 0xef, 0x57, 0xaa, 0x24, 0xab, 0x21, 0xde,
0x73, 0x20, 0x74, 0x33, 0xc8, 0x0d, 0xe7, 0x00, 0x3a, 0x3c, 0xbe, 0x4c, 0x94, 0x92, 0xda, 0xe0,
0xcc, 0x0a, 0x6b, 0xd1, 0x3a, 0xe4, 0x7d, 0x85, 0xfe, 0x94, 0x69, 0x16, 0xa1, 0x41, 0x1d, 0x9f,
0x08, 0x21, 0x13, 0x11, 0x60, 0x84, 0xa2, 0xd2, 0xf5, 0x01, 0xee, 0xa9, 0x32, 0xa3, 0x9e, 0x90,
0x8b, 0xec, 0xf8, 0x8f, 0x46, 0xb5, 0x0d, 0x9a, 0xde, 0x96, 0x49, 0xef, 0x20, 0xf0, 0x8e, 0xa0,
0x99, 0x6d, 0x40, 0xd6, 0xa4, 0x60, 0x99, 0x88, 0x6b, 0x2b, 0xf0, 0x90, 0xe6, 0x8e, 0xff, 0x7d,
0x17, 0x8e, 0x5f, 0x49, 0x31, 0xe7, 0x8b, 0x09, 0x13, 0x6c, 0x61, 0x6b, 0xa6, 0x76, 0x06, 0x97,
0xa8, 0x53, 0x1e, 0x20, 0x79, 0x03, 0xdd, 0x73, 0x14, 0xa8, 0x99, 0xc1, 0xb2, 0x9d, 0xc4, 0x2d,
0xe7, 0xf4, 0xf7, 0x0a, 0xf7, 0xdc, 0x9b, 0x0b, 0x9b, 0x5f, 0xd1, 0xdb, 0x19, 0x3a, 0xe4, 0x2d,
0xfc, 0x37, 0x61, 0x26, 0x58, 0x56, 0x5d, 0xdc, 0x42, 0xd5, 0x2b, 0x23, 0x37, 0x7b, 0x6e, 0xc9,
0x18, 0x3c, 0x38, 0x47, 0x73, 0x7b, 0x63, 0xb7, 0xd0, 0x3e, 0x2e, 0x23, 0xdb, 0x47, 0x92, 0x1d,
0x71, 0xfa, 0xf2, 0xe7, 0xba, 0xef, 0xfc, 0x5a, 0xf7, 0x9d, 0xdf, 0xeb, 0xbe, 0xf3, 0xd1, 0xff,
0xc7, 0xd3, 0xaf, 0x7e, 0x20, 0x4c, 0xf1, 0x20, 0xe4, 0x28, 0xcc, 0xd5, 0xbe, 0x7d, 0xee, 0x4f,
0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x33, 0x34, 0xb3, 0x95, 0x5e, 0x04, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -1034,16 +1025,6 @@ func (m *RepositoryResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.IsDiscoveryEnabled {
i--
if m.IsDiscoveryEnabled {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
}
if m.IsSupported {
i--
if m.IsSupported {
@@ -1266,9 +1247,6 @@ func (m *RepositoryResponse) Size() (n int) {
if m.IsSupported {
n += 2
}
if m.IsDiscoveryEnabled {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
@@ -1915,26 +1893,6 @@ func (m *RepositoryResponse) Unmarshal(dAtA []byte) error {
}
}
m.IsSupported = bool(v != 0)
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field IsDiscoveryEnabled", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.IsDiscoveryEnabled = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipPlugin(dAtA[iNdEx:])

View File

@@ -22,11 +22,11 @@ type PluginConfig struct {
}
type PluginConfigSpec struct {
Version string `json:"version"`
Init Command `json:"init,omitempty"`
Generate Command `json:"generate"`
Discover Discover `json:"discover"`
Parameters Parameters `yaml:"parameters"`
Version string `json:"version"`
Init Command `json:"init,omitempty"`
Generate Command `json:"generate"`
Discover Discover `json:"discover"`
Parameters Parameters `yaml:"parameters"`
}
//Discover holds find and fileName
@@ -84,7 +84,9 @@ func ValidatePluginConfig(config PluginConfig) error {
if len(config.Spec.Generate.Command) == 0 {
return fmt.Errorf("invalid plugin configuration file. spec.generate command should be non-empty")
}
// discovery field is optional as apps can now specify plugin names directly
if config.Spec.Discover.Find.Glob == "" && len(config.Spec.Discover.Find.Command.Command) == 0 && config.Spec.Discover.FileName == "" {
return fmt.Errorf("invalid plugin configuration file. atleast one of discover.find.command or discover.find.glob or discover.fineName should be non-empty")
}
return nil
}

View File

@@ -273,11 +273,11 @@ func (s *Service) matchRepositoryGeneric(stream MatchRepositoryStream) error {
return fmt.Errorf("match repository error receiving stream: %w", err)
}
isSupported, isDiscoveryEnabled, err := s.matchRepository(bufferedCtx, workDir, metadata.GetEnv())
isSupported, err := s.matchRepository(bufferedCtx, workDir, metadata.GetEnv())
if err != nil {
return fmt.Errorf("match repository error: %w", err)
}
repoResponse := &apiclient.RepositoryResponse{IsSupported: isSupported, IsDiscoveryEnabled: isDiscoveryEnabled}
repoResponse := &apiclient.RepositoryResponse{IsSupported: isSupported}
err = stream.SendAndClose(repoResponse)
if err != nil {
@@ -286,9 +286,8 @@ func (s *Service) matchRepositoryGeneric(stream MatchRepositoryStream) error {
return nil
}
func (s *Service) matchRepository(ctx context.Context, workdir string, envEntries []*apiclient.EnvEntry) (isSupported bool, isDiscoveryEnabled bool, err error) {
func (s *Service) matchRepository(ctx context.Context, workdir string, envEntries []*apiclient.EnvEntry) (bool, error) {
config := s.initConstants.PluginConfig
if config.Spec.Discover.FileName != "" {
log.Debugf("config.Spec.Discover.FileName is provided")
pattern := filepath.Join(workdir, config.Spec.Discover.FileName)
@@ -296,9 +295,9 @@ func (s *Service) matchRepository(ctx context.Context, workdir string, envEntrie
if err != nil {
e := fmt.Errorf("error finding filename match for pattern %q: %w", pattern, err)
log.Debug(e)
return false, true, e
return false, e
}
return len(matches) > 0, true, nil
return len(matches) > 0, nil
}
if config.Spec.Discover.Find.Glob != "" {
@@ -310,23 +309,27 @@ func (s *Service) matchRepository(ctx context.Context, workdir string, envEntrie
if err != nil {
e := fmt.Errorf("error finding glob match for pattern %q: %w", pattern, err)
log.Debug(e)
return false, true, e
return false, e
}
return len(matches) > 0, true, nil
}
if len(config.Spec.Discover.Find.Command.Command) > 0 {
log.Debugf("Going to try runCommand.")
env := append(os.Environ(), environ(envEntries)...)
find, err := runCommand(ctx, config.Spec.Discover.Find.Command, workdir, env)
if err != nil {
return false, true, fmt.Errorf("error running find command: %w", err)
if len(matches) > 0 {
return true, nil
}
return find != "", true, nil
return false, nil
}
return false, false, nil
log.Debugf("Going to try runCommand.")
env := append(os.Environ(), environ(envEntries)...)
find, err := runCommand(ctx, config.Spec.Discover.Find.Command, workdir, env)
if err != nil {
return false, fmt.Errorf("error running find command: %w", err)
}
if find != "" {
return true, nil
}
return false, nil
}
// ParametersAnnouncementStream defines an interface able to send/receive a stream of parameter announcements.

View File

@@ -44,7 +44,6 @@ message ManifestResponse {
message RepositoryResponse {
bool isSupported = 1;
bool isDiscoveryEnabled = 2;
}
// ParametersAnnouncementResponse contains a list of announcements. This list represents all the parameters which a CMP

View File

@@ -99,12 +99,11 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
assert.True(t, match)
assert.True(t, discovery)
})
t.Run("will not match plugin by filename if file not found", func(t *testing.T) {
// given
@@ -114,12 +113,11 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
assert.False(t, match)
assert.True(t, discovery)
})
t.Run("will not match a pattern with a syntax error", func(t *testing.T) {
// given
@@ -129,7 +127,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
_, _, err := f.service.matchRepository(context.Background(), f.path, f.env)
_, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.ErrorContains(t, err, "syntax error")
@@ -144,12 +142,11 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
assert.True(t, match)
assert.True(t, discovery)
})
t.Run("will not match plugin by glob if not found", func(t *testing.T) {
// given
@@ -161,12 +158,11 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
assert.False(t, match)
assert.True(t, discovery)
})
t.Run("will throw an error for a bad pattern", func(t *testing.T) {
// given
@@ -178,7 +174,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
_, _, err := f.service.matchRepository(context.Background(), f.path, f.env)
_, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.ErrorContains(t, err, "error finding glob match for pattern")
@@ -195,12 +191,11 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
assert.True(t, match)
assert.True(t, discovery)
})
t.Run("will not match plugin by command when returns no output", func(t *testing.T) {
// given
@@ -214,11 +209,11 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
assert.False(t, match)
assert.True(t, discovery)
})
t.Run("will match plugin because env var defined", func(t *testing.T) {
// given
@@ -232,12 +227,11 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
assert.True(t, match)
assert.True(t, discovery)
})
t.Run("will not match plugin because no env var defined", func(t *testing.T) {
// given
@@ -252,12 +246,11 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
assert.False(t, match)
assert.True(t, discovery)
})
t.Run("will not match plugin by command when command fails", func(t *testing.T) {
// given
@@ -271,25 +264,11 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.Error(t, err)
assert.False(t, match)
assert.True(t, discovery)
})
t.Run("will not match plugin as discovery is not set", func(t *testing.T) {
// given
d := Discover{}
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
assert.False(t, match)
assert.False(t, discovery)
})
}

View File

@@ -1,15 +1,12 @@
package common
import (
"errors"
"os"
"path/filepath"
"strconv"
"time"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Default service addresses and URLS of Argo CD internal services
@@ -225,7 +222,13 @@ const (
// DefaultCMPWorkDirName defines the work directory name used by the cmp-server
DefaultCMPWorkDirName = "_cmp_server"
ConfigMapPluginDeprecationWarning = "argocd-cm plugins are deprecated, and support will be removed in v2.7. Upgrade your plugin to be installed via sidecar. https://argo-cd.readthedocs.io/en/stable/user-guide/config-management-plugins/"
ConfigMapPluginDeprecationWarning = "argocd-cm plugins are deprecated, and support will be removed in v2.6. Upgrade your plugin to be installed via sidecar. https://argo-cd.readthedocs.io/en/stable/user-guide/config-management-plugins/"
ConfigMapPluginCLIDeprecationWarning = "spec.plugin.name is set, which means this Application uses a plugin installed in the " +
"argocd-cm ConfigMap. Installing plugins via that ConfigMap is deprecated in Argo CD v2.5. " +
"Starting in Argo CD v2.6, this Application will fail to sync. Contact your Argo CD admin " +
"to make sure an upgrade plan is in place. More info: " +
"https://argo-cd.readthedocs.io/en/latest/operator-manual/upgrading/2.4-2.5/"
)
const (
@@ -313,10 +316,3 @@ const (
SecurityMedium = 2 // Could indicate malicious events, but has a high likelihood of being user/system error (i.e. access denied)
SecurityLow = 1 // Unexceptional entries (i.e. successful access logs)
)
// Common error messages
const TokenVerificationError = "failed to verify the token"
var TokenVerificationErr = errors.New(TokenVerificationError)
var PermissionDeniedAPIError = status.Error(codes.PermissionDenied, "permission denied")

View File

@@ -335,7 +335,7 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b
}
if !ctrl.canProcessApp(obj) {
// Don't force refresh app if app belongs to a different controller shard or is outside the allowed namespaces.
// Don't force refresh app if app belongs to a different controller shard
continue
}
@@ -415,11 +415,11 @@ 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 := ctrl.getAppProj(a)
if err != nil {
return nil, err
}
orphanedNodesMap := make(map[kube.ResourceKey]appv1.ResourceNode)
warnOrphaned := true
if proj.Spec.OrphanedResources != nil {
@@ -429,6 +429,7 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
}
warnOrphaned = proj.Spec.OrphanedResources.IsWarn()
}
for i := range managedResources {
managedResource := managedResources[i]
delete(orphanedNodesMap, kube.NewResourceKey(managedResource.Group, managedResource.Kind, managedResource.Namespace, managedResource.Name))
@@ -750,7 +751,6 @@ func (ctrl *ApplicationController) Run(ctx context.Context, statusProcessors int
// needs to be the qualified name of the application, i.e. <namespace>/<name>.
func (ctrl *ApplicationController) requestAppRefresh(appName string, compareWith *CompareWith, after *time.Duration) {
key := ctrl.toAppKey(appName)
if compareWith != nil && after != nil {
ctrl.appComparisonTypeRefreshQueue.AddAfter(fmt.Sprintf("%s/%d", key, compareWith), *after)
} else {
@@ -1313,6 +1313,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
}
ctrl.appRefreshQueue.Done(appKey)
}()
obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey.(string))
if err != nil {
log.Errorf("Failed to get application '%s' from informer index: %+v", appKey, err)
@@ -1333,9 +1334,9 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
if !needRefresh {
return
}
app := origApp.DeepCopy()
logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()})
startTime := time.Now()
defer func() {
reconcileDuration := time.Since(startTime)
@@ -1388,38 +1389,15 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
localManifests = opState.Operation.Sync.Manifests
}
revisions := make([]string, 0)
sources := make([]appv1.ApplicationSource, 0)
hasMultipleSources := app.Spec.HasMultipleSources()
// If we have multiple sources, we use all the sources under `sources` field and ignore source under `source` field.
// else we use the source under the source field.
if hasMultipleSources {
for _, source := range app.Spec.Sources {
// We do not perform any filtering of duplicate sources.
// Argo CD will apply and update the resources generated from the sources automatically
// based on the order in which manifests were generated
sources = append(sources, source)
revisions = append(revisions, source.TargetRevision)
}
if comparisonLevel == CompareWithRecent {
revisions = app.Status.Sync.Revisions
}
} else {
revision := app.Spec.GetSource().TargetRevision
if comparisonLevel == CompareWithRecent {
revision = app.Status.Sync.Revision
}
revisions = append(revisions, revision)
sources = append(sources, app.Spec.GetSource())
revision := app.Spec.Source.TargetRevision
if comparisonLevel == CompareWithRecent {
revision = app.Status.Sync.Revision
}
now := metav1.Now()
compareResult := ctrl.appStateManager.CompareAppState(app, project, revisions, sources,
compareResult := ctrl.appStateManager.CompareAppState(app, project, revision, app.Spec.Source,
refreshType == appv1.RefreshTypeHard,
comparisonLevel == CompareWithLatestForceResolve, localManifests, hasMultipleSources)
comparisonLevel == CompareWithLatestForceResolve, localManifests)
for k, v := range compareResult.timings {
logCtx = logCtx.WithField(k, v.Milliseconds())
}
@@ -1460,7 +1438,6 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
return resourceStatusKey(app.Status.Resources[i]) < resourceStatusKey(app.Status.Resources[j])
})
app.Status.SourceType = compareResult.appSourceType
app.Status.SourceTypes = compareResult.appSourceTypes
ctrl.persistAppStatus(origApp, &app.Status)
return
}
@@ -1486,34 +1463,27 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application,
// user requested app refresh.
refreshType = requestedType
reason = fmt.Sprintf("%s refresh requested", refreshType)
} else {
if app.Spec.HasMultipleSources() {
if (len(app.Spec.Sources) != len(app.Status.Sync.ComparedTo.Sources)) || !reflect.DeepEqual(app.Spec.Sources, app.Status.Sync.ComparedTo.Sources) {
reason = "atleast one of the spec.sources differs"
compareWith = CompareWithLatestForceResolve
}
} else if !app.Spec.Source.Equals(app.Status.Sync.ComparedTo.Source) {
reason = "spec.source differs"
compareWith = CompareWithLatestForceResolve
} else if hardExpired || softExpired {
// The commented line below mysteriously crashes if app.Status.ReconciledAt is nil
// reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout)
//TODO: find existing Golang bug or create a new one
reconciledAtStr := "never"
if app.Status.ReconciledAt != nil {
reconciledAtStr = app.Status.ReconciledAt.String()
}
reason = fmt.Sprintf("comparison expired, requesting refresh. reconciledAt: %v, expiry: %v", reconciledAtStr, statusRefreshTimeout)
if hardExpired {
reason = fmt.Sprintf("comparison expired, requesting hard refresh. reconciledAt: %v, expiry: %v", reconciledAtStr, statusHardRefreshTimeout)
refreshType = appv1.RefreshTypeHard
}
} else if !app.Spec.Destination.Equals(app.Status.Sync.ComparedTo.Destination) {
reason = "spec.destination differs"
} else if requested, level := ctrl.isRefreshRequested(app.QualifiedName()); requested {
compareWith = level
reason = "controller refresh requested"
} else if !app.Spec.Source.Equals(app.Status.Sync.ComparedTo.Source) {
reason = "spec.source differs"
compareWith = CompareWithLatestForceResolve
} else if hardExpired || softExpired {
// The commented line below mysteriously crashes if app.Status.ReconciledAt is nil
// reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout)
//TODO: find existing Golang bug or create a new one
reconciledAtStr := "never"
if app.Status.ReconciledAt != nil {
reconciledAtStr = app.Status.ReconciledAt.String()
}
reason = fmt.Sprintf("comparison expired, requesting refresh. reconciledAt: %v, expiry: %v", reconciledAtStr, statusRefreshTimeout)
if hardExpired {
reason = fmt.Sprintf("comparison expired, requesting hard refresh. reconciledAt: %v, expiry: %v", reconciledAtStr, statusHardRefreshTimeout)
refreshType = appv1.RefreshTypeHard
}
} else if !app.Spec.Destination.Equals(app.Status.Sync.ComparedTo.Destination) {
reason = "spec.destination differs"
} else if requested, level := ctrl.isRefreshRequested(app.QualifiedName()); requested {
compareWith = level
reason = "controller refresh requested"
}
if reason != "" {
@@ -1550,9 +1520,7 @@ func (ctrl *ApplicationController) refreshAppConditions(app *appv1.Application)
func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Application) {
logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()})
app.Spec = *argo.NormalizeApplicationSpec(&app.Spec)
patch, modified, err := diff.CreateTwoWayMergePatch(orig, app, appv1.Application{})
if err != nil {
logCtx.Errorf("error constructing app spec patch: %v", err)
} else if modified {
@@ -1596,6 +1564,7 @@ func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, new
logCtx.Infof("No status changes. Skipping patch")
return
}
logCtx.Debugf("patch: %s", string(patch))
appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(orig.Namespace)
_, err = appClient.Patch(context.Background(), orig.Name, types.MergePatchType, patch, metav1.PatchOptions{})
if err != nil {
@@ -1611,7 +1580,6 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
return nil
}
logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()})
if app.Operation != nil {
logCtx.Infof("Skipping auto-sync: another operation is in progress")
return nil
@@ -1643,15 +1611,13 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
}
desiredCommitSHA := syncStatus.Revision
desiredCommitSHAsMS := syncStatus.Revisions
alreadyAttempted, attemptPhase := alreadyAttemptedSync(app, desiredCommitSHA, desiredCommitSHAsMS, app.Spec.HasMultipleSources())
alreadyAttempted, attemptPhase := alreadyAttemptedSync(app, desiredCommitSHA)
selfHeal := app.Spec.SyncPolicy.Automated.SelfHeal
op := appv1.Operation{
Sync: &appv1.SyncOperation{
Revision: desiredCommitSHA,
Prune: app.Spec.SyncPolicy.Automated.Prune,
SyncOptions: app.Spec.SyncPolicy.SyncOptions,
Revisions: desiredCommitSHAsMS,
},
InitiatedBy: appv1.OperationInitiator{Automated: true},
Retry: appv1.RetryStrategy{Limit: 5},
@@ -1703,6 +1669,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: message}
}
}
appIf := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace)
_, err := argo.SetAppOperation(appIf, app.Name, &op)
if err != nil {
@@ -1717,41 +1684,20 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
// alreadyAttemptedSync returns whether or not the most recent sync was performed against the
// commitSHA and with the same app source config which are currently set in the app
func alreadyAttemptedSync(app *appv1.Application, commitSHA string, commitSHAsMS []string, hasMultipleSources bool) (bool, synccommon.OperationPhase) {
func alreadyAttemptedSync(app *appv1.Application, commitSHA string) (bool, synccommon.OperationPhase) {
if app.Status.OperationState == nil || app.Status.OperationState.Operation.Sync == nil || app.Status.OperationState.SyncResult == nil {
return false, ""
}
if hasMultipleSources {
if !reflect.DeepEqual(app.Status.OperationState.SyncResult.Revisions, commitSHAsMS) {
return false, ""
}
} else {
if app.Status.OperationState.SyncResult.Revision != commitSHA {
return false, ""
}
}
if hasMultipleSources {
// Ignore differences in target revision, since we already just verified commitSHAs are equal,
// and we do not want to trigger auto-sync due to things like HEAD != master
specSources := app.Spec.Sources.DeepCopy()
syncSources := app.Status.OperationState.SyncResult.Sources.DeepCopy()
for _, source := range specSources {
source.TargetRevision = ""
}
for _, source := range syncSources {
source.TargetRevision = ""
}
return reflect.DeepEqual(app.Spec.Sources, app.Status.OperationState.SyncResult.Sources), app.Status.OperationState.Phase
} else {
// Ignore differences in target revision, since we already just verified commitSHAs are equal,
// and we do not want to trigger auto-sync due to things like HEAD != master
specSource := app.Spec.Source.DeepCopy()
specSource.TargetRevision = ""
syncResSource := app.Status.OperationState.SyncResult.Source.DeepCopy()
syncResSource.TargetRevision = ""
return reflect.DeepEqual(app.Spec.GetSource(), app.Status.OperationState.SyncResult.Source), app.Status.OperationState.Phase
if app.Status.OperationState.SyncResult.Revision != commitSHA {
return false, ""
}
// Ignore differences in target revision, since we already just verified commitSHAs are equal,
// and we do not want to trigger auto-sync due to things like HEAD != master
specSource := app.Spec.Source.DeepCopy()
specSource.TargetRevision = ""
syncResSource := app.Status.OperationState.SyncResult.Source.DeepCopy()
syncResSource.TargetRevision = ""
return reflect.DeepEqual(app.Spec.Source, app.Status.OperationState.SyncResult.Source), app.Status.OperationState.Phase
}
func (ctrl *ApplicationController) shouldSelfHeal(app *appv1.Application) (bool, time.Duration) {
@@ -1773,13 +1719,6 @@ func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
if !ok {
return false
}
// Only process given app if it exists in a watched namespace, or in the
// control plane's namespace.
if app.Namespace != ctrl.namespace && !glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) {
return false
}
if ctrl.clusterFilter != nil {
cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server)
if err != nil {
@@ -1788,6 +1727,12 @@ func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
return ctrl.clusterFilter(cluster)
}
// Only process given app if it exists in a watched namespace, or in the
// control plane's namespace.
if app.Namespace != ctrl.namespace && !glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) {
return false
}
return true
}

View File

@@ -865,7 +865,7 @@ func TestNeedRefreshAppStatus(t *testing.T) {
app.Status.Sync = argoappv1.SyncStatus{
Status: argoappv1.SyncStatusCodeSynced,
ComparedTo: argoappv1.ComparedTo{
Source: app.Spec.GetSource(),
Source: app.Spec.Source,
Destination: app.Spec.Destination,
},
}
@@ -909,7 +909,7 @@ func TestNeedRefreshAppStatus(t *testing.T) {
app.Status.Sync = argoappv1.SyncStatus{
Status: argoappv1.SyncStatusCodeSynced,
ComparedTo: argoappv1.ComparedTo{
Source: app.Spec.GetSource(),
Source: app.Spec.Source,
Destination: app.Spec.Destination,
},
}
@@ -1012,7 +1012,7 @@ func TestUpdateReconciledAt(t *testing.T) {
app := newFakeApp()
reconciledAt := metav1.NewTime(time.Now().Add(-1 * time.Second))
app.Status = argoappv1.ApplicationStatus{ReconciledAt: &reconciledAt}
app.Status.Sync = argoappv1.SyncStatus{ComparedTo: argoappv1.ComparedTo{Source: app.Spec.GetSource(), Destination: app.Spec.Destination}}
app.Status.Sync = argoappv1.SyncStatus{ComparedTo: argoappv1.ComparedTo{Source: app.Spec.Source, Destination: app.Spec.Destination}}
ctrl := newFakeController(&fakeData{
apps: []runtime.Object{app, &defaultProj},
manifestResponse: &apiclient.ManifestResponse{
@@ -1373,31 +1373,3 @@ func TestToAppKey(t *testing.T) {
})
}
}
func Test_canProcessApp(t *testing.T) {
app := newFakeApp()
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}})
ctrl.applicationNamespaces = []string{"good"}
t.Run("without cluster filter, good namespace", func(t *testing.T) {
app.Namespace = "good"
canProcess := ctrl.canProcessApp(app)
assert.True(t, canProcess)
})
t.Run("without cluster filter, bad namespace", func(t *testing.T) {
app.Namespace = "bad"
canProcess := ctrl.canProcessApp(app)
assert.False(t, canProcess)
})
t.Run("with cluster filter, good namespace", func(t *testing.T) {
app.Namespace = "good"
ctrl.clusterFilter = func(_ *argoappv1.Cluster) bool { return true }
canProcess := ctrl.canProcessApp(app)
assert.True(t, canProcess)
})
t.Run("with cluster filter, bad namespace", func(t *testing.T) {
app.Namespace = "bad"
ctrl.clusterFilter = func(_ *argoappv1.Cluster) bool { return true }
canProcess := ctrl.canProcessApp(app)
assert.False(t, canProcess)
})
}

View File

@@ -117,7 +117,7 @@ func (c *clusterInfoUpdater) updateClusterInfo(cluster appv1.Cluster, info *cach
}
if info != nil {
clusterInfo.ServerVersion = info.K8SVersion
clusterInfo.APIVersions = argo.APIResourcesToStrings(info.APIResources, true)
clusterInfo.APIVersions = argo.APIResourcesToStrings(info.APIResources, false)
if info.LastCacheSyncTime == nil {
clusterInfo.ConnectionState.Status = appv1.ConnectionStatusUnknown
} else if info.SyncError == nil {

View File

@@ -381,7 +381,7 @@ func (c *appCollector) collectApps(ch chan<- prometheus.Metric, app *argoappv1.A
healthStatus = health.HealthStatusUnknown
}
addGauge(descAppInfo, 1, git.NormalizeGitURL(app.Spec.GetSource().RepoURL), app.Spec.Destination.Server, app.Spec.Destination.Namespace, string(syncStatus), string(healthStatus), operation)
addGauge(descAppInfo, 1, git.NormalizeGitURL(app.Spec.Source.RepoURL), app.Spec.Destination.Server, app.Spec.Destination.Namespace, string(syncStatus), string(healthStatus), operation)
if len(c.appLabels) > 0 {
labelValues := []string{}

View File

@@ -62,7 +62,7 @@ type managedResource struct {
// AppStateManager defines methods which allow to compare application spec and actual application state.
type AppStateManager interface {
CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localObjects []string, hasMultipleSources bool) *comparisonResult
CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revision string, source v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localObjects []string) *comparisonResult
SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState)
}
@@ -75,8 +75,6 @@ type comparisonResult struct {
reconciliationResult sync.ReconciliationResult
diffConfig argodiff.DiffConfig
appSourceType v1alpha1.ApplicationSourceType
// appSourceTypes stores the SourceType for each application source under sources field
appSourceTypes []v1alpha1.ApplicationSourceType
// timings maps phases of comparison to the duration it took to complete (for statistical purposes)
timings map[string]time.Duration
diffResultList *diff.DiffResultList
@@ -107,8 +105,7 @@ type appStateManager struct {
persistResourceHealth bool
}
func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, map[*v1alpha1.ApplicationSource]*apiclient.ManifestResponse, error) {
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) {
ts := stats.NewTimingStats()
helmRepos, err := m.db.ListHelmRepositories(context.Background())
if err != nil {
@@ -118,7 +115,11 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp
if err != nil {
return nil, nil, err
}
ts.AddCheckpoint("helm_ms")
repo, err := m.db.GetRepository(context.Background(), source.RepoURL)
if err != nil {
return nil, nil, err
}
ts.AddCheckpoint("repo_ms")
helmRepositoryCredentials, err := m.db.GetAllHelmRepositoryCredentials(context.Background())
if err != nil {
@@ -128,6 +129,15 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp
if err != nil {
return nil, nil, err
}
conn, repoClient, err := m.repoClientset.NewRepoServerClient()
if err != nil {
return nil, nil, err
}
defer io.Close(conn)
if revision == "" {
revision = source.TargetRevision
}
plugins, err := m.settingsMgr.GetConfigManagementPlugins()
if err != nil {
@@ -148,87 +158,48 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp
return nil, nil, err
}
helmOptions, err := m.settingsMgr.GetHelmSettings()
kustomizeOptions, err := kustomizeSettings.GetOptions(app.Spec.Source)
if err != nil {
return nil, nil, err
}
helmOptions, err := m.settingsMgr.GetHelmSettings()
if err != nil {
return nil, nil, err
}
ts.AddCheckpoint("build_options_ms")
serverVersion, apiResources, err := m.liveStateCache.GetVersionsInfo(app.Spec.Destination.Server)
if err != nil {
return nil, nil, err
}
conn, repoClient, err := m.repoClientset.NewRepoServerClient()
ts.AddCheckpoint("version_ms")
manifestInfo, err := repoClient.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: repo,
Repos: permittedHelmRepos,
Revision: revision,
NoCache: noCache,
NoRevisionCache: noRevisionCache,
AppLabelKey: appLabelKey,
AppName: app.InstanceName(m.namespace),
Namespace: app.Spec.Destination.Namespace,
ApplicationSource: &source,
Plugins: tools,
KustomizeOptions: kustomizeOptions,
KubeVersion: serverVersion,
ApiVersions: argo.APIResourcesToStrings(apiResources, true),
VerifySignature: verifySignature,
HelmRepoCreds: permittedHelmCredentials,
TrackingMethod: string(argo.GetTrackingMethod(m.settingsMgr)),
EnabledSourceTypes: enabledSourceTypes,
HelmOptions: helmOptions,
})
if err != nil {
return nil, nil, err
}
defer io.Close(conn)
targetObjs, err := unmarshalManifests(manifestInfo.Manifests)
manifestInfoMap := make(map[*v1alpha1.ApplicationSource]*apiclient.ManifestResponse)
targetObjs := make([]*unstructured.Unstructured, 0)
// Store the map of all sources having ref field into a map for applications with sources field
refSources, err := argo.GetRefSources(context.Background(), app.Spec, m.db)
if err != nil {
return nil, nil, fmt.Errorf("failed to get ref sources: %v", err)
}
for i, source := range sources {
if len(revisions) < len(sources) || revisions[i] == "" {
revisions[i] = source.TargetRevision
}
ts.AddCheckpoint("helm_ms")
repo, err := m.db.GetRepository(context.Background(), source.RepoURL)
if err != nil {
return nil, nil, err
}
kustomizeOptions, err := kustomizeSettings.GetOptions(source)
if err != nil {
return nil, nil, err
}
ts.AddCheckpoint("version_ms")
log.Debugf("Generating Manifest for source %s revision %s", source, revisions[i])
manifestInfo, err := repoClient.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: repo,
Repos: permittedHelmRepos,
Revision: revisions[i],
NoCache: noCache,
NoRevisionCache: noRevisionCache,
AppLabelKey: appLabelKey,
AppName: app.InstanceName(m.namespace),
Namespace: app.Spec.Destination.Namespace,
ApplicationSource: &source,
Plugins: tools,
KustomizeOptions: kustomizeOptions,
KubeVersion: serverVersion,
ApiVersions: argo.APIResourcesToStrings(apiResources, true),
VerifySignature: verifySignature,
HelmRepoCreds: permittedHelmCredentials,
TrackingMethod: string(argo.GetTrackingMethod(m.settingsMgr)),
EnabledSourceTypes: enabledSourceTypes,
HelmOptions: helmOptions,
HasMultipleSources: app.Spec.HasMultipleSources(),
RefSources: refSources,
})
if err != nil {
return nil, nil, err
}
// GenerateManifest can return empty ManifestResponse without error if app has multiple sources
// and if any of the source does not have path and chart field not specified.
// In that scenario, we continue to the next source
if app.Spec.HasMultipleSources() && len(manifestInfo.Manifests) == 0 {
continue
}
targetObj, err := unmarshalManifests(manifestInfo.Manifests)
if err != nil {
return nil, nil, err
}
targetObjs = append(targetObjs, targetObj...)
manifestInfoMap[&source] = manifestInfo
return nil, nil, err
}
ts.AddCheckpoint("unmarshal_ms")
@@ -238,7 +209,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp
}
logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds())
logCtx.Info("getRepoObjs stats")
return targetObjs, manifestInfoMap, nil
return targetObjs, manifestInfo, nil
}
func unmarshalManifests(manifests []string) ([]*unstructured.Unstructured, error) {
@@ -354,7 +325,7 @@ func verifyGnuPGSignature(revision string, project *appv1.AppProject, manifestIn
// CompareAppState compares application git state to the live app state, using the specified
// revision and supplied source. If revision or overrides are empty, then compares against
// revision and overrides in the app spec.
func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localManifests []string, hasMultipleSources bool) *comparisonResult {
func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revision string, source v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localManifests []string) *comparisonResult {
ts := stats.NewTimingStats()
appLabelKey, resourceOverrides, resFilter, err := m.getComparisonSettings()
@@ -362,24 +333,12 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
// return unknown comparison result if basic comparison settings cannot be loaded
if err != nil {
if hasMultipleSources {
return &comparisonResult{
syncStatus: &v1alpha1.SyncStatus{
ComparedTo: appv1.ComparedTo{Destination: app.Spec.Destination, Sources: sources},
Status: appv1.SyncStatusCodeUnknown,
Revisions: revisions,
},
healthStatus: &appv1.HealthStatus{Status: health.HealthStatusUnknown},
}
} else {
return &comparisonResult{
syncStatus: &v1alpha1.SyncStatus{
ComparedTo: appv1.ComparedTo{Source: sources[0], Destination: app.Spec.Destination},
Status: appv1.SyncStatusCodeUnknown,
Revision: revisions[0],
},
healthStatus: &appv1.HealthStatus{Status: health.HealthStatusUnknown},
}
return &comparisonResult{
syncStatus: &v1alpha1.SyncStatus{
ComparedTo: appv1.ComparedTo{Source: source, Destination: app.Spec.Destination},
Status: appv1.SyncStatusCodeUnknown,
},
healthStatus: &appv1.HealthStatus{Status: health.HealthStatusUnknown},
}
}
@@ -397,21 +356,11 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
logCtx.Infof("Comparing app state (cluster: %s, namespace: %s)", app.Spec.Destination.Server, app.Spec.Destination.Namespace)
var targetObjs []*unstructured.Unstructured
var manifestInfo *apiclient.ManifestResponse
now := metav1.Now()
var manifestInfoMap map[*v1alpha1.ApplicationSource]*apiclient.ManifestResponse
if len(localManifests) == 0 {
// If the length of revisions is not same as the length of sources,
// we take the revisions from the sources directly for all the sources.
if len(revisions) != len(sources) {
revisions = make([]string, 0)
for _, source := range sources {
revisions = append(revisions, source.TargetRevision)
}
}
targetObjs, manifestInfoMap, err = m.getRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project)
targetObjs, manifestInfo, err = m.getRepoObjs(app, source, appLabelKey, revision, noCache, noRevisionCache, verifySignature, project)
if err != nil {
targetObjs = make([]*unstructured.Unstructured, 0)
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
@@ -433,10 +382,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
failedToLoadObjs = true
}
}
// empty out manifestInfoMap
for as := range manifestInfoMap {
delete(manifestInfoMap, as)
}
manifestInfo = nil
}
ts.AddCheckpoint("git_ms")
@@ -470,7 +416,6 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
failedToLoadObjs = true
}
logCtx.Debugf("Retrieved lived manifests")
// filter out all resources which are not permitted in the application project
@@ -514,16 +459,10 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
log.Warnf("Could not get compare options from ConfigMap (assuming defaults): %v", err)
compareOptions = settings.GetDefaultDiffOptions()
}
manifestRevisions := make([]string, 0)
for _, manifestInfo := range manifestInfoMap {
manifestRevisions = append(manifestRevisions, manifestInfo.Revision)
}
// restore comparison using cached diff result if previous comparison was performed for the same revision
revisionChanged := len(manifestInfoMap) != len(sources) || !reflect.DeepEqual(app.Status.Sync.Revisions, manifestRevisions)
specChanged := !reflect.DeepEqual(app.Status.Sync.ComparedTo, appv1.ComparedTo{Source: app.Spec.GetSource(), Destination: app.Spec.Destination, Sources: sources})
revisionChanged := manifestInfo == nil || app.Status.Sync.Revision != manifestInfo.Revision
specChanged := !reflect.DeepEqual(app.Status.Sync.ComparedTo, appv1.ComparedTo{Source: app.Spec.Source, Destination: app.Spec.Destination})
_, refreshRequested := app.IsRefreshRequested()
noCache = noCache || refreshRequested || app.Status.Expired(m.statusRefreshTimeout) || specChanged || revisionChanged
@@ -652,32 +591,16 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
if failedToLoadObjs {
syncCode = v1alpha1.SyncStatusCodeUnknown
}
var revision string
if !hasMultipleSources && len(manifestRevisions) > 0 {
revision = manifestRevisions[0]
syncStatus := v1alpha1.SyncStatus{
ComparedTo: appv1.ComparedTo{
Source: source,
Destination: app.Spec.Destination,
},
Status: syncCode,
}
var syncStatus v1alpha1.SyncStatus
if hasMultipleSources {
syncStatus = v1alpha1.SyncStatus{
ComparedTo: appv1.ComparedTo{
Destination: app.Spec.Destination,
Sources: sources,
},
Status: syncCode,
Revisions: manifestRevisions,
}
} else {
syncStatus = v1alpha1.SyncStatus{
ComparedTo: appv1.ComparedTo{
Destination: app.Spec.Destination,
Source: app.Spec.GetSource(),
},
Status: syncCode,
Revision: revision,
}
if manifestInfo != nil {
syncStatus.Revision = manifestInfo.Revision
}
ts.AddCheckpoint("sync_ms")
healthStatus, err := setApplicationHealth(managedResources, resourceSummaries, resourceOverrides, app, m.persistResourceHealth)
@@ -688,10 +611,8 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
// Git has already performed the signature verification via its GPG interface, and the result is available
// in the manifest info received from the repository server. We now need to form our opinion about the result
// and stop processing if we do not agree about the outcome.
for _, manifestInfo := range manifestInfoMap {
if gpg.IsGPGEnabled() && verifySignature && manifestInfo != nil {
conditions = append(conditions, verifyGnuPGSignature(manifestInfo.Revision, project, manifestInfo)...)
}
if gpg.IsGPGEnabled() && verifySignature && manifestInfo != nil {
conditions = append(conditions, verifyGnuPGSignature(revision, project, manifestInfo)...)
}
compRes := comparisonResult{
@@ -703,18 +624,9 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
diffConfig: diffConfig,
diffResultList: diffResults,
}
if hasMultipleSources {
for _, manifestInfo := range manifestInfoMap {
compRes.appSourceTypes = append(compRes.appSourceTypes, appv1.ApplicationSourceType(manifestInfo.SourceType))
}
} else {
for _, manifestInfo := range manifestInfoMap {
compRes.appSourceType = v1alpha1.ApplicationSourceType(manifestInfo.SourceType)
break
}
if manifestInfo != nil {
compRes.appSourceType = v1alpha1.ApplicationSourceType(manifestInfo.SourceType)
}
app.Status.SetConditions(conditions, map[appv1.ApplicationConditionType]bool{
appv1.ApplicationConditionComparisonError: true,
appv1.ApplicationConditionSharedResourceWarning: true,
@@ -726,29 +638,18 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
return &compRes
}
func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, revisions []string, sources []v1alpha1.ApplicationSource, hasMultipleSources bool, startedAt metav1.Time) error {
func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, startedAt metav1.Time) error {
var nextID int64
if len(app.Status.History) > 0 {
nextID = app.Status.History.LastRevisionHistory().ID + 1
}
if hasMultipleSources {
app.Status.History = append(app.Status.History, v1alpha1.RevisionHistory{
DeployedAt: metav1.NewTime(time.Now().UTC()),
DeployStartedAt: &startedAt,
ID: nextID,
Sources: sources,
Revisions: revisions,
})
} else {
app.Status.History = append(app.Status.History, v1alpha1.RevisionHistory{
Revision: revision,
DeployedAt: metav1.NewTime(time.Now().UTC()),
DeployStartedAt: &startedAt,
ID: nextID,
Source: source,
})
}
app.Status.History = append(app.Status.History, v1alpha1.RevisionHistory{
Revision: revision,
DeployedAt: metav1.NewTime(time.Now().UTC()),
DeployStartedAt: &startedAt,
ID: nextID,
Source: source,
})
app.Status.History = app.Status.History.Trunc(app.Spec.GetRevisionHistoryLimit())

View File

@@ -38,11 +38,7 @@ func TestCompareAppStateEmpty(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -65,11 +61,7 @@ func TestCompareAppStateMissing(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
@@ -96,11 +88,7 @@ func TestCompareAppStateExtra(t *testing.T) {
},
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
assert.Equal(t, 1, len(compRes.resources))
@@ -126,11 +114,7 @@ func TestCompareAppStateHook(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
assert.Equal(t, 0, len(compRes.resources))
@@ -157,11 +141,7 @@ func TestCompareAppStateSkipHook(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
assert.Equal(t, 1, len(compRes.resources))
@@ -187,11 +167,7 @@ func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) {
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -219,11 +195,7 @@ func TestCompareAppStateExtraHook(t *testing.T) {
},
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -266,11 +238,7 @@ func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) {
},
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, 1, len(app.Status.Conditions))
@@ -321,11 +289,7 @@ func TestSetHealth(t *testing.T) {
},
})
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.Equal(t, health.HealthStatusHealthy, compRes.healthStatus.Status)
}
@@ -357,11 +321,7 @@ func TestSetHealthSelfReferencedApp(t *testing.T) {
},
})
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.Equal(t, health.HealthStatusHealthy, compRes.healthStatus.Status)
}
@@ -431,11 +391,7 @@ func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) {
},
})
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.Equal(t, health.HealthStatusUnknown, compRes.healthStatus.Status)
assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
@@ -482,7 +438,7 @@ func Test_appStateManager_persistRevisionHistory(t *testing.T) {
app.Spec.RevisionHistoryLimit = &i
}
addHistory := func() {
err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1.Time{})
err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, metav1.Time{})
assert.NoError(t, err)
}
addHistory()
@@ -518,7 +474,7 @@ func Test_appStateManager_persistRevisionHistory(t *testing.T) {
assert.Len(t, app.Status.History, 9)
metav1NowTime := metav1.NewTime(time.Now())
err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1NowTime)
err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, metav1NowTime)
assert.NoError(t, err)
assert.Equal(t, app.Status.History.LastRevisionHistory().DeployStartedAt, &metav1NowTime)
}
@@ -572,11 +528,7 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -598,11 +550,7 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -631,11 +579,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -657,11 +601,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -683,11 +623,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -709,11 +645,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -738,11 +670,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
ctrl := newFakeController(&data)
testProj := signedProj
testProj.Spec.SignatureKeys[0].KeyID = "4AEE18F83AFDEB24"
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes := ctrl.appStateManager.CompareAppState(app, &testProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &testProj, "abc123", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -767,11 +695,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
// it doesn't matter for our test whether local manifests are valid
localManifests := []string{"foobar"}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, localManifests)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
@@ -796,11 +720,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -809,7 +729,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
assert.Len(t, app.Status.Conditions, 0)
}
// Signature required and local manifests supplied and GPG subsystem is disabled - sync
// Signature required and local manifests supplied and GPG subystem is disabled - sync
{
app := newFakeApp()
data := fakeData{
@@ -825,11 +745,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
// it doesn't matter for our test whether local manifests are valid
localManifests := []string{""}
ctrl := newFakeController(&data)
sources := make([]argoappv1.ApplicationSource, 0)
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, localManifests)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)

View File

@@ -4,13 +4,12 @@ import (
"context"
"encoding/json"
"fmt"
cdcommon "github.com/argoproj/argo-cd/v2/common"
"os"
"strconv"
"sync/atomic"
"time"
cdcommon "github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/gitops-engine/pkg/sync"
"github.com/argoproj/gitops-engine/pkg/sync/common"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
@@ -66,8 +65,6 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
var syncOp v1alpha1.SyncOperation
var syncRes *v1alpha1.SyncOperationResult
var source v1alpha1.ApplicationSource
var sources []v1alpha1.ApplicationSource
revisions := make([]string, 0)
if state.Operation.Sync == nil {
state.Phase = common.OperationFailed
@@ -85,53 +82,31 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
return
}
if syncOp.Source == nil || (syncOp.Sources != nil && len(syncOp.Sources) > 0) {
// normal sync case (where source is taken from app.spec.sources)
if app.Spec.HasMultipleSources() {
sources = app.Spec.Sources
} else {
// normal sync case (where source is taken from app.spec.source)
source = app.Spec.GetSource()
sources = make([]v1alpha1.ApplicationSource, 0)
}
if syncOp.Source == nil {
// normal sync case (where source is taken from app.spec.source)
source = app.Spec.Source
} else {
// rollback case
if app.Spec.HasMultipleSources() {
sources = state.Operation.Sync.Sources
} else {
source = *state.Operation.Sync.Source
sources = make([]v1alpha1.ApplicationSource, 0)
}
source = *state.Operation.Sync.Source
}
if state.SyncResult != nil {
syncRes = state.SyncResult
revision = state.SyncResult.Revision
revisions = append(revisions, state.SyncResult.Revisions...)
} else {
syncRes = &v1alpha1.SyncOperationResult{}
// status.operationState.syncResult.source. must be set properly since auto-sync relies
// on this information to decide if it should sync (if source is different than the last
// sync attempt)
if app.Spec.HasMultipleSources() {
syncRes.Sources = sources
} else {
syncRes.Source = source
}
syncRes.Source = source
state.SyncResult = syncRes
}
// if we get here, it means we did not remember a commit SHA which we should be syncing to.
// This typically indicates we are just about to begin a brand new sync/rollback operation.
// Take the value in the requested operation. We will resolve this to a SHA later.
if app.Spec.HasMultipleSources() {
if len(revisions) != len(sources) {
revisions = syncOp.Revisions
}
} else {
if revision == "" {
revision = syncOp.Revision
}
if revision == "" {
// if we get here, it means we did not remember a commit SHA which we should be syncing to.
// This typically indicates we are just about to begin a brand new sync/rollback operation.
// Take the value in the requested operation. We will resolve this to a SHA later.
revision = syncOp.Revision
}
proj, err := argo.GetAppProject(app, listersv1alpha1.NewAppProjectLister(m.projInformer.GetIndexer()), m.namespace, m.settingsMgr, m.db, context.TODO())
@@ -141,23 +116,10 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
return
}
if app.Spec.HasMultipleSources() {
revisions = syncRes.Revisions
} else {
revisions = append(revisions, revision)
}
if !app.Spec.HasMultipleSources() {
sources = []v1alpha1.ApplicationSource{source}
revisions = []string{revision}
}
compareResult := m.CompareAppState(app, proj, revisions, sources, false, true, syncOp.Manifests, app.Spec.HasMultipleSources())
compareResult := m.CompareAppState(app, proj, revision, source, false, true, syncOp.Manifests)
// We now have a concrete commit SHA. Save this in the sync result revision so that we remember
// what we should be syncing to when resuming operations.
syncRes.Revision = compareResult.syncStatus.Revision
syncRes.Revisions = compareResult.syncStatus.Revisions
// If there are any comparison or spec errors error conditions do not perform the operation
if errConditions := app.Status.GetConditions(map[v1alpha1.ApplicationConditionType]bool{
@@ -340,7 +302,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
logEntry.WithField("duration", time.Since(start)).Info("sync/terminate complete")
if !syncOp.DryRun && len(syncOp.Resources) == 0 && state.Phase.Successful() {
err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source, compareResult.syncStatus.Revisions, compareResult.syncStatus.ComparedTo.Sources, app.Spec.HasMultipleSources(), state.StartedAt)
err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source, state.StartedAt)
if err != nil {
state.Phase = common.OperationError
state.Message = fmt.Sprintf("failed to record sync to history: %v", err)

View File

@@ -50,12 +50,12 @@ func TestPersistRevisionHistory(t *testing.T) {
}}
ctrl.appStateManager.SyncAppState(app, opState)
// Ensure we record spec.source into sync result
assert.Equal(t, app.Spec.GetSource(), opState.SyncResult.Source)
assert.Equal(t, app.Spec.Source, opState.SyncResult.Source)
updatedApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(context.Background(), app.Name, v1.GetOptions{})
assert.Nil(t, err)
assert.Equal(t, 1, len(updatedApp.Status.History))
assert.Equal(t, app.Spec.GetSource(), updatedApp.Status.History[0].Source)
assert.Equal(t, app.Spec.Source, updatedApp.Status.History[0].Source)
assert.Equal(t, "abc123", updatedApp.Status.History[0].Revision)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -1,78 +1,49 @@
# Release Process And Cadence
## Release Cycle
Argo CD is being developed using the following process:
### Schedule
* Maintainers commit to work on set of features and enhancements and create GitHub milestone to track the work.
* We are trying to avoid delaying release and prefer moving the feature into the next release if we cannot complete it on time.
* The new release is published every **3 months**.
* Critical bug-fixes are cherry-picked into the release branch and delivered using patch releases as frequently as needed.
These are the upcoming releases dates:
## Release Planning
| Release | Release Planning Meeting | Release Candidate 1 | General Availability | Release Champion | Checklist |
|---------|--------------------------|-----------------------|----------------------|-------------------------------------------------------|---------------------------------------------------------------|
| v2.6 | Monday, Dec. 12, 2022 | Monday, Dec. 19, 2022 | Monday, Feb. 6, 2023 | [William Tam](https://github.com/wtam2018) | [checklist](https://github.com/argoproj/argo-cd/issues/11563) |
| v2.7 | Monday, Mar. 6, 2023 | Monday, Mar. 20, 2023 | Monday, May. 1, 2023 | [Pavel Kostohrys](https://github.com/pasha-codefresh) |
| v2.8 | Monday, Jun. 5, 2023 | Monday, Jun. 19, 2023 | Monday, Aug. 7, 2023 | [Keith Chong](https://github.keithchong)
| v2.9 | Monday, Sep. 4, 2023 | Monday, Sep. 18, 2023 | Monday, Nov. 6, 2023 |
We are using GitHub milestones to perform release planning and tracking. Each release milestone includes two type of issues:
Actual release dates might differ from the plan by a few days.
* Issues that maintainers committed to working on. Maintainers decide which features they are committing to work on during the next release based on
their availability. Typically issues added offline by each maintainer and finalized during the contributors' meeting. Each such issue should be
assigned to maintainer who plans to implement and test it.
* Nice to have improvements contributed by community contributors. Nice to have issues are typically not critical, smallish enhancements that could
be contributed by community contributors. Maintainers are not committing to implement them but committing to review PR from the community.
### Release Process
The milestone should have a clear description of the most important features as well as the expected end date. This should provide clarity to end-users
about what to expect from the next release and when.
#### Minor Releases (e.g. 2.x.0)
In addition to the next milestone, we need to maintain a draft of the upcoming release milestone.
A minor Argo CD release occurs four times a year, once every three months. Each General Availability (GA) release is
preceded by several Release Candidates (RCs). The first RC is released three weeks before the scheduled GA date. This
effectively means that there is a three-week feature freeze.
## Community Contributions
These are the approximate release dates:
We receive a lot of contributions from our awesome community, and we're very grateful for that fact. However, reviewing and testing PRs is a lot of (unplanned) work and therefore, we cannot guarantee that contributions (especially large or complex ones) made by the community receive a timely review within a release's time frame. Maintainers may decide on their own to put work on a PR together with the contributor and in this case, the maintainer will self-assigned the PR and thereby committing to review, eventually merge and later test it on the release scope.
* The first Monday of February
* The first Monday of May
* The first Monday of August
* The first Monday of November
## Release Testing
Dates may be shifted slightly to accommodate holidays. Those shifts should be minimal.
We need to make sure that each change, both from maintainers and community contributors, is tested well and have someone who is going to fix last-minute
bugs. In order to ensure it, each merged pull request must have an assigned maintainer before it gets merged. The assigned maintainer will be working on
testing the introduced changes and fixing of any introduced bugs.
#### Patch Releases (e.g. 2.5.x)
We have a code freeze period two weeks before the release until the release branch is created. During code freeze no feature PR should be merged and it is ok
to merge bug fixes.
Argo CD patch releases occur on an as-needed basis. Only the three most recent minor versions are eligible for patch
releases. Versions older than the three most recent minor versions are considered EOL and will not receive bug fixes or
security updates.
Maintainers assigned to a PR that's been merged should drive testing and work on fixing last-minute issues. For tracking purposes after verifying PR the assigned
the maintainer should label it with a `verified` label.
#### Minor Release Planning Meeting
## Releasing
Roughly two weeks before the RC date, there will be a meeting to discuss which features are planned for the RC. This meeting is
for contributors to advocate for certain features. Features which have at least one approver (besides the contributor)
who can assure they will review/merge by the RC date will be included in the release milestone. All other features will
be dropped from the milestone (and potentially shifted to the next one).
The releasing procedure is described in [releasing](./releasing.md) document. Before closing the release milestone following should be verified:
Since not everyone will be able to attend the meeting, there will be a meeting doc. Contributors can add their feature
to a table, and Approvers can add their name to the table. Features with a corresponding approver will remain in the
release milestone.
#### Release Champion
To help manage all the steps involved in a release, we will have a Release Champion. The Release Champion will be
responsible for a checklist of items for their release. The checklist is an issue template in the Argo CD repository.
The Release Champion can be anyone in the Argo CD community. Some tasks (like cherry-picking bug fixes and cutting
releases) require [Approver](https://github.com/argoproj/argoproj/blob/master/community/membership.md#community-membership)
membership. The Release Champion can delegate tasks when necessary and will be responsible for coordinating with the
Approver.
### Feature Acceptance Criteria
To be eligible for inclusion in a minor release, a new feature must meet the following criteria before the releases RC
date.
If it is a large feature that involves significant design decisions, that feature must be described in a Proposal, and
that Proposal must be reviewed and merged.
The feature PR must include:
* Tests (passing)
* Documentation
* If necessary, a note in the Upgrading docs for the planned minor release
* The PR must be reviewed, approved, and merged by an Approver.
If these criteria are not met by the RC date, the feature will be ineligible for inclusion in the RC series or GA for
that minor release. It will have to wait for the next minor release.
- [ ] 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

View File

@@ -1,220 +0,0 @@
# Applications in any namespace
**Current feature state**: Beta
!!! warning
Please read this documentation carefully before you enable this feature. Misconfiguration could lead to potential security issues.
## Introduction
As of version 2.5, Argo CD supports managing `Application` resources in namespaces other than the control plane's namespace (which is usually `argocd`), but this feature has to be explicitly enabled and configured appropriately.
Argo CD administrators can define a certain set of namespaces where `Application` resources may be created, updated and reconciled in. However, applications in these additional namespaces will only be allowed to use certain `AppProjects`, as configured by the Argo CD administrators. This allows ordinary Argo CD users (e.g. application teams) to use patterns like declarative management of `Application` resources, implementing app-of-apps and others without the risk of a privilege escalation through usage of other `AppProjects` that would exceed the permissions granted to the application teams.
Some manual steps will need to be performed by the Argo CD administrator in order to enable this feature.
!!! note
This feature is considered beta as of now. Some of the implementation details may change over the course of time until it is promoted to a stable status. We will be happy if early adopters use this feature and provide us with bug reports and feedback.
## Prerequisites
### Cluster-scoped Argo CD installation
This feature can only be enabled and used when your Argo CD is installed as a cluster-wide instance, so it has permissions to list and manipulate resources on a cluster scope. It will *not* work with an Argo CD installed in namespace-scoped mode.
### Switch resource tracking method
Also, while technically not necessary, it is strongly suggested that you switch the application tracking method from the default `label` setting to either `annotation` or `annotation+label`. The reasonsing for this is, that application names will be a composite of the namespace's name and the name of the `Application`, and this can easily exceed the 63 characters length limit imposed on label values. Annotations have a notably greater length limit.
To enable annotation based resource tracking, refer to the documentation about [resource tracking methods](../../user-guide/resource_tracking/)
## Implementation details
### Overview
In order for an application to be managed and reconciled outside the Argo CD's control plane namespace, two prerequisites must match:
1. The `Application`'s namespace must be explicitly enabled using the `--application-namespaces` parameter for the `argocd-application-controller` and `argocd-server` workloads. This parameter controls the list of namespaces that Argo CD will be allowed to source `Application` resources from globally. Any namespace not configured here cannot be used from any `AppProject`.
1. The `AppProject` referenced by the `.spec.project` field of the `Application` must have the namespace listed in its `.spec.sourceNamespaces` field. This setting will determine whether an `Application` may use a certain `AppProject`. If an `Application` specifies an `AppProject` that is not allowed, Argo CD refuses to process this `Application`. As stated above, any namespace configured in the `.spec.sourceNamespaces` field must also be enabled globally.
`Applications` in different namespaces can be created and managed just like any other `Application` in the `argocd` namespace previously, either declaratively or through the Argo CD API (e.g. using the CLI, the web UI, the REST API, etc).
### Reconfigure Argo CD to allow certain namespaces
#### Change workload startup parameters
In order to enable this feature, the Argo CD administrator must reconfigure the `argocd-server` and `argocd-application-controller` workloads to add the `--application-namespaces` parameter to the container's startup command.
The `--application-namespaces` parameter takes a comma-separated list of namespaces where `Applications` are to be allowed in. Each entry of the list supports shell-style wildcards such as `*`, so for example the entry `app-team-*` would match `app-team-one` and `app-team-two`. To enable all namespaces on the cluster where Argo CD is running on, you can just specify `*`, i.e. `--application-namespaces=*`.
The startup parameters for both, the `argocd-server` and the `argocd-application-controller` can also be conveniently set up and kept in sync by specifying the `application.namespaces` settings in the `argocd-cmd-params-cm` ConfigMap _instead_ of changing the manifests for the respective workloads. For example:
```yaml
data:
application.namespaces: app-team-one, app-team-two
```
would allow the `app-team-one` and `app-team-two` namespaces for managing `Application` resources. After a change to the `argocd-cmd-params-cm` namespace, the appropriate workloads need to be restarted:
```bash
kubectl rollout restart -n argocd deployment argocd-server
kubectl rollout restart -n argocd statefulset argocd-application-controller
```
#### Adapt Kubernetes RBAC
We decided to not extend the Kubernetes RBAC for the `argocd-server` workload by default for the time being. If you want `Applications` in other namespaces to be managed by the Argo CD API (i.e. the CLI and UI), you need to extend the Kubernetes permissions for the `argocd-server` ServiceAccount.
We supply a `ClusterRole` and `ClusterRoleBinding` suitable for this purpose in the `examples/k8s-rbac/argocd-server-applications` directory. For a default Argo CD installation (i.e. installed to the `argocd` namespace), you can just apply them as-is:
```shell
kubectl apply -f examples/k8s-rbac/argocd-server-applications/
```
!!! note
At some later point in time, we may make this cluster role part of the default installation manifests.
### Allowing additional namespaces in an AppProject
Any user with Kubernetes access to the Argo CD control plane's namespace (`argocd`), especially those with permissions to create or update `Applications` in a declarative way, is to be considered an Argo CD admin.
This prevented unprivileged Argo CD users from declaratively creating or managing `Applications` in the past. Those users were constrained to using the API instead, subject to Argo CD RBAC which ensures only `Applications` in allowed `AppProjects` were created.
For an `Application` to be created outside the `argocd` namespace, the `AppProject` referred to in the `Application`'s `.spec.project` field must include the `Application`'s namespace in its `.spec.sourceNamespaces` field.
For example, consider the two following (incomplete) `AppProject` specs:
```yaml
kind: AppProject
apiVersion: argoproj.io/v1alpha1
metadata:
name: project-one
namespace: argocd
spec:
sourceNamespaces:
- namespace-one
```
and
```yaml
kind: AppProject
apiVersion: argoproj.io/v1alpha1
metadata:
name: project-two
namespace: argocd
spec:
sourceNamespaces:
- namespace-two
```
In order for an Application to set `.spec.project` to `project-one`, it would have to be created in either namespace `namespace-one` or `argocd`. Likewise, in order for an Application to set `.spec.project` to `project-two`, it would have to be created in either namespace `namespace-two` or `argocd`.
If an Application in `namespace-two` would set their `.spec.project` to `project-one` or an Application in `namespace-one` would set their `.spec.project` to `project-two`, Argo CD would consider this as a permission violation and refuse to reconcile the Application.
Also, the Argo CD API will enforce these constraints, regardless of the Argo CD RBAC permissions.
The `.spec.sourceNamespaces` field of the `AppProject` is a list that can contain an arbitrary amount of namespaces, and each entry supports shell-style wildcard, so that you can allow namespaces with patterns like `team-one-*`.
!!! warning
Do not add user controlled namespaces in the `.spec.sourceNamespaces` field of any privileged AppProject like the `default` project. Always make sure that the AppProject follows the principle of granting least required privileges. Never grant access to the `argocd` namespace within the AppProject.
!!! note
For backwards compatibility, Applications in the Argo CD control plane's namespace (`argocd`) are allowed to set their `.spec.project` field to reference any AppProject, regardless of the restrictions placed by the AppProject's `.spec.sourceNamespaces` field.
### Application names
For the CLI and UI, applications are now referred to and displayed as in the format `<namespace>/<name>`.
For backwards compatibility, if the namespace of the Application is the control plane's namespace (i.e. `argocd`), the `<namespace>` can be omitted from the application name when referring to it. For example, the application names `argocd/someapp` and `someapp` are semantically the same and refer to the same application in the CLI and the UI.
### Application RBAC
The RBAC syntax for Application objects has been changed from `<project>/<application>` to `<project>/<namespace>/<application>` to accomodate the need to restrict access based on the source namespace of the Application to be managed.
For backwards compatibility, Applications in the `argocd` namespace can still be refered to as `<project>/<application>` in the RBAC policy rules.
Wildcards do not make any distinction between project and application namespaces yet. For example, the following RBAC rule would match any application belonging to project `foo`, regardless of the namespace it is created in:
```
p, somerole, applications, get, foo/*, allow
```
If you want to restrict access to be granted only to `Applications` in project `foo` within namespace `bar`, the rule would need to be adapted as follows:
```
p, somerole, applications, get, foo/bar/*, allow
```
## Managing applications in other namespaces
### Declaratively
For declarative management of Applications, just create the Application from a YAML or JSON manifest in the desired namespace. Make sure that the `.spec.project` field refers to an AppProject that allows this namespace. For example, the following (incomplete) Application manifest creates an Application in the namespace `some-namespace`:
```yaml
kind: Application
apiVersion: argoproj.io/v1alpha1
metadata:
name: some-app
namespace: some-namespace
spec:
project: some-project
# ...
```
The project `some-project` will then need to specify `some-namespace` in the list of allowed source namespaces, e.g.
```yaml
kind: AppProject
apiVersion: argoproj.io/v1alpha1
metadata:
name: some-project
namespace: argocd
spec:
sourceNamespaces:
- some-namespace
```
### Using the CLI
You can use all existing Argo CD CLI commands for managing applications in other namespaces, exactly as you would use the CLI to manage applications in the control plane's namespace.
For example, to retrieve the `Application` named `foo` in the namespace `bar`, you can use the following CLI command:
```shell
argocd app get foo/bar
```
Likewise, to manage this application, keep referring to it as `foo/bar`:
```bash
# Create an application
argocd app create foo/bar ...
# Sync the application
argocd app sync foo/bar
# Delete the application
argocd app delete foo/bar
# Retrieve application's manifest
argocd app manifests foo/bar
```
As stated previously, for applications in the Argo CD's control plane namespace, you can omit the namespace from the application name.
### Using the UI
Similar to the CLI, you can refer to the application in the UI as `foo/bar`.
For example, to create an application named `bar` in the namespace `foo` in the web UI, set the application name in the creation dialogue's _Application Name_ field to `foo/bar`. If the namespace is omitted, the control plane's namespace will be used.
### Using the REST API
If you are using the REST API, the namespace for `Application` cannot be specified as the application name, and resources need to be specified using the optional `appNamespace` query parameter. For example, to work with the `Application` resource named `foo` in the namespace `bar`, the request would look like follows:
```bash
GET /api/v1/applications/foo?appNamespace=bar
```
For other operations such as `POST` and `PUT`, the `appNamespace` parameter must be part of the request's payload.
For `Application` resources in the control plane namespace, this parameter can be omitted.

View File

@@ -45,9 +45,6 @@ spec:
valueFiles:
- values-prod.yaml
# Ignore locally missing valueFiles when installing Helm chart. Defaults to false
ignoreMissingValueFiles: false
# Values file as block file
values: |
ingress:
@@ -64,9 +61,6 @@ spec:
hosts:
- mydomain.example.com
# Skip custom resource definition installation if chart contains custom resource definitions. Defaults to false
skipCrds: false
# Optional Helm version to template with. If omitted it will fall back to look at the 'apiVersion' in Chart.yaml
# and decide which Helm binary to use automatically. This field can be either 'v2' or 'v3'.
version: v2
@@ -132,20 +126,10 @@ spec:
- name: map-param
map:
param-name: param-value
# Sources field specifies the list of sources for the application
sources:
- repoURL: https://github.com/argoproj/argocd-example-apps.git # Can point to either a Helm chart repo or a git repo.
targetRevision: HEAD # For Helm, this refers to the chart version.
path: guestbook # This has no meaning for Helm charts pulled directly from a Helm repo instead of git.
ref: my-repo # For Helm, acts as a reference to this source for fetching values files from this source. Has no meaning when under `source` field
# Destination cluster and namespace to deploy the application
destination:
# cluster API URL
server: https://kubernetes.default.svc
# or cluster name
# name: in-cluster
# The namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace
namespace: guestbook

View File

@@ -18,7 +18,7 @@ See 'How to modify ApplicationSet container parameters' below for detailed steps
The ApplicationSet controller supports a parameter `--policy`, which is specified on launch (within the controller Deployment container), and which restricts what types of modifications will be made to managed Argo CD `Application` resources.
The `--policy` parameter takes one of the following valid values: `sync`, `create-only`, `create-update`, and `create-delete`. (`sync` is the default, which is used if the `--policy` parameter is not specified; the other policies are described below).
The `--policy` parameter takes three values: `sync`, `create-only`, and `create-update`. (`sync` is the default, which is used if the `--policy` parameter is not specified; the other policies are described below).
To allow the ApplicationSet controller to *create* `Application` resources, but prevent any further modification, such as deletion, or modification of Application fields, add this parameter in the ApplicationSet controller:
```
@@ -34,13 +34,6 @@ To allow the ApplicationSet controller to create or modify `Application` resourc
This may be useful to users looking for additional protection against deletion of the Applications generated by the controller.
### Policy - `create-delete`: Prevent ApplicationSet controller from updating Applications
To allow the ApplicationSet controller to create or delete `Application` resources, but prevent Applications from being updated, add the following parameter to the ApplicationSet controller `Deployment`:
```
--policy create-delete
```
### Prevent an `Application`'s child resources from being deleted, when the parent Application is deleted
By default, when an `Application` resource is deleted by the ApplicationSet controller, all of the child resources of the Application will be deleted as well (such as, all of the Application's `Deployments`, `Services`, etc).

View File

@@ -70,8 +70,6 @@ The generator parameters are:
**Note**: The right-most path name always becomes `{{path.basename}}`. For example, for `- path: /one/two/three/four`, `{{path.basename}}` is `four`.
**Note**: If the `pathParamPrefix` option is specified, all `path`-related parameter names above will be prefixed with the specified value and a dot separator. E.g., if `pathParamPrefix` is `myRepo`, then the generated parameter name would be `myRepo.path` instead of `path`. Using this option is necessary in a Matrix generator where both child generators are Git generators (to avoid conflicts when merging the child generators items).
Whenever a new Helm chart/Kustomize YAML/Application/plain subdirectory is added to the Git repository, the ApplicationSet controller will detect this change and automatically deploy the resulting manifests within new `Application` resources.
As with other generators, clusters *must* already be defined within Argo CD, in order to generate Applications for them.
@@ -286,7 +284,6 @@ In addition to the flattened key/value pairs from the configuration file, the fo
**Note**: The right-most *directory* name always becomes `{{path.basename}}`. For example, from `- path: /one/two/three/four/config.json`, `{{path.basename}}` will be `four`.
The filename can always be accessed using `{{path.filename}}`.
**Note**: If the `pathParamPrefix` option is specified, all `path`-related parameter names above will be prefixed with the specified value and a dot separator. E.g., if `pathParamPrefix` is `myRepo`, then the generated parameter name would be `myRepo.path` instead of `path`. Using this option is necessary in a Matrix generator where both child generators are Git generators (to avoid conflicts when merging the child generators items).
## Webhook Configuration

View File

@@ -11,8 +11,6 @@ By combining both generators parameters, to produce every possible combination,
Any set of generators may be used, with the combined values of those generators inserted into the `template` parameters, as usual.
**Note**: If both child generators are Git generators, one or both of them must use the `pathParamPrefix` option to avoid conflicts when merging the child generators items.
## Example: Git Directory generator + Cluster generator
As an example, imagine that we have two clusters:
@@ -168,102 +166,6 @@ In the 2nd child generator, the label selector with label `kubernetes.io/environ
So in the above example, clusters with the label `kubernetes.io/environment: prod` will have only prod-specific configuration (ie. `prod/config.json`) applied to it, wheres clusters
with the label `kubernetes.io/environment: dev` will have only dev-specific configuration (ie. `dev/config.json`)
## Example: Two Git Generators Using `pathParamPrefix`
The matrix generator will fail if its children produce results containing identical keys with differing values.
This poses a problem for matrix generators where both children are Git generators since they auto-populate `path`-related parameters in their outputs.
To avoid this problem, specify a `pathParamPrefix` on one or both of the child generators to avoid conflicting parameter keys in the output.
```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: two-gits-with-path-param-prefix
spec:
generators:
- matrix:
generators:
# git file generator referencing files containing details about each
# app to be deployed (e.g., `appName`).
- git:
repoURL: https://github.com/some-org/some-repo.git
revision: HEAD
files:
- path: "apps/*.json"
pathParamPrefix: app
# git file generator referencing files containing details about
# locations to which each app should deploy (e.g., `region` and
# `clusterName`).
- git:
repoURL: https://github.com/some-org/some-repo.git
revision: HEAD
files:
- path: "targets/{{appName}}/*.json"
pathParamPrefix: target
template: {} # ...
```
Then, given the following file structure/content:
```
├── apps
│ ├── app-one.json
│ │ { "appName": "app-one" }
│ └── app-two.json
│ { "appName": "app-two" }
└── targets
├── app-one
│ ├── east-cluster-one.json
│ │ { "region": "east", "clusterName": "cluster-one" }
│ └── east-cluster-two.json
│ { "region": "east", "clusterName": "cluster-two" }
└── app-two
├── east-cluster-one.json
│ { "region": "east", "clusterName": "cluster-one" }
└── west-cluster-three.json
{ "region": "west", "clusterName": "cluster-three" }
```
…the matrix generator above would yield the following results:
```yaml
- appName: app-one
app.path: /apps
app.path.filename: app-one.json
# plus additional path-related parameters from the first child generator, all
# prefixed with "app".
region: east
clusterName: cluster-one
target.path: /targets/app-one
target.path.filename: east-cluster-one.json
# plus additional path-related parameters from the second child generator, all
# prefixed with "target".
- appName: app-one
app.path: /apps
app.path.filename: app-one.json
region: east
clusterName: cluster-two
target.path: /targets/app-one
target.path.filename: east-cluster-two.json
- appName: app-two
app.path: /apps
app.path.filename: app-two.json
region: east
clusterName: cluster-one
target.path: /targets/app-two
target.path.filename: east-cluster-one.json
- appName: app-two
app.path: /apps
app.path.filename: app-two.json
region: west
clusterName: cluster-three
target.path: /targets/app-two
target.path.filename: west-cluster-three.json
```
## Restrictions
1. The Matrix generator currently only supports combining the outputs of only two child generators (eg does not support generating combinations for 3 or more).

View File

@@ -60,7 +60,7 @@ spec:
* `repo`: Required name of the GitHub repository.
* `api`: If using GitHub Enterprise, the URL to access it. (Optional)
* `tokenRef`: A `Secret` name and key containing the GitHub access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories. (Optional)
* `labels`: Filter the PRs to those containing **all** of the labels listed. (Optional)
* `labels`: Labels is used to filter the PRs that you want to target. (Optional)
* `appSecretName`: A `Secret` name containing a GitHub App secret in [repo-creds format][repo-creds].
[repo-creds]: ../declarative-setup.md#repository-credentials
@@ -274,7 +274,6 @@ spec:
* `branch_slug`: The branch name will be cleaned to be conform to the DNS label standard as defined in [RFC 1123](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names), and truncated to 50 characters to give room to append/suffix-ing it with 13 more characters.
* `head_sha`: This is the SHA of the head of the pull request.
* `head_short_sha`: This is the short SHA of the head of the pull request (8 characters long or the length of the head SHA if it's shorter).
* `labels`: The array of pull request labels. (Supported only for Go Template ApplicationSet manifests.)
## Webhook Configuration
@@ -319,7 +318,3 @@ The Pull Request Generator will requeue when the next action occurs.
- `merge`
For more information about each event, please refer to the [official documentation](https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#merge-request-events).
## Lifecycle
An Application will be generated when a Pull Request is discovered when the configured criteria is met - i.e. for GitHub when a Pull Request matches the specified `labels` and/or `pullRequestState`. Application will be removed when a Pull Request no longer meets the specified criteria.

View File

@@ -219,41 +219,6 @@ spec:
* `api`: Optional. URL to Azure DevOps. If not set, `https://dev.azure.com` is used.
* `allBranches`: Optional, default `false`. If `true`, scans every branch of eligible repositories. If `false`, check only the default branch of the eligible repositories.
## Bitbucket Cloud
The Bitbucket mode uses the Bitbucket API V2 to scan a workspace in bitbucket.org.
```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapps
spec:
generators:
- scmProvider:
bitbucket:
# The workspace id (slug).
owner: "example-owner"
# The user to use for basic authentication with an app password.
user: "example-user"
# If true, scan every branch of every repository. If false, scan only the main branch. Defaults to false.
allBranches: true
# Reference to a Secret containing an app password.
appPasswordRef:
secretName: appPassword
key: password
template:
# ...
```
* `owner`: The workspace ID (slug) to use when looking up repositories.
* `user`: The user to use for authentication to the Bitbucket API V2 at bitbucket.org.
* `allBranches`: By default (false) the template will only be evaluated for the main branch of each repo. If this is true, every branch of every repository will be passed to the filters. If using this flag, you likely want to use a `branchMatch` filter.
* `appPasswordRef`: A `Secret` name and key containing the bitbucket app password to use for requests.
This SCM provider does not yet support label filtering
Available clone protocols are `ssh` and `https`.
## Filters

View File

@@ -87,7 +87,7 @@ By activating Go Templating, `{{ .path }}` becomes an object. Therefore, some ch
generators' templating:
- `{{ path }}` becomes `{{ .path.path }}`
- `{{ path[n] }}` becomes `{{ index .path.segments n }}`
- `{{ path[n] }}` becomes `{{ .path.segments[n] }}`
Here is an example:
@@ -155,7 +155,7 @@ It is also possible to use Sprig functions to construct the path variables manua
| `{{path.filename}}` | `{{.path.filename}}` | `{{.path.filename}}` |
| `{{path.basenameNormalized}}` | `{{.path.basenameNormalized}}` | `{{normalize .path.path}}` |
| `{{path.filenameNormalized}}` | `{{.path.filenameNormalized}}` | `{{normalize .path.filename}}` |
| `{{path[N]}}` | `-` | `{{index .path.segments N}}` |
| `{{path[N]}}` | `{{.path.segments[N]}}` | `{{index (splitList "/" .path.path) N}}` |
## Examples

View File

@@ -1,111 +0,0 @@
# Progressive Syncs
!!! warning "Alpha Feature"
This is an experimental, alpha-quality feature that allows you to control the order in which the ApplicationSet controller will create or update the Applications owned by an ApplicationSet resource. It may be removed in future releases or modified in backwards-incompatible ways.
## Use Cases
The Progressive Syncs feature set is intended to be light and flexible. The feature only interacts with the health of managed Applications. It is not intended to support direct integrations with other Rollout controllers (such as the native ReplicaSet controller or Argo Rollouts).
* Progressive Syncs watch for the managed Application resources to become "Healthy" before proceeding to the next stage.
* Deployments, DaemonSets, StatefulSets, and [Argo Rollouts](https://argoproj.github.io/argo-rollouts/) are all supported, because the Application enters a "Progressing" state while pods are being rolled out. In fact, any resource with a health check that can report a "Progressing" status is supported.
* [Argo CD Resource Hooks](../../user-guide/resource_hooks.md) are supported. We recommend this approach for users that need advanced functionality when an Argo Rollout cannot be used, such as smoke testing after a DaemonSet change.
## Enabling Progressive Syncs
As an experimental feature, progressive syncs must be explicitly enabled, in one of these ways.
1. Pass `--enable-progressive-syncs` to the ApplicationSet controller args.
1. Set `ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS=true` in the ApplicationSet controller environment variables.
1. Set `applicationsetcontroller.enable.progressive.syncs: true` in the Argo CD ConfigMap.
## Strategies
* AllAtOnce (default)
* RollingSync
### AllAtOnce
This default Application update behavior is unchanged from the original ApplicationSet implementation.
All Applications managed by the ApplicationSet resource are updated simultaneously when the ApplicationSet is updated.
### RollingSync
This update strategy allows you to group Applications by labels present on the generated Application resources.
When the ApplicationSet changes, the changes will be applied to each group of Application resources sequentially.
* Application groups are selected using their labels and `matchExpressions`.
* All `matchExpressions` must be true for an Application to be selected (multiple expressions match with AND behavior).
* The `In` and `NotIn` operators must match at least one value to be considered true (OR behavior).
* The `NotIn` operatorn has priority in the event that both a `NotIn` and `In` operator produce a match.
* All Applications in each group must become Healthy before the ApplicationSet controller will proceed to update the next group of Applications.
* The number of simultaneous Application updates in a group will not exceed its `maxUpdate` parameter (default is 100%, unbounded).
* RollingSync will capture external changes outside the ApplicationSet resource, since it relies on watching the OutOfSync status of the managed Applications.
* RollingSync will force all generated Applications to have autosync disabled. Warnings are printed in the applicationset-controller logs for any Application specs with an automated syncPolicy enabled.
* Sync operations are triggered the same way as if they were triggered by the UI or CLI (by directly setting the `operation` status field on the Application resource). This means that a RollingSync will respect sync windows just as if a user had clicked the "Sync" button in the Argo UI.
* When a sync is triggered, the sync is performed with the same syncPolicy configured for the Application. For example, this preserves the Application's retry settings.
* If an Application is considered "Pending" for `applicationsetcontroller.default.application.progressing.timeout` seconds, the Application is automatically moved to Healthy status (default 300).
#### Example
The following example illustrates how to stage a progressive sync over Applications with explicitly configured environment labels.
Once a change is pushed, the following will happen in order.
* All `env-dev` Applications will be updated simultaneously.
* The rollout will wait for all `env-qa` Applications to be manually synced via the `argocd` CLI or by clicking the Sync button in the UI.
* 10% of all `env-prod` Applications will be updated at a time until all `env-prod` Applications have been updated.
```
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook
spec:
generators:
- list:
elements:
- cluster: engineering-dev
url: https://1.2.3.4
env: env-dev
- cluster: engineering-qa
url: https://2.4.6.8
env: env-qa
- cluster: engineering-prod
url: https://9.8.7.6/
env: env-prod
strategy:
type: RollingSync
rollingSync:
steps:
- matchExpressions:
- key: envLabel
operator: In
values:
- env-dev
#maxUpdate: 100% # if undefined, all applications matched are updated together (default is 100%)
- matchExpressions:
- key: envLabel
operator: In
values:
- env-qa
maxUpdate: 0 # if 0, no matched applications will be updated
- matchExpressions:
- key: envLabel
operator: In
values:
- env-prod
maxUpdate: 10% # maxUpdate supports both integer and percentage string values (rounds down, but floored at 1 Application for >0%)
goTemplate: true
template:
metadata:
name: '{{.cluster}}-guestbook'
labels:
envLabel: '{{.env}}'
spec:
project: my-project
source:
repoURL: https://github.com/infra-team/cluster-deployments.git
targetRevision: HEAD
path: guestbook/{{.cluster}}
destination:
server: '{{.url}}'
namespace: guestbook
```

View File

@@ -47,7 +47,7 @@ data:
help.download.windows-amd64: "path-or-url-to-download"
# A dex connector configuration (optional). See SSO configuration documentation:
# https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/user-management/index.md#sso
# https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/sso
# https://dexidp.io/docs/connectors/
dex.config: |
connectors:
@@ -305,29 +305,3 @@ data:
# token verification to pass despite the OIDC provider having an invalid certificate. Only set to "true" if you
# understand the risks.
oidc.tls.insecure.skip.verify: "false"
# Add Deep Links to ArgoCD UI
# sample project level links
project.links: |
- url: https://myaudit-system.com?project={{.metadata.name}}
title: Audit
description: system audit logs
icon.class: "fa-book"
# sample application level links
application.links: |
# pkg.go.dev/text/template is used for evaluating url templates
- url: https://mycompany.splunk.com?search={{.spec.destination.namespace}}
title: Splunk
# conditionally show link e.g. for specific project
# github.com/antonmedv/expr is used for evaluation of conditions
- url: https://mycompany.splunk.com?search={{.spec.destination.namespace}}
title: Splunk
if: spec.project == "default"
- url: https://{{.metadata.annotations.splunkhost}}?search={{.spec.destination.namespace}}
title: Splunk
if: metadata.annotations.splunkhost
# sample resource level links
resource.links: |
- url: https://mycompany.splunk.com?search={{.metadata.namespace}}
title: Splunk
if: kind == "Pod" || kind == "Deployment"

View File

@@ -104,8 +104,6 @@ data:
server.app.state.cache.expiration: "1h0m0s"
# Cache expiration default (default 24h0m0s)
server.default.cache.expiration: "24h0m0s"
# Enable the experimental proxy extension feature
server.enable.proxy.extension: "false"
## Repo-server properties
# Set the logging format. One of: text|json (default "text")
@@ -152,7 +150,7 @@ data:
applicationsetcontroller.enable.leader.election: "false"
# Argo CD repo namespace (default: argocd)
applicationsetcontroller.namespace: ""
# "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion), 'create-delete' (no update)"
# "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion)"
applicationsetcontroller.policy: "sync"
# Print debug logs. Takes precedence over loglevel
applicationsetcontroller.debug: "false"
@@ -164,5 +162,3 @@ data:
applicationsetcontroller.dryrun: "false"
# Enable git submodule support
applicationsetcontroller.enable.git.submodule: "true"
# Enables use of the Progressive Syncs capability
applicationsetcontroller.enable.progressive.syncs: "false"

View File

@@ -4,33 +4,18 @@
apiVersion: v1
kind: Secret
metadata:
name: my-private-https-repo
name: my-private-repo
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository
stringData:
url: https://github.com/argoproj/argocd-example-apps
url: https://github.com/argoproj/my-private-repository
password: my-password
username: my-username
insecure: "true" # Ignore validity of server's TLS certificate. Defaults to "false"
forceHttpBasicAuth: "true" # Skip auth method negotiation and force usage of HTTP basic auth. Defaults to "false"
enableLfs: "true" # Enable git-lfs for this repository. Defaults to "false"
---
apiVersion: v1
kind: Secret
metadata:
name: my-private-ssh-repo
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository
stringData:
url: ssh://git@github.com/argoproj/argocd-example-apps
sshPrivateKey: |
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
insecure: "true" # Do not perform a host key check for the server. Defaults to "false"
enableLfs: "true" # Enable git-lfs for this repository. Defaults to "false"
---
apiVersion: v1
kind: Secret

View File

@@ -7,7 +7,7 @@ other than what Argo CD bundles. Some reasons to do this might be:
* To upgrade/downgrade to a specific version of a tool due to bugs or bug fixes.
* To install additional dependencies to be used by kustomize's configmap/secret generators.
(e.g. curl, vault, gpg, AWS CLI)
* To install a [config management plugin](config-management-plugins.md).
* To install a [config management plugin](../user-guide/config-management-plugins.md).
As the Argo CD repo-server is the single service responsible for generating Kubernetes manifests, it
can be customized to use alternative toolchain required by your environment.
@@ -51,7 +51,7 @@ following example builds an entirely customized repo-server from a Dockerfile, i
dependencies that may be needed for generating manifests.
```Dockerfile
FROM argoproj/argocd:v2.5.4 # Replace tag with the appropriate argo version
FROM argoproj/argocd:latest
# Switch to root for the ability to perform install
USER root

View File

@@ -255,33 +255,6 @@ stringData:
-----END OPENSSH PRIVATE KEY-----
```
Example for Google Cloud Source repositories:
```yaml
kind: Secret
metadata:
name: github-repo
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository
stringData:
type: git
repo: https://source.developers.google.com/p/my-google-project/r/my-repo
gcpServiceAccountKey: |
{
"type": "service_account",
"project_id": "my-google-project",
"private_key_id": "REDACTED",
"private_key": "-----BEGIN PRIVATE KEY-----\nREDACTED\n-----END PRIVATE KEY-----\n",
"client_email": "argocd-service-account@my-google-project.iam.gserviceaccount.com",
"client_id": "REDACTED",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/argocd-service-account%40my-google-project.iam.gserviceaccount.com"
}
```
!!! tip
The Kubernetes documentation has [instructions for creating a secret containing a private key](https://kubernetes.io/docs/concepts/configuration/secret/#use-case-pod-with-ssh-keys).

View File

@@ -1,63 +0,0 @@
# Deep Links
Deep links allow users to quickly redirect to third-party systems, such as Splunk, Datadog, etc. from the Argo CD
user interface.
Argo CD administrator will be able to configure links to third-party systems by providing
deep link templates configured in `argocd-cm`. The templates can be conditionally rendered and are able
to reference different types of resources relating to where the links show up, this includes projects, applications,
or individual resources (pods, services, etc.).
## Configuring Deep Links
The configuration for Deep Links is present in `argocd-cm` as `<location>.links` fields where
`<location>` determines where it will be displayed. The possible values for `<location>` are :
- `project` : all links under this field will show up in the project tab in the Argo CD UI
- `application` : all links under this field will show up in the application summary tab
- `resource` : all links under this field will show up in the resource (deployments, pods, services, etc.) summary tab
Each link in the list has five subfields :
1. `title` : title/tag that will be displayed in the UI corresponding to that link
2. `url` : the actual URL where the deep link will redirect to, this field can be templated to use data from the
corresponing application, project or resource objects (depending on where it is located). This uses [text/template](pkg.go.dev/text/template) pkg for templating
3. `description` (optional) : a description for what the deep link is about
4. `icon.class` (optional) : a font-awesome icon class to be used when displaying the links in dropdown menus
5. `if` (optional) : a conditional statement that results in either `true` or `false`, it also has access to the same
data as the `url` field. If the condition resolves to `true` the deep link will be displayed - else it will be hidden. If
the field is omitted, by default the deep links will be displayed. This uses [antonmedv/expr](https://github.com/antonmedv/expr/tree/master/docs) for evaluating conditions
!!!note
For resources of kind Secret the data fields are redacted but other fields are accessible for templating the deep links.
!!!warning
Make sure to validate the url templates and inputs to prevent data leaks or possible generation of any malicious links.
An example `argocd-cm.yaml` file with deep links and their variations :
```yaml
# sample project level links
project.links: |
- url: https://myaudit-system.com?project={{.metadata.name}}
title: Audit
description: system audit logs
icon.class: "fa-book"
# sample application level links
application.links: |
# pkg.go.dev/text/template is used for evaluating url templates
- url: https://mycompany.splunk.com?search={{.spec.destination.namespace}}
title: Splunk
# conditionally show link e.g. for specific project
# github.com/antonmedv/expr is used for evaluation of conditions
- url: https://mycompany.splunk.com?search={{.spec.destination.namespace}}
title: Splunk
if: spec.project == "default"
- url: https://{{.metadata.annotations.splunkhost}}?search={{.spec.destination.namespace}}
title: Splunk
if: metadata.annotations.splunkhost
# sample resource level links
resource.links: |
- url: https://mycompany.splunk.com?search={{.metadata.namespace}}
title: Splunk
if: kind == "Pod" || kind == "Deployment"
```

View File

@@ -16,8 +16,6 @@ with at least one value for `hostname` or `IP`.
### Ingress
* The `status.loadBalancer.ingress` list is non-empty, with at least one value for `hostname` or `IP`.
### Job
* If job `.spec.suspended` is set to 'true', then the job and app health will be marked as suspended.
### PersistentVolumeClaim
* The `status.phase` is `Bound`
@@ -40,7 +38,7 @@ metadata:
data:
resource.customizations: |
argoproj.io/Application:
health.lua: |
health.lua: |
hs = {}
hs.status = "Progressing"
hs.message = ""
@@ -66,11 +64,11 @@ There are two ways to configure a custom health check. The next two sections des
### Way 1. Define a Custom Health Check in `argocd-cm` ConfigMap
Custom health checks can be defined in
Custom health checks can be defined in
```yaml
resource.customizations: |
<group/kind>:
health.lua: |
health.lua: |
```
field of `argocd-cm`. If you are using argocd-operator, this is overridden by [the argocd-operator resourceCustomizations](https://argocd-operator.readthedocs.io/en/latest/reference/argocd/#resource-customizations).
@@ -103,24 +101,15 @@ data:
hs.message = "Waiting for certificate"
return hs
```
In order to prevent duplication of the custom health check for potentially multiple resources, it is also possible to specify a wildcard in the resource kind, and anywhere in the resource group, like this:
In order to prevent duplication of the same custom health check for potentially multiple resources, it is also possible to specify a wildcard in the resource kind, like this:
```yaml
resource.customizations: |
ec2.aws.crossplane.io/*:
health.lua: |
...
```
```yaml
resource.customizations: |
*.aws.crossplane.io/*:
health.lua: |
...
```
The `obj` is a global variable which contains the resource. The script must return an object with status and optional message field.
The custom health check might return one of the following health statuses:

View File

@@ -6,8 +6,8 @@ A set of HA manifests are provided for users who wish to run Argo CD in a highly
[Manifests ⧉](https://github.com/argoproj/argo-cd/tree/master/manifests)
> **NOTE:** The HA installation will require at least three different nodes due to pod anti-affinity roles in the
> specs. Additionally, IPv6 only clusters are not supported.
!!! note
The HA installation will require at least three different nodes due to pod anti-affinity roles in the specs.
## Scaling Up
@@ -188,4 +188,4 @@ spec:
targetRevision: HEAD
path: my-application
# ...
```
```

View File

@@ -80,7 +80,7 @@ resources:
## 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/main/charts/argo-cd).
[argo-helm/charts/argo-cd](https://github.com/argoproj/argo-helm/tree/master/charts/argo-cd).
## Supported versions

View File

@@ -51,7 +51,7 @@ You can send [simple text](https://developers.google.com/chat/reference/message-
```yaml
template.app-sync-succeeded: |
message: The app {{ .app.metadata.name }} has successfully synced!
message: The app {{ .app.metadata.name }} has succesfully synced!
```
A card message can be defined as follows:
@@ -65,7 +65,7 @@ template.app-sync-succeeded: |
sections:
- widgets:
- textParagraph:
text: The app {{ .app.metadata.name }} has successfully synced!
text: The app {{ .app.metadata.name }} has succesfully synced!
- widgets:
- keyValue:
topLabel: Repository
@@ -79,14 +79,3 @@ template.app-sync-succeeded: |
```
The card message can be written in JSON too.
## Chat Threads
It is possible send both simple text and card messages in a chat thread by specifying a unique key for the thread. The thread key can be defined as follows:
```yaml
template.app-sync-succeeded: |
message: The app {{ .app.metadata.name }} has succesfully synced!
googlechat:
threadKey: {{ .app.metadata.name }}
```

View File

@@ -1,61 +0,0 @@
# NewRelic
## Parameters
* `apiURL` - the api server url, e.g. https://api.newrelic.com
* `apiKey` - a [NewRelic ApiKey](https://docs.newrelic.com/docs/apis/rest-api-v2/get-started/introduction-new-relic-rest-api-v2/#api_key)
## Configuration
1. Create a NewRelic [Api Key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#user-api-key)
2. Store apiKey in `argocd-notifications-secret` Secret and configure NewRelic integration in `argocd-notifications-cm` ConfigMap
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: <config-map-name>
data:
service.newrelic: |
apiURL: <api-url>
apiKey: $newrelic-apiKey
```
```yaml
apiVersion: v1
kind: Secret
metadata:
name: <secret-name>
stringData:
newrelic-apiKey: apiKey
```
3. Copy [Application ID](https://docs.newrelic.com/docs/apis/rest-api-v2/get-started/get-app-other-ids-new-relic-one/#apm)
4. Create subscription for your NewRelic integration
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
annotations:
notifications.argoproj.io/subscribe.<trigger-name>.newrelic: <app-id>
```
## Templates
* `description` - __optional__, high-level description of this deployment, visible in the [Summary](https://docs.newrelic.com/docs/apm/applications-menu/monitoring/apm-overview-page) page and on the [Deployments](https://docs.newrelic.com/docs/apm/applications-menu/events/deployments-page) page when you select an individual deployment.
* Defaults to `message`
* `changelog` - __optional__, A summary of what changed in this deployment, visible in the [Deployments](https://docs.newrelic.com/docs/apm/applications-menu/events/deployments-page) page when you select (selected deployment) > Change log.
* Defaults to `{{(call .repo.GetCommitMetadata .app.status.sync.revision).Message}}`
* `user` - __optional__, A username to associate with the deployment, visible in the [Summary](https://docs.newrelic.com/docs/apm/applications-menu/events/deployments-page) and on the [Deployments](https://docs.newrelic.com/docs/apm/applications-menu/events/deployments-page).
* Defaults to `{{(call .repo.GetCommitMetadata .app.status.sync.revision).Author}}`
```yaml
context: |
argocdUrl: https://example.com/argocd
template.app-deployed: |
message: Application {{.app.metadata.name}} has successfully deployed.
newrelic:
description: Application {{.app.metadata.name}} has successfully deployed
```

View File

@@ -29,56 +29,48 @@ The Slack notification service configuration includes following settings:
1. Invite your slack bot to this channel **otherwise slack bot won't be able to deliver notifications to this channel**
1. Store Oauth access token in `argocd-notifications-secret` secret
```yaml
apiVersion: v1
kind: Secret
metadata:
name: <secret-name>
stringData:
slack-token: <Oauth-access-token>
```
apiVersion: v1
kind: Secret
metadata:
name: <secret-name>
stringData:
slack-token: <Oauth-access-token>
1. Define service type slack in data section of `argocd-notifications-cm` configmap:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: <config-map-name>
data:
service.slack: |
token: $slack-token
```
apiVersion: v1
kind: ConfigMap
metadata:
name: <config-map-name>
data:
service.slack: |
token: $slack-token
1. Add annotation in application yaml file to enable notifications for specific argocd app
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
annotations:
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: my_channel
```
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
annotations:
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: my_channel
1. Annotation with more than one trigger multiple of destinations and recipients
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
annotations:
notifications.argoproj.io/subscriptions: |
- trigger: [on-scaling-replica-set, on-rollout-updated, on-rollout-step-completed]
destinations:
- service: slack
recipients: [my-channel-1, my-channel-2]
- service: email
recipients: [recipient-1, recipient-2, recipient-3 ]
- trigger: [on-rollout-aborted, on-analysis-run-failed, on-analysis-run-error]
destinations:
- service: slack
recipients: [my-channel-21, my-channel-22]
```
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
annotations:
notifications.argoproj.io/subscriptions: |
- trigger: [on-scaling-replica-set, on-rollout-updated, on-rollout-step-completed]
destinations:
- service: slack
recipients: [my-channel-1, my-channel-2]
- service: email
recipients: [recipient-1, recipient-2, recipient-3 ]
- trigger: [on-rollout-aborted, on-analysis-run-failed, on-analysis-run-error]
destinations:
- service: slack
recipients: [my-channel-21, my-channel-22]
## Templates

View File

@@ -1,41 +0,0 @@
# Webex Teams
## Parameters
The Webex Teams notification service configuration includes following settings:
* `token` - the app token
## Configuration
1. Create a Webex [Bot](https://developer.webex.com/docs/bots)
1. Copy the bot access [token](https://developer.webex.com/my-apps) and store it in the `argocd-notifications-secret` Secret and configure Webex Teams integration in `argocd-notifications-cm` ConfigMap
``` yaml
apiVersion: v1
kind: Secret
metadata:
name: <secret-name>
stringData:
webex-token: <bot access token>
```
``` yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: <config-map-name>
data:
service.webex: |
token: $webex-token
```
1. Create subscription for your Webex Teams integration
``` yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
annotations:
notifications.argoproj.io/subscribe.<trigger-name>.webex: <personal email or room id>
```

View File

@@ -1,19 +1,8 @@
# Webhook
## Configuration
The webhook notification service allows sending a generic HTTP request using the templatized request body and URL.
Using Webhook you might trigger a Jenkins job, update Github commit status.
## Parameters
The Webhook notification service configuration includes following settings:
- `url` - the url to send the webhook to
- `headers` - optional, the headers to pass along with the webhook
- `basicAuth` - optional, the basic authentication to pass along with the webook
- `insecureSkipVerify` - optional bool, true or false
## Configuration
Use the following steps to configure webhook:
1 Register webhook in `argocd-notifications-cm` ConfigMap:
@@ -32,7 +21,6 @@ data:
basicAuth: #optional username password
username: <username>
password: <api-key>
insecureSkipVerify: true #optional bool
```
2 Define template that customizes webhook request method, path and body:

View File

@@ -15,11 +15,9 @@ spec:
- '*'
# Only permit applications to deploy to the guestbook namespace in the same cluster
# Destination clusters can be identified by 'server', 'name', or both.
destinations:
- namespace: guestbook
server: https://kubernetes.default.svc
name: in-cluster
# Deny all cluster-scoped resources from being created, except for Namespace
clusterResourceWhitelist:

View File

@@ -9,8 +9,9 @@ Operators can add actions to custom resources in form of a Lua script and expand
Argo CD supports custom resource actions written in [Lua](https://www.lua.org/). This is useful if you:
* Have a custom resource for which Argo CD does not provide any built-in actions.
* Have a commonly performed manual task that might be error prone if executed by users via `kubectl`
* Have a custom resource for which Argo CD does not provide any built-in actions.
* Have a commonly performed manual task that might be error prone if executed by users via `kubectl`
You can define your own custom resource actions in the `argocd-cm` ConfigMap.

View File

@@ -1,11 +1,6 @@
# Secret Management
Argo CD is un-opinionated about how secrets are managed. There are many ways to do it, and there's no one-size-fits-all solution.
Many solutions use plugins to inject secrets into the application manifests. See [Mitigating Risks of Secret-Injection Plugins](#mitigating-risks-of-secret-injection-plugins)
below to make sure you use those plugins securely.
Here are some ways people are doing GitOps secrets:
Argo CD is un-opinionated about how secrets are managed. There's many ways to do it and there's no one-size-fits-all solution. Here's some ways people are doing GitOps secrets:
* [Bitnami Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)
* [External Secrets Operator](https://github.com/external-secrets/external-secrets)
@@ -17,20 +12,5 @@ Here are some ways people are doing GitOps secrets:
* [KSOPS](https://github.com/viaduct-ai/kustomize-sops#argo-cd-integration)
* [argocd-vault-plugin](https://github.com/argoproj-labs/argocd-vault-plugin)
* [argocd-vault-replacer](https://github.com/crumbhole/argocd-vault-replacer)
* [Kubernetes Secrets Store CSI Driver](https://github.com/kubernetes-sigs/secrets-store-csi-driver)
For discussion, see [#1364](https://github.com/argoproj/argo-cd/issues/1364)
## Mitigating Risks of Secret-Injection Plugins
Argo CD caches the manifests generated by plugins, along with the injected secrets, in its Redis instance. Those
manifests are also available via the repo-server API (a gRPC service). This means that the secrets are available to
anyone who has access to the Redis instance or to the repo-server.
Consider these steps to mitigate the risks of secret-injection plugins:
1. Set up network policies to prevent direct access to Argo CD components (Redis and the repo-server). Make sure your
cluster supports those network policies and can actually enforce them.
2. Consider running Argo CD on its own cluster, with no other applications running on it.
3. [Enable password authentication on the Redis instance](https://github.com/argoproj/argo-cd/issues/3130) (currently
only supported for non-HA Argo CD installations).

View File

@@ -32,7 +32,6 @@ argocd-server [flags]
--dex-server-strict-tls Perform strict validation of TLS certificates when connecting to dex server
--disable-auth Disable client authentication
--enable-gzip Enable GZIP compression
--enable-proxy-extension Enable Proxy Extension feature
--gloglevel int Set the glog logging level
-h, --help help for argocd-server
--insecure Run server without TLS

View File

@@ -4,17 +4,21 @@ All Argo CD container images are signed by cosign. Checksums are created for the
## Prerequisites
- Cosign [installation instructions](https://docs.sigstore.dev/cosign/installation)
- Obtain or have a copy of ```argocd-cosign.pub```, which can be located in the assets section of the [release page](https://github.com/argoproj/argo-cd/releases)
Once you have installed cosign, you can use ```argocd-cosign.pub``` to verify the signed assets or container images.
- Obtain or have a copy of the [public key](https://github.com/argoproj/argo-cd/blob/master/argocd-cosign.pub) ```argocd-cosign.pub```
Once you have installed cosign, you can use [argocd-cosign.pub](https://github.com/argoproj/argo-cd/blob/master/argocd-cosign.pub) to verify the signed assets or container images.
```
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEesHEB7vX5Y2RxXypjMy1nI1z7iRG
JI9/gt/sYqzpsa65aaNP4npM43DDxoIy/MQBo9s/mxGxmA+8UXeDpVC9vw==
-----END PUBLIC KEY-----
```
## Verification of container images
```bash
cosign verify --key argocd-cosign.pub quay.io/argoproj/argocd:<VERSION>
cosign verify --key argocd-cosign.pub quay.io/argoproj/argocd:latest
Verification for quay.io/argoproj/argocd:<VERSION> --
Verification for quay.io/argoproj/argocd:latest --
The following checks were performed on each of these signatures:
* The cosign claims were validated
* The signatures were verified against the specified public key
@@ -23,9 +27,6 @@ The following checks were performed on each of these signatures:
## Verification of signed assets
```bash
cosign verify-blob --key cosign.pub --signature $(cat argocd-<VERSION>-checksums.sig) argocd-$VERSION-checksums.txt
cosign verify-blob --key cosign.pub --signature $(cat argocd-$VERSION-checksums.sig) argocd-$VERSION-checksums.txt
Verified OK
```
## Admission controllers
Cosign is compatible with several types of admission controllers. Please see the [Cosign documentation](https://docs.sigstore.dev/cosign/overview/#kubernetes-integrations) for supported controllers

View File

@@ -1,4 +1,4 @@
# v1.8 to 2.0
# v1.8 to v2.0
## Redis Upgraded to v6.2.1

View File

@@ -36,7 +36,7 @@ data:
## Removed Python from the base image
If you are using a [Config Management Plugin](../config-management-plugins.md) that relies on Python, you
If you are using a [Config Management Plugin](../../user-guide/config-management-plugins.md) that relies on Python, you
will need to build a custom image on the Argo CD base to install Python.
## Upgraded Kustomize Version

View File

@@ -176,7 +176,7 @@ that uses the Service Account for auth), be sure to test before deploying the 2.
### Remove the shared volume from any sidecar plugins
As a security enhancement, [sidecar plugins](../config-management-plugins.md#option-2-configure-plugin-via-sidecar)
As a security enhancement, [sidecar plugins](../../user-guide/config-management-plugins.md#option-2-configure-plugin-via-sidecar)
no longer share the /tmp directory with the repo-server.
If you have one or more sidecar plugins enabled, replace the /tmp volume mount for each sidecar to use a volume specific

View File

@@ -34,7 +34,7 @@ p, role:org-admin, exec, create, *, allow
## argocd-cm plugins (CMPs) are deprecated
Starting with Argo CD v2.5, installing config management plugins (CMPs) via the `argocd-cm` ConfigMap is deprecated.
~~Support will be removed in v2.6.~~ Support will be removed in v2.7.
Support will be removed in v2.6.
You can continue to use the plugins by [installing them as sidecars](https://argo-cd.readthedocs.io/en/stable/user-guide/config-management-plugins/)
on the repo-server Deployment.
@@ -47,8 +47,6 @@ following message:
> argocd-cm plugins are deprecated, and support will be removed in v2.6. Upgrade your plugin to be installed via sidecar. https://argo-cd.readthedocs.io/en/stable/user-guide/config-management-plugins/
**NOTE:** removal of argocd-cm plugin support was delayed to v2.7. Update your logs scan to use `v2.7` instead of `v2.6`.
If you run `argocd app list` as admin, the list of Applications using deprecated plugins will be logged as a warning.
## Dex server TLS configuration
@@ -99,14 +97,14 @@ When using `argocd app diff --local`, code from the repo server is run on the us
In order to support CMPs and reduce local requirements, we have implemented *server-side generation* of local manifests via the `--server-side-generate` argument. For example, `argocd app diff --local repoDir --server-side-generate` will upload the contents of `repoDir` to the repo server and run your manifest generation pipeline against it, the same as it would for a Git repo.
In ~~v2.6~~ v2.7, the `--server-side-generate` argument will become the default, ~~and client-side generation will be removed~~ and client-side generation will be supported as an alternative.
In v2.6, the `--server-side-generate` argument will become the default and client-side generation will be removed.
!!! warning
The semantics of *where* Argo will start generating manifests within a repo has changed between client-side and server-side generation. With client-side generation, the application's path (`spec.source.path`) was ignored and the value of `--local-repo-root` was effectively used (by default `/` relative to `--local`).
For example, given an application that has an application path of `/manifests`, you would have had to run `argocd app diff --local yourRepo/manifests`. This behavior did not match the repo server's process of downloading the full repo/chart and then beginning generation in the path specified in the application manifest.
When switching to server-side generation, `--local` should point to the root of your repo *without* including your `spec.source.path`. This is especially important to keep in mind when `--server-side-generate` becomes the default in v2.7. Existing scripts utilizing `diff --local` may break in v2.7 if `spec.source.path` was not `/`.
When switching to server-side generation, `--local` should point to the root of your repo *without* including your `spec.source.path`. This is especially important to keep in mind when `--server-side-generate` becomes the default in v2.6. Existing scripts utilizing `diff --local` may break in v2.6 if `spec.source.path` was not `/`.
## Upgraded Kustomize Version

View File

@@ -6,26 +6,3 @@ Argo CD 2.5 introduced [Go templating in ApplicationSets](https://argo-cd.readth
Argo CD 2.6 upgrades Sprig to v3. That upgrade includes an upgrade of [Masterminds/semver](https://github.com/Masterminds/semver/releases) to v3.
Masterminds/semver v3 changed the behavior of the `^` prefix in semantic version constraints. If you are using Go-templated ApplicationSets which include references to [Sprig's semver functions](https://masterminds.github.io/sprig/semver.html) and use the `^` prefix, read the [Masterminds/semver changelog](https://github.com/Masterminds/semver/releases/tag/v3.0.0) to understand how your ApplicationSets' behavior may change.
## Applications with suspended jobs now marked "Suspended" instead of "Progressing"
Prior to Argo CD v2.6, an Application managing a suspended Job would be marked as "Progressing". This was confusing/unexpected behavior for many. Starting with v2.6, Argo CD will mark such Applications as "Suspended".
If you have processes which rely on the previous behavior (for example, a CI job with an argocd app wait call), update those before upgrading to v2.6.
## The API Server now requires tokens to include the `aud` claim by default
Argo CD v2.6 now requires that the `aud` claim be present in the token used to authenticate to the API Server. This is a
security improvement, as it prevents tokens from being used against the API Server which were not intended for it.
If you rely on an OIDC provider which does not provide a `aud` claim, you can disable this requirement by setting the
`skipAudienceCheckWhenTokenHasNoAudience` flag to `true` in your Argo CD OIDC configuration. (See the
[OIDC configuration documentation](https://argo-cd.readthedocs.io/en/stable/operator-manual/user-management/#existing-oidc-provider)
for an example.)
## Removal of argocd-cm plugin support delayed until 2.7
Support for argocd-cm plugins was previously scheduled for 2.6. At the time, sidecar plugins could not be specified by
name. Argo CD v2.6 introduces support for specifying sidecar plugins by name.
Removal of argocd-cm plugin support has been delayed until 2.7 to provide a transition time for users who need to
specify plugins by name.

View File

@@ -37,7 +37,6 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/<v
<hr/>
* [v2.5 to v2.6](./2.5-2.6.md)
* [v2.4 to v2.5](./2.4-2.5.md)
* [v2.3 to v2.4](./2.3-2.4.md)
* [v2.2 to v2.3](./2.2-2.3.md)

View File

@@ -301,19 +301,6 @@ data:
issuer: https://dev-123456.oktapreview.com
clientID: aaaabbbbccccddddeee
clientSecret: $oidc.okta.clientSecret
# Optional list of allowed aud claims. If omitted or empty, defaults to the clientID value above (and the
# cliCientID, if that is also specified). If you specify a list and want the clientID to be allowed, you must
# explicitly include it in the list.
# Token verification will pass if any of the token's audiences matches any of the audiences in this list.
allowedAudiences:
- aaaabbbbccccddddeee
- qqqqwwwweeeerrrrttt
# Optional. If false, tokens without an audience will always fail validation. If true, tokens without an audience
# will always pass validation.
# Defaults to true for Argo CD < 2.6.0. Defaults to false for Argo CD >= 2.6.0.
skipAudienceCheckWhenTokenHasNoAudience: true
# Optional set of OIDC scopes to request. If omitted, defaults to: ["openid", "profile", "email", "groups"]
requestedScopes: ["openid", "profile", "email", "groups"]

View File

@@ -17,8 +17,6 @@ Configure the client by setting the __Access Type__ to _confidential_ and set th
hostname. It should be https://{hostname}/auth/callback (you can also leave the default less secure https://{hostname}/* ). You can also set the
__Base URL__ to _/applications_.
If you want to allow command line access, __Access Type__ must be set to _public_ and you also need to add http://localhost:8085/auth/callback in the list of Valid Redirect URIs. Then users can login using `argocd login {hostname} --sso`.
![Keycloak configure client](../../assets/keycloak-configure-client.png "Keycloak configure client")
Make sure to click __Save__. You should now have a new tab called __Credentials__. You can copy the Secret that we'll use in our ArgoCD

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