mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 17:48:47 +01:00
Compare commits
1 Commits
commit-ser
...
temp-cherr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f697b6920 |
@@ -18,10 +18,8 @@ hack/
|
||||
docs/
|
||||
examples/
|
||||
.github/
|
||||
!test/container
|
||||
!test/e2e/testdata
|
||||
!test/fixture
|
||||
!test/remote
|
||||
!test/container
|
||||
!hack/installers
|
||||
!hack/gpg-wrapper.sh
|
||||
!hack/git-verify-wrapper.sh
|
||||
|
||||
6
.github/pull_request_template.md
vendored
6
.github/pull_request_template.md
vendored
@@ -1,8 +1,6 @@
|
||||
<!--
|
||||
Note on DCO:
|
||||
|
||||
If the DCO action in the integration test fails, one or more of your commits are not signed off. Please click on the *Details* link next to the DCO action for instructions on how to resolve this.
|
||||
-->
|
||||
|
||||
Checklist:
|
||||
|
||||
@@ -16,8 +14,8 @@ Checklist:
|
||||
* [ ] Optional. My organization is added to USERS.md.
|
||||
* [ ] I have signed off all my commits as required by [DCO](https://github.com/argoproj/argoproj/blob/master/community/CONTRIBUTING.md#legal)
|
||||
* [ ] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
|
||||
* [ ] My build is green ([troubleshooting builds](https://argo-cd.readthedocs.io/en/latest/developer-guide/ci/)).
|
||||
* [ ] My build is green ([troubleshooting builds](https://argo-cd.readthedocs.io/en/latest/developer-guide/ci/)).
|
||||
* [ ] My new feature complies with the [feature status](https://github.com/argoproj/argoproj/blob/master/community/feature-status.md) guidelines.
|
||||
* [ ] I have added a brief description of why this PR is necessary and/or what this PR solves.
|
||||
|
||||
<!-- Please see [Contribution FAQs](https://argo-cd.readthedocs.io/en/latest/developer-guide/faq/) if you have questions about your pull-request. -->
|
||||
Please see [Contribution FAQs](https://argo-cd.readthedocs.io/en/latest/developer-guide/faq/) if you have questions about your pull-request.
|
||||
|
||||
2
.github/workflows/README.md
vendored
2
.github/workflows/README.md
vendored
@@ -16,7 +16,7 @@
|
||||
## image-reuse.yaml
|
||||
|
||||
- The resuable workflow can be used to publish or build images with multiple container registries(Quay,GHCR, dockerhub), and then sign them with cosign when an image is published.
|
||||
- A GO version `must` be specified e.g. 1.21
|
||||
- A GO version `must` be specified e.g. 1.19
|
||||
- The image name for each registry *must* contain the tag. Note: multiple tags are allowed for each registry using a CSV type.
|
||||
- Multiple platforms can be specified e.g. linux/amd64,linux/arm64
|
||||
- Images are not published by default. A boolean value must be set to `true` to push images.
|
||||
|
||||
67
.github/workflows/ci-build.yaml
vendored
67
.github/workflows/ci-build.yaml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
env:
|
||||
# Golang version to use across CI steps
|
||||
GOLANG_VERSION: '1.21'
|
||||
GOLANG_VERSION: '1.20'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -28,9 +28,9 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Download all Go modules
|
||||
@@ -46,13 +46,13 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -70,16 +70,16 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
|
||||
uses: golangci/golangci-lint-action@639cd343e1d3b897ff35927a75193d57cfcba299 # v3.6.0
|
||||
with:
|
||||
version: v1.54.0
|
||||
args: --enable gofmt --timeout 10m --exclude SA5011 --verbose --max-issues-per-linter 0 --max-same-issues 0
|
||||
version: v1.51.0
|
||||
args: --timeout 10m --exclude SA5011 --verbose
|
||||
|
||||
test-go:
|
||||
name: Run unit tests for Go packages
|
||||
@@ -93,11 +93,11 @@ jobs:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -138,12 +138,12 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-local
|
||||
- name: Generate code coverage artifacts
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: code-coverage
|
||||
path: coverage.out
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results/
|
||||
@@ -160,11 +160,11 @@ jobs:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -184,7 +184,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -205,7 +205,7 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-race-local
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: race-results
|
||||
path: test-results/
|
||||
@@ -215,9 +215,9 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Create symlink in GOPATH
|
||||
@@ -263,14 +263,14 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
|
||||
with:
|
||||
node-version: '20.7.0'
|
||||
node-version: '20.3.1'
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -300,12 +300,12 @@ jobs:
|
||||
sonar_secret: ${{ secrets.SONAR_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -360,9 +360,8 @@ jobs:
|
||||
name: Run end-to-end tests
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
k3s-version: [v1.28.2, v1.27.6, v1.26.9, v1.25.14]
|
||||
k3s-version: [v1.27.2, v1.26.0, v1.25.4, v1.24.3]
|
||||
needs:
|
||||
- build-go
|
||||
env:
|
||||
@@ -380,9 +379,9 @@ jobs:
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: GH actions workaround - Kill XSP4 process
|
||||
@@ -401,7 +400,7 @@ jobs:
|
||||
sudo chmod go-r $HOME/.kube/config
|
||||
kubectl version
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -457,7 +456,7 @@ jobs:
|
||||
set -x
|
||||
make test-e2e-local
|
||||
- name: Upload e2e-server logs
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: e2e-server-k8s${{ matrix.k3s-version }}.log
|
||||
path: /tmp/e2e-server.log
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
20
.github/workflows/image-reuse.yaml
vendored
20
.github/workflows/image-reuse.yaml
vendored
@@ -58,28 +58,28 @@ jobs:
|
||||
image-digest: ${{ steps.image.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.3.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
if: ${{ github.ref_type == 'tag'}}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.3.0
|
||||
if: ${{ github.ref_type != 'tag'}}
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
|
||||
with:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2
|
||||
uses: sigstore/cosign-installer@d13028333d784fcc802b67ec924bcebe75aa0a5f # v3.1.0
|
||||
with:
|
||||
cosign-release: 'v2.0.2'
|
||||
cosign-release: 'v2.0.0'
|
||||
|
||||
- uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0
|
||||
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
- uses: docker/setup-buildx-action@ecf95283f03858871ff00b787d79c419715afc34 # v2.7.0
|
||||
|
||||
- name: Setup tags for container image as a CSV type
|
||||
run: |
|
||||
@@ -135,14 +135,6 @@ jobs:
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
||||
echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@4d9e71b726748f254fe64fa44d273194bd18ec91
|
||||
with:
|
||||
large-packages: false
|
||||
docker-images: false
|
||||
swap-storage: false
|
||||
tool-cache: false
|
||||
|
||||
- name: Build and push container image
|
||||
id: image
|
||||
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 #v4.1.1
|
||||
|
||||
8
.github/workflows/image.yaml
vendored
8
.github/workflows/image.yaml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
image-tag: ${{ steps.image.outputs.tag}}
|
||||
platforms: ${{ steps.platforms.outputs.platforms }}
|
||||
steps:
|
||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
|
||||
- name: Set image tag for ghcr
|
||||
run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
uses: ./.github/workflows/image-reuse.yaml
|
||||
with:
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
go-version: 1.21
|
||||
go-version: 1.20
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: false
|
||||
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
quay_image_name: quay.io/argoproj/argocd:latest
|
||||
ghcr_image_name: ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
go-version: 1.21
|
||||
go-version: 1.20
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: true
|
||||
secrets:
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.3.0
|
||||
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
|
||||
env:
|
||||
TOKEN: ${{ secrets.TOKEN }}
|
||||
|
||||
2
.github/workflows/init-release.yaml
vendored
2
.github/workflows/init-release.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.2.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
26
.github/workflows/release.yaml
vendored
26
.github/workflows/release.yaml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
GOLANG_VERSION: '1.21' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
GOLANG_VERSION: '1.20' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
|
||||
jobs:
|
||||
argocd-image:
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
with:
|
||||
quay_image_name: quay.io/argoproj/argocd:${{ github.ref_name }}
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
go-version: 1.21
|
||||
go-version: 1.20
|
||||
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
|
||||
push: true
|
||||
secrets:
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
@@ -88,14 +88,14 @@ jobs:
|
||||
echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
|
||||
uses: goreleaser/goreleaser-action@336e29918d653399e599bfca99fadc1d7ffbc9f7 # v4.3.0
|
||||
id: run-goreleaser
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean --timeout 55m
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
KUBECTL_VERSION: ${{ env.KUBECTL_VERSION }}
|
||||
KUBECTL_VERSION: ${{ env.KUBECTL_VERSION }}
|
||||
GIT_TREE_STATE: ${{ env.GIT_TREE_STATE }}
|
||||
|
||||
- name: Generate subject for provenance
|
||||
@@ -139,13 +139,13 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.2.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
@@ -178,7 +178,7 @@ jobs:
|
||||
fi
|
||||
|
||||
cd /tmp && tar -zcf sbom.tar.gz *.spdx
|
||||
|
||||
|
||||
- name: Generate SBOM hash
|
||||
shell: bash
|
||||
id: sbom-hash
|
||||
@@ -187,7 +187,7 @@ jobs:
|
||||
# base64 -w0 encodes to base64 and outputs on a single line.
|
||||
# sha256sum /tmp/sbom.tar.gz ... | base64 -w0
|
||||
echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
|
||||
- name: Upload SBOM
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
|
||||
env:
|
||||
@@ -195,7 +195,7 @@ jobs:
|
||||
with:
|
||||
files: |
|
||||
/tmp/sbom.tar.gz
|
||||
|
||||
|
||||
sbom-provenance:
|
||||
needs: [generate-sbom]
|
||||
permissions:
|
||||
@@ -209,7 +209,7 @@ jobs:
|
||||
base64-subjects: "${{ needs.generate-sbom.outputs.hashes }}"
|
||||
provenance-name: "argocd-sbom.intoto.jsonl"
|
||||
upload-assets: true
|
||||
|
||||
|
||||
post-release:
|
||||
needs:
|
||||
- argocd-image
|
||||
@@ -222,7 +222,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.2.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
4
.github/workflows/scorecard.yaml
vendored
4
.github/workflows/scorecard.yaml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
||||
2
.github/workflows/update-snyk.yaml
vendored
2
.github/workflows/update-snyk.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build reports
|
||||
|
||||
2
.gitpod.Dockerfile
vendored
2
.gitpod.Dockerfile
vendored
@@ -1,4 +1,4 @@
|
||||
FROM gitpod/workspace-full@sha256:511cecde4dc129ca9eb4cc4c479d61f95e5485ebe320a07f5b902f11899956a3
|
||||
FROM gitpod/workspace-full@sha256:d5787229cd062aceae91109f1690013d3f25062916492fb7f444d13de3186178
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
11
CODEOWNERS
11
CODEOWNERS
@@ -1,11 +0,0 @@
|
||||
# All
|
||||
** @argoproj/argocd-approvers
|
||||
|
||||
# Docs
|
||||
/docs/** @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/USERS.md @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/mkdocs.yml @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
|
||||
# CI
|
||||
/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||
/.goreleaser.yaml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||
@@ -1 +0,0 @@
|
||||
Please refer to [the Contribution Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/code-contributions/)
|
||||
@@ -4,7 +4,7 @@ ARG BASE_IMAGE=docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fca
|
||||
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
||||
# Also used as the image in CI jobs so needs all dependencies
|
||||
####################################################################################################
|
||||
FROM docker.io/library/golang:1.21.3@sha256:02d7116222536a5cf0fcf631f90b507758b669648e0f20186d2dc94a9b419a9b AS builder
|
||||
FROM docker.io/library/golang:1.20.10@sha256:ed6c4a5918b0a1ffb97970f6493d742dc5c7ebf3ccbd417c215d52830b57b994 AS builder
|
||||
|
||||
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
|
||||
@@ -83,7 +83,7 @@ WORKDIR /home/argocd
|
||||
####################################################################################################
|
||||
# Argo CD UI stage
|
||||
####################################################################################################
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/node:20.6.1@sha256:14bd39208dbc0eb171cbfb26ccb9ac09fa1b2eba04ccd528ab5d12983fd9ee24 AS argocd-ui
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/node:20.3.1@sha256:2f0b0c15f97441defa812268ee943bbfaaf666ea6cf7cac62ee3f127906b35c6 AS argocd-ui
|
||||
|
||||
WORKDIR /src
|
||||
COPY ["ui/package.json", "ui/yarn.lock", "./"]
|
||||
@@ -101,7 +101,7 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP
|
||||
####################################################################################################
|
||||
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
||||
####################################################################################################
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21.3@sha256:02d7116222536a5cf0fcf631f90b507758b669648e0f20186d2dc94a9b419a9b AS argocd-build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.20.10@sha256:ed6c4a5918b0a1ffb97970f6493d742dc5c7ebf3ccbd417c215d52830b57b994 AS argocd-build
|
||||
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
|
||||
17
Makefile
17
Makefile
@@ -49,7 +49,7 @@ ARGOCD_E2E_DEX_PORT?=5556
|
||||
ARGOCD_E2E_YARN_HOST?=localhost
|
||||
ARGOCD_E2E_DISABLE_AUTH?=
|
||||
|
||||
ARGOCD_E2E_TEST_TIMEOUT?=60m
|
||||
ARGOCD_E2E_TEST_TIMEOUT?=45m
|
||||
|
||||
ARGOCD_IN_CI?=false
|
||||
ARGOCD_TEST_E2E?=true
|
||||
@@ -352,7 +352,7 @@ lint-local:
|
||||
golangci-lint --version
|
||||
# NOTE: If you get a "Killed" OOM message, try reducing the value of GOGC
|
||||
# See https://github.com/golangci/golangci-lint#memory-usage-of-golangci-lint
|
||||
GOGC=$(ARGOCD_LINT_GOGC) GOMAXPROCS=2 golangci-lint run --enable gofmt --fix --verbose --timeout 3000s --max-issues-per-linter 0 --max-same-issues 0
|
||||
GOGC=$(ARGOCD_LINT_GOGC) GOMAXPROCS=2 golangci-lint run --fix --verbose --timeout 3000s
|
||||
|
||||
.PHONY: lint-ui
|
||||
lint-ui: test-tools-image
|
||||
@@ -386,9 +386,9 @@ test: test-tools-image
|
||||
.PHONY: test-local
|
||||
test-local:
|
||||
if test "$(TEST_MODULE)" = ""; then \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -coverprofile=coverage.out; \
|
||||
./hack/test.sh -coverprofile=coverage.out `go list ./... | grep -v 'test/e2e'`; \
|
||||
else \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \
|
||||
./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \
|
||||
fi
|
||||
|
||||
.PHONY: test-race
|
||||
@@ -400,9 +400,9 @@ test-race: test-tools-image
|
||||
.PHONY: test-race-local
|
||||
test-race-local:
|
||||
if test "$(TEST_MODULE)" = ""; then \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -race -coverprofile=coverage.out; \
|
||||
./hack/test.sh -race -coverprofile=coverage.out `go list ./... | grep -v 'test/e2e'`; \
|
||||
else \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -race -coverprofile=coverage.out; \
|
||||
./hack/test.sh -race -coverprofile=coverage.out "$(TEST_MODULE)"; \
|
||||
fi
|
||||
|
||||
# Run the E2E test suite. E2E test servers (see start-e2e target) must be
|
||||
@@ -416,7 +416,7 @@ test-e2e:
|
||||
test-e2e-local: cli-local
|
||||
# NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system
|
||||
export GO111MODULE=off
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=5 PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v
|
||||
ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v ./test/e2e
|
||||
|
||||
# Spawns a shell in the test server container for debugging purposes
|
||||
debug-test-server: test-tools-image
|
||||
@@ -557,7 +557,6 @@ install-tools-local: install-test-tools-local install-codegen-tools-local instal
|
||||
install-test-tools-local:
|
||||
./hack/install.sh kustomize
|
||||
./hack/install.sh helm-linux
|
||||
./hack/install.sh gotestsum
|
||||
|
||||
# Installs all tools required for running codegen (Linux packages)
|
||||
.PHONY: install-codegen-tools-local
|
||||
@@ -653,4 +652,4 @@ help:
|
||||
@echo 'codegen:'
|
||||
@echo ' codegen(-local) -- if using -local, run the following targets first'
|
||||
@echo ' install-codegen-tools-local -- run this to install the codegen tools'
|
||||
@echo ' install-go-tools-local -- run this to install go libraries for codegen'
|
||||
@echo ' install-go-tools-local -- run this to install go libraries for codegen'
|
||||
@@ -56,7 +56,7 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
### Blogs and Presentations
|
||||
|
||||
1. [Awesome-Argo: A Curated List of Awesome Projects and Resources Related to Argo](https://github.com/terrytangyuan/awesome-argo)
|
||||
1. [Unveil the Secret Ingredients of Continuous Delivery at Enterprise Scale with Argo CD](https://akuity.io/blog/unveil-the-secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argocd-kubecon-china-2021/)
|
||||
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)
|
||||
|
||||
@@ -50,7 +50,7 @@ of releasing it within a patch branch for the currently supported releases.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find a security related bug in Argo CD, we kindly ask you for responsible
|
||||
If you find a security related bug in ArgoCD, we kindly ask you for responsible
|
||||
disclosure and for giving us appropriate time to react, analyze and develop a
|
||||
fix to mitigate the found security vulnerability.
|
||||
|
||||
|
||||
17
USERS.md
17
USERS.md
@@ -7,7 +7,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
|
||||
1. [127Labs](https://127labs.com/)
|
||||
1. [3Rein](https://www.3rein.com/)
|
||||
1. [4data](https://4data.ch/)
|
||||
1. [7shifts](https://www.7shifts.com/)
|
||||
1. [Adevinta](https://www.adevinta.com/)
|
||||
1. [Adfinis](https://adfinis.com)
|
||||
@@ -25,7 +24,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [AppDirect](https://www.appdirect.com)
|
||||
1. [Arctiq Inc.](https://www.arctiq.ca)
|
||||
1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/)
|
||||
1. [Autodesk](https://www.autodesk.com)
|
||||
1. [Axual B.V.](https://axual.com)
|
||||
1. [Back Market](https://www.backmarket.com)
|
||||
1. [Baloise](https://www.baloise.com)
|
||||
@@ -44,7 +42,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Capital One](https://www.capitalone.com)
|
||||
1. [CARFAX](https://www.carfax.com)
|
||||
1. [CARFAX Europe](https://www.carfax.eu)
|
||||
1. [Carrefour Group](https://www.carrefour.com)
|
||||
1. [Casavo](https://casavo.com)
|
||||
1. [Celonis](https://www.celonis.com/)
|
||||
1. [CERN](https://home.cern/)
|
||||
@@ -74,7 +71,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Devtron Labs](https://github.com/devtron-labs/devtron)
|
||||
1. [DigitalOcean](https://www.digitalocean.com)
|
||||
1. [Divistant](https://divistant.com)
|
||||
1. [Dott](https://ridedott.com)
|
||||
1. [Doximity](https://www.doximity.com/)
|
||||
1. [EDF Renewables](https://www.edf-re.com/)
|
||||
1. [edX](https://edx.org)
|
||||
@@ -86,11 +82,9 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Energisme](https://energisme.com/)
|
||||
1. [enigmo](https://enigmo.co.jp/)
|
||||
1. [Envoy](https://envoy.com/)
|
||||
1. [Factorial](https://factorialhr.com/)
|
||||
1. [Farfetch](https://www.farfetch.com)
|
||||
1. [Faro](https://www.faro.com/)
|
||||
1. [Fave](https://myfave.com)
|
||||
1. [Flexport](https://www.flexport.com/)
|
||||
1. [Flip](https://flip.id)
|
||||
1. [Fonoa](https://www.fonoa.com/)
|
||||
1. [freee](https://corp.freee.co.jp/en/company/)
|
||||
@@ -132,7 +126,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Info Support](https://www.infosupport.com/)
|
||||
1. [InsideBoard](https://www.insideboard.com)
|
||||
1. [Intuit](https://www.intuit.com/)
|
||||
1. [Jellysmack](https://www.jellysmack.com)
|
||||
1. [Joblift](https://joblift.com/)
|
||||
1. [JovianX](https://www.jovianx.com/)
|
||||
1. [Kaltura](https://corp.kaltura.com/)
|
||||
@@ -146,10 +139,8 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Kinguin](https://www.kinguin.net/)
|
||||
1. [KintoHub](https://www.kintohub.com/)
|
||||
1. [KompiTech GmbH](https://www.kompitech.com/)
|
||||
1. [KPMG](https://kpmg.com/uk)
|
||||
1. [KubeSphere](https://github.com/kubesphere)
|
||||
1. [Kurly](https://www.kurly.com/)
|
||||
1. [Kvist](https://kvistsolutions.com)
|
||||
1. [LexisNexis](https://www.lexisnexis.com/)
|
||||
1. [Lian Chu Securities](https://lczq.com)
|
||||
1. [Liatrio](https://www.liatrio.com)
|
||||
@@ -169,7 +160,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Meican](https://meican.com/)
|
||||
1. [Meilleurs Agents](https://www.meilleursagents.com/)
|
||||
1. [Mercedes-Benz Tech Innovation](https://www.mercedes-benz-techinnovation.com/)
|
||||
1. [Mercedes-Benz.io](https://www.mercedes-benz.io/)
|
||||
1. [Metanet](http://www.metanet.co.kr/en/)
|
||||
1. [MindSpore](https://mindspore.cn)
|
||||
1. [Mirantis](https://mirantis.com/)
|
||||
@@ -210,7 +200,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Patreon](https://www.patreon.com/)
|
||||
1. [PayPay](https://paypay.ne.jp/)
|
||||
1. [Peloton Interactive](https://www.onepeloton.com/)
|
||||
1. [Percona](https://percona.com/)
|
||||
1. [PGS](https://www.pgs.com)
|
||||
1. [Pigment](https://www.gopigment.com/)
|
||||
1. [Pipefy](https://www.pipefy.com/)
|
||||
@@ -245,9 +234,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Sap Labs](http://sap.com)
|
||||
1. [Sauce Labs](https://saucelabs.com/)
|
||||
1. [Schwarz IT](https://jobs.schwarz/it-mission)
|
||||
1. [SCRM Lidl International Hub](https://scrm.lidl)
|
||||
1. [SEEK](https://seek.com.au)
|
||||
1. [Semgrep](https://semgrep.com)
|
||||
1. [SI Analytics](https://si-analytics.ai)
|
||||
1. [Skit](https://skit.ai/)
|
||||
1. [Skyscanner](https://www.skyscanner.net/)
|
||||
@@ -285,21 +272,17 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Trendyol](https://www.trendyol.com/)
|
||||
1. [tru.ID](https://tru.id)
|
||||
1. [Trusting Social](https://trustingsocial.com/)
|
||||
1. [Twilio Segment](https://segment.com/)
|
||||
1. [Twilio SendGrid](https://sendgrid.com)
|
||||
1. [tZERO](https://www.tzero.com/)
|
||||
1. [U.S. Veterans Affairs Department](https://www.va.gov/)
|
||||
1. [UBIO](https://ub.io/)
|
||||
1. [UFirstGroup](https://www.ufirstgroup.com/en/)
|
||||
1. [ungleich.ch](https://ungleich.ch/)
|
||||
1. [Unifonic Inc](https://www.unifonic.com/)
|
||||
1. [Universidad Mesoamericana](https://www.umes.edu.gt/)
|
||||
1. [Upsider Inc.](https://up-sider.com/lp/)
|
||||
1. [Urbantz](https://urbantz.com/)
|
||||
1. [Vectra](https://www.vectra.ai)
|
||||
1. [Veepee](https://www.veepee.com)
|
||||
1. [Viaduct](https://www.viaduct.ai/)
|
||||
1. [VietMoney](https://vietmoney.vn/)
|
||||
1. [Vinted](https://vinted.com/)
|
||||
1. [Virtuo](https://www.govirtuo.com/)
|
||||
1. [VISITS Technologies](https://visits.world/en)
|
||||
|
||||
@@ -28,11 +28,9 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
k8scache "k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
@@ -81,13 +79,10 @@ type ApplicationSetReconciler struct {
|
||||
Policy argov1alpha1.ApplicationsSyncPolicy
|
||||
EnablePolicyOverride bool
|
||||
utils.Renderer
|
||||
ArgoCDNamespace string
|
||||
ApplicationSetNamespaces []string
|
||||
EnableProgressiveSyncs bool
|
||||
SCMRootCAPath string
|
||||
GlobalPreservedAnnotations []string
|
||||
GlobalPreservedLabels []string
|
||||
Cache cache.Cache
|
||||
ArgoCDNamespace string
|
||||
ApplicationSetNamespaces []string
|
||||
EnableProgressiveSyncs bool
|
||||
SCMRootCAPath string
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets,verbs=get;list;watch;create;update;patch;delete
|
||||
@@ -114,7 +109,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
// Log a warning if there are unrecognized generators
|
||||
_ = utils.CheckInvalidGenerators(&applicationSetInfo)
|
||||
// desiredApplications is the main list of all expected Applications from all generators in this appset.
|
||||
desiredApplications, applicationSetReason, err := r.generateApplications(logCtx, applicationSetInfo)
|
||||
desiredApplications, applicationSetReason, err := r.generateApplications(applicationSetInfo)
|
||||
if err != nil {
|
||||
_ = r.setApplicationSetStatusCondition(ctx,
|
||||
&applicationSetInfo,
|
||||
@@ -161,9 +156,9 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
if r.EnableProgressiveSyncs {
|
||||
if applicationSetInfo.Spec.Strategy == nil && len(applicationSetInfo.Status.ApplicationStatus) > 0 {
|
||||
// If appset used progressive sync but stopped, clean up the progressive sync application statuses
|
||||
logCtx.Infof("Removing %v unnecessary AppStatus entries from ApplicationSet %v", len(applicationSetInfo.Status.ApplicationStatus), applicationSetInfo.Name)
|
||||
log.Infof("Removing %v unnecessary AppStatus entries from ApplicationSet %v", len(applicationSetInfo.Status.ApplicationStatus), applicationSetInfo.Name)
|
||||
|
||||
err := r.setAppSetApplicationStatus(ctx, logCtx, &applicationSetInfo, []argov1alpha1.ApplicationSetApplicationStatus{})
|
||||
err := r.setAppSetApplicationStatus(ctx, &applicationSetInfo, []argov1alpha1.ApplicationSetApplicationStatus{})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("failed to clear previous AppSet application statuses for %v: %w", applicationSetInfo.Name, err)
|
||||
}
|
||||
@@ -178,7 +173,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
appMap[app.Name] = app
|
||||
}
|
||||
|
||||
appSyncMap, err = r.performProgressiveSyncs(ctx, logCtx, applicationSetInfo, applications, desiredApplications, appMap)
|
||||
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)
|
||||
}
|
||||
@@ -216,7 +211,7 @@ 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(logCtx, &applicationSetInfo, appSyncMap, appMap, validApps)
|
||||
validApps, err = r.syncValidApplications(ctx, &applicationSetInfo, appSyncMap, appMap, validApps)
|
||||
|
||||
if err != nil {
|
||||
_ = r.setApplicationSetStatusCondition(ctx,
|
||||
@@ -234,7 +229,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
}
|
||||
|
||||
if utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowUpdate() {
|
||||
err = r.createOrUpdateInCluster(ctx, logCtx, applicationSetInfo, validApps)
|
||||
err = r.createOrUpdateInCluster(ctx, applicationSetInfo, validApps)
|
||||
if err != nil {
|
||||
_ = r.setApplicationSetStatusCondition(ctx,
|
||||
&applicationSetInfo,
|
||||
@@ -248,7 +243,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
} else {
|
||||
err = r.createInCluster(ctx, logCtx, applicationSetInfo, validApps)
|
||||
err = r.createInCluster(ctx, applicationSetInfo, validApps)
|
||||
if err != nil {
|
||||
_ = r.setApplicationSetStatusCondition(ctx,
|
||||
&applicationSetInfo,
|
||||
@@ -264,7 +259,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
}
|
||||
|
||||
if utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete() {
|
||||
err = r.deleteInCluster(ctx, logCtx, applicationSetInfo, desiredApplications)
|
||||
err = r.deleteInCluster(ctx, applicationSetInfo, desiredApplications)
|
||||
if err != nil {
|
||||
_ = r.setApplicationSetStatusCondition(ctx,
|
||||
&applicationSetInfo,
|
||||
@@ -438,7 +433,8 @@ func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Con
|
||||
errorsByIndex[i] = fmt.Errorf("ApplicationSet %s contains applications with duplicate name: %s", applicationSetInfo.Name, app.Name)
|
||||
continue
|
||||
}
|
||||
_, err := r.ArgoAppClientset.ArgoprojV1alpha1().AppProjects(r.ArgoCDNamespace).Get(ctx, app.Spec.GetProject(), metav1.GetOptions{})
|
||||
|
||||
proj, err := r.ArgoAppClientset.ArgoprojV1alpha1().AppProjects(r.ArgoCDNamespace).Get(ctx, app.Spec.GetProject(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
errorsByIndex[i] = fmt.Errorf("application references project %s which does not exist", app.Spec.Project)
|
||||
@@ -452,6 +448,15 @@ func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Con
|
||||
continue
|
||||
}
|
||||
|
||||
conditions, err := argoutil.ValidatePermissions(ctx, &app.Spec, proj, r.ArgoDB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(conditions) > 0 {
|
||||
errorsByIndex[i] = fmt.Errorf("application spec is invalid: %s", argoutil.FormatAppConditions(conditions))
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return errorsByIndex, nil
|
||||
@@ -489,7 +494,7 @@ func getTempApplication(applicationSetTemplate argov1alpha1.ApplicationSetTempla
|
||||
return &tmplApplication
|
||||
}
|
||||
|
||||
func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, applicationSetInfo argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) {
|
||||
func (r *ApplicationSetReconciler) generateApplications(applicationSetInfo argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) {
|
||||
var res []argov1alpha1.Application
|
||||
|
||||
var firstError error
|
||||
@@ -498,7 +503,7 @@ func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, appli
|
||||
for _, requestedGenerator := range applicationSetInfo.Spec.Generators {
|
||||
t, err := generators.Transform(requestedGenerator, r.Generators, applicationSetInfo.Spec.Template, &applicationSetInfo, map[string]interface{}{})
|
||||
if err != nil {
|
||||
logCtx.WithError(err).WithField("generator", requestedGenerator).
|
||||
log.WithError(err).WithField("generator", requestedGenerator).
|
||||
Error("error generating application from params")
|
||||
if firstError == nil {
|
||||
firstError = err
|
||||
@@ -513,7 +518,7 @@ func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, appli
|
||||
for _, p := range a.Params {
|
||||
app, err := r.Renderer.RenderTemplateParams(tmplApplication, applicationSetInfo.Spec.SyncPolicy, p, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
|
||||
if err != nil {
|
||||
logCtx.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator).
|
||||
log.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator).
|
||||
Error("error generating application from params")
|
||||
|
||||
if firstError == nil {
|
||||
@@ -526,8 +531,8 @@ func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, appli
|
||||
}
|
||||
}
|
||||
|
||||
logCtx.WithField("generator", requestedGenerator).Infof("generated %d applications", len(res))
|
||||
logCtx.WithField("generator", requestedGenerator).Debugf("apps from generator: %+v", res)
|
||||
log.WithField("generator", requestedGenerator).Infof("generated %d applications", len(res))
|
||||
log.WithField("generator", requestedGenerator).Debugf("apps from generator: %+v", res)
|
||||
}
|
||||
|
||||
return res, applicationSetReason, firstError
|
||||
@@ -541,24 +546,22 @@ func ignoreNotAllowedNamespaces(namespaces []string) predicate.Predicate {
|
||||
}
|
||||
}
|
||||
|
||||
func appControllerIndexer(rawObj client.Object) []string {
|
||||
// grab the job object, extract the owner...
|
||||
app := rawObj.(*argov1alpha1.Application)
|
||||
owner := metav1.GetControllerOf(app)
|
||||
if owner == nil {
|
||||
return nil
|
||||
}
|
||||
// ...make sure it's a application set...
|
||||
if owner.APIVersion != argov1alpha1.SchemeGroupVersion.String() || owner.Kind != "ApplicationSet" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ...and if so, return it
|
||||
return []string{owner.Name}
|
||||
}
|
||||
|
||||
func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProgressiveSyncs bool, maxConcurrentReconciliations int) error {
|
||||
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &argov1alpha1.Application{}, ".metadata.controller", appControllerIndexer); err != nil {
|
||||
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &argov1alpha1.Application{}, ".metadata.controller", func(rawObj client.Object) []string {
|
||||
// grab the job object, extract the owner...
|
||||
app := rawObj.(*argov1alpha1.Application)
|
||||
owner := metav1.GetControllerOf(app)
|
||||
if owner == nil {
|
||||
return nil
|
||||
}
|
||||
// ...make sure it's a application set...
|
||||
if owner.APIVersion != argov1alpha1.SchemeGroupVersion.String() || owner.Kind != "ApplicationSet" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ...and if so, return it
|
||||
return []string{owner.Name}
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error setting up with manager: %w", err)
|
||||
}
|
||||
|
||||
@@ -579,37 +582,18 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProg
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *ApplicationSetReconciler) updateCache(ctx context.Context, obj client.Object, logger *log.Entry) {
|
||||
informer, err := r.Cache.GetInformer(ctx, obj)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get informer: %v", err)
|
||||
return
|
||||
}
|
||||
// The controller runtime abstract away informers creation
|
||||
// so unfortunately could not find any other way to access informer store.
|
||||
k8sInformer, ok := informer.(k8scache.SharedInformer)
|
||||
if !ok {
|
||||
logger.Error("informer is not a kubernetes informer")
|
||||
return
|
||||
}
|
||||
if err := k8sInformer.GetStore().Update(obj); err != nil {
|
||||
logger.Errorf("failed to update cache: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// createOrUpdateInCluster will create / update application resources in the cluster.
|
||||
// - For new applications, it will call create
|
||||
// - For existing application, it will call update
|
||||
// The function also adds owner reference to all applications, and uses it to delete them.
|
||||
func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
|
||||
func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
|
||||
|
||||
var firstError error
|
||||
// Creates or updates the application in appList
|
||||
for _, generatedApp := range desiredApplications {
|
||||
generatedApp.Namespace = applicationSet.Namespace
|
||||
|
||||
appLog := logCtx.WithFields(log.Fields{"app": generatedApp.QualifiedName()})
|
||||
appLog := log.WithFields(log.Fields{"app": generatedApp.Name, "appSet": applicationSet.Name})
|
||||
generatedApp.Namespace = applicationSet.Namespace
|
||||
|
||||
// Normalize to avoid fighting with the application controller.
|
||||
generatedApp.Spec = *argoutil.NormalizeApplicationSpec(&generatedApp.Spec)
|
||||
@@ -625,7 +609,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
|
||||
},
|
||||
}
|
||||
|
||||
action, err := utils.CreateOrUpdate(ctx, appLog, r.Client, applicationSet.Spec.IgnoreApplicationDifferences, found, func() error {
|
||||
action, err := utils.CreateOrUpdate(ctx, r.Client, found, func() error {
|
||||
// Copy only the Application/ObjectMeta fields that are significant, from the generatedApp
|
||||
found.Spec = generatedApp.Spec
|
||||
|
||||
@@ -635,21 +619,9 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
|
||||
}
|
||||
|
||||
preservedAnnotations := make([]string, 0)
|
||||
preservedLabels := make([]string, 0)
|
||||
|
||||
if applicationSet.Spec.PreservedFields != nil {
|
||||
preservedAnnotations = append(preservedAnnotations, applicationSet.Spec.PreservedFields.Annotations...)
|
||||
preservedLabels = append(preservedLabels, applicationSet.Spec.PreservedFields.Labels...)
|
||||
}
|
||||
|
||||
if len(r.GlobalPreservedAnnotations) > 0 {
|
||||
preservedAnnotations = append(preservedAnnotations, r.GlobalPreservedAnnotations...)
|
||||
}
|
||||
|
||||
if len(r.GlobalPreservedLabels) > 0 {
|
||||
preservedLabels = append(preservedLabels, r.GlobalPreservedLabels...)
|
||||
}
|
||||
|
||||
// Preserve specially treated argo cd annotations:
|
||||
// * https://github.com/argoproj/applicationset/issues/180
|
||||
// * https://github.com/argoproj/argo-cd/issues/10500
|
||||
@@ -663,21 +635,10 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
|
||||
generatedApp.Annotations[key] = state
|
||||
}
|
||||
}
|
||||
|
||||
for _, key := range preservedLabels {
|
||||
if state, exists := found.ObjectMeta.Labels[key]; exists {
|
||||
if generatedApp.Labels == nil {
|
||||
generatedApp.Labels = map[string]string{}
|
||||
}
|
||||
generatedApp.Labels[key] = state
|
||||
}
|
||||
}
|
||||
|
||||
found.ObjectMeta.Annotations = generatedApp.Annotations
|
||||
|
||||
found.ObjectMeta.Finalizers = generatedApp.Finalizers
|
||||
found.ObjectMeta.Labels = generatedApp.Labels
|
||||
|
||||
return controllerutil.SetControllerReference(&applicationSet, found, r.Scheme)
|
||||
})
|
||||
|
||||
@@ -688,24 +649,16 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
|
||||
}
|
||||
continue
|
||||
}
|
||||
r.updateCache(ctx, found, appLog)
|
||||
|
||||
if action != controllerutil.OperationResultNone {
|
||||
// Don't pollute etcd with "unchanged Application" events
|
||||
r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, fmt.Sprint(action), "%s Application %q", action, generatedApp.Name)
|
||||
appLog.Logf(log.InfoLevel, "%s Application", action)
|
||||
} else {
|
||||
// "unchanged Application" can be inferred by Reconcile Complete with no action being listed
|
||||
// Or enable debug logging
|
||||
appLog.Logf(log.DebugLevel, "%s Application", action)
|
||||
}
|
||||
r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, fmt.Sprint(action), "%s Application %q", action, generatedApp.Name)
|
||||
appLog.Logf(log.InfoLevel, "%s Application", action)
|
||||
}
|
||||
return firstError
|
||||
}
|
||||
|
||||
// createInCluster will filter from the desiredApplications only the application that needs to be created
|
||||
// Then it will call createOrUpdateInCluster to do the actual create
|
||||
func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
|
||||
func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
|
||||
|
||||
var createApps []argov1alpha1.Application
|
||||
current, err := r.getCurrentApplications(ctx, applicationSet)
|
||||
@@ -728,7 +681,7 @@ func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, logCtx *
|
||||
}
|
||||
}
|
||||
|
||||
return r.createOrUpdateInCluster(ctx, logCtx, applicationSet, createApps)
|
||||
return r.createOrUpdateInCluster(ctx, applicationSet, createApps)
|
||||
}
|
||||
|
||||
func (r *ApplicationSetReconciler) getCurrentApplications(_ context.Context, applicationSet argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) {
|
||||
@@ -737,7 +690,7 @@ func (r *ApplicationSetReconciler) getCurrentApplications(_ context.Context, app
|
||||
err := r.Client.List(context.Background(), ¤t, client.MatchingFields{".metadata.controller": applicationSet.Name})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retrieving applications: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return current.Items, nil
|
||||
@@ -745,7 +698,7 @@ func (r *ApplicationSetReconciler) getCurrentApplications(_ context.Context, app
|
||||
|
||||
// deleteInCluster will delete Applications that are currently on the cluster, but not in appList.
|
||||
// The function must be called after all generators had been called and generated applications
|
||||
func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
|
||||
func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
|
||||
// settingsMgr := settings.NewSettingsManager(context.TODO(), r.KubeClientset, applicationSet.Namespace)
|
||||
// argoDB := db.NewDB(applicationSet.Namespace, settingsMgr, r.KubeClientset)
|
||||
// clusterList, err := argoDB.ListClusters(ctx)
|
||||
@@ -769,15 +722,15 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *
|
||||
// Delete apps that are not in m[string]bool
|
||||
var firstError error
|
||||
for _, app := range current {
|
||||
logCtx = logCtx.WithField("app", app.QualifiedName())
|
||||
appLog := log.WithFields(log.Fields{"app": app.Name, "appSet": applicationSet.Name})
|
||||
_, exists := m[app.Name]
|
||||
|
||||
if !exists {
|
||||
|
||||
// Removes the Argo CD resources finalizer if the application contains an invalid target (eg missing cluster)
|
||||
err := r.removeFinalizerOnInvalidDestination(ctx, applicationSet, &app, clusterList, logCtx)
|
||||
err := r.removeFinalizerOnInvalidDestination(ctx, applicationSet, &app, clusterList, appLog)
|
||||
if err != nil {
|
||||
logCtx.WithError(err).Error("failed to update Application")
|
||||
appLog.WithError(err).Error("failed to update Application")
|
||||
if firstError != nil {
|
||||
firstError = err
|
||||
}
|
||||
@@ -786,14 +739,14 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *
|
||||
|
||||
err = r.Client.Delete(ctx, &app)
|
||||
if err != nil {
|
||||
logCtx.WithError(err).Error("failed to delete Application")
|
||||
appLog.WithError(err).Error("failed to delete Application")
|
||||
if firstError != nil {
|
||||
firstError = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, "Deleted", "Deleted Application %q", app.Name)
|
||||
logCtx.Log(log.InfoLevel, "Deleted application")
|
||||
appLog.Log(log.InfoLevel, "Deleted application")
|
||||
}
|
||||
}
|
||||
return firstError
|
||||
@@ -854,42 +807,36 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte
|
||||
|
||||
// If the finalizer length changed (due to filtering out an Argo finalizer), update the finalizer list on the app
|
||||
if len(newFinalizers) != len(app.Finalizers) {
|
||||
updated := app.DeepCopy()
|
||||
updated.Finalizers = newFinalizers
|
||||
patch := client.MergeFrom(app)
|
||||
if log.IsLevelEnabled(log.DebugLevel) {
|
||||
utils.LogPatch(appLog, patch, updated)
|
||||
}
|
||||
if err := r.Client.Patch(ctx, updated, patch); err != nil {
|
||||
return fmt.Errorf("error updating finalizers: %w", err)
|
||||
}
|
||||
r.updateCache(ctx, updated, appLog)
|
||||
// Application must have updated list of finalizers
|
||||
updated.DeepCopyInto(app)
|
||||
app.Finalizers = newFinalizers
|
||||
|
||||
r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, "Updated", "Updated Application %q finalizer before deletion, because application has an invalid destination", app.Name)
|
||||
appLog.Log(log.InfoLevel, "Updating application finalizer before deletion, because application has an invalid destination")
|
||||
|
||||
err := r.Client.Update(ctx, app, &client.UpdateOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error updating finalizers: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context, logCtx *log.Entry, appset argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, desiredApplications []argov1alpha1.Application, appMap map[string]argov1alpha1.Application) (map[string]bool, error) {
|
||||
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(logCtx, appset, desiredApplications)
|
||||
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, logCtx, &appset, applications, appStepMap)
|
||||
_, err = r.updateApplicationSetApplicationStatus(ctx, &appset, applications, appStepMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update applicationset app status: %w", err)
|
||||
}
|
||||
|
||||
logCtx.Infof("ApplicationSet %v step list:", appset.Name)
|
||||
log.Infof("ApplicationSet %v step list:", appset.Name)
|
||||
for i, step := range appDependencyList {
|
||||
logCtx.Infof("step %v: %+v", i+1, step)
|
||||
log.Infof("step %v: %+v", i+1, step)
|
||||
}
|
||||
|
||||
appSyncMap, err := r.buildAppSyncMap(ctx, appset, appDependencyList, appMap)
|
||||
@@ -897,9 +844,9 @@ func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context,
|
||||
return nil, fmt.Errorf("failed to build app sync map: %w", err)
|
||||
}
|
||||
|
||||
logCtx.Infof("Application allowed to sync before maxUpdate?: %+v", appSyncMap)
|
||||
log.Infof("Application allowed to sync before maxUpdate?: %+v", appSyncMap)
|
||||
|
||||
_, err = r.updateApplicationSetApplicationStatusProgress(ctx, logCtx, &appset, appSyncMap, appStepMap, appMap)
|
||||
_, err = r.updateApplicationSetApplicationStatusProgress(ctx, &appset, appSyncMap, appStepMap, appMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update applicationset application status progress: %w", err)
|
||||
}
|
||||
@@ -913,7 +860,7 @@ func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context,
|
||||
}
|
||||
|
||||
// this list tracks which Applications belong to each RollingUpdate step
|
||||
func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([][]string, map[string]int, error) {
|
||||
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
|
||||
@@ -940,9 +887,9 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, app
|
||||
for _, matchExpression := range step.MatchExpressions {
|
||||
|
||||
if val, ok := app.Labels[matchExpression.Key]; ok {
|
||||
valueMatched := labelMatchedExpression(logCtx, val, matchExpression)
|
||||
valueMatched := labelMatchedExpression(val, matchExpression)
|
||||
|
||||
if !valueMatched { // none of the matchExpression values was a match with the Application's labels
|
||||
if !valueMatched { // none of the matchExpression values was a match with the Application'ss labels
|
||||
selected = false
|
||||
break
|
||||
}
|
||||
@@ -955,7 +902,7 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, app
|
||||
if selected {
|
||||
appDependencyList[i] = append(appDependencyList[i], app.Name)
|
||||
if val, ok := appStepMap[app.Name]; ok {
|
||||
logCtx.Warnf("AppSet '%v' has a invalid matchExpression that selects Application '%v' label twice, in steps %v and %v", applicationSet.Name, app.Name, val+1, i+1)
|
||||
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
|
||||
}
|
||||
@@ -966,9 +913,9 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, app
|
||||
return appDependencyList, appStepMap, nil
|
||||
}
|
||||
|
||||
func labelMatchedExpression(logCtx *log.Entry, val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool {
|
||||
func labelMatchedExpression(val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool {
|
||||
if matchExpression.Operator != "In" && matchExpression.Operator != "NotIn" {
|
||||
logCtx.Errorf("skipping AppSet rollingUpdate step Application selection, invalid matchExpression operator provided: %q ", matchExpression.Operator)
|
||||
log.Errorf("skipping AppSet rollingUpdate step Application selection, invalid matchExpression operator provided: %q ", matchExpression.Operator)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1072,7 +1019,7 @@ func statusStrings(app argov1alpha1.Application) (string, string, string) {
|
||||
}
|
||||
|
||||
// check the status of each Application's status and promote Applications to the next status if needed
|
||||
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, appStepMap map[string]int) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
|
||||
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))
|
||||
@@ -1105,7 +1052,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
||||
}
|
||||
|
||||
if appOutdated && currentAppStatus.Status != "Waiting" && currentAppStatus.Status != "Pending" {
|
||||
logCtx.Infof("Application %v is outdated, updating its ApplicationSet status to Waiting", app.Name)
|
||||
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."
|
||||
@@ -1117,15 +1064,15 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
||||
// this covers race conditions where syncs initiated by RollingSync miraculously have a sync time before the transition to Pending state occurred (could be a few seconds)
|
||||
if operationPhaseString == "Succeeded" && app.Status.OperationState.StartedAt.Add(time.Duration(10)*time.Second).After(currentAppStatus.LastTransitionTime.Time) {
|
||||
if !app.Status.OperationState.StartedAt.After(currentAppStatus.LastTransitionTime.Time) {
|
||||
logCtx.Warnf("Application %v was synced less than 10s prior to entering Pending status, we'll assume the AppSet controller triggered this sync and update its status to Progressing", app.Name)
|
||||
log.Warnf("Application %v was synced less than 10s prior to entering Pending status, we'll assume the AppSet controller triggered this sync and update its status to Progressing", app.Name)
|
||||
}
|
||||
logCtx.Infof("Application %v has completed a sync successfully, updating its ApplicationSet status to Progressing", app.Name)
|
||||
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" {
|
||||
logCtx.Infof("Application %v has entered Progressing status, updating its ApplicationSet status to Progressing", app.Name)
|
||||
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."
|
||||
@@ -1134,7 +1081,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
||||
}
|
||||
|
||||
if currentAppStatus.Status == "Waiting" && isApplicationHealthy(app) {
|
||||
logCtx.Infof("Application %v is already synced and healthy, updating its ApplicationSet status to Healthy", app.Name)
|
||||
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."
|
||||
@@ -1142,7 +1089,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
||||
}
|
||||
|
||||
if currentAppStatus.Status == "Progressing" && isApplicationHealthy(app) {
|
||||
logCtx.Infof("Application %v has completed Progressing status, updating its ApplicationSet status to Healthy", app.Name)
|
||||
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."
|
||||
@@ -1152,7 +1099,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
||||
appStatuses = append(appStatuses, currentAppStatus)
|
||||
}
|
||||
|
||||
err := r.setAppSetApplicationStatus(ctx, logCtx, applicationSet, appStatuses)
|
||||
err := r.setAppSetApplicationStatus(ctx, applicationSet, appStatuses)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set AppSet application statuses: %w", err)
|
||||
}
|
||||
@@ -1161,7 +1108,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
||||
}
|
||||
|
||||
// check Applications that are in Waiting status and promote them to Pending if needed
|
||||
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appStepMap map[string]int, appMap map[string]argov1alpha1.Application) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
|
||||
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))
|
||||
@@ -1203,7 +1150,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
|
||||
if maxUpdate != nil {
|
||||
maxUpdateVal, err := intstr.GetScaledValueFromIntOrPercent(maxUpdate, totalCountMap[appStepMap[appStatus.Application]], false)
|
||||
if err != nil {
|
||||
logCtx.Warnf("AppSet '%v' has a invalid maxUpdate value '%+v', ignoring maxUpdate logic for this step: %v", applicationSet.Name, maxUpdate, err)
|
||||
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
|
||||
@@ -1213,13 +1160,13 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
|
||||
|
||||
if updateCountMap[appStepMap[appStatus.Application]] >= maxUpdateVal {
|
||||
maxUpdateAllowed = false
|
||||
logCtx.Infof("Application %v is not allowed to update yet, %v/%v Applications already updating in step %v in AppSet %v", appStatus.Application, updateCountMap[appStepMap[appStatus.Application]], maxUpdateVal, appStepMap[appStatus.Application]+1, applicationSet.Name)
|
||||
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 {
|
||||
logCtx.Infof("Application %v moved to Pending status, watching for the Application to start Progressing", appStatus.Application)
|
||||
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."
|
||||
@@ -1232,7 +1179,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
|
||||
}
|
||||
}
|
||||
|
||||
err := r.setAppSetApplicationStatus(ctx, logCtx, applicationSet, appStatuses)
|
||||
err := r.setAppSetApplicationStatus(ctx, applicationSet, appStatuses)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set AppSet app status: %w", err)
|
||||
}
|
||||
@@ -1294,7 +1241,7 @@ func findApplicationStatusIndex(appStatuses []argov1alpha1.ApplicationSetApplica
|
||||
|
||||
// setApplicationSetApplicationStatus updates the ApplicatonSet's status field
|
||||
// with any new/changed Application statuses.
|
||||
func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, applicationStatuses []argov1alpha1.ApplicationSetApplicationStatus) error {
|
||||
func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, applicationStatuses []argov1alpha1.ApplicationSetApplicationStatus) error {
|
||||
needToUpdateStatus := false
|
||||
|
||||
if len(applicationStatuses) != len(applicationSet.Status.ApplicationStatus) {
|
||||
@@ -1328,7 +1275,7 @@ func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Contex
|
||||
err := r.Client.Status().Update(ctx, applicationSet)
|
||||
if err != nil {
|
||||
|
||||
logCtx.Errorf("unable to set application set status: %v", err)
|
||||
log.Errorf("unable to set application set status: %v", err)
|
||||
return fmt.Errorf("unable to set application set status: %v", err)
|
||||
}
|
||||
|
||||
@@ -1343,7 +1290,7 @@ func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Contex
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appMap map[string]argov1alpha1.Application, validApps []argov1alpha1.Application) ([]argov1alpha1.Application, error) {
|
||||
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
|
||||
@@ -1363,7 +1310,7 @@ func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, appl
|
||||
|
||||
// check appSyncMap to determine which Applications are ready to be updated and which should be skipped
|
||||
if appSyncMap[validApps[i].Name] && appMap[validApps[i].Name].Status.Sync.Status == "OutOfSync" && appSetStatusPending {
|
||||
logCtx.Infof("triggering sync for application: %v, prune enabled: %v", validApps[i].Name, pruneEnabled)
|
||||
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])
|
||||
@@ -1407,51 +1354,29 @@ func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {
|
||||
CreateFunc: func(e event.CreateEvent) bool {
|
||||
// if we are the owner and there is a create event, we most likely created it and do not need to
|
||||
// re-reconcile
|
||||
if log.IsLevelEnabled(log.DebugLevel) {
|
||||
var appName string
|
||||
app, isApp := e.Object.(*argov1alpha1.Application)
|
||||
if isApp {
|
||||
appName = app.QualifiedName()
|
||||
}
|
||||
log.WithField("app", appName).Debugln("received create event from owning an application")
|
||||
}
|
||||
log.Debugln("received create event from owning an application")
|
||||
return false
|
||||
},
|
||||
DeleteFunc: func(e event.DeleteEvent) bool {
|
||||
if log.IsLevelEnabled(log.DebugLevel) {
|
||||
var appName string
|
||||
app, isApp := e.Object.(*argov1alpha1.Application)
|
||||
if isApp {
|
||||
appName = app.QualifiedName()
|
||||
}
|
||||
log.WithField("app", appName).Debugln("received delete event from owning an application")
|
||||
}
|
||||
log.Debugln("received delete event from owning an application")
|
||||
return true
|
||||
},
|
||||
UpdateFunc: func(e event.UpdateEvent) bool {
|
||||
log.Debugln("received update event from owning an application")
|
||||
appOld, isApp := e.ObjectOld.(*argov1alpha1.Application)
|
||||
if !isApp {
|
||||
return false
|
||||
}
|
||||
logCtx := log.WithField("app", appOld.QualifiedName())
|
||||
logCtx.Debugln("received update event from owning an application")
|
||||
appNew, isApp := e.ObjectNew.(*argov1alpha1.Application)
|
||||
if !isApp {
|
||||
return false
|
||||
}
|
||||
requeue := shouldRequeueApplicationSet(appOld, appNew, enableProgressiveSyncs)
|
||||
logCtx.WithField("requeue", requeue).Debugf("requeue: %t caused by application %s\n", requeue, appNew.Name)
|
||||
log.Debugf("requeue: %t caused by application %s\n", requeue, appNew.Name)
|
||||
return requeue
|
||||
},
|
||||
GenericFunc: func(e event.GenericEvent) bool {
|
||||
if log.IsLevelEnabled(log.DebugLevel) {
|
||||
var appName string
|
||||
app, isApp := e.Object.(*argov1alpha1.Application)
|
||||
if isApp {
|
||||
appName = app.QualifiedName()
|
||||
}
|
||||
log.WithField("app", appName).Debugln("received generic event from owning an application")
|
||||
}
|
||||
log.Debugln("received generic event from owning an application")
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
@@ -19,10 +19,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
k8scache "k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
crtclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
@@ -42,34 +40,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
)
|
||||
|
||||
type fakeStore struct {
|
||||
k8scache.Store
|
||||
}
|
||||
|
||||
func (f *fakeStore) Update(obj interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeInformer struct {
|
||||
k8scache.SharedInformer
|
||||
}
|
||||
|
||||
func (f *fakeInformer) AddIndexers(indexers k8scache.Indexers) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeInformer) GetStore() k8scache.Store {
|
||||
return &fakeStore{}
|
||||
}
|
||||
|
||||
type fakeCache struct {
|
||||
cache.Cache
|
||||
}
|
||||
|
||||
func (f *fakeCache) GetInformer(ctx context.Context, obj crtclient.Object) (cache.Informer, error) {
|
||||
return &fakeInformer{}, nil
|
||||
}
|
||||
|
||||
type generatorMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
@@ -215,10 +185,9 @@ func TestExtractApplications(t *testing.T) {
|
||||
},
|
||||
Renderer: &rendererMock,
|
||||
KubeClientset: kubefake.NewSimpleClientset(),
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
|
||||
got, reason, err := r.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||
got, reason, err := r.generateApplications(v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
@@ -331,7 +300,7 @@ func TestMergeTemplateApplications(t *testing.T) {
|
||||
KubeClientset: kubefake.NewSimpleClientset(),
|
||||
}
|
||||
|
||||
got, _, _ := r.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||
got, _, _ := r.generateApplications(v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
@@ -979,296 +948,6 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
// For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1191138278
|
||||
name: "Ensure that ignored targetRevision difference doesn't cause an update, even if another field changes",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
IgnoreApplicationDifferences: v1alpha1.ApplicationSetIgnoreDifferences{
|
||||
{JQPathExpressions: []string{".spec.source.targetRevision"}},
|
||||
},
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
TargetRevision: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingApps: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "2",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
TargetRevision: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
desiredApps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
// The targetRevision is ignored, so this should not be updated.
|
||||
TargetRevision: "foo",
|
||||
// This should be updated.
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
{Name: "hi", Value: "there"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "3",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
// This is the existing value from the cluster, which should not be updated because the field is ignored.
|
||||
TargetRevision: "bar",
|
||||
// This was missing on the cluster, so it should be added.
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
{Name: "hi", Value: "there"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
// For this use case: https://github.com/argoproj/argo-cd/pull/14743#issuecomment-1761954799
|
||||
name: "ignore parameters added to a multi-source app in the cluster",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
IgnoreApplicationDifferences: v1alpha1.ApplicationSetIgnoreDifferences{
|
||||
{JQPathExpressions: []string{`.spec.sources[] | select(.repoURL | contains("test-repo")).helm.parameters`}},
|
||||
},
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Sources: []v1alpha1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Values: "foo: bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingApps: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "2",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Sources: []v1alpha1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Values: "foo: bar",
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
{Name: "hi", Value: "there"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
desiredApps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Sources: []v1alpha1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Values: "foo: bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
// This should not be updated, because reconciliation shouldn't modify the App.
|
||||
ResourceVersion: "2",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Sources: []v1alpha1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Values: "foo: bar",
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
// This existed only in the cluster, but it shouldn't be removed, because the field is ignored.
|
||||
{Name: "hi", Value: "there"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "Demonstrate limitation of MergePatch", // Maybe we can fix this in Argo CD 3.0: https://github.com/argoproj/argo-cd/issues/15975
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
IgnoreApplicationDifferences: v1alpha1.ApplicationSetIgnoreDifferences{
|
||||
{JQPathExpressions: []string{`.spec.sources[] | select(.repoURL | contains("test-repo")).helm.parameters`}},
|
||||
},
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Sources: []v1alpha1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Values: "new: values",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingApps: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "2",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Sources: []v1alpha1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Values: "foo: bar",
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
{Name: "hi", Value: "there"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
desiredApps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Sources: []v1alpha1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Values: "new: values",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "3",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
Sources: []v1alpha1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://git.example.com/test-org/test-repo.git",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Values: "new: values",
|
||||
// The Parameters field got blown away, because the values field changed. MergePatch
|
||||
// doesn't merge list items, it replaces the whole list if an item changes.
|
||||
// If we eventually add a `name` field to Sources, we can use StrategicMergePatch.
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
|
||||
@@ -1282,17 +961,16 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
|
||||
initObjs = append(initObjs, &a)
|
||||
}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)),
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
|
||||
err = r.createOrUpdateInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps)
|
||||
assert.NoError(t, err)
|
||||
err = r.createOrUpdateInCluster(context.TODO(), c.appSet, c.desiredApps)
|
||||
assert.Nil(t, err)
|
||||
|
||||
for _, obj := range c.expected {
|
||||
got := &v1alpha1.Application{}
|
||||
@@ -1302,6 +980,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
|
||||
}, got)
|
||||
|
||||
err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, obj, *got)
|
||||
}
|
||||
})
|
||||
@@ -1375,7 +1054,7 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
|
||||
|
||||
initObjs := []crtclient.Object{&app, &appSet}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-secret",
|
||||
@@ -1401,7 +1080,6 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(10),
|
||||
KubeClientset: kubeclientset,
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
//settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "namespace")
|
||||
//argoDB := db.NewDB("namespace", settingsMgr, r.KubeClientset)
|
||||
@@ -1537,7 +1215,7 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
|
||||
|
||||
initObjs := []crtclient.Object{&app, &appSet}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-secret",
|
||||
@@ -1563,7 +1241,6 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(10),
|
||||
KubeClientset: kubeclientset,
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
// settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "argocd")
|
||||
// argoDB := db.NewDB("argocd", settingsMgr, r.KubeClientset)
|
||||
@@ -1769,16 +1446,15 @@ func TestCreateApplications(t *testing.T) {
|
||||
initObjs = append(initObjs, &a)
|
||||
}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)),
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
|
||||
err = r.createInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.apps)
|
||||
err = r.createInCluster(context.TODO(), c.appSet, c.apps)
|
||||
assert.Nil(t, err)
|
||||
|
||||
for _, obj := range c.expected {
|
||||
@@ -1913,7 +1589,7 @@ func TestDeleteInCluster(t *testing.T) {
|
||||
initObjs = append(initObjs, &temp)
|
||||
}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
@@ -1922,7 +1598,7 @@ func TestDeleteInCluster(t *testing.T) {
|
||||
KubeClientset: kubefake.NewSimpleClientset(),
|
||||
}
|
||||
|
||||
err = r.deleteInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps)
|
||||
err = r.deleteInCluster(context.TODO(), c.appSet, c.desiredApps)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// For each of the expected objects, verify they exist on the cluster
|
||||
@@ -1983,7 +1659,6 @@ func TestGetMinRequeueAfter(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(0),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": &generatorMock10,
|
||||
"Git": &generatorMock1,
|
||||
@@ -2197,7 +1872,6 @@ func TestValidateGeneratedApplications(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoCDNamespace: "namespace",
|
||||
@@ -2240,7 +1914,7 @@ func TestValidateGeneratedApplications(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
func TestReconcilerValidationErrorBehaviour(t *testing.T) {
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
@@ -2248,8 +1922,9 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
err = v1alpha1.AddToScheme(scheme)
|
||||
assert.Nil(t, err)
|
||||
|
||||
project := v1alpha1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "good-project", Namespace: "argocd"},
|
||||
defaultProject := v1alpha1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"},
|
||||
Spec: v1alpha1.AppProjectSpec{SourceRepos: []string{"*"}, Destinations: []v1alpha1.ApplicationDestination{{Namespace: "*", Server: "https://good-cluster"}}},
|
||||
}
|
||||
appSet := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -2262,22 +1937,22 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
{
|
||||
List: &v1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{{
|
||||
Raw: []byte(`{"project": "good-project"}`),
|
||||
Raw: []byte(`{"cluster": "good-cluster","url": "https://good-cluster"}`),
|
||||
}, {
|
||||
Raw: []byte(`{"project": "bad-project"}`),
|
||||
Raw: []byte(`{"cluster": "bad-cluster","url": "https://bad-cluster"}`),
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||
Name: "{{.project}}",
|
||||
Name: "{{.cluster}}",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"},
|
||||
Project: "{{.project}}",
|
||||
Destination: v1alpha1.ApplicationDestination{Server: "https://kubernetes.default.svc"},
|
||||
Project: "default",
|
||||
Destination: v1alpha1.ApplicationDestination{Server: "{{.url}}"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2285,9 +1960,9 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
|
||||
kubeclientset := kubefake.NewSimpleClientset()
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{&project}
|
||||
argoObjs := []runtime.Object{&defaultProject}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
|
||||
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
|
||||
badCluster := v1alpha1.Cluster{Server: "https://bad-cluster", Name: "bad-cluster"}
|
||||
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
|
||||
@@ -2301,7 +1976,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -2327,12 +2001,12 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
var app v1alpha1.Application
|
||||
|
||||
// make sure good app got created
|
||||
err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "good-project"}, &app)
|
||||
err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "good-cluster"}, &app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, app.Name, "good-project")
|
||||
assert.Equal(t, app.Name, "good-cluster")
|
||||
|
||||
// make sure bad app was not created
|
||||
err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "bad-project"}, &app)
|
||||
err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "bad-cluster"}, &app)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -2371,14 +2045,13 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -2441,7 +2114,7 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{&defaultProject}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
|
||||
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
|
||||
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
|
||||
argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{
|
||||
@@ -2453,7 +2126,6 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(recordBuffer),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -2611,7 +2283,7 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{&defaultProject}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
|
||||
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
|
||||
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
|
||||
argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{
|
||||
@@ -2623,7 +2295,6 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(recordBuffer),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -2804,7 +2475,6 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"PullRequest": &generatorMock,
|
||||
},
|
||||
@@ -2812,7 +2482,7 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
||||
KubeClientset: kubefake.NewSimpleClientset(),
|
||||
}
|
||||
|
||||
gotApp, _, _ := appSetReconciler.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||
gotApp, _, _ := appSetReconciler.generateApplications(v1alpha1.ApplicationSet{
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{{
|
||||
@@ -2922,14 +2592,13 @@ func TestPolicies(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(10),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -3092,7 +2761,6 @@ func TestSetApplicationSetApplicationStatus(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -3101,7 +2769,7 @@ func TestSetApplicationSetApplicationStatus(t *testing.T) {
|
||||
KubeClientset: kubeclientset,
|
||||
}
|
||||
|
||||
err = r.setAppSetApplicationStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appStatuses)
|
||||
err = r.setAppSetApplicationStatus(context.TODO(), &cc.appSet, cc.appStatuses)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, cc.expectedAppStatuses, cc.appSet.Status.ApplicationStatus)
|
||||
@@ -3857,14 +3525,13 @@ func TestBuildAppDependencyList(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
KubeClientset: kubeclientset,
|
||||
}
|
||||
|
||||
appDependencyList, appStepMap, err := r.buildAppDependencyList(log.NewEntry(log.StandardLogger()), cc.appSet, cc.apps)
|
||||
appDependencyList, appStepMap, err := r.buildAppDependencyList(context.TODO(), cc.appSet, cc.apps)
|
||||
assert.Equal(t, err, nil, "expected no errors, but errors occured")
|
||||
assert.Equal(t, cc.expectedList, appDependencyList, "expected appDependencyList did not match actual")
|
||||
assert.Equal(t, cc.expectedStepMap, appStepMap, "expected appStepMap did not match actual")
|
||||
@@ -4451,7 +4118,6 @@ func TestBuildAppSyncMap(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
@@ -5111,14 +4777,13 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
KubeClientset: kubeclientset,
|
||||
}
|
||||
|
||||
appStatuses, err := r.updateApplicationSetApplicationStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps, cc.appStepMap)
|
||||
appStatuses, err := r.updateApplicationSetApplicationStatus(context.TODO(), &cc.appSet, cc.apps, cc.appStepMap)
|
||||
|
||||
// opt out of testing the LastTransitionTime is accurate
|
||||
for i := range appStatuses {
|
||||
@@ -5865,14 +5530,13 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
KubeClientset: kubeclientset,
|
||||
}
|
||||
|
||||
appStatuses, err := r.updateApplicationSetApplicationStatusProgress(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appSyncMap, cc.appStepMap, cc.appMap)
|
||||
appStatuses, err := r.updateApplicationSetApplicationStatusProgress(context.TODO(), &cc.appSet, cc.appSyncMap, cc.appStepMap, cc.appMap)
|
||||
|
||||
// opt out of testing the LastTransitionTime is accurate
|
||||
for i := range appStatuses {
|
||||
|
||||
@@ -60,9 +60,9 @@ func TestRequeueAfter(t *testing.T) {
|
||||
"List": generators.NewListGenerator(),
|
||||
"Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"),
|
||||
"Git": generators.NewGitGenerator(mockServer),
|
||||
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, "", []string{""}, true),
|
||||
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, "", []string{""}),
|
||||
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
|
||||
"PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}, true),
|
||||
"PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}),
|
||||
}
|
||||
|
||||
nestedGenerators := map[string]generators.Generator{
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: guestbook
|
||||
spec:
|
||||
generators:
|
||||
- scmProvider:
|
||||
gitlab:
|
||||
api: https://gitlab.com
|
||||
group: test-argocd-proton
|
||||
includeSubgroups: true
|
||||
cloneProtocol: https
|
||||
filters:
|
||||
- repositoryMatch: test-app
|
||||
template:
|
||||
metadata:
|
||||
name: '{{ repository }}-guestbook'
|
||||
spec:
|
||||
project: "default"
|
||||
source:
|
||||
repoURL: '{{ url }}'
|
||||
targetRevision: '{{ branch }}'
|
||||
path: guestbook
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: guestbook
|
||||
@@ -78,7 +78,7 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
|
||||
// ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters
|
||||
clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing clusters: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if clustersFromArgoCD == nil {
|
||||
|
||||
@@ -74,7 +74,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
// ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters
|
||||
clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing clusters: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if clustersFromArgoCD == nil {
|
||||
@@ -85,7 +85,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
cm, err := g.clientset.CoreV1().ConfigMaps(g.namespace).Get(g.ctx, appSetGenerator.ClusterDecisionResource.ConfigMapRef, metav1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading configMapRef: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract GVK data for the dynamic client to use
|
||||
|
||||
@@ -125,7 +125,7 @@ func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSet
|
||||
func flattenParameters(in map[string]interface{}) (map[string]string, error) {
|
||||
flat, err := flatten.Flatten(in, "", flatten.DotStyle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error flatenning parameters: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(map[string]string, len(flat))
|
||||
|
||||
@@ -66,7 +66,7 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating params from git: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
@@ -77,7 +77,7 @@ func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoproj
|
||||
// Directories, not files
|
||||
allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting directories from repo: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
@@ -92,7 +92,7 @@ func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoproj
|
||||
|
||||
res, err := g.generateParamsFromApps(requestedApps, appSetGenerator, useGoTemplate, goTemplateOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating params from apps: %w", err)
|
||||
return nil, fmt.Errorf("failed to generate params from apps: %w", err)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
@@ -148,9 +148,6 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
||||
return nil, fmt.Errorf("unable to parse file: %v", err)
|
||||
}
|
||||
objectsFound = append(objectsFound, singleObj)
|
||||
} else if len(objectsFound) == 0 {
|
||||
// If file is valid but empty, add a default empty item
|
||||
objectsFound = append(objectsFound, map[string]interface{}{})
|
||||
}
|
||||
|
||||
res := []map[string]interface{}{}
|
||||
@@ -180,7 +177,7 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
||||
} else {
|
||||
flat, err := flatten.Flatten(objectFound, "", flatten.DotStyle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error flattening object: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range flat {
|
||||
params[k] = fmt.Sprintf("%v", v)
|
||||
|
||||
@@ -4,173 +4,119 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
|
||||
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func Test_generateParamsFromGitFile(t *testing.T) {
|
||||
defaultContent := []byte(`
|
||||
values := map[string]string{}
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
|
||||
foo:
|
||||
bar: baz
|
||||
`)
|
||||
type args struct {
|
||||
filePath string
|
||||
fileContent []byte
|
||||
values map[string]string
|
||||
useGoTemplate bool
|
||||
goTemplateOptions []string
|
||||
pathParamPrefix string
|
||||
`), values, false, nil, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []map[string]interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
assert.Equal(t, []map[string]interface{}{
|
||||
{
|
||||
name: "empty file returns path parameters",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: []byte(""),
|
||||
values: map[string]string{},
|
||||
useGoTemplate: false,
|
||||
},
|
||||
want: []map[string]interface{}{
|
||||
{
|
||||
"path": "path/dir",
|
||||
"path.basename": "dir",
|
||||
"path.filename": "file_name.yaml",
|
||||
"path.basenameNormalized": "dir",
|
||||
"path.filenameNormalized": "file-name.yaml",
|
||||
"path[0]": "path",
|
||||
"path[1]": "dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid json/yaml file returns error",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: []byte("this is not json or yaml"),
|
||||
values: map[string]string{},
|
||||
useGoTemplate: false,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "file parameters are added to params",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{},
|
||||
useGoTemplate: false,
|
||||
},
|
||||
want: []map[string]interface{}{
|
||||
{
|
||||
"foo.bar": "baz",
|
||||
"path": "path/dir",
|
||||
"path.basename": "dir",
|
||||
"path.filename": "file_name.yaml",
|
||||
"path.basenameNormalized": "dir",
|
||||
"path.filenameNormalized": "file-name.yaml",
|
||||
"path[0]": "path",
|
||||
"path[1]": "dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path parameter are prefixed",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{},
|
||||
useGoTemplate: false,
|
||||
pathParamPrefix: "myRepo",
|
||||
},
|
||||
want: []map[string]interface{}{
|
||||
{
|
||||
"foo.bar": "baz",
|
||||
"myRepo.path": "path/dir",
|
||||
"myRepo.path.basename": "dir",
|
||||
"myRepo.path.filename": "file_name.yaml",
|
||||
"myRepo.path.basenameNormalized": "dir",
|
||||
"myRepo.path.filenameNormalized": "file-name.yaml",
|
||||
"myRepo.path[0]": "path",
|
||||
"myRepo.path[1]": "dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "file parameters are added to params with go template",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{},
|
||||
useGoTemplate: true,
|
||||
},
|
||||
want: []map[string]interface{}{
|
||||
{
|
||||
"foo": map[string]interface{}{
|
||||
"bar": "baz",
|
||||
},
|
||||
"path": map[string]interface{}{
|
||||
"path": "path/dir",
|
||||
"basename": "dir",
|
||||
"filename": "file_name.yaml",
|
||||
"basenameNormalized": "dir",
|
||||
"filenameNormalized": "file-name.yaml",
|
||||
"segments": []string{
|
||||
"path",
|
||||
"dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path parameter are prefixed with go template",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{},
|
||||
useGoTemplate: true,
|
||||
pathParamPrefix: "myRepo",
|
||||
},
|
||||
want: []map[string]interface{}{
|
||||
{
|
||||
"foo": map[string]interface{}{
|
||||
"bar": "baz",
|
||||
},
|
||||
"myRepo": map[string]interface{}{
|
||||
"path": map[string]interface{}{
|
||||
"path": "path/dir",
|
||||
"basename": "dir",
|
||||
"filename": "file_name.yaml",
|
||||
"basenameNormalized": "dir",
|
||||
"filenameNormalized": "file-name.yaml",
|
||||
"segments": []string{
|
||||
"path",
|
||||
"dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"foo.bar": "baz",
|
||||
"path": "path/dir",
|
||||
"path.basename": "dir",
|
||||
"path.filename": "file_name.yaml",
|
||||
"path.basenameNormalized": "dir",
|
||||
"path.filenameNormalized": "file-name.yaml",
|
||||
"path[0]": "path",
|
||||
"path[1]": "dir",
|
||||
},
|
||||
}, params)
|
||||
}
|
||||
|
||||
func Test_generatePrefixedParamsFromGitFile(t *testing.T) {
|
||||
values := map[string]string{}
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
|
||||
foo:
|
||||
bar: baz
|
||||
`), values, false, nil, "myRepo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile(tt.args.filePath, tt.args.fileContent, tt.args.values, tt.args.useGoTemplate, tt.args.goTemplateOptions, tt.args.pathParamPrefix)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GitGenerator.generateParamsFromGitFile() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.want, params)
|
||||
})
|
||||
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) {
|
||||
values := map[string]string{}
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
|
||||
foo:
|
||||
bar: baz
|
||||
`), values, true, nil, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, []map[string]interface{}{
|
||||
{
|
||||
"foo": map[string]interface{}{
|
||||
"bar": "baz",
|
||||
},
|
||||
"path": map[string]interface{}{
|
||||
"path": "path/dir",
|
||||
"basename": "dir",
|
||||
"filename": "file_name.yaml",
|
||||
"basenameNormalized": "dir",
|
||||
"filenameNormalized": "file-name.yaml",
|
||||
"segments": []string{
|
||||
"path",
|
||||
"dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, params)
|
||||
}
|
||||
|
||||
func Test_generatePrefixedParamsFromGitFileGoTemplate(t *testing.T) {
|
||||
values := map[string]string{}
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
|
||||
foo:
|
||||
bar: baz
|
||||
`), values, true, nil, "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) {
|
||||
@@ -305,7 +251,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
repoApps: []string{},
|
||||
repoError: fmt.Errorf("error"),
|
||||
expected: []map[string]interface{}{},
|
||||
expectedError: fmt.Errorf("error generating params from git: error getting directories from repo: error"),
|
||||
expectedError: fmt.Errorf("error"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -601,7 +547,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
repoApps: []string{},
|
||||
repoError: fmt.Errorf("error"),
|
||||
expected: []map[string]interface{}{},
|
||||
expectedError: fmt.Errorf("error generating params from git: error getting directories from repo: error"),
|
||||
expectedError: fmt.Errorf("error"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -796,7 +742,7 @@ func TestGitGenerateParamsFromFiles(t *testing.T) {
|
||||
repoFileContents: map[string][]byte{},
|
||||
repoPathsError: fmt.Errorf("paths error"),
|
||||
expected: []map[string]interface{}{},
|
||||
expectedError: fmt.Errorf("error generating params from git: paths error"),
|
||||
expectedError: fmt.Errorf("paths error"),
|
||||
},
|
||||
{
|
||||
name: "test invalid JSON file returns error",
|
||||
@@ -806,7 +752,7 @@ func TestGitGenerateParamsFromFiles(t *testing.T) {
|
||||
},
|
||||
repoPathsError: nil,
|
||||
expected: []map[string]interface{}{},
|
||||
expectedError: fmt.Errorf("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"),
|
||||
expectedError: fmt.Errorf("unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"),
|
||||
},
|
||||
{
|
||||
name: "test JSON array",
|
||||
@@ -1102,7 +1048,7 @@ func TestGitGenerateParamsFromFilesGoTemplate(t *testing.T) {
|
||||
repoFileContents: map[string][]byte{},
|
||||
repoPathsError: fmt.Errorf("paths error"),
|
||||
expected: []map[string]interface{}{},
|
||||
expectedError: fmt.Errorf("error generating params from git: paths error"),
|
||||
expectedError: fmt.Errorf("paths error"),
|
||||
},
|
||||
{
|
||||
name: "test invalid JSON file returns error",
|
||||
@@ -1112,7 +1058,7 @@ func TestGitGenerateParamsFromFilesGoTemplate(t *testing.T) {
|
||||
},
|
||||
repoPathsError: nil,
|
||||
expected: []map[string]interface{}{},
|
||||
expectedError: fmt.Errorf("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"),
|
||||
expectedError: fmt.Errorf("unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"),
|
||||
},
|
||||
{
|
||||
name: "test JSON array",
|
||||
|
||||
@@ -83,7 +83,7 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshling decoded ElementsYaml %v", err)
|
||||
}
|
||||
res = append(res, yamlElements...)
|
||||
res = append(res, yamlElements...)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
|
||||
@@ -50,7 +50,7 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
||||
|
||||
g0, err := m.getParams(appSetGenerator.Matrix.Generators[0], appSet, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error failed to get params for first generator in matrix generator: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
for _, a := range g0 {
|
||||
g1, err := m.getParams(appSetGenerator.Matrix.Generators[1], appSet, a)
|
||||
@@ -61,11 +61,11 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
||||
|
||||
if appSet.Spec.GoTemplate {
|
||||
tmp := map[string]interface{}{}
|
||||
if err := mergo.Merge(&tmp, b, mergo.WithOverride); err != nil {
|
||||
return nil, fmt.Errorf("failed to merge params from the second generator in the matrix generator with temp map: %w", err)
|
||||
if err := mergo.Merge(&tmp, a); err != nil {
|
||||
return nil, fmt.Errorf("failed to merge params from the first generator in the matrix generator with temp map: %w", err)
|
||||
}
|
||||
if err := mergo.Merge(&tmp, a, mergo.WithOverride); err != nil {
|
||||
return nil, fmt.Errorf("failed to merge params from the second generator in the matrix generator with the first: %w", err)
|
||||
if err := mergo.Merge(&tmp, b); err != nil {
|
||||
return nil, fmt.Errorf("failed to merge params from the first generator in the matrix generator with the second: %w", err)
|
||||
}
|
||||
res = append(res, tmp)
|
||||
} else {
|
||||
@@ -94,7 +94,7 @@ func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Appli
|
||||
}
|
||||
mergeGen, err := getMergeGenerator(appSetBaseGenerator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retrieving merge generator: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
if mergeGen != nil && !appSet.Spec.ApplyNestedSelectors {
|
||||
foundSelector := dropDisabledNestedSelectors(mergeGen.Generators)
|
||||
@@ -146,15 +146,13 @@ func (m *MatrixGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap
|
||||
matrixGen, _ := getMatrixGenerator(r)
|
||||
mergeGen, _ := getMergeGenerator(r)
|
||||
base := &argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
List: r.List,
|
||||
Clusters: r.Clusters,
|
||||
Git: r.Git,
|
||||
PullRequest: r.PullRequest,
|
||||
Plugin: r.Plugin,
|
||||
SCMProvider: r.SCMProvider,
|
||||
ClusterDecisionResource: r.ClusterDecisionResource,
|
||||
Matrix: matrixGen,
|
||||
Merge: mergeGen,
|
||||
List: r.List,
|
||||
Clusters: r.Clusters,
|
||||
Git: r.Git,
|
||||
PullRequest: r.PullRequest,
|
||||
Plugin: r.Plugin,
|
||||
Matrix: matrixGen,
|
||||
Merge: mergeGen,
|
||||
}
|
||||
generators := GetRelevantGenerators(base, m.supportedGenerators)
|
||||
|
||||
|
||||
@@ -271,28 +271,6 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
||||
{"a": "2", "b": "2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "parameter override: first list elements take precedence",
|
||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
List: &argoprojiov1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{
|
||||
{Raw: []byte(`{"booleanFalse": false, "booleanTrue": true, "stringFalse": "false", "stringTrue": "true"}`)},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
List: &argoprojiov1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{
|
||||
{Raw: []byte(`{"booleanFalse": true, "booleanTrue": false, "stringFalse": "true", "stringTrue": "false"}`)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{"booleanFalse": false, "booleanTrue": true, "stringFalse": "false", "stringTrue": "true"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "returns error if there is less than two base generators",
|
||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
||||
@@ -426,10 +404,6 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
||||
|
||||
pullRequestGenerator := &argoprojiov1alpha1.PullRequestGenerator{}
|
||||
|
||||
scmGenerator := &argoprojiov1alpha1.SCMProviderGenerator{}
|
||||
|
||||
duckTypeGenerator := &argoprojiov1alpha1.DuckTypeGenerator{}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
||||
@@ -487,30 +461,6 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
||||
},
|
||||
expected: time.Duration(30 * time.Minute),
|
||||
},
|
||||
{
|
||||
name: "returns the default time for duck type generator",
|
||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
Git: gitGenerator,
|
||||
},
|
||||
{
|
||||
ClusterDecisionResource: duckTypeGenerator,
|
||||
},
|
||||
},
|
||||
expected: time.Duration(3 * time.Minute),
|
||||
},
|
||||
{
|
||||
name: "returns the default time for scm generator",
|
||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
Git: gitGenerator,
|
||||
},
|
||||
{
|
||||
SCMProvider: scmGenerator,
|
||||
},
|
||||
},
|
||||
expected: time.Duration(30 * time.Minute),
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
@@ -521,22 +471,18 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
||||
|
||||
for _, g := range testCaseCopy.baseGenerators {
|
||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Git: g.Git,
|
||||
List: g.List,
|
||||
PullRequest: g.PullRequest,
|
||||
SCMProvider: g.SCMProvider,
|
||||
ClusterDecisionResource: g.ClusterDecisionResource,
|
||||
Git: g.Git,
|
||||
List: g.List,
|
||||
PullRequest: g.PullRequest,
|
||||
}
|
||||
mock.On("GetRequeueAfter", &gitGeneratorSpec).Return(testCaseCopy.gitGetRequeueAfter, nil)
|
||||
}
|
||||
|
||||
var matrixGenerator = NewMatrixGenerator(
|
||||
map[string]Generator{
|
||||
"Git": mock,
|
||||
"List": &ListGenerator{},
|
||||
"PullRequest": &PullRequestGenerator{},
|
||||
"SCMProvider": &SCMProviderGenerator{},
|
||||
"ClusterDecisionResource": &DuckTypeGenerator{},
|
||||
"Git": mock,
|
||||
"List": &ListGenerator{},
|
||||
"PullRequest": &PullRequestGenerator{},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -38,10 +38,10 @@ func NewMergeGenerator(supportedGenerators map[string]Generator) Generator {
|
||||
// in slices ordered according to the order of the given generators.
|
||||
func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([][]map[string]interface{}, error) {
|
||||
var paramSets [][]map[string]interface{}
|
||||
for i, generator := range generators {
|
||||
for _, generator := range generators {
|
||||
generatorParamSets, err := m.getParams(generator, appSet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting params from generator %d of %d: %w", i+1, len(generators), err)
|
||||
return nil, err
|
||||
}
|
||||
// concatenate param lists produced by each generator
|
||||
paramSets = append(paramSets, generatorParamSets)
|
||||
@@ -61,18 +61,18 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl
|
||||
|
||||
paramSetsFromGenerators, err := m.getParamSetsForAllGenerators(appSetGenerator.Merge.Generators, appSet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting param sets from generators: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseParamSetsByMergeKey, err := getParamSetsByMergeKey(appSetGenerator.Merge.MergeKeys, paramSetsFromGenerators[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting param sets by merge key: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, paramSets := range paramSetsFromGenerators[1:] {
|
||||
paramSetsByMergeKey, err := getParamSetsByMergeKey(appSetGenerator.Merge.MergeKeys, paramSets)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting param sets by merge key: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for mergeKeyValue, baseParamSet := range baseParamSetsByMergeKey {
|
||||
@@ -80,13 +80,13 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl
|
||||
|
||||
if appSet.Spec.GoTemplate {
|
||||
if err := mergo.Merge(&baseParamSet, overrideParamSet, mergo.WithOverride); err != nil {
|
||||
return nil, fmt.Errorf("error merging base param set with override param set: %w", err)
|
||||
return nil, fmt.Errorf("failed to merge base param set with override param set: %w", err)
|
||||
}
|
||||
baseParamSetsByMergeKey[mergeKeyValue] = baseParamSet
|
||||
} else {
|
||||
overriddenParamSet, err := utils.CombineStringMapsAllowDuplicates(baseParamSet, overrideParamSet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error combining string maps: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
baseParamSetsByMergeKey[mergeKeyValue] = utils.ConvertToMapStringInterface(overriddenParamSet)
|
||||
}
|
||||
@@ -125,7 +125,7 @@ func getParamSetsByMergeKey(mergeKeys []string, paramSets []map[string]interface
|
||||
}
|
||||
paramSetKeyJson, err := json.Marshal(paramSetKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshalling param set key json: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
paramSetKeyString := string(paramSetKeyJson)
|
||||
if _, exists := paramSetsByMergeKey[paramSetKeyString]; exists {
|
||||
@@ -201,15 +201,13 @@ func (m *MergeGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.App
|
||||
matrixGen, _ := getMatrixGenerator(r)
|
||||
mergeGen, _ := getMergeGenerator(r)
|
||||
base := &argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
List: r.List,
|
||||
Clusters: r.Clusters,
|
||||
Git: r.Git,
|
||||
PullRequest: r.PullRequest,
|
||||
Plugin: r.Plugin,
|
||||
SCMProvider: r.SCMProvider,
|
||||
ClusterDecisionResource: r.ClusterDecisionResource,
|
||||
Matrix: matrixGen,
|
||||
Merge: mergeGen,
|
||||
List: r.List,
|
||||
Clusters: r.Clusters,
|
||||
Git: r.Git,
|
||||
PullRequest: r.PullRequest,
|
||||
Plugin: r.Plugin,
|
||||
Matrix: matrixGen,
|
||||
Merge: mergeGen,
|
||||
}
|
||||
generators := GetRelevantGenerators(base, m.supportedGenerators)
|
||||
|
||||
@@ -236,7 +234,7 @@ func getMergeGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*arg
|
||||
}
|
||||
merge, err := argoprojiov1alpha1.ToNestedMergeGenerator(r.Merge)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting to nested merge generator: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
return merge.ToMergeGenerator(), nil
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
||||
|
||||
pluginClient, err := g.getPluginFromGenerator(ctx, applicationSetInfo.Name, providerConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting plugin from generator: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list, err := pluginClient.List(ctx, providerConfig.Input.Parameters)
|
||||
@@ -81,7 +81,7 @@ func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
||||
|
||||
res, err := g.generateParams(appSetGenerator, applicationSetInfo, list.Output.Parameters, appSetGenerator.Plugin.Input.Parameters, applicationSetInfo.Spec.GoTemplate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating params: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
@@ -108,7 +108,7 @@ func (g *PluginGenerator) getPluginFromGenerator(ctx context.Context, appSetName
|
||||
|
||||
pluginClient, err := plugin.NewPluginService(ctx, appSetName, cm["baseUrl"], token, requestTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initializing plugin client: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
return pluginClient, nil
|
||||
}
|
||||
|
||||
@@ -475,7 +475,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: fmt.Errorf("error getting plugin from generator: error fetching Secret token: error fetching secret default/argocd-secret: secrets \"argocd-secret\" not found"),
|
||||
expectedError: fmt.Errorf("error fetching Secret token: error fetching secret default/argocd-secret: secrets \"argocd-secret\" not found"),
|
||||
},
|
||||
{
|
||||
name: "no configmap",
|
||||
@@ -522,7 +522,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: configmaps \"\" not found"),
|
||||
expectedError: fmt.Errorf("error fetching ConfigMap: configmaps \"\" not found"),
|
||||
},
|
||||
{
|
||||
name: "no baseUrl",
|
||||
@@ -577,7 +577,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: baseUrl not found in ConfigMap"),
|
||||
expectedError: fmt.Errorf("error fetching ConfigMap: baseUrl not found in ConfigMap"),
|
||||
},
|
||||
{
|
||||
name: "no token",
|
||||
@@ -624,7 +624,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: token not found in ConfigMap"),
|
||||
expectedError: fmt.Errorf("error fetching ConfigMap: token not found in ConfigMap"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -27,16 +27,14 @@ type PullRequestGenerator struct {
|
||||
auth SCMAuthProviders
|
||||
scmRootCAPath string
|
||||
allowedSCMProviders []string
|
||||
enableSCMProviders bool
|
||||
}
|
||||
|
||||
func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string, enableSCMProviders bool) Generator {
|
||||
func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string) Generator {
|
||||
g := &PullRequestGenerator{
|
||||
client: client,
|
||||
auth: auth,
|
||||
scmRootCAPath: scmRootCAPath,
|
||||
allowedSCMProviders: allowedScmProviders,
|
||||
enableSCMProviders: enableSCMProviders,
|
||||
}
|
||||
g.selectServiceProviderFunc = g.selectServiceProvider
|
||||
return g
|
||||
@@ -68,7 +66,7 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
ctx := context.Background()
|
||||
svc, err := g.selectServiceProviderFunc(ctx, appSetGenerator.PullRequest, applicationSetInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to select pull request service provider: %w", err)
|
||||
return nil, fmt.Errorf("failed to select pull request service provider: %v", err)
|
||||
}
|
||||
|
||||
pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters)
|
||||
@@ -123,18 +121,17 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
|
||||
// selectServiceProvider selects the provider to get pull requests from the configuration
|
||||
func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, generatorConfig *argoprojiov1alpha1.PullRequestGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||
if !g.enableSCMProviders {
|
||||
return nil, ErrSCMProvidersDisabled
|
||||
}
|
||||
if err := ScmProviderAllowed(applicationSetInfo, generatorConfig, g.allowedSCMProviders); err != nil {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %w", err)
|
||||
}
|
||||
|
||||
if generatorConfig.Github != nil {
|
||||
if !ScmProviderAllowed(applicationSetInfo, generatorConfig.Github.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", generatorConfig.Github.API)
|
||||
}
|
||||
return g.github(ctx, generatorConfig.Github, applicationSetInfo)
|
||||
}
|
||||
if generatorConfig.GitLab != nil {
|
||||
providerConfig := generatorConfig.GitLab
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
|
||||
}
|
||||
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret token: %v", err)
|
||||
@@ -143,6 +140,9 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
||||
}
|
||||
if generatorConfig.Gitea != nil {
|
||||
providerConfig := generatorConfig.Gitea
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", generatorConfig.Gitea.API)
|
||||
}
|
||||
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret token: %v", err)
|
||||
@@ -151,6 +151,9 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
||||
}
|
||||
if generatorConfig.BitbucketServer != nil {
|
||||
providerConfig := generatorConfig.BitbucketServer
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
|
||||
}
|
||||
if providerConfig.BasicAuth != nil {
|
||||
password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
return pullrequest.NewFakeService(
|
||||
ctx,
|
||||
[]*pullrequest.PullRequest{
|
||||
{
|
||||
&pullrequest.PullRequest{
|
||||
Number: 1,
|
||||
Branch: "branch1",
|
||||
TargetBranch: "master",
|
||||
@@ -56,7 +56,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
return pullrequest.NewFakeService(
|
||||
ctx,
|
||||
[]*pullrequest.PullRequest{
|
||||
{
|
||||
&pullrequest.PullRequest{
|
||||
Number: 2,
|
||||
Branch: "feat/areally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
|
||||
TargetBranch: "feat/anotherreally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
|
||||
@@ -85,7 +85,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
return pullrequest.NewFakeService(
|
||||
ctx,
|
||||
[]*pullrequest.PullRequest{
|
||||
{
|
||||
&pullrequest.PullRequest{
|
||||
Number: 1,
|
||||
Branch: "a-very-short-sha",
|
||||
TargetBranch: "master",
|
||||
@@ -125,7 +125,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
return pullrequest.NewFakeService(
|
||||
ctx,
|
||||
[]*pullrequest.PullRequest{
|
||||
{
|
||||
&pullrequest.PullRequest{
|
||||
Number: 1,
|
||||
Branch: "branch1",
|
||||
TargetBranch: "master",
|
||||
@@ -162,7 +162,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
return pullrequest.NewFakeService(
|
||||
ctx,
|
||||
[]*pullrequest.PullRequest{
|
||||
{
|
||||
&pullrequest.PullRequest{
|
||||
Number: 1,
|
||||
Branch: "branch1",
|
||||
TargetBranch: "master",
|
||||
@@ -278,7 +278,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
providerConfig *argoprojiov1alpha1.PullRequestGenerator
|
||||
expectedError error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "Error Github",
|
||||
@@ -287,7 +287,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: &ErrDisallowedSCMProvider{},
|
||||
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Gitlab",
|
||||
@@ -296,7 +296,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: &ErrDisallowedSCMProvider{},
|
||||
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Gitea",
|
||||
@@ -305,7 +305,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: &ErrDisallowedSCMProvider{},
|
||||
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Bitbucket",
|
||||
@@ -314,7 +314,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: &ErrDisallowedSCMProvider{},
|
||||
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
"gitea.myorg.com",
|
||||
"bitbucket.myorg.com",
|
||||
"azuredevops.myorg.com",
|
||||
}, true)
|
||||
})
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -346,29 +346,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
_, err := pullRequestGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
|
||||
assert.Error(t, err, "Must return an error")
|
||||
assert.ErrorAs(t, err, testCaseCopy.expectedError)
|
||||
assert.Equal(t, testCaseCopy.expectedError, err.Error())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSCMProviderDisabled_PRGenerator(t *testing.T) {
|
||||
generator := NewPullRequestGenerator(nil, SCMAuthProviders{}, "", []string{}, false)
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
},
|
||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
|
||||
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
|
||||
Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
assert.ErrorIs(t, err, ErrSCMProvidersDisabled)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package generators
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -32,26 +31,24 @@ type SCMProviderGenerator struct {
|
||||
SCMAuthProviders
|
||||
scmRootCAPath string
|
||||
allowedSCMProviders []string
|
||||
enableSCMProviders bool
|
||||
}
|
||||
|
||||
type SCMAuthProviders struct {
|
||||
GitHubApps github_app_auth.Credentials
|
||||
}
|
||||
|
||||
func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool) Generator {
|
||||
func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string) Generator {
|
||||
return &SCMProviderGenerator{
|
||||
client: client,
|
||||
SCMAuthProviders: providers,
|
||||
scmRootCAPath: scmRootCAPath,
|
||||
allowedSCMProviders: allowedSCMProviders,
|
||||
enableSCMProviders: enableSCMProviders,
|
||||
}
|
||||
}
|
||||
|
||||
// Testing generator
|
||||
func NewTestSCMProviderGenerator(overrideProvider scm_provider.SCMProviderService) Generator {
|
||||
return &SCMProviderGenerator{overrideProvider: overrideProvider, enableSCMProviders: true}
|
||||
return &SCMProviderGenerator{overrideProvider: overrideProvider}
|
||||
}
|
||||
|
||||
func (g *SCMProviderGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
||||
@@ -68,34 +65,14 @@ func (g *SCMProviderGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.A
|
||||
return &appSetGenerator.SCMProvider.Template
|
||||
}
|
||||
|
||||
var ErrSCMProvidersDisabled = errors.New("scm providers are disabled")
|
||||
|
||||
type ErrDisallowedSCMProvider struct {
|
||||
Provider string
|
||||
Allowed []string
|
||||
}
|
||||
|
||||
func NewErrDisallowedSCMProvider(provider string, allowed []string) ErrDisallowedSCMProvider {
|
||||
return ErrDisallowedSCMProvider{
|
||||
Provider: provider,
|
||||
Allowed: allowed,
|
||||
}
|
||||
}
|
||||
|
||||
func (e ErrDisallowedSCMProvider) Error() string {
|
||||
return fmt.Sprintf("scm provider %q not allowed, must use one of the following: %s", e.Provider, strings.Join(e.Allowed, ", "))
|
||||
}
|
||||
|
||||
func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, generator SCMGeneratorWithCustomApiUrl, allowedScmProviders []string) error {
|
||||
url := generator.CustomApiUrl()
|
||||
|
||||
func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, url string, allowedScmProviders []string) bool {
|
||||
if url == "" || len(allowedScmProviders) == 0 {
|
||||
return nil
|
||||
return true
|
||||
}
|
||||
|
||||
for _, allowedScmProvider := range allowedScmProviders {
|
||||
if url == allowedScmProvider {
|
||||
return nil
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,9 +80,9 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, g
|
||||
common.SecurityField: common.SecurityMedium,
|
||||
"applicationset": applicationSetInfo.Name,
|
||||
"appSetNamespace": applicationSetInfo.Namespace,
|
||||
}).Debugf("attempted to use disallowed SCM %q, must use one of the following: %s", url, strings.Join(allowedScmProviders, ", "))
|
||||
}).Debugf("attempted to use disallowed SCM %q", url)
|
||||
|
||||
return NewErrDisallowedSCMProvider(url, allowedScmProviders)
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
@@ -117,37 +94,38 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
|
||||
if !g.enableSCMProviders {
|
||||
return nil, ErrSCMProvidersDisabled
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
// Create the SCM provider helper.
|
||||
providerConfig := appSetGenerator.SCMProvider
|
||||
|
||||
if err := ScmProviderAllowed(applicationSetInfo, providerConfig, g.allowedSCMProviders); err != nil {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %w", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
var provider scm_provider.SCMProviderService
|
||||
if g.overrideProvider != nil {
|
||||
provider = g.overrideProvider
|
||||
} else if providerConfig.Github != nil {
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Github.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Github.API)
|
||||
}
|
||||
var err error
|
||||
provider, err = g.githubProvider(ctx, providerConfig.Github, applicationSetInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("scm provider: %w", err)
|
||||
}
|
||||
} else if providerConfig.Gitlab != nil {
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Gitlab.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Gitlab.API)
|
||||
}
|
||||
token, err := g.getSecretRef(ctx, providerConfig.Gitlab.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Gitlab token: %v", err)
|
||||
}
|
||||
provider, err = scm_provider.NewGitlabProvider(ctx, providerConfig.Gitlab.Group, token, providerConfig.Gitlab.API, providerConfig.Gitlab.AllBranches, providerConfig.Gitlab.IncludeSubgroups, providerConfig.Gitlab.WillIncludeSharedProjects(), providerConfig.Gitlab.Insecure, g.scmRootCAPath, providerConfig.Gitlab.Topic)
|
||||
provider, err = scm_provider.NewGitlabProvider(ctx, providerConfig.Gitlab.Group, token, providerConfig.Gitlab.API, providerConfig.Gitlab.AllBranches, providerConfig.Gitlab.IncludeSubgroups, providerConfig.Gitlab.Insecure, g.scmRootCAPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initializing Gitlab service: %v", err)
|
||||
}
|
||||
} else if providerConfig.Gitea != nil {
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Gitea.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Gitea.API)
|
||||
}
|
||||
token, err := g.getSecretRef(ctx, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Gitea token: %v", err)
|
||||
@@ -158,6 +136,9 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
}
|
||||
} else if providerConfig.BitbucketServer != nil {
|
||||
providerConfig := providerConfig.BitbucketServer
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
|
||||
}
|
||||
var scmError error
|
||||
if providerConfig.BasicAuth != nil {
|
||||
password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace)
|
||||
@@ -172,6 +153,9 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
return nil, fmt.Errorf("error initializing Bitbucket Server service: %v", scmError)
|
||||
}
|
||||
} else if providerConfig.AzureDevOps != nil {
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.AzureDevOps.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.AzureDevOps.API)
|
||||
}
|
||||
token, err := g.getSecretRef(ctx, providerConfig.AzureDevOps.AccessTokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Azure Devops access token: %v", err)
|
||||
|
||||
@@ -108,26 +108,26 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"organization": "myorg",
|
||||
"repository": "repo1",
|
||||
"url": "git@github.com:myorg/repo1.git",
|
||||
"branch": "main",
|
||||
"organization": "myorg",
|
||||
"repository": "repo1",
|
||||
"url": "git@github.com:myorg/repo1.git",
|
||||
"branch": "main",
|
||||
"branchNormalized": "main",
|
||||
"sha": "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
||||
"short_sha": "0bc57212",
|
||||
"short_sha_7": "0bc5721",
|
||||
"labels": "prod,staging",
|
||||
"sha": "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
||||
"short_sha": "0bc57212",
|
||||
"short_sha_7": "0bc5721",
|
||||
"labels": "prod,staging",
|
||||
},
|
||||
{
|
||||
"organization": "myorg",
|
||||
"repository": "repo2",
|
||||
"url": "git@github.com:myorg/repo2.git",
|
||||
"branch": "main",
|
||||
"organization": "myorg",
|
||||
"repository": "repo2",
|
||||
"url": "git@github.com:myorg/repo2.git",
|
||||
"branch": "main",
|
||||
"branchNormalized": "main",
|
||||
"sha": "59d0",
|
||||
"short_sha": "59d0",
|
||||
"short_sha_7": "59d0",
|
||||
"labels": "",
|
||||
"sha": "59d0",
|
||||
"short_sha": "59d0",
|
||||
"short_sha_7": "59d0",
|
||||
"labels": "",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -174,7 +174,7 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
||||
mockProvider := &scm_provider.MockProvider{
|
||||
Repos: testCaseCopy.repos,
|
||||
}
|
||||
scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider, enableSCMProviders: true}
|
||||
scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider}
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -205,7 +205,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
providerConfig *argoprojiov1alpha1.SCMProviderGenerator
|
||||
expectedError error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "Error Github",
|
||||
@@ -214,7 +214,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: &ErrDisallowedSCMProvider{},
|
||||
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Gitlab",
|
||||
@@ -223,7 +223,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: &ErrDisallowedSCMProvider{},
|
||||
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Gitea",
|
||||
@@ -232,7 +232,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: &ErrDisallowedSCMProvider{},
|
||||
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Bitbucket",
|
||||
@@ -241,7 +241,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: &ErrDisallowedSCMProvider{},
|
||||
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error AzureDevops",
|
||||
@@ -250,7 +250,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: &ErrDisallowedSCMProvider{},
|
||||
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -260,16 +260,13 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scmGenerator := &SCMProviderGenerator{
|
||||
allowedSCMProviders: []string{
|
||||
"github.myorg.com",
|
||||
"gitlab.myorg.com",
|
||||
"gitea.myorg.com",
|
||||
"bitbucket.myorg.com",
|
||||
"azuredevops.myorg.com",
|
||||
},
|
||||
enableSCMProviders: true,
|
||||
}
|
||||
scmGenerator := &SCMProviderGenerator{allowedSCMProviders: []string{
|
||||
"github.myorg.com",
|
||||
"gitlab.myorg.com",
|
||||
"gitea.myorg.com",
|
||||
"bitbucket.myorg.com",
|
||||
"azuredevops.myorg.com",
|
||||
}}
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -285,29 +282,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
_, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
|
||||
assert.Error(t, err, "Must return an error")
|
||||
assert.ErrorAs(t, err, testCaseCopy.expectedError)
|
||||
assert.Equal(t, testCaseCopy.expectedError, err.Error())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSCMProviderDisabled_SCMGenerator(t *testing.T) {
|
||||
generator := &SCMProviderGenerator{enableSCMProviders: false}
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
},
|
||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
|
||||
SCMProvider: &argoprojiov1alpha1.SCMProviderGenerator{
|
||||
Github: &argoprojiov1alpha1.SCMProviderGeneratorGithub{
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
assert.ErrorIs(t, err, ErrSCMProvidersDisabled)
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package generators
|
||||
|
||||
type SCMGeneratorWithCustomApiUrl interface {
|
||||
CustomApiUrl() string
|
||||
}
|
||||
@@ -269,9 +269,9 @@ func TestGetGiteaPRLabelNames(t *testing.T) {
|
||||
{
|
||||
Name: "PR has labels",
|
||||
PullLabels: []*gitea.Label{
|
||||
{Name: "label1"},
|
||||
{Name: "label2"},
|
||||
{Name: "label3"},
|
||||
&gitea.Label{Name: "label1"},
|
||||
&gitea.Label{Name: "label2"},
|
||||
&gitea.Label{Name: "label3"},
|
||||
},
|
||||
ExpectedResult: []string{"label1", "label2", "label3"},
|
||||
},
|
||||
|
||||
@@ -22,9 +22,9 @@ func TestContainLabels(t *testing.T) {
|
||||
Name: "Match labels",
|
||||
Labels: []string{"label1", "label2"},
|
||||
PullLabels: []*github.Label{
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
&github.Label{Name: toPtr("label1")},
|
||||
&github.Label{Name: toPtr("label2")},
|
||||
&github.Label{Name: toPtr("label3")},
|
||||
},
|
||||
Expect: true,
|
||||
},
|
||||
@@ -32,9 +32,9 @@ func TestContainLabels(t *testing.T) {
|
||||
Name: "Not match labels",
|
||||
Labels: []string{"label1", "label4"},
|
||||
PullLabels: []*github.Label{
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
&github.Label{Name: toPtr("label1")},
|
||||
&github.Label{Name: toPtr("label2")},
|
||||
&github.Label{Name: toPtr("label3")},
|
||||
},
|
||||
Expect: false,
|
||||
},
|
||||
@@ -42,9 +42,9 @@ func TestContainLabels(t *testing.T) {
|
||||
Name: "No specify",
|
||||
Labels: []string{},
|
||||
PullLabels: []*github.Label{
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
&github.Label{Name: toPtr("label1")},
|
||||
&github.Label{Name: toPtr("label2")},
|
||||
&github.Label{Name: toPtr("label3")},
|
||||
},
|
||||
Expect: true,
|
||||
},
|
||||
@@ -68,9 +68,9 @@ func TestGetGitHubPRLabelNames(t *testing.T) {
|
||||
{
|
||||
Name: "PR has labels",
|
||||
PullLabels: []*github.Label{
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
&github.Label{Name: toPtr("label1")},
|
||||
&github.Label{Name: toPtr("label2")},
|
||||
&github.Label{Name: toPtr("label3")},
|
||||
},
|
||||
ExpectedResult: []string{"label1", "label2", "label3"},
|
||||
},
|
||||
|
||||
@@ -58,13 +58,13 @@ func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision s
|
||||
}
|
||||
closer, client, err := a.repoServerClientSet.NewRepoServerClient()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initialising new repo server client: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
defer io.Close(closer)
|
||||
|
||||
fileResponse, err := client.GetGitFiles(ctx, fileRequest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retrieving Git files: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
return fileResponse.GetMap(), nil
|
||||
}
|
||||
@@ -83,13 +83,13 @@ func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revi
|
||||
|
||||
closer, client, err := a.repoServerClientSet.NewRepoServerClient()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initialising new repo server client: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
defer io.Close(closer)
|
||||
|
||||
dirResponse, err := client.GetGitDirectories(ctx, dirRequest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retrieving Git Directories: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
return dirResponse.GetPaths(), nil
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -61,7 +62,7 @@ func TestBitbucketHasRepo(t *testing.T) {
|
||||
}))
|
||||
defer func() { testServer.Close() }()
|
||||
|
||||
t.Setenv("BITBUCKET_API_BASE_URL", testServer.URL)
|
||||
os.Setenv("BITBUCKET_API_BASE_URL", testServer.URL)
|
||||
cases := []struct {
|
||||
name, path, repo, owner, sha string
|
||||
status int
|
||||
@@ -448,7 +449,7 @@ func TestBitbucketListRepos(t *testing.T) {
|
||||
}))
|
||||
defer func() { testServer.Close() }()
|
||||
|
||||
t.Setenv("BITBUCKET_API_BASE_URL", testServer.URL)
|
||||
os.Setenv("BITBUCKET_API_BASE_URL", testServer.URL)
|
||||
cases := []struct {
|
||||
name, proto, owner string
|
||||
hasError, allBranches bool
|
||||
|
||||
@@ -13,17 +13,15 @@ import (
|
||||
)
|
||||
|
||||
type GitlabProvider struct {
|
||||
client *gitlab.Client
|
||||
organization string
|
||||
allBranches bool
|
||||
includeSubgroups bool
|
||||
includeSharedProjects bool
|
||||
topic string
|
||||
client *gitlab.Client
|
||||
organization string
|
||||
allBranches bool
|
||||
includeSubgroups bool
|
||||
}
|
||||
|
||||
var _ SCMProviderService = &GitlabProvider{}
|
||||
|
||||
func NewGitlabProvider(ctx context.Context, organization string, token string, url string, allBranches, includeSubgroups, includeSharedProjects, insecure bool, scmRootCAPath, topic string) (*GitlabProvider, error) {
|
||||
func NewGitlabProvider(ctx context.Context, organization string, token string, url string, allBranches, includeSubgroups, insecure bool, scmRootCAPath string) (*GitlabProvider, error) {
|
||||
// Undocumented environment variable to set a default token, to be used in testing to dodge anonymous rate limits.
|
||||
if token == "" {
|
||||
token = os.Getenv("GITLAB_TOKEN")
|
||||
@@ -49,8 +47,7 @@ func NewGitlabProvider(ctx context.Context, organization string, token string, u
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &GitlabProvider{client: client, organization: organization, allBranches: allBranches, includeSubgroups: includeSubgroups, includeSharedProjects: includeSharedProjects, topic: topic}, nil
|
||||
return &GitlabProvider{client: client, organization: organization, allBranches: allBranches, includeSubgroups: includeSubgroups}, nil
|
||||
}
|
||||
|
||||
func (g *GitlabProvider) GetBranches(ctx context.Context, repo *Repository) ([]*Repository, error) {
|
||||
@@ -78,10 +75,7 @@ func (g *GitlabProvider) ListRepos(ctx context.Context, cloneProtocol string) ([
|
||||
opt := &gitlab.ListGroupProjectsOptions{
|
||||
ListOptions: gitlab.ListOptions{PerPage: 100},
|
||||
IncludeSubGroups: &g.includeSubgroups,
|
||||
WithShared: &g.includeSharedProjects,
|
||||
Topic: &g.topic,
|
||||
}
|
||||
|
||||
repos := []*Repository{}
|
||||
for {
|
||||
gitlabRepos, resp, err := g.client.Groups.ListGroupProjects(g.organization, opt)
|
||||
|
||||
@@ -19,7 +19,7 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
switch r.RequestURI {
|
||||
case "/api/v4":
|
||||
fmt.Println("here1")
|
||||
case "/api/v4/groups/test-argocd-proton/projects?include_subgroups=false&per_page=100", "/api/v4/groups/test-argocd-proton/projects?include_subgroups=false&per_page=100&topic=&with_shared=false":
|
||||
case "/api/v4/groups/test-argocd-proton/projects?include_subgroups=false&per_page=100":
|
||||
fmt.Println("here")
|
||||
_, err := io.WriteString(w, `[{
|
||||
"id": 27084533,
|
||||
@@ -30,12 +30,8 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
"path_with_namespace": "test-argocd-proton/argocd",
|
||||
"created_at": "2021-06-01T17:30:44.724Z",
|
||||
"default_branch": "master",
|
||||
"tag_list": [
|
||||
"test-topic"
|
||||
],
|
||||
"topics": [
|
||||
"test-topic"
|
||||
],
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
"http_url_to_repo": "https://gitlab.com/test-argocd-proton/argocd.git",
|
||||
"web_url": "https://gitlab.com/test-argocd-proton/argocd",
|
||||
@@ -147,650 +143,6 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
case "/api/v4/groups/test-argocd-proton/projects?include_subgroups=true&per_page=100&topic=&with_shared=false":
|
||||
fmt.Println("here")
|
||||
_, err := io.WriteString(w, `[{
|
||||
"id": 27084533,
|
||||
"description": "",
|
||||
"name": "argocd",
|
||||
"name_with_namespace": "test argocd proton / argocd",
|
||||
"path": "argocd",
|
||||
"path_with_namespace": "test-argocd-proton/argocd",
|
||||
"created_at": "2021-06-01T17:30:44.724Z",
|
||||
"default_branch": "master",
|
||||
"tag_list": [
|
||||
"test-topic",
|
||||
"specific-topic"
|
||||
],
|
||||
"topics": [
|
||||
"test-topic",
|
||||
"specific-topic"
|
||||
],
|
||||
"ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
"http_url_to_repo": "https://gitlab.com/test-argocd-proton/argocd.git",
|
||||
"web_url": "https://gitlab.com/test-argocd-proton/argocd",
|
||||
"readme_url": null,
|
||||
"avatar_url": null,
|
||||
"forks_count": 0,
|
||||
"star_count": 0,
|
||||
"last_activity_at": "2021-06-04T08:19:51.656Z",
|
||||
"namespace": {
|
||||
"id": 12258515,
|
||||
"name": "test argocd proton",
|
||||
"path": "test-argocd-proton",
|
||||
"kind": "gro* Connection #0 to host gitlab.com left intact up ",
|
||||
"full_path ": "test - argocd - proton ",
|
||||
"parent_id ": null,
|
||||
"avatar_url ": null,
|
||||
"web_url ": "https: //gitlab.com/groups/test-argocd-proton"
|
||||
},
|
||||
"container_registry_image_prefix": "registry.gitlab.com/test-argocd-proton/argocd",
|
||||
"_links": {
|
||||
"self": "https://gitlab.com/api/v4/projects/27084533",
|
||||
"issues": "https://gitlab.com/api/v4/projects/27084533/issues",
|
||||
"merge_requests": "https://gitlab.com/api/v4/projects/27084533/merge_requests",
|
||||
"repo_branches": "https://gitlab.com/api/v4/projects/27084533/repository/branches",
|
||||
"labels": "https://gitlab.com/api/v4/projects/27084533/labels",
|
||||
"events": "https://gitlab.com/api/v4/projects/27084533/events",
|
||||
"members": "https://gitlab.com/api/v4/projects/27084533/members",
|
||||
"cluster_agents": "https://gitlab.com/api/v4/projects/27084533/cluster_agents"
|
||||
},
|
||||
"packages_enabled": true,
|
||||
"empty_repo": false,
|
||||
"archived": false,
|
||||
"visibility": "public",
|
||||
"resolve_outdated_diff_discussions": false,
|
||||
"container_expiration_policy": {
|
||||
"cadence": "1d",
|
||||
"enabled": false,
|
||||
"keep_n": 10,
|
||||
"older_than": "90d",
|
||||
"name_regex": ".*",
|
||||
"name_regex_keep": null,
|
||||
"next_run_at": "2021-06-02T17:30:44.740Z"
|
||||
},
|
||||
"issues_enabled": true,
|
||||
"merge_requests_enabled": true,
|
||||
"wiki_enabled": true,
|
||||
"jobs_enabled": true,
|
||||
"snippets_enabled": true,
|
||||
"container_registry_enabled": true,
|
||||
"service_desk_enabled": true,
|
||||
"can_create_merge_request_in": false,
|
||||
"issues_access_level": "enabled",
|
||||
"repository_access_level": "enabled",
|
||||
"merge_requests_access_level": "enabled",
|
||||
"forking_access_level": "enabled",
|
||||
"wiki_access_level": "enabled",
|
||||
"builds_access_level": "enabled",
|
||||
"snippets_access_level": "enabled",
|
||||
"pages_access_level": "enabled",
|
||||
"operations_access_level": "enabled",
|
||||
"analytics_access_level": "enabled",
|
||||
"container_registry_access_level": "enabled",
|
||||
"security_and_compliance_access_level": "private",
|
||||
"emails_disabled": null,
|
||||
"shared_runners_enabled": true,
|
||||
"lfs_enabled": true,
|
||||
"creator_id": 2378866,
|
||||
"import_status": "none",
|
||||
"open_issues_count": 0,
|
||||
"ci_default_git_depth": 50,
|
||||
"ci_forward_deployment_enabled": true,
|
||||
"ci_job_token_scope_enabled": false,
|
||||
"public_jobs": true,
|
||||
"build_timeout": 3600,
|
||||
"auto_cancel_pending_pipelines": "enabled",
|
||||
"ci_config_path": "",
|
||||
"shared_with_groups": [],
|
||||
"only_allow_merge_if_pipeline_succeeds": false,
|
||||
"allow_merge_on_skipped_pipeline": null,
|
||||
"restrict_user_defined_variables": false,
|
||||
"request_access_enabled": true,
|
||||
"only_allow_merge_if_all_discussions_are_resolved": false,
|
||||
"remove_source_branch_after_merge": true,
|
||||
"printing_merge_request_link_enabled": true,
|
||||
"merge_method": "merge",
|
||||
"squash_option": "default_off",
|
||||
"suggestion_commit_message": null,
|
||||
"merge_commit_template": null,
|
||||
"squash_commit_template": null,
|
||||
"auto_devops_enabled": false,
|
||||
"auto_devops_deploy_strategy": "continuous",
|
||||
"autoclose_referenced_issues": true,
|
||||
"keep_latest_artifact": true,
|
||||
"runner_token_expiration_interval": null,
|
||||
"approvals_before_merge": 0,
|
||||
"mirror": false,
|
||||
"external_authorization_classification_label": "",
|
||||
"marked_for_deletion_at": null,
|
||||
"marked_for_deletion_on": null,
|
||||
"requirements_enabled": true,
|
||||
"requirements_access_level": "enabled",
|
||||
"security_and_compliance_enabled": false,
|
||||
"compliance_frameworks": [],
|
||||
"issues_template": null,
|
||||
"merge_requests_template": null,
|
||||
"merge_pipelines_enabled": false,
|
||||
"merge_trains_enabled": false
|
||||
},
|
||||
{
|
||||
"id": 27084538,
|
||||
"description": "This is a Project from a Subgroup",
|
||||
"name": "argocd-subgroup",
|
||||
"name_with_namespace": "test argocd proton / subgroup / argocd-subgroup",
|
||||
"path": "argocd-subgroup",
|
||||
"path_with_namespace": "test-argocd-proton/subgroup/argocd-subgroup",
|
||||
"created_at": "2021-06-01T17:30:44.724Z",
|
||||
"default_branch": "master",
|
||||
"tag_list": [
|
||||
"test-topic"
|
||||
],
|
||||
"topics": [
|
||||
"test-topic"
|
||||
],
|
||||
"ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/subgroup/argocd-subgroup.git",
|
||||
"http_url_to_repo": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup.git",
|
||||
"web_url": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup",
|
||||
"readme_url": null,
|
||||
"avatar_url": null,
|
||||
"forks_count": 0,
|
||||
"star_count": 0,
|
||||
"last_activity_at": "2021-06-04T08:19:51.656Z",
|
||||
"namespace": {
|
||||
"id": 12258542,
|
||||
"name": "subgroup",
|
||||
"path": "subgroup",
|
||||
"kind": "group ",
|
||||
"full_path ": "test-argocd-proton/subgroup",
|
||||
"parent_id ": 12258515,
|
||||
"avatar_url ": null,
|
||||
"web_url ": "https: //gitlab.com/groups/test-argocd-proton/subgroup"
|
||||
},
|
||||
"container_registry_image_prefix": "registry.gitlab.com/test-argocd-proton/subgroup/argocd",
|
||||
"_links": {
|
||||
"self": "https://gitlab.com/api/v4/projects/27084538",
|
||||
"issues": "https://gitlab.com/api/v4/projects/27084538/issues",
|
||||
"merge_requests": "https://gitlab.com/api/v4/projects/27084538/merge_requests",
|
||||
"repo_branches": "https://gitlab.com/api/v4/projects/27084538/repository/branches",
|
||||
"labels": "https://gitlab.com/api/v4/projects/27084538/labels",
|
||||
"events": "https://gitlab.com/api/v4/projects/27084538/events",
|
||||
"members": "https://gitlab.com/api/v4/projects/27084538/members",
|
||||
"cluster_agents": "https://gitlab.com/api/v4/projects/27084538/cluster_agents"
|
||||
},
|
||||
"packages_enabled": true,
|
||||
"empty_repo": false,
|
||||
"archived": false,
|
||||
"visibility": "public",
|
||||
"resolve_outdated_diff_discussions": false,
|
||||
"container_expiration_policy": {
|
||||
"cadence": "1d",
|
||||
"enabled": false,
|
||||
"keep_n": 10,
|
||||
"older_than": "90d",
|
||||
"name_regex": ".*",
|
||||
"name_regex_keep": null,
|
||||
"next_run_at": "2021-06-02T17:30:44.740Z"
|
||||
},
|
||||
"issues_enabled": true,
|
||||
"merge_requests_enabled": true,
|
||||
"wiki_enabled": true,
|
||||
"jobs_enabled": true,
|
||||
"snippets_enabled": true,
|
||||
"container_registry_enabled": true,
|
||||
"service_desk_enabled": true,
|
||||
"can_create_merge_request_in": false,
|
||||
"issues_access_level": "enabled",
|
||||
"repository_access_level": "enabled",
|
||||
"merge_requests_access_level": "enabled",
|
||||
"forking_access_level": "enabled",
|
||||
"wiki_access_level": "enabled",
|
||||
"builds_access_level": "enabled",
|
||||
"snippets_access_level": "enabled",
|
||||
"pages_access_level": "enabled",
|
||||
"operations_access_level": "enabled",
|
||||
"analytics_access_level": "enabled",
|
||||
"container_registry_access_level": "enabled",
|
||||
"security_and_compliance_access_level": "private",
|
||||
"emails_disabled": null,
|
||||
"shared_runners_enabled": true,
|
||||
"lfs_enabled": true,
|
||||
"creator_id": 2378866,
|
||||
"import_status": "none",
|
||||
"open_issues_count": 0,
|
||||
"ci_default_git_depth": 50,
|
||||
"ci_forward_deployment_enabled": true,
|
||||
"ci_job_token_scope_enabled": false,
|
||||
"public_jobs": true,
|
||||
"build_timeout": 3600,
|
||||
"auto_cancel_pending_pipelines": "enabled",
|
||||
"ci_config_path": "",
|
||||
"shared_with_groups": [],
|
||||
"only_allow_merge_if_pipeline_succeeds": false,
|
||||
"allow_merge_on_skipped_pipeline": null,
|
||||
"restrict_user_defined_variables": false,
|
||||
"request_access_enabled": true,
|
||||
"only_allow_merge_if_all_discussions_are_resolved": false,
|
||||
"remove_source_branch_after_merge": true,
|
||||
"printing_merge_request_link_enabled": true,
|
||||
"merge_method": "merge",
|
||||
"squash_option": "default_off",
|
||||
"suggestion_commit_message": null,
|
||||
"merge_commit_template": null,
|
||||
"squash_commit_template": null,
|
||||
"auto_devops_enabled": false,
|
||||
"auto_devops_deploy_strategy": "continuous",
|
||||
"autoclose_referenced_issues": true,
|
||||
"keep_latest_artifact": true,
|
||||
"runner_token_expiration_interval": null,
|
||||
"approvals_before_merge": 0,
|
||||
"mirror": false,
|
||||
"external_authorization_classification_label": "",
|
||||
"marked_for_deletion_at": null,
|
||||
"marked_for_deletion_on": null,
|
||||
"requirements_enabled": true,
|
||||
"requirements_access_level": "enabled",
|
||||
"security_and_compliance_enabled": false,
|
||||
"compliance_frameworks": [],
|
||||
"issues_template": null,
|
||||
"merge_requests_template": null,
|
||||
"merge_pipelines_enabled": false,
|
||||
"merge_trains_enabled": false
|
||||
}
|
||||
]`)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
case "/api/v4/groups/test-argocd-proton/projects?include_subgroups=false&per_page=100&topic=specific-topic&with_shared=false":
|
||||
fmt.Println("here")
|
||||
_, err := io.WriteString(w, `[{
|
||||
"id": 27084533,
|
||||
"description": "",
|
||||
"name": "argocd",
|
||||
"name_with_namespace": "test argocd proton / argocd",
|
||||
"path": "argocd",
|
||||
"path_with_namespace": "test-argocd-proton/argocd",
|
||||
"created_at": "2021-06-01T17:30:44.724Z",
|
||||
"default_branch": "master",
|
||||
"tag_list": [
|
||||
"test-topic",
|
||||
"specific-topic"
|
||||
],
|
||||
"topics": [
|
||||
"test-topic",
|
||||
"specific-topic"
|
||||
],
|
||||
"ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
"http_url_to_repo": "https://gitlab.com/test-argocd-proton/argocd.git",
|
||||
"web_url": "https://gitlab.com/test-argocd-proton/argocd",
|
||||
"readme_url": null,
|
||||
"avatar_url": null,
|
||||
"forks_count": 0,
|
||||
"star_count": 0,
|
||||
"last_activity_at": "2021-06-04T08:19:51.656Z",
|
||||
"namespace": {
|
||||
"id": 12258515,
|
||||
"name": "test argocd proton",
|
||||
"path": "test-argocd-proton",
|
||||
"kind": "gro* Connection #0 to host gitlab.com left intact up ",
|
||||
"full_path ": "test - argocd - proton ",
|
||||
"parent_id ": null,
|
||||
"avatar_url ": null,
|
||||
"web_url ": "https: //gitlab.com/groups/test-argocd-proton"
|
||||
},
|
||||
"container_registry_image_prefix": "registry.gitlab.com/test-argocd-proton/argocd",
|
||||
"_links": {
|
||||
"self": "https://gitlab.com/api/v4/projects/27084533",
|
||||
"issues": "https://gitlab.com/api/v4/projects/27084533/issues",
|
||||
"merge_requests": "https://gitlab.com/api/v4/projects/27084533/merge_requests",
|
||||
"repo_branches": "https://gitlab.com/api/v4/projects/27084533/repository/branches",
|
||||
"labels": "https://gitlab.com/api/v4/projects/27084533/labels",
|
||||
"events": "https://gitlab.com/api/v4/projects/27084533/events",
|
||||
"members": "https://gitlab.com/api/v4/projects/27084533/members",
|
||||
"cluster_agents": "https://gitlab.com/api/v4/projects/27084533/cluster_agents"
|
||||
},
|
||||
"packages_enabled": true,
|
||||
"empty_repo": false,
|
||||
"archived": false,
|
||||
"visibility": "public",
|
||||
"resolve_outdated_diff_discussions": false,
|
||||
"container_expiration_policy": {
|
||||
"cadence": "1d",
|
||||
"enabled": false,
|
||||
"keep_n": 10,
|
||||
"older_than": "90d",
|
||||
"name_regex": ".*",
|
||||
"name_regex_keep": null,
|
||||
"next_run_at": "2021-06-02T17:30:44.740Z"
|
||||
},
|
||||
"issues_enabled": true,
|
||||
"merge_requests_enabled": true,
|
||||
"wiki_enabled": true,
|
||||
"jobs_enabled": true,
|
||||
"snippets_enabled": true,
|
||||
"container_registry_enabled": true,
|
||||
"service_desk_enabled": true,
|
||||
"can_create_merge_request_in": false,
|
||||
"issues_access_level": "enabled",
|
||||
"repository_access_level": "enabled",
|
||||
"merge_requests_access_level": "enabled",
|
||||
"forking_access_level": "enabled",
|
||||
"wiki_access_level": "enabled",
|
||||
"builds_access_level": "enabled",
|
||||
"snippets_access_level": "enabled",
|
||||
"pages_access_level": "enabled",
|
||||
"operations_access_level": "enabled",
|
||||
"analytics_access_level": "enabled",
|
||||
"container_registry_access_level": "enabled",
|
||||
"security_and_compliance_access_level": "private",
|
||||
"emails_disabled": null,
|
||||
"shared_runners_enabled": true,
|
||||
"lfs_enabled": true,
|
||||
"creator_id": 2378866,
|
||||
"import_status": "none",
|
||||
"open_issues_count": 0,
|
||||
"ci_default_git_depth": 50,
|
||||
"ci_forward_deployment_enabled": true,
|
||||
"ci_job_token_scope_enabled": false,
|
||||
"public_jobs": true,
|
||||
"build_timeout": 3600,
|
||||
"auto_cancel_pending_pipelines": "enabled",
|
||||
"ci_config_path": "",
|
||||
"shared_with_groups": [],
|
||||
"only_allow_merge_if_pipeline_succeeds": false,
|
||||
"allow_merge_on_skipped_pipeline": null,
|
||||
"restrict_user_defined_variables": false,
|
||||
"request_access_enabled": true,
|
||||
"only_allow_merge_if_all_discussions_are_resolved": false,
|
||||
"remove_source_branch_after_merge": true,
|
||||
"printing_merge_request_link_enabled": true,
|
||||
"merge_method": "merge",
|
||||
"squash_option": "default_off",
|
||||
"suggestion_commit_message": null,
|
||||
"merge_commit_template": null,
|
||||
"squash_commit_template": null,
|
||||
"auto_devops_enabled": false,
|
||||
"auto_devops_deploy_strategy": "continuous",
|
||||
"autoclose_referenced_issues": true,
|
||||
"keep_latest_artifact": true,
|
||||
"runner_token_expiration_interval": null,
|
||||
"approvals_before_merge": 0,
|
||||
"mirror": false,
|
||||
"external_authorization_classification_label": "",
|
||||
"marked_for_deletion_at": null,
|
||||
"marked_for_deletion_on": null,
|
||||
"requirements_enabled": true,
|
||||
"requirements_access_level": "enabled",
|
||||
"security_and_compliance_enabled": false,
|
||||
"compliance_frameworks": [],
|
||||
"issues_template": null,
|
||||
"merge_requests_template": null,
|
||||
"merge_pipelines_enabled": false,
|
||||
"merge_trains_enabled": false
|
||||
}
|
||||
]`)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
case "/api/v4/groups/test-argocd-proton/projects?include_subgroups=true&per_page=100&topic=&with_shared=true":
|
||||
fmt.Println("here")
|
||||
_, err := io.WriteString(w, `[{
|
||||
"id": 27084533,
|
||||
"description": "",
|
||||
"name": "argocd",
|
||||
"name_with_namespace": "test argocd proton / argocd",
|
||||
"path": "argocd",
|
||||
"path_with_namespace": "test-argocd-proton/argocd",
|
||||
"created_at": "2021-06-01T17:30:44.724Z",
|
||||
"default_branch": "master",
|
||||
"tag_list": [
|
||||
"test-topic"
|
||||
],
|
||||
"topics": [
|
||||
"test-topic"
|
||||
],
|
||||
"ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
"http_url_to_repo": "https://gitlab.com/test-argocd-proton/argocd.git",
|
||||
"web_url": "https://gitlab.com/test-argocd-proton/argocd",
|
||||
"readme_url": null,
|
||||
"avatar_url": null,
|
||||
"forks_count": 0,
|
||||
"star_count": 0,
|
||||
"last_activity_at": "2021-06-04T08:19:51.656Z",
|
||||
"namespace": {
|
||||
"id": 12258515,
|
||||
"name": "test argocd proton",
|
||||
"path": "test-argocd-proton",
|
||||
"kind": "gro* Connection #0 to host gitlab.com left intact up ",
|
||||
"full_path ": "test - argocd - proton ",
|
||||
"parent_id ": null,
|
||||
"avatar_url ": null,
|
||||
"web_url ": "https: //gitlab.com/groups/test-argocd-proton"
|
||||
},
|
||||
"container_registry_image_prefix": "registry.gitlab.com/test-argocd-proton/argocd",
|
||||
"_links": {
|
||||
"self": "https://gitlab.com/api/v4/projects/27084533",
|
||||
"issues": "https://gitlab.com/api/v4/projects/27084533/issues",
|
||||
"merge_requests": "https://gitlab.com/api/v4/projects/27084533/merge_requests",
|
||||
"repo_branches": "https://gitlab.com/api/v4/projects/27084533/repository/branches",
|
||||
"labels": "https://gitlab.com/api/v4/projects/27084533/labels",
|
||||
"events": "https://gitlab.com/api/v4/projects/27084533/events",
|
||||
"members": "https://gitlab.com/api/v4/projects/27084533/members",
|
||||
"cluster_agents": "https://gitlab.com/api/v4/projects/27084533/cluster_agents"
|
||||
},
|
||||
"packages_enabled": true,
|
||||
"empty_repo": false,
|
||||
"archived": false,
|
||||
"visibility": "public",
|
||||
"resolve_outdated_diff_discussions": false,
|
||||
"container_expiration_policy": {
|
||||
"cadence": "1d",
|
||||
"enabled": false,
|
||||
"keep_n": 10,
|
||||
"older_than": "90d",
|
||||
"name_regex": ".*",
|
||||
"name_regex_keep": null,
|
||||
"next_run_at": "2021-06-02T17:30:44.740Z"
|
||||
},
|
||||
"issues_enabled": true,
|
||||
"merge_requests_enabled": true,
|
||||
"wiki_enabled": true,
|
||||
"jobs_enabled": true,
|
||||
"snippets_enabled": true,
|
||||
"container_registry_enabled": true,
|
||||
"service_desk_enabled": true,
|
||||
"can_create_merge_request_in": false,
|
||||
"issues_access_level": "enabled",
|
||||
"repository_access_level": "enabled",
|
||||
"merge_requests_access_level": "enabled",
|
||||
"forking_access_level": "enabled",
|
||||
"wiki_access_level": "enabled",
|
||||
"builds_access_level": "enabled",
|
||||
"snippets_access_level": "enabled",
|
||||
"pages_access_level": "enabled",
|
||||
"operations_access_level": "enabled",
|
||||
"analytics_access_level": "enabled",
|
||||
"container_registry_access_level": "enabled",
|
||||
"security_and_compliance_access_level": "private",
|
||||
"emails_disabled": null,
|
||||
"shared_runners_enabled": true,
|
||||
"lfs_enabled": true,
|
||||
"creator_id": 2378866,
|
||||
"import_status": "none",
|
||||
"open_issues_count": 0,
|
||||
"ci_default_git_depth": 50,
|
||||
"ci_forward_deployment_enabled": true,
|
||||
"ci_job_token_scope_enabled": false,
|
||||
"public_jobs": true,
|
||||
"build_timeout": 3600,
|
||||
"auto_cancel_pending_pipelines": "enabled",
|
||||
"ci_config_path": "",
|
||||
"shared_with_groups": [],
|
||||
"only_allow_merge_if_pipeline_succeeds": false,
|
||||
"allow_merge_on_skipped_pipeline": null,
|
||||
"restrict_user_defined_variables": false,
|
||||
"request_access_enabled": true,
|
||||
"only_allow_merge_if_all_discussions_are_resolved": false,
|
||||
"remove_source_branch_after_merge": true,
|
||||
"printing_merge_request_link_enabled": true,
|
||||
"merge_method": "merge",
|
||||
"squash_option": "default_off",
|
||||
"suggestion_commit_message": null,
|
||||
"merge_commit_template": null,
|
||||
"squash_commit_template": null,
|
||||
"auto_devops_enabled": false,
|
||||
"auto_devops_deploy_strategy": "continuous",
|
||||
"autoclose_referenced_issues": true,
|
||||
"keep_latest_artifact": true,
|
||||
"runner_token_expiration_interval": null,
|
||||
"approvals_before_merge": 0,
|
||||
"mirror": false,
|
||||
"external_authorization_classification_label": "",
|
||||
"marked_for_deletion_at": null,
|
||||
"marked_for_deletion_on": null,
|
||||
"requirements_enabled": true,
|
||||
"requirements_access_level": "enabled",
|
||||
"security_and_compliance_enabled": false,
|
||||
"compliance_frameworks": [],
|
||||
"issues_template": null,
|
||||
"merge_requests_template": null,
|
||||
"merge_pipelines_enabled": false,
|
||||
"merge_trains_enabled": false
|
||||
},
|
||||
{
|
||||
"id": 27084534,
|
||||
"description": "This is a Shared Project",
|
||||
"name": "shared-argocd",
|
||||
"name_with_namespace": "shared project to test argocd proton / argocd",
|
||||
"path": "shared-argocd",
|
||||
"path_with_namespace": "test-shared-argocd-proton/shared-argocd",
|
||||
"created_at": "2021-06-11T17:30:44.724Z",
|
||||
"default_branch": "master",
|
||||
"tag_list": [
|
||||
"test-topic"
|
||||
],
|
||||
"topics": [
|
||||
"test-topic"
|
||||
],
|
||||
"ssh_url_to_repo": "git@gitlab.com:test-shared-argocd-proton/shared-argocd.git",
|
||||
"http_url_to_repo": "https://gitlab.com/test-shared-argocd-proton/shared-argocd.git",
|
||||
"web_url": "https://gitlab.com/test-shared-argocd-proton/shared-argocd",
|
||||
"readme_url": null,
|
||||
"avatar_url": null,
|
||||
"forks_count": 0,
|
||||
"star_count": 0,
|
||||
"last_activity_at": "2021-06-04T08:19:51.656Z",
|
||||
"namespace": {
|
||||
"id": 12258518,
|
||||
"name": "test shared argocd proton",
|
||||
"path": "test-shared-argocd-proton",
|
||||
"kind": "group",
|
||||
"full_path ": "test-shared-argocd-proton",
|
||||
"parent_id ": null,
|
||||
"avatar_url ": null,
|
||||
"web_url ": "https: //gitlab.com/groups/test-shared-argocd-proton"
|
||||
},
|
||||
"container_registry_image_prefix": "registry.gitlab.com/test-shared-argocd-proton/shared-argocd",
|
||||
"_links": {
|
||||
"self": "https://gitlab.com/api/v4/projects/27084534",
|
||||
"issues": "https://gitlab.com/api/v4/projects/27084534/issues",
|
||||
"merge_requests": "https://gitlab.com/api/v4/projects/27084534/merge_requests",
|
||||
"repo_branches": "https://gitlab.com/api/v4/projects/27084534/repository/branches",
|
||||
"labels": "https://gitlab.com/api/v4/projects/27084534/labels",
|
||||
"events": "https://gitlab.com/api/v4/projects/27084534/events",
|
||||
"members": "https://gitlab.com/api/v4/projects/27084534/members",
|
||||
"cluster_agents": "https://gitlab.com/api/v4/projects/27084534/cluster_agents"
|
||||
},
|
||||
"packages_enabled": true,
|
||||
"empty_repo": false,
|
||||
"archived": false,
|
||||
"visibility": "public",
|
||||
"resolve_outdated_diff_discussions": false,
|
||||
"container_expiration_policy": {
|
||||
"cadence": "1d",
|
||||
"enabled": false,
|
||||
"keep_n": 10,
|
||||
"older_than": "90d",
|
||||
"name_regex": ".*",
|
||||
"name_regex_keep": null,
|
||||
"next_run_at": "2021-06-12T17:30:44.740Z"
|
||||
},
|
||||
"issues_enabled": true,
|
||||
"merge_requests_enabled": true,
|
||||
"wiki_enabled": true,
|
||||
"jobs_enabled": true,
|
||||
"snippets_enabled": true,
|
||||
"container_registry_enabled": true,
|
||||
"service_desk_enabled": true,
|
||||
"can_create_merge_request_in": false,
|
||||
"issues_access_level": "enabled",
|
||||
"repository_access_level": "enabled",
|
||||
"merge_requests_access_level": "enabled",
|
||||
"forking_access_level": "enabled",
|
||||
"wiki_access_level": "enabled",
|
||||
"builds_access_level": "enabled",
|
||||
"snippets_access_level": "enabled",
|
||||
"pages_access_level": "enabled",
|
||||
"operations_access_level": "enabled",
|
||||
"analytics_access_level": "enabled",
|
||||
"container_registry_access_level": "enabled",
|
||||
"security_and_compliance_access_level": "private",
|
||||
"emails_disabled": null,
|
||||
"shared_runners_enabled": true,
|
||||
"lfs_enabled": true,
|
||||
"creator_id": 2378866,
|
||||
"import_status": "none",
|
||||
"open_issues_count": 0,
|
||||
"ci_default_git_depth": 50,
|
||||
"ci_forward_deployment_enabled": true,
|
||||
"ci_job_token_scope_enabled": false,
|
||||
"public_jobs": true,
|
||||
"build_timeout": 3600,
|
||||
"auto_cancel_pending_pipelines": "enabled",
|
||||
"ci_config_path": "",
|
||||
"shared_with_groups": [
|
||||
{
|
||||
"group_id": 12258515,
|
||||
"group_name": "test-argocd-proton",
|
||||
"group_full_path": "test-shared-argocd-proton",
|
||||
"group_access_level": 30,
|
||||
"expires_at": null
|
||||
}
|
||||
],
|
||||
"only_allow_merge_if_pipeline_succeeds": false,
|
||||
"allow_merge_on_skipped_pipeline": null,
|
||||
"restrict_user_defined_variables": false,
|
||||
"request_access_enabled": true,
|
||||
"only_allow_merge_if_all_discussions_are_resolved": false,
|
||||
"remove_source_branch_after_merge": true,
|
||||
"printing_merge_request_link_enabled": true,
|
||||
"merge_method": "merge",
|
||||
"squash_option": "default_off",
|
||||
"suggestion_commit_message": null,
|
||||
"merge_commit_template": null,
|
||||
"squash_commit_template": null,
|
||||
"auto_devops_enabled": false,
|
||||
"auto_devops_deploy_strategy": "continuous",
|
||||
"autoclose_referenced_issues": true,
|
||||
"keep_latest_artifact": true,
|
||||
"runner_token_expiration_interval": null,
|
||||
"approvals_before_merge": 0,
|
||||
"mirror": false,
|
||||
"external_authorization_classification_label": "",
|
||||
"marked_for_deletion_at": null,
|
||||
"marked_for_deletion_on": null,
|
||||
"requirements_enabled": true,
|
||||
"requirements_access_level": "enabled",
|
||||
"security_and_compliance_enabled": false,
|
||||
"compliance_frameworks": [],
|
||||
"issues_template": null,
|
||||
"merge_requests_template": null,
|
||||
"merge_pipelines_enabled": false,
|
||||
"merge_trains_enabled": false
|
||||
}]`)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
case "/api/v4/projects/27084533/repository/branches/master":
|
||||
fmt.Println("returning")
|
||||
_, err := io.WriteString(w, `{
|
||||
@@ -877,116 +229,6 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
case "/api/v4/projects/27084534/repository/branches?per_page=100":
|
||||
_, err := io.WriteString(w, `[{
|
||||
"name": "master",
|
||||
"commit": {
|
||||
"id": "8898d7999fc99dd0fd578650b58b244fc63f6b53",
|
||||
"short_id": "8898d799",
|
||||
"created_at": "2021-06-04T08:24:44.000+00:00",
|
||||
"parent_ids": null,
|
||||
"title": "Merge branch 'pipeline-1317911429' into 'master'",
|
||||
"message": "Merge branch 'pipeline-1317911429' into 'master'",
|
||||
"author_name": "Martin Vozník",
|
||||
"author_email": "martin@voznik.cz",
|
||||
"authored_date": "2021-06-04T08:24:44.000+00:00",
|
||||
"committer_name": "Martin Vozník",
|
||||
"committer_email": "martin@voznik.cz",
|
||||
"committed_date": "2021-06-04T08:24:44.000+00:00",
|
||||
"trailers": null,
|
||||
"web_url": "https://gitlab.com/test-shared-argocd-proton/shared-argocd/-/commit/8898d7999fc99dd0fd578650b58b244fc63f6b53"
|
||||
},
|
||||
"merged": false,
|
||||
"protected": true,
|
||||
"developers_can_push": false,
|
||||
"developers_can_merge": false,
|
||||
"can_push": false,
|
||||
"default": true,
|
||||
"web_url": "https://gitlab.com/test-shared-argocd-proton/shared-argocd/-/tree/master"
|
||||
}, {
|
||||
"name": "pipeline-2310077506",
|
||||
"commit": {
|
||||
"id": "0f92540e5f396ba960adea4ed0aa905baf3f73d1",
|
||||
"short_id": "0f92540e",
|
||||
"created_at": "2021-06-01T18:39:59.000+00:00",
|
||||
"parent_ids": null,
|
||||
"title": "[testapp-ci] manifests/demo/test-app.yaml: release v1.0.1",
|
||||
"message": "[testapp-ci] manifests/demo/test-app.yaml: release v1.0.1",
|
||||
"author_name": "ci-test-app",
|
||||
"author_email": "mvoznik+cicd@protonmail.com",
|
||||
"authored_date": "2021-06-01T18:39:59.000+00:00",
|
||||
"committer_name": "ci-test-app",
|
||||
"committer_email": "mvoznik+cicd@protonmail.com",
|
||||
"committed_date": "2021-06-01T18:39:59.000+00:00",
|
||||
"trailers": null,
|
||||
"web_url": "https://gitlab.com/test-shared-argocd-proton/shared-argocd/-/commit/0f92540e5f396ba960adea4ed0aa905baf3f73d1"
|
||||
},
|
||||
"merged": false,
|
||||
"protected": false,
|
||||
"developers_can_push": false,
|
||||
"developers_can_merge": false,
|
||||
"can_push": false,
|
||||
"default": false,
|
||||
"web_url": "https://gitlab.com/test-shared-argocd-proton/shared-argocd/-/tree/pipeline-1310077506"
|
||||
}]`)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
case "/api/v4/projects/27084538/repository/branches?per_page=100":
|
||||
_, err := io.WriteString(w, `[{
|
||||
"name": "master",
|
||||
"commit": {
|
||||
"id": "8898d7999fc99dd0fd578650b58b244fc63f6b58",
|
||||
"short_id": "8898d801",
|
||||
"created_at": "2021-06-04T08:24:44.000+00:00",
|
||||
"parent_ids": null,
|
||||
"title": "Merge branch 'pipeline-1317911429' into 'master'",
|
||||
"message": "Merge branch 'pipeline-1317911429' into 'master'",
|
||||
"author_name": "Martin Vozník",
|
||||
"author_email": "martin@voznik.cz",
|
||||
"authored_date": "2021-06-04T08:24:44.000+00:00",
|
||||
"committer_name": "Martin Vozník",
|
||||
"committer_email": "martin@voznik.cz",
|
||||
"committed_date": "2021-06-04T08:24:44.000+00:00",
|
||||
"trailers": null,
|
||||
"web_url": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup/-/commit/8898d7999fc99dd0fd578650b58b244fc63f6b53"
|
||||
},
|
||||
"merged": false,
|
||||
"protected": true,
|
||||
"developers_can_push": false,
|
||||
"developers_can_merge": false,
|
||||
"can_push": false,
|
||||
"default": true,
|
||||
"web_url": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup/-/tree/master"
|
||||
}, {
|
||||
"name": "pipeline-2310077506",
|
||||
"commit": {
|
||||
"id": "0f92540e5f396ba960adea4ed0aa905baf3f73d1",
|
||||
"short_id": "0f92540e",
|
||||
"created_at": "2021-06-01T18:39:59.000+00:00",
|
||||
"parent_ids": null,
|
||||
"title": "[testapp-ci] manifests/demo/test-app.yaml: release v1.0.1",
|
||||
"message": "[testapp-ci] manifests/demo/test-app.yaml: release v1.0.1",
|
||||
"author_name": "ci-test-app",
|
||||
"author_email": "mvoznik+cicd@protonmail.com",
|
||||
"authored_date": "2021-06-01T18:39:59.000+00:00",
|
||||
"committer_name": "ci-test-app",
|
||||
"committer_email": "mvoznik+cicd@protonmail.com",
|
||||
"committed_date": "2021-06-01T18:39:59.000+00:00",
|
||||
"trailers": null,
|
||||
"web_url": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup/-/commit/0f92540e5f396ba960adea4ed0aa905baf3f73d1"
|
||||
},
|
||||
"merged": false,
|
||||
"protected": false,
|
||||
"developers_can_push": false,
|
||||
"developers_can_merge": false,
|
||||
"can_push": false,
|
||||
"default": false,
|
||||
"web_url": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup/-/tree/pipeline-1310077506"
|
||||
}]`)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
case "/api/v4/projects/test-argocd-proton%2Fargocd":
|
||||
fmt.Println("auct")
|
||||
_, err := io.WriteString(w, `{
|
||||
@@ -998,12 +240,8 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
"path_with_namespace": "test-argocd-proton/argocd",
|
||||
"created_at": "2021-06-01T17:30:44.724Z",
|
||||
"default_branch": "master",
|
||||
"tag_list": [
|
||||
"test-topic"
|
||||
],
|
||||
"topics": [
|
||||
"test-topic"
|
||||
],
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
"http_url_to_repo": "https://gitlab.com/test-argocd-proton/argocd.git",
|
||||
"web_url": "https://gitlab.com/test-argocd-proton/argocd",
|
||||
@@ -1048,10 +286,10 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
}
|
||||
func TestGitlabListRepos(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, proto, url, topic string
|
||||
hasError, allBranches, includeSubgroups, includeSharedProjects, insecure bool
|
||||
branches []string
|
||||
filters []v1alpha1.SCMProviderGeneratorFilter
|
||||
name, proto, url string
|
||||
hasError, allBranches, includeSubgroups, insecure bool
|
||||
branches []string
|
||||
filters []v1alpha1.SCMProviderGeneratorFilter
|
||||
}{
|
||||
{
|
||||
name: "blank protocol",
|
||||
@@ -1079,66 +317,32 @@ func TestGitlabListRepos(t *testing.T) {
|
||||
url: "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
branches: []string{"master"},
|
||||
},
|
||||
{
|
||||
name: "all subgroups",
|
||||
allBranches: true,
|
||||
url: "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
branches: []string{"master"},
|
||||
includeSharedProjects: false,
|
||||
includeSubgroups: true,
|
||||
},
|
||||
{
|
||||
name: "all subgroups and shared projects",
|
||||
allBranches: true,
|
||||
url: "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
branches: []string{"master"},
|
||||
includeSharedProjects: true,
|
||||
includeSubgroups: true,
|
||||
},
|
||||
{
|
||||
name: "specific topic",
|
||||
allBranches: true,
|
||||
url: "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
branches: []string{"master"},
|
||||
includeSubgroups: false,
|
||||
topic: "specific-topic",
|
||||
},
|
||||
}
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gitlabMockHandler(t)(w, r)
|
||||
}))
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
provider, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, c.allBranches, c.includeSubgroups, c.includeSharedProjects, c.insecure, "", c.topic)
|
||||
provider, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, c.allBranches, c.includeSubgroups, c.insecure, "")
|
||||
rawRepos, err := ListRepos(context.Background(), provider, c.filters, c.proto)
|
||||
if c.hasError {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
// Just check that this one project shows up. Not a great test but better than nothing?
|
||||
// Just check that this one project shows up. Not a great test but better thing nothing?
|
||||
repos := []*Repository{}
|
||||
uniqueRepos := map[string]int{}
|
||||
branches := []string{}
|
||||
for _, r := range rawRepos {
|
||||
if r.Repository == "argocd" {
|
||||
repos = append(repos, r)
|
||||
branches = append(branches, r.Branch)
|
||||
}
|
||||
uniqueRepos[r.Repository]++
|
||||
}
|
||||
assert.NotEmpty(t, repos)
|
||||
assert.Equal(t, c.url, repos[0].URL)
|
||||
for _, b := range c.branches {
|
||||
assert.Contains(t, branches, b)
|
||||
}
|
||||
// In case of listing subgroups, validate the number of returned projects
|
||||
if c.includeSubgroups || c.includeSharedProjects {
|
||||
assert.Equal(t, 2, len(uniqueRepos))
|
||||
}
|
||||
// In case we filter on the topic, ensure we got only one repo returned
|
||||
if c.topic != "" {
|
||||
assert.Equal(t, 1, len(uniqueRepos))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1148,7 +352,7 @@ func TestGitlabHasPath(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gitlabMockHandler(t)(w, r)
|
||||
}))
|
||||
host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, true, false, "", "")
|
||||
host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, false, "")
|
||||
repo := &Repository{
|
||||
Organization: "test-argocd-proton",
|
||||
Repository: "argocd",
|
||||
@@ -1194,7 +398,7 @@ func TestGitlabGetBranches(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gitlabMockHandler(t)(w, r)
|
||||
}))
|
||||
host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, true, false, "", "")
|
||||
host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, false, "")
|
||||
|
||||
repo := &Repository{
|
||||
RepositoryId: 27084533,
|
||||
|
||||
@@ -180,7 +180,7 @@ func secretToCluster(s *corev1.Secret) (*appv1.Cluster, error) {
|
||||
if val, err := strconv.Atoi(string(shardStr)); err != nil {
|
||||
log.Warnf("Error while parsing shard in cluster secret '%s': %v", s.Name, err)
|
||||
} else {
|
||||
shard = pointer.Int64(int64(val))
|
||||
shard = pointer.Int64Ptr(int64(val))
|
||||
}
|
||||
}
|
||||
cluster := appv1.Cluster{
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
kubetesting "k8s.io/client-go/testing"
|
||||
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -68,7 +69,7 @@ func createClusterSecret(secretName string, clusterName string, clusterServer st
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: secretName,
|
||||
Namespace: fakeNamespace,
|
||||
Namespace: utils.ArgoCDNamespace,
|
||||
Labels: map[string]string{
|
||||
ArgoCDSecretTypeLabel: ArgoCDSecretTypeCluster,
|
||||
},
|
||||
@@ -110,7 +111,7 @@ func TestValidateDestination(t *testing.T) {
|
||||
objects = append(objects, secret)
|
||||
kubeclientset := fake.NewSimpleClientset(objects...)
|
||||
|
||||
appCond := ValidateDestination(context.Background(), &dest, kubeclientset, fakeNamespace)
|
||||
appCond := ValidateDestination(context.Background(), &dest, kubeclientset, utils.ArgoCDNamespace)
|
||||
assert.Nil(t, appCond)
|
||||
assert.Equal(t, "https://127.0.0.1:6443", dest.Server)
|
||||
assert.True(t, dest.IsServerInferred())
|
||||
@@ -123,7 +124,7 @@ func TestValidateDestination(t *testing.T) {
|
||||
Namespace: "default",
|
||||
}
|
||||
|
||||
err := ValidateDestination(context.Background(), &dest, nil, fakeNamespace)
|
||||
err := ValidateDestination(context.Background(), &dest, nil, utils.ArgoCDNamespace)
|
||||
assert.Equal(t, "application destination can't have both name and server defined: minikube https://127.0.0.1:6443", err.Error())
|
||||
assert.False(t, dest.IsServerInferred())
|
||||
})
|
||||
@@ -138,7 +139,7 @@ func TestValidateDestination(t *testing.T) {
|
||||
return true, nil, fmt.Errorf("an error occurred")
|
||||
})
|
||||
|
||||
err := ValidateDestination(context.Background(), &dest, kubeclientset, fakeNamespace)
|
||||
err := ValidateDestination(context.Background(), &dest, kubeclientset, utils.ArgoCDNamespace)
|
||||
assert.Equal(t, "unable to find destination server: an error occurred", err.Error())
|
||||
assert.False(t, dest.IsServerInferred())
|
||||
})
|
||||
@@ -153,7 +154,7 @@ func TestValidateDestination(t *testing.T) {
|
||||
objects = append(objects, secret)
|
||||
kubeclientset := fake.NewSimpleClientset(objects...)
|
||||
|
||||
err := ValidateDestination(context.Background(), &dest, kubeclientset, fakeNamespace)
|
||||
err := ValidateDestination(context.Background(), &dest, kubeclientset, utils.ArgoCDNamespace)
|
||||
assert.Equal(t, "unable to find destination server: there are no clusters with this name: minikube", err.Error())
|
||||
assert.False(t, dest.IsServerInferred())
|
||||
})
|
||||
@@ -170,7 +171,7 @@ func TestValidateDestination(t *testing.T) {
|
||||
objects = append(objects, secret, secret2)
|
||||
kubeclientset := fake.NewSimpleClientset(objects...)
|
||||
|
||||
err := ValidateDestination(context.Background(), &dest, kubeclientset, fakeNamespace)
|
||||
err := ValidateDestination(context.Background(), &dest, kubeclientset, utils.ArgoCDNamespace)
|
||||
assert.Equal(t, "unable to find destination server: there are 2 clusters with the same name: [https://127.0.0.1:2443 https://127.0.0.1:8443]", err.Error())
|
||||
assert.False(t, dest.IsServerInferred())
|
||||
})
|
||||
|
||||
@@ -2,24 +2,18 @@ package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
)
|
||||
|
||||
// CreateOrUpdate overrides "sigs.k8s.io/controller-runtime" function
|
||||
@@ -35,7 +29,7 @@ import (
|
||||
// The MutateFn is called regardless of creating or updating an object.
|
||||
//
|
||||
// It returns the executed operation and an error.
|
||||
func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ignoreAppDifferences argov1alpha1.ApplicationSetIgnoreDifferences, obj *argov1alpha1.Application, f controllerutil.MutateFn) (controllerutil.OperationResult, error) {
|
||||
func CreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f controllerutil.MutateFn) (controllerutil.OperationResult, error) {
|
||||
|
||||
key := client.ObjectKeyFromObject(obj)
|
||||
if err := c.Get(ctx, key, obj); err != nil {
|
||||
@@ -51,24 +45,11 @@ func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ign
|
||||
return controllerutil.OperationResultCreated, nil
|
||||
}
|
||||
|
||||
normalizedLive := obj.DeepCopy()
|
||||
|
||||
// Mutate the live object to match the desired state.
|
||||
existing := obj.DeepCopyObject()
|
||||
if err := mutate(f, key, obj); err != nil {
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
|
||||
// Apply ignoreApplicationDifferences rules to remove ignored fields from both the live and the desired state. This
|
||||
// prevents those differences from appearing in the diff and therefore in the patch.
|
||||
err := applyIgnoreDifferences(ignoreAppDifferences, normalizedLive, obj)
|
||||
if err != nil {
|
||||
return controllerutil.OperationResultNone, fmt.Errorf("failed to apply ignore differences: %w", err)
|
||||
}
|
||||
|
||||
// Normalize to avoid diffing on unimportant differences.
|
||||
normalizedLive.Spec = *argo.NormalizeApplicationSpec(&normalizedLive.Spec)
|
||||
obj.Spec = *argo.NormalizeApplicationSpec(&obj.Spec)
|
||||
|
||||
equality := conversion.EqualitiesOrDie(
|
||||
func(a, b resource.Quantity) bool {
|
||||
// Ignore formatting, only care that numeric value stayed the same.
|
||||
@@ -94,34 +75,16 @@ func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ign
|
||||
},
|
||||
)
|
||||
|
||||
if equality.DeepEqual(normalizedLive, obj) {
|
||||
if equality.DeepEqual(existing, obj) {
|
||||
return controllerutil.OperationResultNone, nil
|
||||
}
|
||||
|
||||
patch := client.MergeFrom(normalizedLive)
|
||||
if log.IsLevelEnabled(log.DebugLevel) {
|
||||
LogPatch(logCtx, patch, obj)
|
||||
}
|
||||
if err := c.Patch(ctx, obj, patch); err != nil {
|
||||
if err := c.Update(ctx, obj); err != nil {
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
return controllerutil.OperationResultUpdated, nil
|
||||
}
|
||||
|
||||
func LogPatch(logCtx *log.Entry, patch client.Patch, obj *argov1alpha1.Application) {
|
||||
patchBytes, err := patch.Data(obj)
|
||||
if err != nil {
|
||||
logCtx.Errorf("failed to generate patch: %v", err)
|
||||
}
|
||||
// Get the patch as a plain object so it is easier to work with in json logs.
|
||||
var patchObj map[string]interface{}
|
||||
err = json.Unmarshal(patchBytes, &patchObj)
|
||||
if err != nil {
|
||||
logCtx.Errorf("failed to unmarshal patch: %v", err)
|
||||
}
|
||||
logCtx.WithField("patch", patchObj).Debug("patching application")
|
||||
}
|
||||
|
||||
// mutate wraps a MutateFn and applies validation to its result
|
||||
func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object) error {
|
||||
if err := f(); err != nil {
|
||||
@@ -132,71 +95,3 @@ func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyIgnoreDifferences applies the ignore differences rules to the found application. It modifies the applications in place.
|
||||
func applyIgnoreDifferences(applicationSetIgnoreDifferences argov1alpha1.ApplicationSetIgnoreDifferences, found *argov1alpha1.Application, generatedApp *argov1alpha1.Application) error {
|
||||
if len(applicationSetIgnoreDifferences) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
generatedAppCopy := generatedApp.DeepCopy()
|
||||
diffConfig, err := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(applicationSetIgnoreDifferences.ToApplicationIgnoreDifferences(), nil, false).
|
||||
WithNoCache().
|
||||
Build()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build diff config: %w", err)
|
||||
}
|
||||
unstructuredFound, err := appToUnstructured(found)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert found application to unstructured: %w", err)
|
||||
}
|
||||
unstructuredGenerated, err := appToUnstructured(generatedApp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert found application to unstructured: %w", err)
|
||||
}
|
||||
result, err := argodiff.Normalize([]*unstructured.Unstructured{unstructuredFound}, []*unstructured.Unstructured{unstructuredGenerated}, diffConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to normalize application spec: %w", err)
|
||||
}
|
||||
if len(result.Lives) != 1 {
|
||||
return fmt.Errorf("expected 1 normalized application, got %d", len(result.Lives))
|
||||
}
|
||||
foundJsonNormalized, err := json.Marshal(result.Lives[0].Object)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal normalized app to json: %w", err)
|
||||
}
|
||||
foundNormalized := &argov1alpha1.Application{}
|
||||
err = json.Unmarshal(foundJsonNormalized, &foundNormalized)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal normalized app to json: %w", err)
|
||||
}
|
||||
if len(result.Targets) != 1 {
|
||||
return fmt.Errorf("expected 1 normalized application, got %d", len(result.Targets))
|
||||
}
|
||||
foundNormalized.DeepCopyInto(found)
|
||||
generatedJsonNormalized, err := json.Marshal(result.Targets[0].Object)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal normalized app to json: %w", err)
|
||||
}
|
||||
generatedAppNormalized := &argov1alpha1.Application{}
|
||||
err = json.Unmarshal(generatedJsonNormalized, &generatedAppNormalized)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal normalized app json to structured app: %w", err)
|
||||
}
|
||||
generatedAppNormalized.DeepCopyInto(generatedApp)
|
||||
// Prohibit jq queries from mutating silly things.
|
||||
generatedApp.TypeMeta = generatedAppCopy.TypeMeta
|
||||
generatedApp.Name = generatedAppCopy.Name
|
||||
generatedApp.Namespace = generatedAppCopy.Namespace
|
||||
generatedApp.Operation = generatedAppCopy.Operation
|
||||
return nil
|
||||
}
|
||||
|
||||
func appToUnstructured(app client.Object) (*unstructured.Unstructured, error) {
|
||||
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(app)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert app object to unstructured: %w", err)
|
||||
}
|
||||
return &unstructured.Unstructured{Object: u}, nil
|
||||
}
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func Test_applyIgnoreDifferences(t *testing.T) {
|
||||
appMeta := metav1.TypeMeta{
|
||||
APIVersion: v1alpha1.ApplicationSchemaGroupVersionKind.GroupVersion().String(),
|
||||
Kind: v1alpha1.ApplicationSchemaGroupVersionKind.Kind,
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
ignoreDifferences v1alpha1.ApplicationSetIgnoreDifferences
|
||||
foundApp string
|
||||
generatedApp string
|
||||
expectedApp string
|
||||
}{
|
||||
{
|
||||
name: "empty ignoreDifferences",
|
||||
foundApp: `
|
||||
spec: {}`,
|
||||
generatedApp: `
|
||||
spec: {}`,
|
||||
expectedApp: `
|
||||
spec: {}`,
|
||||
},
|
||||
{
|
||||
// For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1191138278
|
||||
name: "ignore target revision with jq",
|
||||
ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{
|
||||
{JQPathExpressions: []string{".spec.source.targetRevision"}},
|
||||
},
|
||||
foundApp: `
|
||||
spec:
|
||||
source:
|
||||
targetRevision: foo`,
|
||||
generatedApp: `
|
||||
spec:
|
||||
source:
|
||||
targetRevision: bar`,
|
||||
expectedApp: `
|
||||
spec:
|
||||
source:
|
||||
targetRevision: foo`,
|
||||
},
|
||||
{
|
||||
// For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1103593714
|
||||
name: "ignore helm parameter with jq",
|
||||
ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{
|
||||
{JQPathExpressions: []string{`.spec.source.helm.parameters | select(.name == "image.tag")`}},
|
||||
},
|
||||
foundApp: `
|
||||
spec:
|
||||
source:
|
||||
helm:
|
||||
parameters:
|
||||
- name: image.tag
|
||||
value: test
|
||||
- name: another
|
||||
value: value`,
|
||||
generatedApp: `
|
||||
spec:
|
||||
source:
|
||||
helm:
|
||||
parameters:
|
||||
- name: image.tag
|
||||
value: v1.0.0
|
||||
- name: another
|
||||
value: value`,
|
||||
expectedApp: `
|
||||
spec:
|
||||
source:
|
||||
helm:
|
||||
parameters:
|
||||
- name: image.tag
|
||||
value: test
|
||||
- name: another
|
||||
value: value`,
|
||||
},
|
||||
{
|
||||
// For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1191138278
|
||||
name: "ignore auto-sync in appset when it's not in the cluster with jq",
|
||||
ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{
|
||||
{JQPathExpressions: []string{".spec.syncPolicy.automated"}},
|
||||
},
|
||||
foundApp: `
|
||||
spec:
|
||||
syncPolicy:
|
||||
retry:
|
||||
limit: 5`,
|
||||
generatedApp: `
|
||||
spec:
|
||||
syncPolicy:
|
||||
automated:
|
||||
selfHeal: true
|
||||
retry:
|
||||
limit: 5`,
|
||||
expectedApp: `
|
||||
spec:
|
||||
syncPolicy:
|
||||
retry:
|
||||
limit: 5`,
|
||||
},
|
||||
{
|
||||
name: "ignore auto-sync in the cluster when it's not in the appset with jq",
|
||||
ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{
|
||||
{JQPathExpressions: []string{".spec.syncPolicy.automated"}},
|
||||
},
|
||||
foundApp: `
|
||||
spec:
|
||||
syncPolicy:
|
||||
automated:
|
||||
selfHeal: true
|
||||
retry:
|
||||
limit: 5`,
|
||||
generatedApp: `
|
||||
spec:
|
||||
syncPolicy:
|
||||
retry:
|
||||
limit: 5`,
|
||||
expectedApp: `
|
||||
spec:
|
||||
syncPolicy:
|
||||
automated:
|
||||
selfHeal: true
|
||||
retry:
|
||||
limit: 5`,
|
||||
},
|
||||
{
|
||||
// For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1420656537
|
||||
name: "ignore a one-off annotation with jq",
|
||||
ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{
|
||||
{JQPathExpressions: []string{`.metadata.annotations | select(.["foo.bar"] == "baz")`}},
|
||||
},
|
||||
foundApp: `
|
||||
metadata:
|
||||
annotations:
|
||||
foo.bar: baz
|
||||
some.other: annotation`,
|
||||
generatedApp: `
|
||||
metadata:
|
||||
annotations:
|
||||
some.other: annotation`,
|
||||
expectedApp: `
|
||||
metadata:
|
||||
annotations:
|
||||
foo.bar: baz
|
||||
some.other: annotation`,
|
||||
},
|
||||
{
|
||||
// For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1515672638
|
||||
name: "ignore the source.plugin field with a json pointer",
|
||||
ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{
|
||||
{JSONPointers: []string{"/spec/source/plugin"}},
|
||||
},
|
||||
foundApp: `
|
||||
spec:
|
||||
source:
|
||||
plugin:
|
||||
parameters:
|
||||
- name: url
|
||||
string: https://example.com`,
|
||||
generatedApp: `
|
||||
spec:
|
||||
source:
|
||||
plugin:
|
||||
parameters:
|
||||
- name: url
|
||||
string: https://example.com/wrong`,
|
||||
expectedApp: `
|
||||
spec:
|
||||
source:
|
||||
plugin:
|
||||
parameters:
|
||||
- name: url
|
||||
string: https://example.com`,
|
||||
},
|
||||
{
|
||||
// For this use case: https://github.com/argoproj/argo-cd/pull/14743#issuecomment-1761954799
|
||||
name: "ignore parameters added to a multi-source app in the cluster",
|
||||
ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{
|
||||
{JQPathExpressions: []string{`.spec.sources[] | select(.repoURL | contains("test-repo")).helm.parameters`}},
|
||||
},
|
||||
foundApp: `
|
||||
spec:
|
||||
sources:
|
||||
- repoURL: https://git.example.com/test-org/test-repo
|
||||
helm:
|
||||
parameters:
|
||||
- name: test
|
||||
value: hi`,
|
||||
generatedApp: `
|
||||
spec:
|
||||
sources:
|
||||
- repoURL: https://git.example.com/test-org/test-repo`,
|
||||
expectedApp: `
|
||||
spec:
|
||||
sources:
|
||||
- repoURL: https://git.example.com/test-org/test-repo
|
||||
helm:
|
||||
parameters:
|
||||
- name: test
|
||||
value: hi`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
foundApp := v1alpha1.Application{TypeMeta: appMeta}
|
||||
err := yaml.Unmarshal([]byte(tc.foundApp), &foundApp)
|
||||
require.NoError(t, err, tc.foundApp)
|
||||
generatedApp := v1alpha1.Application{TypeMeta: appMeta}
|
||||
err = yaml.Unmarshal([]byte(tc.generatedApp), &generatedApp)
|
||||
require.NoError(t, err, tc.generatedApp)
|
||||
err = applyIgnoreDifferences(tc.ignoreDifferences, &foundApp, &generatedApp)
|
||||
require.NoError(t, err)
|
||||
yamlFound, err := yaml.Marshal(tc.foundApp)
|
||||
require.NoError(t, err)
|
||||
yamlExpected, err := yaml.Marshal(tc.expectedApp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, string(yamlExpected), string(yamlFound))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// SanitizeName sanitizes the name in accordance with the below rules
|
||||
// 1. contain no more than 253 characters
|
||||
// 2. contain only lowercase alphanumeric characters, '-' or '.'
|
||||
// 3. start and end with an alphanumeric character
|
||||
func SanitizeName(name string) string {
|
||||
invalidDNSNameChars := regexp.MustCompile("[^-a-z0-9.]")
|
||||
maxDNSNameLength := 253
|
||||
|
||||
name = strings.ToLower(name)
|
||||
name = invalidDNSNameChars.ReplaceAllString(name, "-")
|
||||
if len(name) > maxDNSNameLength {
|
||||
name = name[:maxDNSNameLength]
|
||||
}
|
||||
|
||||
return strings.Trim(name, "-.")
|
||||
}
|
||||
|
||||
// This has been copied from helm and may be removed as soon as it is retrofited in sprig
|
||||
// toYAML takes an interface, marshals it to yaml, and returns a string. It will
|
||||
// always return a string, even on marshal error (empty string).
|
||||
//
|
||||
// This is designed to be called from a template.
|
||||
func toYAML(v interface{}) (string, error) {
|
||||
data, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
// Swallow errors inside of a template.
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSuffix(string(data), "\n"), nil
|
||||
}
|
||||
|
||||
// This has been copied from helm and may be removed as soon as it is retrofited in sprig
|
||||
// fromYAML converts a YAML document into a map[string]interface{}.
|
||||
//
|
||||
// This is not a general-purpose YAML parser, and will not parse all valid
|
||||
// YAML documents. Additionally, because its intended use is within templates
|
||||
// it tolerates errors. It will insert the returned error message string into
|
||||
// m["Error"] in the returned map.
|
||||
func fromYAML(str string) (map[string]interface{}, error) {
|
||||
m := map[string]interface{}{}
|
||||
|
||||
if err := yaml.Unmarshal([]byte(str), &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// This has been copied from helm and may be removed as soon as it is retrofited in sprig
|
||||
// fromYAMLArray converts a YAML array into a []interface{}.
|
||||
//
|
||||
// This is not a general-purpose YAML parser, and will not parse all valid
|
||||
// YAML documents. Additionally, because its intended use is within templates
|
||||
// it tolerates errors. It will insert the returned error message string as
|
||||
// the first and only item in the returned array.
|
||||
func fromYAMLArray(str string) ([]interface{}, error) {
|
||||
a := []interface{}{}
|
||||
|
||||
if err := yaml.Unmarshal([]byte(str), &a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
@@ -32,9 +32,6 @@ func init() {
|
||||
delete(sprigFuncMap, "expandenv")
|
||||
delete(sprigFuncMap, "getHostByName")
|
||||
sprigFuncMap["normalize"] = SanitizeName
|
||||
sprigFuncMap["toYaml"] = toYAML
|
||||
sprigFuncMap["fromYaml"] = fromYAML
|
||||
sprigFuncMap["fromYamlArray"] = fromYAMLArray
|
||||
}
|
||||
|
||||
type Renderer interface {
|
||||
@@ -94,7 +91,6 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
}
|
||||
// Unwrap the newly created pointer
|
||||
if err := r.deeplyReplace(copy.Elem(), originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -115,7 +111,6 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
|
||||
copyValue := reflectValue.Elem()
|
||||
if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
copy.Set(copyValue)
|
||||
@@ -152,7 +147,6 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
}
|
||||
copy.Field(i).Set(reflect.ValueOf(data))
|
||||
} else if err := r.deeplyReplace(copy.Field(i), original.Field(i), replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -167,7 +161,6 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
|
||||
for i := 0; i < original.Len(); i += 1 {
|
||||
if err := r.deeplyReplace(copy.Index(i), original.Index(i), replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -188,7 +181,6 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
copyValue := reflect.New(originalValue.Type()).Elem()
|
||||
|
||||
if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -196,7 +188,6 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
if key.Kind() == reflect.String {
|
||||
templatedKey, err := r.Replace(key.String(), replaceMap, useGoTemplate, goTemplateOptions)
|
||||
if err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
key = reflect.ValueOf(templatedKey)
|
||||
@@ -211,7 +202,6 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
strToTemplate := original.String()
|
||||
templated, err := r.Replace(strToTemplate, replaceMap, useGoTemplate, goTemplateOptions)
|
||||
if err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
if copy.CanSet() {
|
||||
@@ -434,6 +424,23 @@ func NormalizeBitbucketBasePath(basePath string) string {
|
||||
return basePath
|
||||
}
|
||||
|
||||
// SanitizeName sanitizes the name in accordance with the below rules
|
||||
// 1. contain no more than 253 characters
|
||||
// 2. contain only lowercase alphanumeric characters, '-' or '.'
|
||||
// 3. start and end with an alphanumeric character
|
||||
func SanitizeName(name string) string {
|
||||
invalidDNSNameChars := regexp.MustCompile("[^-a-z0-9.]")
|
||||
maxDNSNameLength := 253
|
||||
|
||||
name = strings.ToLower(name)
|
||||
name = invalidDNSNameChars.ReplaceAllString(name, "-")
|
||||
if len(name) > maxDNSNameLength {
|
||||
name = name[:maxDNSNameLength]
|
||||
}
|
||||
|
||||
return strings.Trim(name, "-.")
|
||||
}
|
||||
|
||||
func getTlsConfigWithCACert(scmRootCAPath string) *tls.Config {
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
|
||||
@@ -555,64 +555,6 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) {
|
||||
templateOptions: []string{"missingkey=error"},
|
||||
errorMessage: `failed to execute go template --> {{.doesnotexist}} <--: template: :1:6: executing "" at <.doesnotexist>: map has no entry for key "doesnotexist"`,
|
||||
},
|
||||
{
|
||||
name: "toYaml",
|
||||
fieldVal: `{{ toYaml . | indent 2 }}`,
|
||||
expectedVal: " foo:\n bar:\n bool: true\n number: 2\n str: Hello world",
|
||||
params: map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"bool": true,
|
||||
"number": 2,
|
||||
"str": "Hello world",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "toYaml Error",
|
||||
fieldVal: `{{ toYaml . | indent 2 }}`,
|
||||
expectedVal: " foo:\n bar:\n bool: true\n number: 2\n str: Hello world",
|
||||
errorMessage: "failed to execute go template {{ toYaml . | indent 2 }}: template: :1:3: executing \"\" at <toYaml .>: error calling toYaml: error marshaling into JSON: json: unsupported type: func(*string)",
|
||||
params: map[string]interface{}{
|
||||
"foo": func(test *string) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fromYaml",
|
||||
fieldVal: `{{ get (fromYaml .value) "hello" }}`,
|
||||
expectedVal: "world",
|
||||
params: map[string]interface{}{
|
||||
"value": "hello: world",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fromYaml error",
|
||||
fieldVal: `{{ get (fromYaml .value) "hello" }}`,
|
||||
expectedVal: "world",
|
||||
errorMessage: "failed to execute go template {{ get (fromYaml .value) \"hello\" }}: template: :1:8: executing \"\" at <fromYaml .value>: error calling fromYaml: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}",
|
||||
params: map[string]interface{}{
|
||||
"value": "non\n compliant\n yaml",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fromYamlArray",
|
||||
fieldVal: `{{ fromYamlArray .value | last }}`,
|
||||
expectedVal: "bonjour tout le monde",
|
||||
params: map[string]interface{}{
|
||||
"value": "- hello world\n- bonjour tout le monde",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fromYamlArray error",
|
||||
fieldVal: `{{ fromYamlArray .value | last }}`,
|
||||
expectedVal: "bonjour tout le monde",
|
||||
errorMessage: "failed to execute go template {{ fromYamlArray .value | last }}: template: :1:3: executing \"\" at <fromYamlArray .value>: error calling fromYamlArray: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type []interface {}",
|
||||
params: map[string]interface{}{
|
||||
"value": "non\n compliant\n yaml",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
{
|
||||
"id": "2ab4e3d3-b7a6-425e-92b1-5a9982c1269e",
|
||||
"eventType": "git.pullrequest.created",
|
||||
"publisherId": "tfs",
|
||||
"scope": "all",
|
||||
"message": {
|
||||
"text": "Jamal Hartnett created a new pull request",
|
||||
"html": "Jamal Hartnett created a new pull request",
|
||||
"markdown": "Jamal Hartnett created a new pull request"
|
||||
},
|
||||
"detailedMessage": {
|
||||
"text": "Jamal Hartnett created a new pull request\r\n\r\n- Merge status: Succeeded\r\n- Merge commit: eef717(https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n",
|
||||
"html": "Jamal Hartnett created a new pull request\r\n<ul>\r\n<li>Merge status: Succeeded</li>\r\n<li>Merge commit: <a href=\"https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72\">eef717</a></li>\r\n</ul>",
|
||||
"markdown": "Jamal Hartnett created a new pull request\r\n\r\n+ Merge status: Succeeded\r\n+ Merge commit: [eef717](https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n"
|
||||
},
|
||||
"resource": {
|
||||
"repository": {
|
||||
"id": "4bc14d40-c903-45e2-872e-0462c7748079",
|
||||
"name": "Fabrikam",
|
||||
"url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079",
|
||||
"project": {
|
||||
"id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
|
||||
"name": "DefaultCollection",
|
||||
"url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
|
||||
"state": "wellFormed"
|
||||
},
|
||||
"defaultBranch": "refs/heads/master",
|
||||
"remoteUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam"
|
||||
},
|
||||
"pullRequestId": 1,
|
||||
"status": "active",
|
||||
"createdBy": {
|
||||
"id": "54d125f7-69f7-4191-904f-c5b96b6261c8",
|
||||
"displayName": "Jamal Hartnett",
|
||||
"uniqueName": "fabrikamfiber4@hotmail.com",
|
||||
"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8",
|
||||
"imageUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8"
|
||||
},
|
||||
"creationDate": "2014-06-17T16:55:46.589889Z",
|
||||
"title": "my first pull request",
|
||||
"description": " - test2\r\n",
|
||||
"sourceRefName": "refs/heads/mytopic",
|
||||
"targetRefName": "refs/heads/master",
|
||||
"mergeStatus": "succeeded",
|
||||
"mergeId": "a10bb228-6ba6-4362-abd7-49ea21333dbd",
|
||||
"lastMergeSourceCommit": {
|
||||
"commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
|
||||
"url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
|
||||
},
|
||||
"lastMergeTargetCommit": {
|
||||
"commitId": "a511f535b1ea495ee0c903badb68fbc83772c882",
|
||||
"url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/a511f535b1ea495ee0c903badb68fbc83772c882"
|
||||
},
|
||||
"lastMergeCommit": {
|
||||
"commitId": "eef717f69257a6333f221566c1c987dc94cc0d72",
|
||||
"url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72"
|
||||
},
|
||||
"reviewers": [
|
||||
{
|
||||
"reviewerUrl": null,
|
||||
"vote": 0,
|
||||
"id": "2ea2d095-48f9-4cd6-9966-62f6f574096c",
|
||||
"displayName": "[Mobile]\\Mobile Team",
|
||||
"uniqueName": "vstfs:///Classification/TeamProject/f0811a3b-8c8a-4e43-a3bf-9a049b4835bd\\Mobile Team",
|
||||
"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/2ea2d095-48f9-4cd6-9966-62f6f574096c",
|
||||
"imageUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=2ea2d095-48f9-4cd6-9966-62f6f574096c",
|
||||
"isContainer": true
|
||||
}
|
||||
],
|
||||
"url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1"
|
||||
},
|
||||
"resourceVersion": "1.0",
|
||||
"resourceContainers": {
|
||||
"collection": {
|
||||
"id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
|
||||
},
|
||||
"account": {
|
||||
"id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
|
||||
},
|
||||
"project": {
|
||||
"id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
|
||||
}
|
||||
},
|
||||
"createdDate": "2016-09-19T13:03:27.2879096Z"
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"id": "03c164c2-8912-4d5e-8009-3707d5f83734",
|
||||
"eventType": "git.push",
|
||||
"publisherId": "tfs",
|
||||
"scope": "all",
|
||||
"message": {
|
||||
"text": "Jamal Hartnett pushed updates to branch master of repository Fabrikam-Fiber-Git.",
|
||||
"html": "Jamal Hartnett pushed updates to branch master of repository Fabrikam-Fiber-Git.",
|
||||
"markdown": "Jamal Hartnett pushed updates to branch `master` of repository `Fabrikam-Fiber-Git`."
|
||||
},
|
||||
"detailedMessage": {
|
||||
"text": "Jamal Hartnett pushed 1 commit to branch master of repository Fabrikam-Fiber-Git.\n - Fixed bug in web.config file 33b55f7c",
|
||||
"html": "Jamal Hartnett pushed 1 commit to branch <a href=\"https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/#version=GBmaster\">master</a> of repository <a href=\"https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/\">Fabrikam-Fiber-Git</a>.\n<ul>\n<li>Fixed bug in web.config file <a href=\"https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74\">33b55f7c</a>\n</ul>",
|
||||
"markdown": "Jamal Hartnett pushed 1 commit to branch [master](https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/#version=GBmaster) of repository [Fabrikam-Fiber-Git](https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/).\n* Fixed bug in web.config file [33b55f7c](https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74)"
|
||||
},
|
||||
"resource": {
|
||||
"commits": [
|
||||
{
|
||||
"commitId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74",
|
||||
"author": {
|
||||
"name": "Jamal Hartnett",
|
||||
"email": "fabrikamfiber4@hotmail.com",
|
||||
"date": "2015-02-25T19:01:00Z"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Jamal Hartnett",
|
||||
"email": "fabrikamfiber4@hotmail.com",
|
||||
"date": "2015-02-25T19:01:00Z"
|
||||
},
|
||||
"comment": "Fixed bug in web.config file",
|
||||
"url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74"
|
||||
}
|
||||
],
|
||||
"refUpdates": [
|
||||
{
|
||||
"name": "refs/heads/master",
|
||||
"oldObjectId": "aad331d8d3b131fa9ae03cf5e53965b51942618a",
|
||||
"newObjectId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"id": "278d5cd2-584d-4b63-824a-2ba458937249",
|
||||
"name": "Fabrikam-Fiber-Git",
|
||||
"url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_apis/repos/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249",
|
||||
"project": {
|
||||
"id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
|
||||
"name": "DefaultCollection",
|
||||
"url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
|
||||
"state": "wellFormed"
|
||||
},
|
||||
"defaultBranch": "refs/heads/master",
|
||||
"remoteUrl": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git"
|
||||
},
|
||||
"pushedBy": {
|
||||
"id": "00067FFED5C7AF52@Live.com",
|
||||
"displayName": "Jamal Hartnett",
|
||||
"uniqueName": "Windows Live ID\\fabrikamfiber4@hotmail.com"
|
||||
},
|
||||
"pushId": 14,
|
||||
"date": "2014-05-02T19:17:13.3309587Z",
|
||||
"url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_apis/repos/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/pushes/14"
|
||||
},
|
||||
"resourceVersion": "1.0",
|
||||
"resourceContainers": {
|
||||
"collection": {
|
||||
"id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
|
||||
},
|
||||
"account": {
|
||||
"id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
|
||||
},
|
||||
"project": {
|
||||
"id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
|
||||
}
|
||||
},
|
||||
"createdDate": "2016-09-19T13:03:27.0379153Z"
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"net/http"
|
||||
@@ -20,24 +19,17 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
argosettings "github.com/argoproj/argo-cd/v2/util/settings"
|
||||
|
||||
"github.com/go-playground/webhooks/v6/azuredevops"
|
||||
"github.com/go-playground/webhooks/v6/github"
|
||||
"github.com/go-playground/webhooks/v6/gitlab"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
errBasicAuthVerificationFailed = errors.New("basic auth verification failed")
|
||||
"gopkg.in/go-playground/webhooks.v5/github"
|
||||
"gopkg.in/go-playground/webhooks.v5/gitlab"
|
||||
)
|
||||
|
||||
type WebhookHandler struct {
|
||||
namespace string
|
||||
github *github.Webhook
|
||||
gitlab *gitlab.Webhook
|
||||
azuredevops *azuredevops.Webhook
|
||||
azuredevopsAuthHandler func(r *http.Request) error
|
||||
client client.Client
|
||||
generators map[string]generators.Generator
|
||||
namespace string
|
||||
github *github.Webhook
|
||||
gitlab *gitlab.Webhook
|
||||
client client.Client
|
||||
generators map[string]generators.Generator
|
||||
}
|
||||
|
||||
type gitGeneratorInfo struct {
|
||||
@@ -47,14 +39,8 @@ type gitGeneratorInfo struct {
|
||||
}
|
||||
|
||||
type prGeneratorInfo struct {
|
||||
Azuredevops *prGeneratorAzuredevopsInfo
|
||||
Github *prGeneratorGithubInfo
|
||||
Gitlab *prGeneratorGitlabInfo
|
||||
}
|
||||
|
||||
type prGeneratorAzuredevopsInfo struct {
|
||||
Repo string
|
||||
Project string
|
||||
Github *prGeneratorGithubInfo
|
||||
Gitlab *prGeneratorGitlabInfo
|
||||
}
|
||||
|
||||
type prGeneratorGithubInfo struct {
|
||||
@@ -82,28 +68,13 @@ func NewWebhookHandler(namespace string, argocdSettingsMgr *argosettings.Setting
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to init GitLab webhook: %v", err)
|
||||
}
|
||||
azuredevopsHandler, err := azuredevops.New()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to init Azure DevOps webhook: %v", err)
|
||||
}
|
||||
azuredevopsAuthHandler := func(r *http.Request) error {
|
||||
if argocdSettings.WebhookAzureDevOpsUsername != "" && argocdSettings.WebhookAzureDevOpsPassword != "" {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok || username != argocdSettings.WebhookAzureDevOpsUsername || password != argocdSettings.WebhookAzureDevOpsPassword {
|
||||
return errBasicAuthVerificationFailed
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return &WebhookHandler{
|
||||
namespace: namespace,
|
||||
github: githubHandler,
|
||||
gitlab: gitlabHandler,
|
||||
azuredevops: azuredevopsHandler,
|
||||
azuredevopsAuthHandler: azuredevopsAuthHandler,
|
||||
client: client,
|
||||
generators: generators,
|
||||
namespace: namespace,
|
||||
github: githubHandler,
|
||||
gitlab: gitlabHandler,
|
||||
client: client,
|
||||
generators: generators,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -154,14 +125,6 @@ func (h *WebhookHandler) Handler(w http.ResponseWriter, r *http.Request) {
|
||||
payload, err = h.github.Parse(r, github.PushEvent, github.PullRequestEvent, github.PingEvent)
|
||||
case r.Header.Get("X-Gitlab-Event") != "":
|
||||
payload, err = h.gitlab.Parse(r, gitlab.PushEvents, gitlab.TagEvents, gitlab.MergeRequestEvents)
|
||||
case r.Header.Get("X-Vss-Activityid") != "":
|
||||
if err = h.azuredevopsAuthHandler(r); err != nil {
|
||||
if errors.Is(err, errBasicAuthVerificationFailed) {
|
||||
log.WithField(common.SecurityField, common.SecurityHigh).Infof("Azure DevOps webhook basic auth verification failed")
|
||||
}
|
||||
} else {
|
||||
payload, err = h.azuredevops.Parse(r, azuredevops.GitPushEventType, azuredevops.GitPullRequestCreatedEventType, azuredevops.GitPullRequestUpdatedEventType, azuredevops.GitPullRequestMergedEventType)
|
||||
}
|
||||
default:
|
||||
log.Debug("Ignoring unknown webhook event")
|
||||
http.Error(w, "Unknown webhook event", http.StatusBadRequest)
|
||||
@@ -201,12 +164,6 @@ func getGitGeneratorInfo(payload interface{}) *gitGeneratorInfo {
|
||||
webURL = payload.Project.WebURL
|
||||
revision = parseRevision(payload.Ref)
|
||||
touchedHead = payload.Project.DefaultBranch == revision
|
||||
case azuredevops.GitPushEvent:
|
||||
// See: https://learn.microsoft.com/en-us/azure/devops/service-hooks/events?view=azure-devops#git.push
|
||||
webURL = payload.Resource.Repository.RemoteURL
|
||||
revision = parseRevision(payload.Resource.RefUpdates[0].Name)
|
||||
touchedHead = payload.Resource.RefUpdates[0].Name == payload.Resource.Repository.DefaultBranch
|
||||
// unfortunately, Azure DevOps doesn't provide a list of changed files
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -272,18 +229,6 @@ func getPRGeneratorInfo(payload interface{}) *prGeneratorInfo {
|
||||
Project: strconv.FormatInt(payload.ObjectAttributes.TargetProjectID, 10),
|
||||
APIHostname: urlObj.Hostname(),
|
||||
}
|
||||
case azuredevops.GitPullRequestEvent:
|
||||
if !isAllowedAzureDevOpsPullRequestAction(string(payload.EventType)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
repo := payload.Resource.Repository.Name
|
||||
project := payload.Resource.Repository.Project.Name
|
||||
|
||||
info.Azuredevops = &prGeneratorAzuredevopsInfo{
|
||||
Repo: repo,
|
||||
Project: project,
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -311,13 +256,6 @@ var gitlabAllowedPullRequestActions = []string{
|
||||
"merge",
|
||||
}
|
||||
|
||||
// azuredevopsAllowedPullRequestActions is a list of Azure DevOps actions that allow refresh
|
||||
var azuredevopsAllowedPullRequestActions = []string{
|
||||
"git.pullrequest.created",
|
||||
"git.pullrequest.merged",
|
||||
"git.pullrequest.updated",
|
||||
}
|
||||
|
||||
func isAllowedGithubPullRequestAction(action string) bool {
|
||||
for _, allow := range githubAllowedPullRequestActions {
|
||||
if allow == action {
|
||||
@@ -336,15 +274,6 @@ func isAllowedGitlabPullRequestAction(action string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func isAllowedAzureDevOpsPullRequestAction(action string) bool {
|
||||
for _, allow := range azuredevopsAllowedPullRequestActions {
|
||||
if allow == action {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func shouldRefreshGitGenerator(gen *v1alpha1.GitGenerator, info *gitGeneratorInfo) bool {
|
||||
if gen == nil || info == nil {
|
||||
return false
|
||||
@@ -430,16 +359,6 @@ func shouldRefreshPRGenerator(gen *v1alpha1.PullRequestGenerator, info *prGenera
|
||||
return true
|
||||
}
|
||||
|
||||
if gen.AzureDevOps != nil && info.Azuredevops != nil {
|
||||
if gen.AzureDevOps.Project != info.Azuredevops.Project {
|
||||
return false
|
||||
}
|
||||
if gen.AzureDevOps.Repo != info.Azuredevops.Repo {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -643,7 +562,7 @@ func refreshApplicationSet(c client.Client, appSet *v1alpha1.ApplicationSet) err
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
err := c.Get(context.Background(), types.NamespacedName{Name: appSet.Name, Namespace: appSet.Namespace}, appSet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting ApplicationSet: %w", err)
|
||||
return err
|
||||
}
|
||||
if appSet.Annotations == nil {
|
||||
appSet.Annotations = map[string]string{}
|
||||
|
||||
@@ -146,24 +146,6 @@ func TestWebhookHandler(t *testing.T) {
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedRefresh: false,
|
||||
},
|
||||
{
|
||||
desc: "WebHook from a Azure DevOps repository via Commit",
|
||||
headerKey: "X-Vss-Activityid",
|
||||
headerValue: "Push Hook",
|
||||
payloadFile: "azuredevops-push.json",
|
||||
effectedAppSets: []string{"git-azure-devops", "plugin", "matrix-pull-request-github-plugin"},
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedRefresh: true,
|
||||
},
|
||||
{
|
||||
desc: "WebHook from a Azure DevOps repository via pull request event",
|
||||
headerKey: "X-Vss-Activityid",
|
||||
headerValue: "Pull Request Hook",
|
||||
payloadFile: "azuredevops-pull-request.json",
|
||||
effectedAppSets: []string{"pull-request-azure-devops", "plugin", "matrix-pull-request-github-plugin"},
|
||||
expectedStatusCode: http.StatusOK,
|
||||
expectedRefresh: true,
|
||||
},
|
||||
}
|
||||
|
||||
namespace := "test"
|
||||
@@ -179,10 +161,8 @@ func TestWebhookHandler(t *testing.T) {
|
||||
fc := fake.NewClientBuilder().WithScheme(scheme).WithObjects(
|
||||
fakeAppWithGitGenerator("git-github", namespace, "https://github.com/org/repo"),
|
||||
fakeAppWithGitGenerator("git-gitlab", namespace, "https://gitlab/group/name"),
|
||||
fakeAppWithGitGenerator("git-azure-devops", namespace, "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git"),
|
||||
fakeAppWithGithubPullRequestGenerator("pull-request-github", namespace, "Codertocat", "Hello-World"),
|
||||
fakeAppWithGitlabPullRequestGenerator("pull-request-gitlab", namespace, "100500"),
|
||||
fakeAppWithAzureDevOpsPullRequestGenerator("pull-request-azure-devops", namespace, "DefaultCollection", "Fabrikam"),
|
||||
fakeAppWithPluginGenerator("plugin", namespace),
|
||||
fakeAppWithMatrixAndGitGenerator("matrix-git-github", namespace, "https://github.com/org/repo"),
|
||||
fakeAppWithMatrixAndPullRequestGenerator("matrix-pull-request-github", namespace, "Codertocat", "Hello-World"),
|
||||
@@ -358,27 +338,6 @@ func fakeAppWithGithubPullRequestGenerator(name, namespace, owner, repo string)
|
||||
}
|
||||
}
|
||||
|
||||
func fakeAppWithAzureDevOpsPullRequestGenerator(name, namespace, project, repo string) *v1alpha1.ApplicationSet {
|
||||
return &v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
PullRequest: &v1alpha1.PullRequestGenerator{
|
||||
AzureDevOps: &v1alpha1.PullRequestGeneratorAzureDevOps{
|
||||
Project: project,
|
||||
Repo: repo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func fakeAppWithMatrixAndGitGenerator(name, namespace, repo string) *v1alpha1.ApplicationSet {
|
||||
return &v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
||||
@@ -3,6 +3,5 @@ package assets
|
||||
import "embed"
|
||||
|
||||
// Embedded contains embedded assets
|
||||
//
|
||||
//go:embed *
|
||||
var Embedded embed.FS
|
||||
|
||||
@@ -234,7 +234,7 @@
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "forces application reconciliation if set to 'hard'.",
|
||||
"description": "forces application reconciliation if set to true.",
|
||||
"name": "refresh",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -573,7 +573,7 @@
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "forces application reconciliation if set to 'hard'.",
|
||||
"description": "forces application reconciliation if set to true.",
|
||||
"name": "refresh",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -3816,7 +3816,7 @@
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "forces application reconciliation if set to 'hard'.",
|
||||
"description": "forces application reconciliation if set to true.",
|
||||
"name": "refresh",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -4036,7 +4036,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expiresIn": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "expiresIn represents a duration in seconds"
|
||||
},
|
||||
@@ -4063,14 +4063,14 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expiresAt": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"issuedAt": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
@@ -4162,7 +4162,7 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"name": {
|
||||
@@ -4462,9 +4462,6 @@
|
||||
"clientID": {
|
||||
"type": "string"
|
||||
},
|
||||
"enablePKCEAuthentication": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"idTokenClaims": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
@@ -4629,7 +4626,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
@@ -4768,7 +4765,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"expiresIn": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "expiresIn represents a duration in seconds"
|
||||
},
|
||||
@@ -5092,7 +5089,7 @@
|
||||
}
|
||||
},
|
||||
"runtimeRawExtension": {
|
||||
"description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned\nstruct, and Object in your internal struct. You also need to register your\nvarious plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into\nyour external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.\nThe next step is to copy (using pkg/conversion) into the internal struct. The runtime\npackage's DefaultScheme has conversion functions installed which will unpack the\nJSON stored in RawExtension, turning it into the correct object type, and storing it\nin the Object. (TODO: In the case where the object is of an unknown type, a\nruntime.Unknown object will be created and stored.)\n\n+k8s:deepcopy-gen=true\n+protobuf=true\n+k8s:openapi-gen=true",
|
||||
"description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned\nstruct, and Object in your internal struct. You also need to register your\nvarious plugin types.\n\n// Internal package:\ntype MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.Object `json:\"myPlugin\"`\n}\ntype PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// External package:\ntype MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n}\ntype PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// On the wire, the JSON will look something like this:\n{\n\t\"kind\":\"MyAPIObject\",\n\t\"apiVersion\":\"v1\",\n\t\"myPlugin\": {\n\t\t\"kind\":\"PluginA\",\n\t\t\"aOption\":\"foo\",\n\t},\n}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into\nyour external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.\nThe next step is to copy (using pkg/conversion) into the internal struct. The runtime\npackage's DefaultScheme has conversion functions installed which will unpack the\nJSON stored in RawExtension, turning it into the correct object type, and storing it\nin the Object. (TODO: In the case where the object is of an unknown type, a\nruntime.Unknown object will be created and stored.)\n\n+k8s:deepcopy-gen=true\n+protobuf=true\n+k8s:openapi-gen=true",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"raw": {
|
||||
@@ -5359,7 +5356,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"remainingItemCount": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "remainingItemCount is the number of subsequent items in the list which are not included in this\nlist response. If the list request contained label or field selectors, then the number of\nremaining items is unknown and the field will be left unset and omitted during serialization.\nIf the list is complete (either because it is not chunking or because this is the last chunk),\nthen there are no more remaining items and this field will be left unset and omitted during\nserialization.\nServers older than v1.15 do not set this field.\nThe intended use of the remainingItemCount is *estimating* the size of a collection. Clients\nshould not rely on the remainingItemCount to be set or to be exact.\n+optional"
|
||||
},
|
||||
@@ -5437,7 +5434,7 @@
|
||||
},
|
||||
"seconds": {
|
||||
"description": "Represents seconds of UTC time since Unix epoch\n1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to\n9999-12-31T23:59:59Z inclusive.",
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
@@ -5499,11 +5496,15 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"clusterName": {
|
||||
"description": "Deprecated: ClusterName is a legacy field that was always cleared by\nthe system and never used; it will be removed completely in 1.25.\n\nThe name in the go struct is changed to help clients detect\naccidental use.\n\n+optional",
|
||||
"type": "string"
|
||||
},
|
||||
"creationTimestamp": {
|
||||
"$ref": "#/definitions/v1Time"
|
||||
},
|
||||
"deletionGracePeriodSeconds": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "Number of seconds allowed for this object to gracefully terminate before\nit will be removed from the system. Only set when deletionTimestamp is also set.\nMay only be shortened.\nRead-only.\n+optional"
|
||||
},
|
||||
@@ -5522,7 +5523,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"generation": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "A sequence number representing a specific generation of the desired state.\nPopulated by the system. Read-only.\n+optional"
|
||||
},
|
||||
@@ -5570,8 +5571,8 @@
|
||||
}
|
||||
},
|
||||
"v1ObjectReference": {
|
||||
"description": "ObjectReference contains enough information to let you inspect or modify the referred object.\n---\nNew uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.\n 1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.\n 2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular\n restrictions like, \"must refer only to types A and B\" or \"UID not honored\" or \"name must be restricted\".\n Those cannot be well described when embedded.\n 3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.\n 4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity\n during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple\n and the version of the actual struct is irrelevant.\n 5. We cannot easily change it. Because this type is embedded in many locations, updates to this type\n will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.\n\nInstead of using this type, create a locally provided and used type that is well-focused on your reference.\nFor example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+structType=atomic",
|
||||
"type": "object",
|
||||
"title": "ObjectReference contains enough information to let you inspect or modify the referred object.\n---\nNew uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.\n 1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.\n 2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular\n restrictions like, \"must refer only to types A and B\" or \"UID not honored\" or \"name must be restricted\".\n Those cannot be well described when embedded.\n 3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.\n 4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity\n during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple\n and the version of the actual struct is irrelevant.\n 5. We cannot easily change it. Because this type is embedded in many locations, updates to this type\n will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.\nInstead of using this type, create a locally provided and used type that is well-focused on your reference.\nFor example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+structType=atomic",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"type": "string",
|
||||
@@ -5653,8 +5654,19 @@
|
||||
},
|
||||
"v1Time": {
|
||||
"description": "Time is a wrapper around time.Time which supports correct\nmarshaling to YAML and JSON. Wrappers are provided for many\nof the factory methods that the time package offers.\n\n+protobuf.options.marshal=false\n+protobuf.as=Timestamp\n+protobuf.options.(gogoproto.goproto_stringer)=false",
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nanos": {
|
||||
"description": "Non-negative fractions of a second at nanosecond resolution. Negative\nsecond values with fractions must still have non-negative nanos values\nthat count forward in time. Must be from 0 to 999,999,999\ninclusive. This field may be limited in precision depending on context.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"seconds": {
|
||||
"description": "Represents seconds of UTC time since Unix epoch\n1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to\n9999-12-31T23:59:59Z inclusive.",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1AWSAuthConfig": {
|
||||
"type": "object",
|
||||
@@ -5840,16 +5852,16 @@
|
||||
"title": "ApplicationDestination holds information about the application's destination",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Name is an alternate way of specifying the target cluster by its symbolic name. This must be set if Server is not set.",
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"title": "Name is an alternate way of specifying the target cluster by its symbolic name"
|
||||
},
|
||||
"namespace": {
|
||||
"type": "string",
|
||||
"title": "Namespace specifies the target namespace for the application's resources.\nThe namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace"
|
||||
},
|
||||
"server": {
|
||||
"description": "Server specifies the URL of the target cluster's Kubernetes control plane API. This must be set if Name is not set.",
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"title": "Server specifies the URL of the target cluster and must be set to the Kubernetes control plane API"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -5893,12 +5905,6 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6054,30 +6060,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1ApplicationSetResourceIgnoreDifferences": {
|
||||
"description": "ApplicationSetResourceIgnoreDifferences configures how the ApplicationSet controller will ignore differences in live\napplications when applying changes from generated applications.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"jqPathExpressions": {
|
||||
"description": "JQPathExpressions is a list of JQ path expressions to fields to ignore differences for.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"jsonPointers": {
|
||||
"description": "JSONPointers is a list of JSON pointers to fields to ignore differences for.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"description": "Name is the name of the application to ignore differences for. If not specified, the rule applies to all applications.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1ApplicationSetRolloutStep": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -6126,12 +6108,6 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"ignoreApplicationDifferences": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1ApplicationSetResourceIgnoreDifferences"
|
||||
}
|
||||
},
|
||||
"preservedFields": {
|
||||
"$ref": "#/definitions/v1alpha1ApplicationPreservedFields"
|
||||
},
|
||||
@@ -6422,13 +6398,6 @@
|
||||
"type": "string",
|
||||
"title": "Namespace sets the namespace that Kustomize adds to all resources"
|
||||
},
|
||||
"patches": {
|
||||
"type": "array",
|
||||
"title": "Patches is a list of Kustomize patches",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1KustomizePatch"
|
||||
}
|
||||
},
|
||||
"replicas": {
|
||||
"type": "array",
|
||||
"title": "Replicas is a list of Kustomize Replicas override specifications",
|
||||
@@ -6517,7 +6486,7 @@
|
||||
},
|
||||
"revisionHistoryLimit": {
|
||||
"description": "RevisionHistoryLimit limits the number of items kept in the application's revision history, which is used for informational purposes as well as for rollbacks to previous versions.\nThis should only be changed in exceptional circumstances.\nSetting to zero will store no history. This will reduce storage used.\nIncreasing will increase the space used to store the history, so we do not recommend increasing it.\nDefault is 10.",
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"source": {
|
||||
@@ -6667,7 +6636,7 @@
|
||||
"title": "Duration is the amount to back off. Default unit is seconds, but could also be a duration (e.g. \"2m\", \"1h\")"
|
||||
},
|
||||
"factor": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "Factor is a factor to multiply the base duration after each failed retry"
|
||||
},
|
||||
@@ -6778,7 +6747,7 @@
|
||||
},
|
||||
"shard": {
|
||||
"description": "Shard contains optional shard number. Calculated on the fly by the application controller if not specified.",
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
@@ -6788,7 +6757,7 @@
|
||||
"title": "ClusterCacheInfo contains information about the cluster cache",
|
||||
"properties": {
|
||||
"apisCount": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "APIsCount holds number of observed Kubernetes API count"
|
||||
},
|
||||
@@ -6796,7 +6765,7 @@
|
||||
"$ref": "#/definitions/v1Time"
|
||||
},
|
||||
"resourcesCount": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "ResourcesCount holds number of observed Kubernetes resources"
|
||||
}
|
||||
@@ -6859,7 +6828,7 @@
|
||||
}
|
||||
},
|
||||
"applicationsCount": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "ApplicationsCount is the number of applications managed by Argo CD on the cluster"
|
||||
},
|
||||
@@ -6984,7 +6953,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"requeueAfterSeconds": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"template": {
|
||||
@@ -7072,7 +7041,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"requeueAfterSeconds": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"revision": {
|
||||
@@ -7204,15 +7173,15 @@
|
||||
"title": "TODO: describe this type",
|
||||
"properties": {
|
||||
"capacity": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"requestedByApp": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"requestedByNeighbors": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"resourceName": {
|
||||
@@ -7250,11 +7219,11 @@
|
||||
"title": "JWTToken holds the issuedAt and expiresAt values of a token",
|
||||
"properties": {
|
||||
"exp": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"iat": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"id": {
|
||||
@@ -7301,20 +7270,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1KustomizeGvk": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1KustomizeOptions": {
|
||||
"type": "object",
|
||||
"title": "KustomizeOptions are options for kustomize to use when building manifests",
|
||||
@@ -7329,26 +7284,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1KustomizePatch": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"options": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"target": {
|
||||
"$ref": "#/definitions/v1alpha1KustomizeSelector"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1KustomizeReplica": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7361,34 +7296,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1KustomizeResId": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"gvk": {
|
||||
"$ref": "#/definitions/v1alpha1KustomizeGvk"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1KustomizeSelector": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"annotationSelector": {
|
||||
"type": "string"
|
||||
},
|
||||
"labelSelector": {
|
||||
"type": "string"
|
||||
},
|
||||
"resId": {
|
||||
"$ref": "#/definitions/v1alpha1KustomizeResId"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1ListGenerator": {
|
||||
"type": "object",
|
||||
"title": "ListGenerator include items info",
|
||||
@@ -7515,7 +7422,7 @@
|
||||
"title": "Phase is the current phase of the operation"
|
||||
},
|
||||
"retryCount": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "RetryCount contains time of operation retries"
|
||||
},
|
||||
@@ -7607,7 +7514,7 @@
|
||||
},
|
||||
"requeueAfterSeconds": {
|
||||
"description": "RequeueAfterSeconds determines how long the ApplicationSet controller will wait before reconciling the ApplicationSet again.",
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"template": {
|
||||
@@ -7700,7 +7607,7 @@
|
||||
},
|
||||
"requeueAfterSeconds": {
|
||||
"description": "Standard parameters.",
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"template": {
|
||||
@@ -7907,12 +7814,12 @@
|
||||
"title": "GithubAppEnterpriseBaseURL specifies the GitHub API URL for GitHub app authentication. If empty will default to https://api.github.com"
|
||||
},
|
||||
"githubAppID": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "GithubAppId specifies the Github App ID of the app used to access the repo for GitHub app authentication"
|
||||
},
|
||||
"githubAppInstallationID": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "GithubAppInstallationId specifies the ID of the installed GitHub App for GitHub app authentication"
|
||||
},
|
||||
@@ -7997,12 +7904,12 @@
|
||||
"title": "GithubAppEnterpriseBaseURL specifies the base URL of GitHub Enterprise installation. If empty will default to https://api.github.com"
|
||||
},
|
||||
"githubAppID": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "GithubAppId specifies the ID of the GitHub app used to access the repo"
|
||||
},
|
||||
"githubAppInstallationID": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "GithubAppInstallationId specifies the installation ID of the GitHub App used to access the repo"
|
||||
},
|
||||
@@ -8316,15 +8223,13 @@
|
||||
"$ref": "#/definitions/v1alpha1ResourceRef"
|
||||
}
|
||||
},
|
||||
"resourceRef": {
|
||||
"$ref": "#/definitions/v1alpha1ResourceRef"
|
||||
},
|
||||
"resourceVersion": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v1alpha1ResourceRef"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"v1alpha1ResourceOverride": {
|
||||
"type": "object",
|
||||
@@ -8452,7 +8357,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"syncWave": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"version": {
|
||||
@@ -8469,7 +8374,7 @@
|
||||
},
|
||||
"limit": {
|
||||
"description": "Limit is the maximum number of attempts for retrying a failed sync. If set to 0, no retries will be performed.",
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
@@ -8485,7 +8390,7 @@
|
||||
"$ref": "#/definitions/v1Time"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "ID is an auto incrementing identifier of the RevisionHistory"
|
||||
},
|
||||
@@ -8578,7 +8483,7 @@
|
||||
},
|
||||
"requeueAfterSeconds": {
|
||||
"description": "Standard parameters.",
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"template": {
|
||||
@@ -8783,10 +8688,6 @@
|
||||
"description": "Gitlab group to scan. Required. You can use either the project id (recommended) or the full namespaced path.",
|
||||
"type": "string"
|
||||
},
|
||||
"includeSharedProjects": {
|
||||
"type": "boolean",
|
||||
"title": "When recursing through subgroups, also include shared Projects (true) or scan only the subgroups under same path (false). Defaults to \"true\""
|
||||
},
|
||||
"includeSubgroups": {
|
||||
"type": "boolean",
|
||||
"title": "Recurse through subgroups (true) or scan only the base group (false). Defaults to \"false\""
|
||||
@@ -8797,10 +8698,6 @@
|
||||
},
|
||||
"tokenRef": {
|
||||
"$ref": "#/definitions/v1alpha1SecretRef"
|
||||
},
|
||||
"topic": {
|
||||
"description": "Filter repos list based on Gitlab Topic.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/ratelimiter"
|
||||
"github.com/argoproj/pkg/stats"
|
||||
"github.com/redis/go-redis/v9"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -31,13 +30,11 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
"github.com/argoproj/argo-cd/v2/util/tls"
|
||||
"github.com/argoproj/argo-cd/v2/util/trace"
|
||||
kubeerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// CLIName is the name of the CLI
|
||||
cliName = common.ApplicationController
|
||||
cliName = "argocd-application-controller"
|
||||
// Default time in seconds for application resync period
|
||||
defaultAppResyncPeriod = 180
|
||||
// Default time in seconds for application hard resync period
|
||||
@@ -46,30 +43,27 @@ const (
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
workqueueRateLimit ratelimiter.AppControllerRateLimiterConfig
|
||||
clientConfig clientcmd.ClientConfig
|
||||
appResyncPeriod int64
|
||||
appHardResyncPeriod int64
|
||||
repoServerAddress string
|
||||
repoServerTimeoutSeconds int
|
||||
selfHealTimeoutSeconds int
|
||||
statusProcessors int
|
||||
operationProcessors int
|
||||
glogLevel int
|
||||
metricsPort int
|
||||
metricsCacheExpiration time.Duration
|
||||
metricsAplicationLabels []string
|
||||
kubectlParallelismLimit int64
|
||||
cacheSource func() (*appstatecache.Cache, error)
|
||||
redisClient *redis.Client
|
||||
repoServerPlaintext bool
|
||||
repoServerStrictTLS bool
|
||||
otlpAddress string
|
||||
otlpAttrs []string
|
||||
applicationNamespaces []string
|
||||
persistResourceHealth bool
|
||||
shardingAlgorithm string
|
||||
enableDynamicClusterDistribution bool
|
||||
clientConfig clientcmd.ClientConfig
|
||||
appResyncPeriod int64
|
||||
appHardResyncPeriod int64
|
||||
repoServerAddress string
|
||||
repoServerTimeoutSeconds int
|
||||
selfHealTimeoutSeconds int
|
||||
statusProcessors int
|
||||
operationProcessors int
|
||||
glogLevel int
|
||||
metricsPort int
|
||||
metricsCacheExpiration time.Duration
|
||||
metricsAplicationLabels []string
|
||||
kubectlParallelismLimit int64
|
||||
cacheSrc func() (*appstatecache.Cache, error)
|
||||
redisClient *redis.Client
|
||||
repoServerPlaintext bool
|
||||
repoServerStrictTLS bool
|
||||
otlpAddress string
|
||||
applicationNamespaces []string
|
||||
persistResourceHealth bool
|
||||
shardingAlgorithm string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -97,7 +91,7 @@ func NewCommand() *cobra.Command {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
errors.CheckError(v1alpha1.SetK8SConfigDefaults(config))
|
||||
config.UserAgent = fmt.Sprintf("%s/%s (%s)", common.DefaultApplicationControllerName, vers.Version, vers.Platform)
|
||||
config.UserAgent = fmt.Sprintf("argocd-application-controller/%s (%s)", vers.Version, vers.Platform)
|
||||
|
||||
kubeClient := kubernetes.NewForConfigOrDie(config)
|
||||
appClient := appclientset.NewForConfigOrDie(config)
|
||||
@@ -132,7 +126,7 @@ func NewCommand() *cobra.Command {
|
||||
|
||||
repoClientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds, tlsConfig)
|
||||
|
||||
cache, err := cacheSource()
|
||||
cache, err := cacheSrc()
|
||||
errors.CheckError(err)
|
||||
cache.Cache.SetClient(cacheutil.NewTwoLevelClient(cache.Cache.GetClient(), 10*time.Minute))
|
||||
|
||||
@@ -142,8 +136,7 @@ func NewCommand() *cobra.Command {
|
||||
appController.InvalidateProjectsCache()
|
||||
}))
|
||||
kubectl := kubeutil.NewKubectl()
|
||||
clusterFilter := getClusterFilter(kubeClient, settingsMgr, shardingAlgorithm, enableDynamicClusterDistribution)
|
||||
errors.CheckError(err)
|
||||
clusterFilter := getClusterFilter(kubeClient, settingsMgr, shardingAlgorithm)
|
||||
appController, err = controller.NewApplicationController(
|
||||
namespace,
|
||||
settingsMgr,
|
||||
@@ -162,7 +155,6 @@ func NewCommand() *cobra.Command {
|
||||
persistResourceHealth,
|
||||
clusterFilter,
|
||||
applicationNamespaces,
|
||||
&workqueueRateLimit,
|
||||
)
|
||||
errors.CheckError(err)
|
||||
cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer())
|
||||
@@ -172,7 +164,7 @@ func NewCommand() *cobra.Command {
|
||||
stats.RegisterHeapDumper("memprofile")
|
||||
|
||||
if otlpAddress != "" {
|
||||
closeTracer, err := trace.InitTracer(ctx, "argocd-controller", otlpAddress, otlpAttrs)
|
||||
closeTracer, err := trace.InitTracer(ctx, "argocd-controller", otlpAddress)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize tracing: %v", err)
|
||||
}
|
||||
@@ -204,75 +196,30 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS", false), "Whether to use strict validation of the TLS cert presented by the repo server")
|
||||
command.Flags().StringSliceVar(&metricsAplicationLabels, "metrics-application-labels", []string{}, "List of Application labels that will be added to the argocd_application_labels metric")
|
||||
command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to")
|
||||
command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)")
|
||||
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that applications are allowed to be reconciled from")
|
||||
command.Flags().BoolVar(&persistResourceHealth, "persist-resource-health", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH", true), "Enables storing the managed resources health in the Application CRD")
|
||||
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", env.StringFromEnv(common.EnvControllerShardingAlgorithm, common.DefaultShardingAlgorithm), "Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] ")
|
||||
// global queue rate limit config
|
||||
command.Flags().Int64Var(&workqueueRateLimit.BucketSize, "wq-bucket-size", env.ParseInt64FromEnv("WORKQUEUE_BUCKET_SIZE", 500, 1, math.MaxInt64), "Set Workqueue Rate Limiter Bucket Size, default 500")
|
||||
command.Flags().Int64Var(&workqueueRateLimit.BucketQPS, "wq-bucket-qps", env.ParseInt64FromEnv("WORKQUEUE_BUCKET_QPS", 50, 1, math.MaxInt64), "Set Workqueue Rate Limiter Bucket QPS, default 50")
|
||||
// individual item rate limit config
|
||||
// when WORKQUEUE_FAILURE_COOLDOWN is 0 per item rate limiting is disabled(default)
|
||||
command.Flags().DurationVar(&workqueueRateLimit.FailureCoolDown, "wq-cooldown-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_FAILURE_COOLDOWN_NS", 0, 0, (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Cooldown duration in ns, default 0(per item rate limiter disabled)")
|
||||
command.Flags().DurationVar(&workqueueRateLimit.BaseDelay, "wq-basedelay-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_BASE_DELAY_NS", time.Millisecond.Nanoseconds(), time.Nanosecond.Nanoseconds(), (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Base Delay duration in nanoseconds, default 1000000 (1ms)")
|
||||
command.Flags().DurationVar(&workqueueRateLimit.MaxDelay, "wq-maxdelay-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_MAX_DELAY_NS", time.Second.Nanoseconds(), 1*time.Millisecond.Nanoseconds(), (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Max Delay duration in nanoseconds, default 1000000000 (1s)")
|
||||
command.Flags().Float64Var(&workqueueRateLimit.BackoffFactor, "wq-backoff-factor", env.ParseFloat64FromEnv("WORKQUEUE_BACKOFF_FACTOR", 1.5, 0, math.MaxFloat64), "Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5")
|
||||
command.Flags().BoolVar(&enableDynamicClusterDistribution, "dynamic-cluster-distribution-enabled", env.ParseBoolFromEnv(common.EnvEnableDynamicClusterDistribution, false), "Enables dynamic cluster distribution.")
|
||||
cacheSource = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
})
|
||||
return &command
|
||||
}
|
||||
|
||||
func getClusterFilter(kubeClient *kubernetes.Clientset, settingsMgr *settings.SettingsManager, shardingAlgorithm string, enableDynamicClusterDistribution bool) sharding.ClusterFilterFunction {
|
||||
|
||||
var replicas int
|
||||
func getClusterFilter(kubeClient *kubernetes.Clientset, settingsMgr *settings.SettingsManager, shardingAlgorithm string) sharding.ClusterFilterFunction {
|
||||
replicas := env.ParseNumFromEnv(common.EnvControllerReplicas, 0, 0, math.MaxInt32)
|
||||
shard := env.ParseNumFromEnv(common.EnvControllerShard, -1, -math.MaxInt32, math.MaxInt32)
|
||||
|
||||
applicationControllerName := env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName)
|
||||
appControllerDeployment, err := kubeClient.AppsV1().Deployments(settingsMgr.GetNamespace()).Get(context.Background(), applicationControllerName, metav1.GetOptions{})
|
||||
|
||||
// if the application controller deployment was not found, the Get() call returns an empty Deployment object. So, set the variable to nil explicitly
|
||||
if err != nil && kubeerrors.IsNotFound(err) {
|
||||
appControllerDeployment = nil
|
||||
}
|
||||
|
||||
if enableDynamicClusterDistribution && appControllerDeployment != nil && appControllerDeployment.Spec.Replicas != nil {
|
||||
replicas = int(*appControllerDeployment.Spec.Replicas)
|
||||
} else {
|
||||
replicas = env.ParseNumFromEnv(common.EnvControllerReplicas, 0, 0, math.MaxInt32)
|
||||
}
|
||||
|
||||
var clusterFilter func(cluster *v1alpha1.Cluster) bool
|
||||
if replicas > 1 {
|
||||
// check for shard mapping using configmap if application-controller is a deployment
|
||||
// else use existing logic to infer shard from pod name if application-controller is a statefulset
|
||||
if enableDynamicClusterDistribution && appControllerDeployment != nil {
|
||||
|
||||
if shard < 0 {
|
||||
var err error
|
||||
// retry 3 times if we find a conflict while updating shard mapping configMap.
|
||||
// If we still see conflicts after the retries, wait for next iteration of heartbeat process.
|
||||
for i := 0; i <= common.AppControllerHeartbeatUpdateRetryCount; i++ {
|
||||
shard, err = sharding.GetOrUpdateShardFromConfigMap(kubeClient, settingsMgr, replicas, shard)
|
||||
if !kubeerrors.IsConflict(err) {
|
||||
err = fmt.Errorf("unable to get shard due to error updating the sharding config map: %s", err)
|
||||
break
|
||||
}
|
||||
log.Warnf("conflict when getting shard from shard mapping configMap. Retrying (%d/3)", i)
|
||||
}
|
||||
shard, err = sharding.InferShard()
|
||||
errors.CheckError(err)
|
||||
} else {
|
||||
if shard < 0 {
|
||||
var err error
|
||||
shard, err = sharding.InferShard()
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
log.Infof("Processing clusters from shard %d", shard)
|
||||
db := db.NewDB(settingsMgr.GetNamespace(), settingsMgr, kubeClient)
|
||||
log.Infof("Using filter function: %s", shardingAlgorithm)
|
||||
distributionFunction := sharding.GetDistributionFunction(db, shardingAlgorithm)
|
||||
clusterFilter = sharding.GetClusterFilter(db, distributionFunction, shard)
|
||||
clusterFilter = sharding.GetClusterFilter(distributionFunction, shard)
|
||||
} else {
|
||||
log.Info("Processing all cluster shards")
|
||||
}
|
||||
|
||||
@@ -40,7 +40,10 @@ import (
|
||||
argosettings "github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
var gitSubmoduleEnabled = env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true)
|
||||
// TODO: load this using Cobra.
|
||||
func getSubmoduleEnabled() bool {
|
||||
return env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true)
|
||||
}
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
@@ -63,9 +66,6 @@ func NewCommand() *cobra.Command {
|
||||
maxConcurrentReconciliations int
|
||||
scmRootCAPath string
|
||||
allowedScmProviders []string
|
||||
globalPreservedAnnotations []string
|
||||
globalPreservedLabels []string
|
||||
enableScmProviders bool
|
||||
)
|
||||
scheme := runtime.NewScheme()
|
||||
_ = clientgoscheme.AddToScheme(scheme)
|
||||
@@ -108,8 +108,8 @@ func NewCommand() *cobra.Command {
|
||||
// If the applicationset-namespaces contains only one namespace it corresponds to the current namespace
|
||||
if len(applicationSetNamespaces) == 1 {
|
||||
watchedNamespace = (applicationSetNamespaces)[0]
|
||||
} else if enableScmProviders && len(allowedScmProviders) == 0 {
|
||||
log.Error("When enabling applicationset in any namespace using applicationset-namespaces, you must either set --enable-scm-providers=false or specify --allowed-scm-providers")
|
||||
} else if len(allowedScmProviders) == 0 {
|
||||
log.Error("When enabling applicationset in any namespace using applicationset-namespaces, allowed-scm-providers is required")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -156,16 +156,16 @@ func NewCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
repoClientset := apiclient.NewRepoServerClientset(argocdRepoServer, repoServerTimeoutSeconds, tlsConfig)
|
||||
argoCDService, err := services.NewArgoCDService(argoCDDB, gitSubmoduleEnabled, repoClientset, enableNewGitFileGlobbing)
|
||||
argoCDService, err := services.NewArgoCDService(argoCDDB, getSubmoduleEnabled(), repoClientset, enableNewGitFileGlobbing)
|
||||
errors.CheckError(err)
|
||||
|
||||
terminalGenerators := map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
"Clusters": generators.NewClusterGenerator(mgr.GetClient(), ctx, k8sClient, namespace),
|
||||
"Git": generators.NewGitGenerator(argoCDService),
|
||||
"SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders),
|
||||
"SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders),
|
||||
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
|
||||
"PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders),
|
||||
"PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders),
|
||||
"Plugin": generators.NewPluginGenerator(mgr.GetClient(), ctx, k8sClient, namespace),
|
||||
}
|
||||
|
||||
@@ -203,23 +203,20 @@ func NewCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
if err = (&controllers.ApplicationSetReconciler{
|
||||
Generators: topLevelGenerators,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Recorder: mgr.GetEventRecorderFor("applicationset-controller"),
|
||||
Renderer: &utils.Render{},
|
||||
Policy: policyObj,
|
||||
EnablePolicyOverride: enablePolicyOverride,
|
||||
ArgoAppClientset: appSetConfig,
|
||||
KubeClientset: k8sClient,
|
||||
ArgoDB: argoCDDB,
|
||||
ArgoCDNamespace: namespace,
|
||||
ApplicationSetNamespaces: applicationSetNamespaces,
|
||||
EnableProgressiveSyncs: enableProgressiveSyncs,
|
||||
SCMRootCAPath: scmRootCAPath,
|
||||
GlobalPreservedAnnotations: globalPreservedAnnotations,
|
||||
GlobalPreservedLabels: globalPreservedLabels,
|
||||
Cache: mgr.GetCache(),
|
||||
Generators: topLevelGenerators,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Recorder: mgr.GetEventRecorderFor("applicationset-controller"),
|
||||
Renderer: &utils.Render{},
|
||||
Policy: policyObj,
|
||||
EnablePolicyOverride: enablePolicyOverride,
|
||||
ArgoAppClientset: appSetConfig,
|
||||
KubeClientset: k8sClient,
|
||||
ArgoDB: argoCDDB,
|
||||
ArgoCDNamespace: namespace,
|
||||
ApplicationSetNamespaces: applicationSetNamespaces,
|
||||
EnableProgressiveSyncs: enableProgressiveSyncs,
|
||||
SCMRootCAPath: scmRootCAPath,
|
||||
}).SetupWithManager(mgr, enableProgressiveSyncs, maxConcurrentReconciliations); err != nil {
|
||||
log.Error(err, "unable to create controller", "controller", "ApplicationSet")
|
||||
os.Exit(1)
|
||||
@@ -248,8 +245,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().BoolVar(&debugLog, "debug", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG", false), "Print debug logs. Takes precedence over loglevel")
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().StringSliceVar(&allowedScmProviders, "allowed-scm-providers", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS", []string{}, ","), "The list of allowed custom SCM provider API URLs. This restriction does not apply to SCM or PR generators which do not accept a custom API URL. (Default: Empty = all)")
|
||||
command.Flags().BoolVar(&enableScmProviders, "enable-scm-providers", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS", true), "Enable retrieving information from SCM providers, used by the SCM and PR generators (Default: true)")
|
||||
command.Flags().StringSliceVar(&allowedScmProviders, "allowed-scm-providers", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS", []string{}, ","), "The list of allowed scm providers. (Default: Empty = all)")
|
||||
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.")
|
||||
command.Flags().BoolVar(&enableNewGitFileGlobbing, "enable-new-git-file-globbing", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING", false), "Enable new globbing in Git files generator.")
|
||||
@@ -258,8 +254,6 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.")
|
||||
command.Flags().IntVar(&maxConcurrentReconciliations, "concurrent-reconciliations", env.ParseNumFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_CONCURRENT_RECONCILIATIONS", 10, 1, 100), "Max concurrent reconciliations limit for the controller")
|
||||
command.Flags().StringVar(&scmRootCAPath, "scm-root-ca-path", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH", ""), "Provide Root CA Path for self-signed TLS Certificates")
|
||||
command.Flags().StringSliceVar(&globalPreservedAnnotations, "preserved-annotations", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_ANNOTATIONS", []string{}, ","), "Sets global preserved field values for annotations")
|
||||
command.Flags().StringSliceVar(&globalPreservedLabels, "preserved-labels", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_LABELS", []string{}, ","), "Sets global preserved field values for labels")
|
||||
return &command
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ func NewCommand() *cobra.Command {
|
||||
var (
|
||||
configFilePath string
|
||||
otlpAddress string
|
||||
otlpAttrs []string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -56,7 +55,7 @@ func NewCommand() *cobra.Command {
|
||||
if otlpAddress != "" {
|
||||
var closer func()
|
||||
var err error
|
||||
closer, err = traceutil.InitTracer(ctx, "argocd-cmp-server", otlpAddress, otlpAttrs)
|
||||
closer, err = traceutil.InitTracer(ctx, "argocd-cmp-server", otlpAddress)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize tracing: %v", err)
|
||||
}
|
||||
@@ -83,6 +82,5 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().StringVar(&configFilePath, "config-dir-path", common.DefaultPluginConfigFilePath, "Config management plugin configuration file location, Default is '/home/argocd/cmp-server/config/'")
|
||||
command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_CMP_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to")
|
||||
command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_CMP_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)")
|
||||
return &command
|
||||
}
|
||||
|
||||
@@ -75,26 +75,26 @@ func NewCommand() *cobra.Command {
|
||||
|
||||
restConfig, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create REST client config: %w", err)
|
||||
return err
|
||||
}
|
||||
restConfig.UserAgent = fmt.Sprintf("argocd-notifications-controller/%s (%s)", vers.Version, vers.Platform)
|
||||
dynamicClient, err := dynamic.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create dynamic client: %w", err)
|
||||
return err
|
||||
}
|
||||
k8sClient, err := kubernetes.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Kubernetes client: %w", err)
|
||||
return err
|
||||
}
|
||||
if namespace == "" {
|
||||
namespace, _, err = clientConfig.Namespace()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine controller's host namespace: %w", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
level, err := log.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse log level: %w", err)
|
||||
return err
|
||||
}
|
||||
log.SetLevel(level)
|
||||
|
||||
@@ -106,7 +106,7 @@ func NewCommand() *cobra.Command {
|
||||
log.SetFormatter(&log.TextFormatter{ForceColors: true})
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown log format '%s'", logFormat)
|
||||
return fmt.Errorf("Unknown log format '%s'", logFormat)
|
||||
}
|
||||
|
||||
tlsConfig := apiclient.TLSConfiguration{
|
||||
@@ -119,14 +119,14 @@ func NewCommand() *cobra.Command {
|
||||
fmt.Sprintf("%s/reposerver/tls/ca.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load repo-server certificate pool: %w", err)
|
||||
return err
|
||||
}
|
||||
tlsConfig.Certificates = pool
|
||||
}
|
||||
repoClientset := apiclient.NewRepoServerClientset(argocdRepoServer, 5, tlsConfig)
|
||||
argocdService, err := service.NewArgoCDService(k8sClient, namespace, repoClientset)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize Argo CD service: %w", err)
|
||||
return err
|
||||
}
|
||||
defer argocdService.Close()
|
||||
|
||||
@@ -142,7 +142,7 @@ func NewCommand() *cobra.Command {
|
||||
ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, applicationNamespaces, appLabelSelector, registry, secretName, configMapName)
|
||||
err = ctrl.Init(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize controller: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
go ctrl.Run(ctx, processorsCount)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/pkg/stats"
|
||||
@@ -35,16 +36,33 @@ import (
|
||||
|
||||
const (
|
||||
// CLIName is the name of the CLI
|
||||
cliName = "argocd-repo-server"
|
||||
cliName = "argocd-repo-server"
|
||||
gnuPGSourcePath = "/app/config/gpg/source"
|
||||
|
||||
defaultPauseGenerationAfterFailedGenerationAttempts = 3
|
||||
defaultPauseGenerationOnFailureForMinutes = 60
|
||||
defaultPauseGenerationOnFailureForRequests = 0
|
||||
)
|
||||
|
||||
var (
|
||||
gnuPGSourcePath = env.StringFromEnv(common.EnvGPGDataPath, "/app/config/gpg/source")
|
||||
pauseGenerationAfterFailedGenerationAttempts = env.ParseNumFromEnv(common.EnvPauseGenerationAfterFailedAttempts, 3, 0, math.MaxInt32)
|
||||
pauseGenerationOnFailureForMinutes = env.ParseNumFromEnv(common.EnvPauseGenerationMinutes, 60, 0, math.MaxInt32)
|
||||
pauseGenerationOnFailureForRequests = env.ParseNumFromEnv(common.EnvPauseGenerationRequests, 0, 0, math.MaxInt32)
|
||||
gitSubmoduleEnabled = env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true)
|
||||
)
|
||||
func getGnuPGSourcePath() string {
|
||||
return env.StringFromEnv(common.EnvGPGDataPath, gnuPGSourcePath)
|
||||
}
|
||||
|
||||
func getPauseGenerationAfterFailedGenerationAttempts() int {
|
||||
return env.ParseNumFromEnv(common.EnvPauseGenerationAfterFailedAttempts, defaultPauseGenerationAfterFailedGenerationAttempts, 0, math.MaxInt32)
|
||||
}
|
||||
|
||||
func getPauseGenerationOnFailureForMinutes() int {
|
||||
return env.ParseNumFromEnv(common.EnvPauseGenerationMinutes, defaultPauseGenerationOnFailureForMinutes, 0, math.MaxInt32)
|
||||
}
|
||||
|
||||
func getPauseGenerationOnFailureForRequests() int {
|
||||
return env.ParseNumFromEnv(common.EnvPauseGenerationRequests, defaultPauseGenerationOnFailureForRequests, 0, math.MaxInt32)
|
||||
}
|
||||
|
||||
func getSubmoduleEnabled() bool {
|
||||
return env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true)
|
||||
}
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
@@ -54,7 +72,6 @@ func NewCommand() *cobra.Command {
|
||||
metricsPort int
|
||||
metricsHost string
|
||||
otlpAddress string
|
||||
otlpAttrs []string
|
||||
cacheSrc func() (*reposervercache.Cache, error)
|
||||
tlsConfigCustomizer tls.ConfigCustomizer
|
||||
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
|
||||
@@ -113,10 +130,10 @@ func NewCommand() *cobra.Command {
|
||||
cacheutil.CollectMetrics(redisClient, metricsServer)
|
||||
server, err := reposerver.NewServer(metricsServer, cache, tlsConfigCustomizer, repository.RepoServerInitConstants{
|
||||
ParallelismLimit: parallelismLimit,
|
||||
PauseGenerationAfterFailedGenerationAttempts: pauseGenerationAfterFailedGenerationAttempts,
|
||||
PauseGenerationOnFailureForMinutes: pauseGenerationOnFailureForMinutes,
|
||||
PauseGenerationOnFailureForRequests: pauseGenerationOnFailureForRequests,
|
||||
SubmoduleEnabled: gitSubmoduleEnabled,
|
||||
PauseGenerationAfterFailedGenerationAttempts: getPauseGenerationAfterFailedGenerationAttempts(),
|
||||
PauseGenerationOnFailureForMinutes: getPauseGenerationOnFailureForMinutes(),
|
||||
PauseGenerationOnFailureForRequests: getPauseGenerationOnFailureForRequests(),
|
||||
SubmoduleEnabled: getSubmoduleEnabled(),
|
||||
MaxCombinedDirectoryManifestsSize: maxCombinedDirectoryManifestsQuantity,
|
||||
CMPTarExcludedGlobs: cmpTarExcludedGlobs,
|
||||
AllowOutOfBoundsSymlinks: allowOutOfBoundsSymlinks,
|
||||
@@ -129,7 +146,7 @@ func NewCommand() *cobra.Command {
|
||||
if otlpAddress != "" {
|
||||
var closer func()
|
||||
var err error
|
||||
closer, err = traceutil.InitTracer(ctx, "argocd-repo-server", otlpAddress, otlpAttrs)
|
||||
closer, err = traceutil.InitTracer(ctx, "argocd-repo-server", otlpAddress)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize tracing: %v", err)
|
||||
}
|
||||
@@ -171,12 +188,12 @@ func NewCommand() *cobra.Command {
|
||||
err = gpg.InitializeGnuPG()
|
||||
errors.CheckError(err)
|
||||
|
||||
log.Infof("Populating GnuPG keyring with keys from %s", gnuPGSourcePath)
|
||||
added, removed, err := gpg.SyncKeyRingFromDirectory(gnuPGSourcePath)
|
||||
log.Infof("Populating GnuPG keyring with keys from %s", getGnuPGSourcePath())
|
||||
added, removed, err := gpg.SyncKeyRingFromDirectory(getGnuPGSourcePath())
|
||||
errors.CheckError(err)
|
||||
log.Infof("Loaded %d (and removed %d) keys from keyring", len(added), len(removed))
|
||||
|
||||
go func() { errors.CheckError(reposerver.StartGPGWatcher(gnuPGSourcePath)) }()
|
||||
go func() { errors.CheckError(reposerver.StartGPGWatcher(getGnuPGSourcePath())) }()
|
||||
}
|
||||
|
||||
log.Infof("argocd-repo-server is listening on %s", listener.Addr())
|
||||
@@ -188,6 +205,9 @@ func NewCommand() *cobra.Command {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
if cmdutil.LogFormat == "" {
|
||||
cmdutil.LogFormat = os.Getenv("ARGOCD_REPO_SERVER_LOGLEVEL")
|
||||
}
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_REPO_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_REPO_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().Int64Var(¶llelismLimit, "parallelismlimit", int64(env.ParseNumFromEnv("ARGOCD_REPO_SERVER_PARALLELISM_LIMIT", 0, 0, math.MaxInt32)), "Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit.")
|
||||
@@ -196,7 +216,6 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&metricsHost, "metrics-address", env.StringFromEnv("ARGOCD_REPO_SERVER_METRICS_LISTEN_ADDRESS", common.DefaultAddressRepoServerMetrics), "Listen on given address for metrics")
|
||||
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortRepoServerMetrics, "Start metrics server on given port")
|
||||
command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_REPO_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to")
|
||||
command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_REPO_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)")
|
||||
command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_TLS", false), "Disable TLS on the gRPC endpoint")
|
||||
command.Flags().StringVar(&maxCombinedDirectoryManifestsSize, "max-combined-directory-manifests-size", env.StringFromEnv("ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE", "10M"), "Max combined size of manifest files in a directory-type Application")
|
||||
command.Flags().StringArrayVar(&cmpTarExcludedGlobs, "plugin-tar-exclude", env.StringsFromEnv("ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS", []string{}, ";"), "Globs to filter when sending tarballs to plugins.")
|
||||
|
||||
@@ -35,10 +35,15 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
failureRetryCount = env.ParseNumFromEnv(failureRetryCountEnv, 0, 0, 10)
|
||||
failureRetryPeriodMilliSeconds = env.ParseNumFromEnv(failureRetryPeriodMilliSecondsEnv, 100, 0, 1000)
|
||||
failureRetryCount = 0
|
||||
failureRetryPeriodMilliSeconds = 100
|
||||
)
|
||||
|
||||
func init() {
|
||||
failureRetryCount = env.ParseNumFromEnv(failureRetryCountEnv, failureRetryCount, 0, 10)
|
||||
failureRetryPeriodMilliSeconds = env.ParseNumFromEnv(failureRetryPeriodMilliSecondsEnv, failureRetryPeriodMilliSeconds, 0, 1000)
|
||||
}
|
||||
|
||||
// NewCommand returns a new instance of an argocd command
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
@@ -49,7 +54,6 @@ func NewCommand() *cobra.Command {
|
||||
metricsHost string
|
||||
metricsPort int
|
||||
otlpAddress string
|
||||
otlpAttrs []string
|
||||
glogLevel int
|
||||
clientConfig clientcmd.ClientConfig
|
||||
repoServerTimeoutSeconds int
|
||||
@@ -199,7 +203,7 @@ func NewCommand() *cobra.Command {
|
||||
var closer func()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
if otlpAddress != "" {
|
||||
closer, err = traceutil.InitTracer(ctx, "argocd-server", otlpAddress, otlpAttrs)
|
||||
closer, err = traceutil.InitTracer(ctx, "argocd-server", otlpAddress)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize tracing: %v", err)
|
||||
}
|
||||
@@ -231,7 +235,6 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&metricsHost, env.StringFromEnv("ARGOCD_SERVER_METRICS_LISTEN_ADDRESS", "metrics-address"), common.DefaultAddressAPIServerMetrics, "Listen for metrics on given address")
|
||||
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port")
|
||||
command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to")
|
||||
command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)")
|
||||
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.")
|
||||
command.Flags().StringVar(&frameOptions, "x-frame-options", env.StringFromEnv("ARGOCD_SERVER_X_FRAME_OPTIONS", "sameorigin"), "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".")
|
||||
command.Flags().StringVar(&contentSecurityPolicy, "content-security-policy", env.StringFromEnv("ARGOCD_SERVER_CONTENT_SECURITY_POLICY", "frame-ancestors 'self';"), "Set Content-Security-Policy header in HTTP responses to `value`. To disable, set to \"\".")
|
||||
|
||||
@@ -26,26 +26,12 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
sessionutil "github.com/argoproj/argo-cd/v2/util/session"
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
)
|
||||
|
||||
func NewAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "account",
|
||||
Short: "Manage account settings",
|
||||
Example: templates.Examples(`
|
||||
# List accounts
|
||||
argocd account list
|
||||
|
||||
# Update the current user's password
|
||||
argocd account update-password
|
||||
|
||||
# Can I sync any app?
|
||||
argocd account can-i sync applications '*'
|
||||
|
||||
# Get User information
|
||||
argocd account get-user-info
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
@@ -144,9 +130,9 @@ has appropriate RBAC permissions to change other accounts.
|
||||
},
|
||||
}
|
||||
|
||||
command.Flags().StringVar(¤tPassword, "current-password", "", "Password of the currently logged on user")
|
||||
command.Flags().StringVar(&newPassword, "new-password", "", "New password you want to update to")
|
||||
command.Flags().StringVar(&account, "account", "", "An account name that should be updated. Defaults to current user account")
|
||||
command.Flags().StringVar(¤tPassword, "current-password", "", "password of the currently logged on user")
|
||||
command.Flags().StringVar(&newPassword, "new-password", "", "new password you want to update to")
|
||||
command.Flags().StringVar(&account, "account", "", "an account name that should be updated. Defaults to current user account")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -157,13 +143,6 @@ func NewAccountGetUserInfoCommand(clientOpts *argocdclient.ClientOptions) *cobra
|
||||
var command = &cobra.Command{
|
||||
Use: "get-user-info",
|
||||
Short: "Get user info",
|
||||
Example: templates.Examples(`
|
||||
# Get User information for the currently logged-in user (see 'argocd login')
|
||||
argocd account get-user-info
|
||||
|
||||
# Get User information in yaml format
|
||||
argocd account get-user-info -o yaml
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
|
||||
@@ -36,7 +35,7 @@ var (
|
||||
)
|
||||
|
||||
// NewAdminCommand returns a new instance of an argocd command
|
||||
func NewAdminCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
func NewAdminCommand() *cobra.Command {
|
||||
var (
|
||||
pathOpts = clientcmd.NewDefaultPathOptions()
|
||||
)
|
||||
@@ -50,10 +49,10 @@ func NewAdminCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(NewClusterCommand(clientOpts, pathOpts))
|
||||
command.AddCommand(NewClusterCommand(pathOpts))
|
||||
command.AddCommand(NewProjectsCommand())
|
||||
command.AddCommand(NewSettingsCommand())
|
||||
command.AddCommand(NewAppCommand(clientOpts))
|
||||
command.AddCommand(NewAppCommand())
|
||||
command.AddCommand(NewRepoCommand())
|
||||
command.AddCommand(NewImportCommand())
|
||||
command.AddCommand(NewExportCommand())
|
||||
|
||||
@@ -20,15 +20,13 @@ import (
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/controller"
|
||||
"github.com/argoproj/argo-cd/v2/controller/cache"
|
||||
"github.com/argoproj/argo-cd/v2/controller/metrics"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
appinformers "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
|
||||
reposerverclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
@@ -41,27 +39,17 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
func NewAppCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
func NewAppCommand() *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "app",
|
||||
Short: "Manage applications configuration",
|
||||
Example: `
|
||||
# Compare results of two reconciliations and print diff
|
||||
argocd admin app diff-reconcile-results APPNAME [flags]
|
||||
|
||||
# Generate declarative config for an application
|
||||
argocd admin app generate-spec APPNAME
|
||||
|
||||
# Reconcile all applications and store reconciliation summary in the specified file
|
||||
argocd admin app get-reconcile-results APPNAME
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(NewGenAppSpecCommand())
|
||||
command.AddCommand(NewReconcileCommand(clientOpts))
|
||||
command.AddCommand(NewReconcileCommand())
|
||||
command.AddCommand(NewDiffReconcileResults())
|
||||
return command
|
||||
}
|
||||
@@ -205,14 +193,14 @@ func diffReconcileResults(res1 reconcileResults, res2 reconcileResults) error {
|
||||
for k, v := range resMap1 {
|
||||
firstUn, err := toUnstructured(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error converting first resource to unstructured: %w", err)
|
||||
return err
|
||||
}
|
||||
var secondUn *unstructured.Unstructured
|
||||
second, ok := resMap2[k]
|
||||
if ok {
|
||||
secondUn, err = toUnstructured(second)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error converting second resource to unstructured: %w", err)
|
||||
return err
|
||||
}
|
||||
delete(resMap2, k)
|
||||
}
|
||||
@@ -236,7 +224,7 @@ func diffReconcileResults(res1 reconcileResults, res2 reconcileResults) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
func NewReconcileCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
selector string
|
||||
@@ -271,12 +259,11 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
if repoServerAddress == "" {
|
||||
printLine("Repo server is not provided, trying to port-forward to argocd-repo-server pod.")
|
||||
overrides := clientcmd.ConfigOverrides{}
|
||||
repoServerPodLabelSelector := common.LabelKeyAppName + "=" + clientOpts.RepoServerName
|
||||
repoServerPort, err := kubeutil.PortForward(8081, namespace, &overrides, repoServerPodLabelSelector)
|
||||
repoServerPort, err := kubeutil.PortForward(8081, namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server")
|
||||
errors.CheckError(err)
|
||||
repoServerAddress = fmt.Sprintf("localhost:%d", repoServerPort)
|
||||
}
|
||||
repoServerClient := reposerverclient.NewRepoServerClientset(repoServerAddress, 60, reposerverclient.TLSConfiguration{DisableTLS: false, StrictValidation: false})
|
||||
repoServerClient := argocdclient.NewRepoServerClientset(repoServerAddress, 60, argocdclient.TLSConfiguration{DisableTLS: false, StrictValidation: false})
|
||||
|
||||
appClientset := appclientset.NewForConfigOrDie(cfg)
|
||||
kubeClientset := kubernetes.NewForConfigOrDie(cfg)
|
||||
@@ -341,7 +328,7 @@ func reconcileApplications(
|
||||
kubeClientset kubernetes.Interface,
|
||||
appClientset appclientset.Interface,
|
||||
namespace string,
|
||||
repoServerClient reposerverclient.Clientset,
|
||||
repoServerClient argocdclient.Clientset,
|
||||
selector string,
|
||||
createLiveStateCache func(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache,
|
||||
) ([]appReconcileResult, error) {
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/controller/sharding"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
@@ -40,7 +39,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/text/label"
|
||||
)
|
||||
|
||||
func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientcmd.PathOptions) *cobra.Command {
|
||||
func NewClusterCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "cluster",
|
||||
Short: "Manage clusters configuration",
|
||||
@@ -51,8 +50,8 @@ func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientc
|
||||
|
||||
command.AddCommand(NewClusterConfig())
|
||||
command.AddCommand(NewGenClusterConfigCommand(pathOpts))
|
||||
command.AddCommand(NewClusterStatsCommand(clientOpts))
|
||||
command.AddCommand(NewClusterShardsCommand(clientOpts))
|
||||
command.AddCommand(NewClusterStatsCommand())
|
||||
command.AddCommand(NewClusterShardsCommand())
|
||||
namespacesCommand := NewClusterNamespacesCommand()
|
||||
namespacesCommand.AddCommand(NewClusterEnableNamespacedMode())
|
||||
namespacesCommand.AddCommand(NewClusterDisableNamespacedMode())
|
||||
@@ -69,7 +68,7 @@ type ClusterWithInfo struct {
|
||||
Namespaces []string
|
||||
}
|
||||
|
||||
func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int, redisName string, redisHaProxyName string) ([]ClusterWithInfo, error) {
|
||||
func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int) ([]ClusterWithInfo, error) {
|
||||
settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace)
|
||||
|
||||
argoDB := db.NewDB(namespace, settingsMgr, kubeClient)
|
||||
@@ -80,10 +79,8 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
|
||||
var cache *appstatecache.Cache
|
||||
if portForwardRedis {
|
||||
overrides := clientcmd.ConfigOverrides{}
|
||||
redisHaProxyPodLabelSelector := common.LabelKeyAppName + "=" + redisHaProxyName
|
||||
redisPodLabelSelector := common.LabelKeyAppName + "=" + redisName
|
||||
port, err := kubeutil.PortForward(6379, namespace, &overrides,
|
||||
redisHaProxyPodLabelSelector, redisPodLabelSelector)
|
||||
"app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -124,7 +121,7 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
|
||||
if replicas > 0 {
|
||||
distributionFunction := sharding.GetDistributionFunction(argoDB, common.DefaultShardingAlgorithm)
|
||||
distributionFunction(&cluster)
|
||||
cluster.Shard = pointer.Int64(int64(clusterShard))
|
||||
cluster.Shard = pointer.Int64Ptr(int64(clusterShard))
|
||||
log.Infof("Cluster with uid: %s will be processed by shard %d", cluster.ID, clusterShard)
|
||||
}
|
||||
|
||||
@@ -149,17 +146,16 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
|
||||
return clusters, nil
|
||||
}
|
||||
|
||||
func getControllerReplicas(ctx context.Context, kubeClient *kubernetes.Clientset, namespace string, appControllerName string) (int, error) {
|
||||
appControllerPodLabelSelector := common.LabelKeyAppName + "=" + appControllerName
|
||||
func getControllerReplicas(ctx context.Context, kubeClient *kubernetes.Clientset, namespace string) (int, error) {
|
||||
controllerPods, err := kubeClient.CoreV1().Pods(namespace).List(ctx, v1.ListOptions{
|
||||
LabelSelector: appControllerPodLabelSelector})
|
||||
LabelSelector: "app.kubernetes.io/name=argocd-application-controller"})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(controllerPods.Items), nil
|
||||
}
|
||||
|
||||
func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
func NewClusterShardsCommand() *cobra.Command {
|
||||
var (
|
||||
shard int
|
||||
replicas int
|
||||
@@ -183,14 +179,14 @@ func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
appClient := versioned.NewForConfigOrDie(clientCfg)
|
||||
|
||||
if replicas == 0 {
|
||||
replicas, err = getControllerReplicas(ctx, kubeClient, namespace, clientOpts.AppControllerName)
|
||||
replicas, err = getControllerReplicas(ctx, kubeClient, namespace)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
if replicas == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName)
|
||||
clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard)
|
||||
errors.CheckError(err)
|
||||
if len(clusters) == 0 {
|
||||
return
|
||||
@@ -437,7 +433,7 @@ func NewClusterDisableNamespacedMode() *cobra.Command {
|
||||
return &command
|
||||
}
|
||||
|
||||
func NewClusterStatsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
func NewClusterStatsCommand() *cobra.Command {
|
||||
var (
|
||||
shard int
|
||||
replicas int
|
||||
@@ -461,10 +457,10 @@ func NewClusterStatsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
|
||||
kubeClient := kubernetes.NewForConfigOrDie(clientCfg)
|
||||
appClient := versioned.NewForConfigOrDie(clientCfg)
|
||||
if replicas == 0 {
|
||||
replicas, err = getControllerReplicas(ctx, kubeClient, namespace, clientOpts.AppControllerName)
|
||||
replicas, err = getControllerReplicas(ctx, kubeClient, namespace)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName)
|
||||
clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard)
|
||||
errors.CheckError(err)
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
|
||||
@@ -28,19 +28,10 @@ func NewDashboardCommand() *cobra.Command {
|
||||
|
||||
compression, err := cache.CompressionTypeFromString(compressionStr)
|
||||
errors.CheckError(err)
|
||||
errors.CheckError(headless.MaybeStartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression))
|
||||
errors.CheckError(headless.StartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression))
|
||||
println(fmt.Sprintf("Argo CD UI is available at http://%s:%d", address, port))
|
||||
<-ctx.Done()
|
||||
},
|
||||
Example: `# Start the Argo CD Web UI locally on the default port and address
|
||||
$ argocd admin dashboard
|
||||
|
||||
# Start the Argo CD Web UI locally on a custom port and address
|
||||
$ argocd admin dashboard --port 8080 --address 127.0.0.1
|
||||
|
||||
# Start the Argo CD Web UI with GZip compression
|
||||
$ argocd admin dashboard --redis-compress gzip
|
||||
`,
|
||||
}
|
||||
initialize.InitCommand(cmd)
|
||||
cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port")
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -48,17 +47,6 @@ func NewGenProjectSpecCommand() *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "generate-spec PROJECT",
|
||||
Short: "Generate declarative config for a project",
|
||||
Example: templates.Examples(`
|
||||
# Generate a YAML configuration for a project named "myproject"
|
||||
argocd admin projects generate-spec myproject
|
||||
|
||||
# Generate a JSON configuration for a project named "anotherproject" and specify an output file
|
||||
argocd admin projects generate-spec anotherproject --output json --file config.json
|
||||
|
||||
# Generate a YAML configuration for a project named "someproject" and write it back to the input file
|
||||
argocd admin projects generate-spec someproject --inline
|
||||
`),
|
||||
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
proj, err := cmdutil.ConstructAppProj(fileURL, args, opts, c)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -41,8 +41,6 @@ func NewProjectAllowListGenCommand() *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "generate-allow-list CLUSTERROLE_PATH PROJ_NAME",
|
||||
Short: "Generates project allow list from the specified clusterRole file",
|
||||
Example: `# Generates project allow list from the specified clusterRole file
|
||||
argocd admin proj generate-allow-list /path/to/clusterrole.yaml my-project`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
c.HelpFunc()(c, args)
|
||||
|
||||
@@ -568,7 +568,7 @@ argocd admin settings resource-overrides action list /tmp/deploy.yaml --argocd-c
|
||||
})
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "NAME\tDISABLED\n")
|
||||
_, _ = fmt.Fprintf(w, "NAME\tENABLED\n")
|
||||
for _, action := range availableActions {
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", action.Name, strconv.FormatBool(action.Disabled))
|
||||
}
|
||||
|
||||
@@ -189,6 +189,7 @@ argocd admin settings rbac can someuser create application 'default/app' --defau
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
command.Flags().StringVar(&policyFile, "policy-file", "", "path to the policy file to use")
|
||||
command.Flags().StringVar(&defaultRole, "default-role", "", "name of the default role to use")
|
||||
@@ -201,55 +202,24 @@ argocd admin settings rbac can someuser create application 'default/app' --defau
|
||||
// NewRBACValidateCommand returns a new rbac validate command
|
||||
func NewRBACValidateCommand() *cobra.Command {
|
||||
var (
|
||||
policyFile string
|
||||
namespace string
|
||||
clientConfig clientcmd.ClientConfig
|
||||
policyFile string
|
||||
)
|
||||
|
||||
var command = &cobra.Command{
|
||||
Use: "validate [--policy-file POLICYFILE] [--namespace NAMESPACE]",
|
||||
Use: "validate --policy-file=POLICYFILE",
|
||||
Short: "Validate RBAC policy",
|
||||
Long: `
|
||||
Validates an RBAC policy for being syntactically correct. The policy must be
|
||||
a local file or a K8s ConfigMap in the provided namespace, and in either CSV or K8s ConfigMap format.
|
||||
`,
|
||||
Example: `
|
||||
# Check whether a given policy file is valid using a local policy.csv file.
|
||||
argocd admin settings rbac validate --policy-file policy.csv
|
||||
|
||||
# Policy file can also be K8s config map with data keys like argocd-rbac-cm,
|
||||
# i.e. 'policy.csv' and (optionally) 'policy.default'
|
||||
argocd admin settings rbac validate --policy-file argocd-rbac-cm.yaml
|
||||
|
||||
# If --policy-file is not given, and instead --namespace is giventhe ConfigMap 'argocd-rbac-cm'
|
||||
# from K8s is used.
|
||||
argocd admin settings rbac validate --namespace argocd
|
||||
|
||||
# Either --policy-file or --namespace must be given.
|
||||
a local file, and in either CSV or K8s ConfigMap format.
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
if len(args) > 0 {
|
||||
if policyFile == "" {
|
||||
c.HelpFunc()(c, args)
|
||||
log.Fatalf("too many arguments")
|
||||
log.Fatalf("Please specify policy to validate using --policy-file")
|
||||
}
|
||||
|
||||
if (namespace == "" && policyFile == "") || (namespace != "" && policyFile != "") {
|
||||
c.HelpFunc()(c, args)
|
||||
log.Fatalf("please provide exactly one of --policy-file or --namespace")
|
||||
}
|
||||
|
||||
restConfig, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("could not get config to create k8s client: %v", err)
|
||||
}
|
||||
realClientset, err := kubernetes.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("could not create k8s client: %v", err)
|
||||
}
|
||||
|
||||
userPolicy, _, _ := getPolicy(ctx, policyFile, realClientset, namespace)
|
||||
userPolicy, _, _ := getPolicy(ctx, policyFile, nil, "")
|
||||
if userPolicy != "" {
|
||||
if err := rbac.ValidatePolicy(userPolicy); err == nil {
|
||||
fmt.Printf("Policy is valid.\n")
|
||||
@@ -258,15 +228,11 @@ argocd admin settings rbac validate --namespace argocd
|
||||
fmt.Printf("Policy is invalid: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
log.Fatalf("Policy is empty or could not be loaded.")
|
||||
}
|
||||
},
|
||||
}
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
command.Flags().StringVar(&policyFile, "policy-file", "", "path to the policy file to use")
|
||||
command.Flags().StringVar(&namespace, "namespace", "", "namespace to get argo rbac configmap from")
|
||||
|
||||
command.Flags().StringVar(&policyFile, "policy-file", "", "path to the policy file to use")
|
||||
return command
|
||||
}
|
||||
|
||||
|
||||
@@ -5,42 +5,15 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/assets"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/assets"
|
||||
)
|
||||
|
||||
type FakeClientConfig struct {
|
||||
clientConfig clientcmd.ClientConfig
|
||||
}
|
||||
|
||||
func NewFakeClientConfig(clientConfig clientcmd.ClientConfig) *FakeClientConfig {
|
||||
return &FakeClientConfig{clientConfig: clientConfig}
|
||||
}
|
||||
|
||||
func (f *FakeClientConfig) RawConfig() (clientcmdapi.Config, error) {
|
||||
config, err := f.clientConfig.RawConfig()
|
||||
return config, err
|
||||
}
|
||||
|
||||
func (f *FakeClientConfig) ClientConfig() (*restclient.Config, error) {
|
||||
return f.clientConfig.ClientConfig()
|
||||
}
|
||||
|
||||
func (f *FakeClientConfig) Namespace() (string, bool, error) {
|
||||
return f.clientConfig.Namespace()
|
||||
}
|
||||
|
||||
func (f *FakeClientConfig) ConfigAccess() clientcmd.ConfigAccess {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_isValidRBACAction(t *testing.T) {
|
||||
for k := range validRBACActions {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
@@ -227,19 +200,3 @@ p, role:, certificates, get, .*, allow`
|
||||
require.True(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewRBACCanCommand(t *testing.T) {
|
||||
command := NewRBACCanCommand()
|
||||
|
||||
require.NotNil(t, command)
|
||||
assert.Equal(t, "can", command.Name())
|
||||
assert.Equal(t, "Check RBAC permissions for a role or subject", command.Short)
|
||||
}
|
||||
|
||||
func TestNewRBACValidateCommand(t *testing.T) {
|
||||
command := NewRBACValidateCommand()
|
||||
|
||||
require.NotNil(t, command)
|
||||
assert.Equal(t, "validate", command.Name())
|
||||
assert.Equal(t, "Validate RBAC policy", command.Short)
|
||||
}
|
||||
|
||||
@@ -393,7 +393,7 @@ func TestResourceOverrideAction(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, out, `NAME DISABLED
|
||||
assert.Contains(t, out, `NAME ENABLED
|
||||
restart false
|
||||
resume false
|
||||
`)
|
||||
@@ -440,7 +440,7 @@ resume false
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, out, "NAME")
|
||||
assert.Contains(t, out, "DISABLED")
|
||||
assert.Contains(t, out, "ENABLED")
|
||||
assert.Contains(t, out, "create-a-job")
|
||||
assert.Contains(t, out, "false")
|
||||
})
|
||||
|
||||
@@ -50,7 +50,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/grpc"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/manifeststream"
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
"github.com/argoproj/argo-cd/v2/util/text/label"
|
||||
)
|
||||
|
||||
@@ -260,52 +259,6 @@ func hasAppChanged(appReq, appRes *argoappv1.Application, upsert bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func parentChildDetails(appIf application.ApplicationServiceClient, ctx context.Context, appName string, appNs string) (map[string]argoappv1.ResourceNode, map[string][]string, map[string]struct{}) {
|
||||
|
||||
mapUidToNode := make(map[string]argoappv1.ResourceNode)
|
||||
mapParentToChild := make(map[string][]string)
|
||||
parentNode := make(map[string]struct{})
|
||||
|
||||
resourceTree, err := appIf.ResourceTree(ctx, &application.ResourcesQuery{Name: &appName, AppNamespace: &appNs, ApplicationName: &appName})
|
||||
errors.CheckError(err)
|
||||
|
||||
for _, node := range resourceTree.Nodes {
|
||||
mapUidToNode[node.UID] = node
|
||||
|
||||
if len(node.ParentRefs) > 0 {
|
||||
_, ok := mapParentToChild[node.ParentRefs[0].UID]
|
||||
if !ok {
|
||||
var temp []string
|
||||
mapParentToChild[node.ParentRefs[0].UID] = temp
|
||||
}
|
||||
mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID)
|
||||
} else {
|
||||
parentNode[node.UID] = struct{}{}
|
||||
}
|
||||
}
|
||||
return mapUidToNode, mapParentToChild, parentNode
|
||||
}
|
||||
|
||||
func printHeader(acdClient argocdclient.Client, app *argoappv1.Application, ctx context.Context, windows *argoappv1.SyncWindows, showOperation bool, showParams bool) {
|
||||
aURL := appURL(ctx, acdClient, app.Name)
|
||||
printAppSummaryTable(app, aURL, windows)
|
||||
|
||||
if len(app.Status.Conditions) > 0 {
|
||||
fmt.Println()
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
printAppConditions(w, app)
|
||||
_ = w.Flush()
|
||||
fmt.Println()
|
||||
}
|
||||
if showOperation && app.Status.OperationState != nil {
|
||||
fmt.Println()
|
||||
printOperationResult(app.Status.OperationState)
|
||||
}
|
||||
if showParams {
|
||||
printParams(app)
|
||||
}
|
||||
}
|
||||
|
||||
// NewApplicationGetCommand returns a new instance of an `argocd app get` command
|
||||
func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
@@ -318,37 +271,9 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
var command = &cobra.Command{
|
||||
Use: "get APPNAME",
|
||||
Short: "Get application details",
|
||||
Example: templates.Examples(`
|
||||
# Get basic details about the application "my-app" in wide format
|
||||
argocd app get my-app -o wide
|
||||
|
||||
# Get detailed information about the application "my-app" in YAML format
|
||||
argocd app get my-app -o yaml
|
||||
|
||||
# Get details of the application "my-app" in JSON format
|
||||
argocd get my-app -o json
|
||||
|
||||
# Get application details and include information about the current operation
|
||||
argocd app get my-app --show-operation
|
||||
|
||||
# Show application parameters and overrides
|
||||
argocd app get my-app --show-params
|
||||
|
||||
# Refresh application data when retrieving
|
||||
argocd app get my-app --refresh
|
||||
|
||||
# Perform a hard refresh, including refreshing application data and target manifests cache
|
||||
argocd app get my-app --hard-refresh
|
||||
|
||||
# Get application details and display them in a tree format
|
||||
argocd app get my-app --output tree
|
||||
|
||||
# Get application details and display them in a detailed tree format
|
||||
argocd app get my-app --output tree=detailed
|
||||
`),
|
||||
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
@@ -358,13 +283,11 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
defer argoio.Close(conn)
|
||||
|
||||
appName, appNs := argo.ParseFromQualifiedName(args[0], "")
|
||||
|
||||
app, err := appIf.Get(ctx, &application.ApplicationQuery{
|
||||
Name: &appName,
|
||||
Refresh: getRefreshType(refresh, hardRefresh),
|
||||
AppNamespace: &appNs,
|
||||
})
|
||||
|
||||
errors.CheckError(err)
|
||||
|
||||
pConn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie()
|
||||
@@ -379,33 +302,35 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
err := PrintResource(app, output)
|
||||
errors.CheckError(err)
|
||||
case "wide", "":
|
||||
printHeader(acdClient, app, ctx, windows, showOperation, showParams)
|
||||
aURL := appURL(ctx, acdClient, app.Name)
|
||||
printAppSummaryTable(app, aURL, windows)
|
||||
|
||||
if len(app.Status.Conditions) > 0 {
|
||||
fmt.Println()
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
printAppConditions(w, app)
|
||||
_ = w.Flush()
|
||||
fmt.Println()
|
||||
}
|
||||
if showOperation && app.Status.OperationState != nil {
|
||||
fmt.Println()
|
||||
printOperationResult(app.Status.OperationState)
|
||||
}
|
||||
if showParams {
|
||||
printParams(app)
|
||||
}
|
||||
if len(app.Status.Resources) > 0 {
|
||||
fmt.Println()
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
printAppResources(w, app)
|
||||
_ = w.Flush()
|
||||
}
|
||||
case "tree":
|
||||
printHeader(acdClient, app, ctx, windows, showOperation, showParams)
|
||||
mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs)
|
||||
if len(mapUidToNode) > 0 {
|
||||
fmt.Println()
|
||||
printTreeView(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState)
|
||||
}
|
||||
case "tree=detailed":
|
||||
printHeader(acdClient, app, ctx, windows, showOperation, showParams)
|
||||
mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs)
|
||||
if len(mapUidToNode) > 0 {
|
||||
fmt.Println()
|
||||
printTreeViewDetailed(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState)
|
||||
}
|
||||
default:
|
||||
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
|
||||
}
|
||||
},
|
||||
}
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree")
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide")
|
||||
command.Flags().BoolVar(&showOperation, "show-operation", false, "Show application operation")
|
||||
command.Flags().BoolVar(&showParams, "show-params", false, "Show application parameters and overrides")
|
||||
command.Flags().BoolVar(&refresh, "refresh", false, "Refresh application data when retrieving")
|
||||
@@ -431,44 +356,6 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
var command = &cobra.Command{
|
||||
Use: "logs APPNAME",
|
||||
Short: "Get logs of application pods",
|
||||
Example: templates.Examples(`
|
||||
# Get logs of pods associated with the application "my-app"
|
||||
argocd app logs my-app
|
||||
|
||||
# Get logs of pods associated with the application "my-app" in a specific resource group
|
||||
argocd app logs my-app --group my-group
|
||||
|
||||
# Get logs of pods associated with the application "my-app" in a specific resource kind
|
||||
argocd app logs my-app --kind my-kind
|
||||
|
||||
# Get logs of pods associated with the application "my-app" in a specific namespace
|
||||
argocd app logs my-app --namespace my-namespace
|
||||
|
||||
# Get logs of pods associated with the application "my-app" for a specific resource name
|
||||
argocd app logs my-app --name my-resource
|
||||
|
||||
# Stream logs in real-time for the application "my-app"
|
||||
argocd app logs my-app -f
|
||||
|
||||
# Get the last N lines of logs for the application "my-app"
|
||||
argocd app logs my-app --tail 100
|
||||
|
||||
# Get logs since a specified number of seconds ago
|
||||
argocd app logs my-app --since-seconds 3600
|
||||
|
||||
# Get logs until a specified time (format: "2023-10-10T15:30:00Z")
|
||||
argocd app logs my-app --until-time "2023-10-10T15:30:00Z"
|
||||
|
||||
# Filter logs to show only those containing a specific string
|
||||
argocd app logs my-app --filter "error"
|
||||
|
||||
# Get logs for a specific container within the pods
|
||||
argocd app logs my-app -c my-container
|
||||
|
||||
# Get previously terminated container logs
|
||||
argocd app logs my-app -p
|
||||
`),
|
||||
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -524,8 +411,8 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} // Done with receive message
|
||||
} // Done with retry
|
||||
} //Done with receive message
|
||||
} //Done with retry
|
||||
},
|
||||
}
|
||||
|
||||
@@ -533,12 +420,12 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().StringVar(&kind, "kind", "", "Resource kind")
|
||||
command.Flags().StringVar(&namespace, "namespace", "", "Resource namespace")
|
||||
command.Flags().StringVar(&resourceName, "name", "", "Resource name")
|
||||
command.Flags().BoolVarP(&follow, "follow", "f", false, "Specify if the logs should be streamed")
|
||||
command.Flags().BoolVar(&follow, "follow", false, "Specify if the logs should be streamed")
|
||||
command.Flags().Int64Var(&tail, "tail", 0, "The number of lines from the end of the logs to show")
|
||||
command.Flags().Int64Var(&sinceSeconds, "since-seconds", 0, "A relative time in seconds before the current time from which to show logs")
|
||||
command.Flags().StringVar(&untilTime, "until-time", "", "Show logs until this time")
|
||||
command.Flags().StringVar(&filter, "filter", "", "Show logs contain this string")
|
||||
command.Flags().StringVarP(&container, "container", "c", "", "Optional container name")
|
||||
command.Flags().StringVar(&container, "container", "", "Optional container name")
|
||||
command.Flags().BoolVarP(&previous, "previous", "p", false, "Specify if the previously terminated container logs should be returned")
|
||||
|
||||
return command
|
||||
@@ -716,23 +603,6 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
var command = &cobra.Command{
|
||||
Use: "set APPNAME",
|
||||
Short: "Set application parameters",
|
||||
Example: templates.Examples(`
|
||||
# Set application parameters for the application "my-app"
|
||||
argocd app set my-app --parameter key1=value1 --parameter key2=value2
|
||||
|
||||
# Set and validate application parameters for "my-app"
|
||||
argocd app set my-app --parameter key1=value1 --parameter key2=value2 --validate
|
||||
|
||||
# Set and override application parameters with JSON or YAML file
|
||||
argocd app set my-app --from-file path/to/parameters.json
|
||||
|
||||
# Set and override application parameters with a parameter file
|
||||
argocd app set my-app --parameter-file path/to/parameter-file.yaml
|
||||
|
||||
# Set application parameters and specify the namespace
|
||||
argocd app set my-app --parameter key1=value1 --parameter key2=value2 --namespace my-namespace
|
||||
`),
|
||||
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -889,7 +759,7 @@ func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, n
|
||||
for i, item := range source.Kustomize.Images {
|
||||
if argoappv1.KustomizeImage(kustomizeImage).Match(item) {
|
||||
updated = true
|
||||
// remove i
|
||||
//remove i
|
||||
a := source.Kustomize.Images
|
||||
copy(a[i:], a[i+1:]) // Shift a[i+1:] left one index.
|
||||
a[len(a)-1] = "" // Erase last element (write zero value).
|
||||
@@ -976,9 +846,9 @@ func targetObjects(resources []*argoappv1.ResourceDiff) ([]*unstructured.Unstruc
|
||||
return objs, nil
|
||||
}
|
||||
|
||||
func getLocalObjects(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions,
|
||||
func getLocalObjects(ctx context.Context, app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions,
|
||||
trackingMethod string) []*unstructured.Unstructured {
|
||||
manifestStrings := getLocalObjectsString(ctx, app, proj, local, localRepoRoot, appLabelKey, kubeVersion, apiVersions, kustomizeOptions, trackingMethod)
|
||||
manifestStrings := getLocalObjectsString(ctx, app, local, localRepoRoot, appLabelKey, kubeVersion, apiVersions, kustomizeOptions, trackingMethod)
|
||||
objs := make([]*unstructured.Unstructured, len(manifestStrings))
|
||||
for i := range manifestStrings {
|
||||
obj := unstructured.Unstructured{}
|
||||
@@ -989,21 +859,19 @@ func getLocalObjects(ctx context.Context, app *argoappv1.Application, proj *argo
|
||||
return objs
|
||||
}
|
||||
|
||||
func getLocalObjectsString(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions,
|
||||
func getLocalObjectsString(ctx context.Context, app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions,
|
||||
trackingMethod string) []string {
|
||||
source := app.Spec.GetSource()
|
||||
res, err := repository.GenerateManifests(ctx, local, localRepoRoot, source.TargetRevision, &repoapiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{Repo: source.RepoURL},
|
||||
AppLabelKey: appLabelKey,
|
||||
AppName: app.Name,
|
||||
Namespace: app.Spec.Destination.Namespace,
|
||||
ApplicationSource: &source,
|
||||
KustomizeOptions: kustomizeOptions,
|
||||
KubeVersion: kubeVersion,
|
||||
ApiVersions: apiVersions,
|
||||
TrackingMethod: trackingMethod,
|
||||
ProjectName: proj.Name,
|
||||
ProjectSourceRepos: proj.Spec.SourceRepos,
|
||||
Repo: &argoappv1.Repository{Repo: source.RepoURL},
|
||||
AppLabelKey: appLabelKey,
|
||||
AppName: app.Name,
|
||||
Namespace: app.Spec.Destination.Namespace,
|
||||
ApplicationSource: &source,
|
||||
KustomizeOptions: kustomizeOptions,
|
||||
KubeVersion: kubeVersion,
|
||||
ApiVersions: apiVersions,
|
||||
TrackingMethod: trackingMethod,
|
||||
}, true, &git.NoopCredsStore{}, resource.MustParse("0"), nil)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -1121,8 +989,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
diffOption.cluster = cluster
|
||||
}
|
||||
}
|
||||
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
|
||||
foundDiffs := findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption)
|
||||
foundDiffs := findandPrintDiff(ctx, app, resources, argoSettings, diffOption)
|
||||
if foundDiffs && exitCode {
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -1150,13 +1017,13 @@ type DifferenceOption struct {
|
||||
}
|
||||
|
||||
// findandPrintDiff ... Prints difference between application current state and state stored in git or locally, returns boolean as true if difference is found else returns false
|
||||
func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption) bool {
|
||||
func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption) bool {
|
||||
var foundDiffs bool
|
||||
liveObjs, err := cmdutil.LiveObjects(resources.Items)
|
||||
errors.CheckError(err)
|
||||
items := make([]objKeyLiveTarget, 0)
|
||||
if diffOptions.local != "" {
|
||||
localObjs := groupObjsByKey(getLocalObjects(ctx, app, proj, diffOptions.local, diffOptions.localRepoRoot, argoSettings.AppLabelKey, diffOptions.cluster.Info.ServerVersion, diffOptions.cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod), liveObjs, app.Spec.Destination.Namespace)
|
||||
localObjs := groupObjsByKey(getLocalObjects(ctx, app, diffOptions.local, diffOptions.localRepoRoot, argoSettings.AppLabelKey, diffOptions.cluster.Info.ServerVersion, diffOptions.cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod), liveObjs, app.Spec.Destination.Namespace)
|
||||
items = groupObjsForDiff(resources, localObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace), app.Spec.Destination.Namespace)
|
||||
} else if diffOptions.revision != "" {
|
||||
var unstructureds []*unstructured.Unstructured
|
||||
@@ -1579,7 +1446,6 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
timeout uint
|
||||
selector string
|
||||
resources []string
|
||||
output string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "wait [APPNAME.. | -l selector]",
|
||||
@@ -1628,7 +1494,7 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
}
|
||||
}
|
||||
for _, appName := range appNames {
|
||||
_, _, err := waitOnApplicationStatus(ctx, acdClient, appName, timeout, watch, selectedResources, output)
|
||||
_, _, err := waitOnApplicationStatus(ctx, acdClient, appName, timeout, watch, selectedResources)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
},
|
||||
@@ -1641,7 +1507,6 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().StringArrayVar(&resources, "resource", []string{}, fmt.Sprintf("Sync only specific resources as GROUP%[1]sKIND%[1]sNAME or %[2]sGROUP%[1]sKIND%[1]sNAME. Fields may be blank and '*' can be used. This option may be specified repeatedly", resourceFieldDelimiter, resourceExcludeIndicator))
|
||||
command.Flags().BoolVar(&watch.operation, "operation", false, "Wait for pending operations")
|
||||
command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds")
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree|tree=detailed")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -1653,24 +1518,6 @@ func printAppResources(w io.Writer, app *argoappv1.Application) {
|
||||
}
|
||||
}
|
||||
|
||||
func printTreeView(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "KIND/NAME\tSTATUS\tHEALTH\tMESSAGE\n")
|
||||
for uid := range parentNodes {
|
||||
treeViewAppGet("", nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState, w)
|
||||
}
|
||||
_ = w.Flush()
|
||||
}
|
||||
|
||||
func printTreeViewDetailed(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintf(w, "KIND/NAME\tSTATUS\tHEALTH\tAGE\tMESSAGE\tREASON\n")
|
||||
for uid := range parentNodes {
|
||||
detailedTreeViewAppGet("", nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState, w)
|
||||
}
|
||||
_ = w.Flush()
|
||||
}
|
||||
|
||||
// NewApplicationSyncCommand returns a new instance of an `argocd app sync` command
|
||||
func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
@@ -1685,7 +1532,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
force bool
|
||||
replace bool
|
||||
serverSideApply bool
|
||||
applyOutOfSyncOnly bool
|
||||
async bool
|
||||
retryLimit int64
|
||||
retryBackoffDuration time.Duration
|
||||
@@ -1697,7 +1543,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
diffChanges bool
|
||||
diffChangesConfirm bool
|
||||
projects []string
|
||||
output string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "sync [APPNAME... | -l selector | --project project-name]",
|
||||
@@ -1726,13 +1571,17 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
argocd app sync my-app --resource argoproj.io:Rollout:my-namespace/my-rollout`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
if len(args) == 0 && selector == "" && len(projects) == 0 {
|
||||
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(args) > 1 && selector != "" {
|
||||
log.Fatal("Cannot use selector option when application name(s) passed as argument(s)")
|
||||
}
|
||||
|
||||
acdClient := headless.NewClientOrDie(clientOpts, c)
|
||||
conn, appIf := acdClient.NewApplicationClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
@@ -1777,8 +1626,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("The name of the app is ", appName)
|
||||
|
||||
for _, mfst := range res.Manifests {
|
||||
obj, err := argoappv1.UnmarshalToUnstructured(mfst)
|
||||
errors.CheckError(err)
|
||||
@@ -1847,8 +1694,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
errors.CheckError(err)
|
||||
argoio.Close(conn)
|
||||
|
||||
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
|
||||
localObjsStrings = getLocalObjectsString(ctx, app, proj.Project, local, localRepoRoot, argoSettings.AppLabelKey, cluster.Info.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod)
|
||||
localObjsStrings = getLocalObjectsString(ctx, app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.Info.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod)
|
||||
errors.CheckError(err)
|
||||
diffOption.local = local
|
||||
diffOption.localRepoRoot = localRepoRoot
|
||||
@@ -1864,9 +1710,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
if serverSideApply {
|
||||
items = append(items, common.SyncOptionServerSideApply)
|
||||
}
|
||||
if applyOutOfSyncOnly {
|
||||
items = append(items, common.SyncOptionApplyOutOfSyncOnly)
|
||||
}
|
||||
|
||||
if len(items) == 0 {
|
||||
// for prevent send even empty array if not need
|
||||
@@ -1904,7 +1747,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
Backoff: &argoappv1.Backoff{
|
||||
Duration: retryBackoffDuration.String(),
|
||||
MaxDuration: retryBackoffMaxDuration.String(),
|
||||
Factor: pointer.Int64(retryBackoffFactor),
|
||||
Factor: pointer.Int64Ptr(retryBackoffFactor),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1921,8 +1764,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
foundDiffs := false
|
||||
fmt.Printf("====== Previewing differences between live and desired state of application %s ======\n", appQualifiedName)
|
||||
|
||||
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
|
||||
foundDiffs = findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption)
|
||||
foundDiffs = findandPrintDiff(ctx, app, resources, argoSettings, diffOption)
|
||||
if foundDiffs {
|
||||
if !diffChangesConfirm {
|
||||
yesno := cli.AskToProceed(fmt.Sprintf("Please review changes to application %s shown above. Do you want to continue the sync process? (y/n): ", appQualifiedName))
|
||||
@@ -1938,7 +1780,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
errors.CheckError(err)
|
||||
|
||||
if !async {
|
||||
app, opState, err := waitOnApplicationStatus(ctx, acdClient, appQualifiedName, timeout, watchOpts{operation: true}, selectedResources, output)
|
||||
app, opState, err := waitOnApplicationStatus(ctx, acdClient, appQualifiedName, timeout, watchOpts{operation: true}, selectedResources)
|
||||
errors.CheckError(err)
|
||||
|
||||
if !dryRun {
|
||||
@@ -1971,7 +1813,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().BoolVar(&force, "force", false, "Use a force apply")
|
||||
command.Flags().BoolVar(&replace, "replace", false, "Use a kubectl create/replace instead apply")
|
||||
command.Flags().BoolVar(&serverSideApply, "server-side", false, "Use server-side apply while syncing the application")
|
||||
command.Flags().BoolVar(&applyOutOfSyncOnly, "apply-out-of-sync-only", false, "Sync only out-of-sync resources")
|
||||
command.Flags().BoolVar(&async, "async", false, "Do not wait for application to sync before continuing")
|
||||
command.Flags().StringVar(&local, "local", "", "Path to a local directory. When this flag is present no git queries will be made")
|
||||
command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root")
|
||||
@@ -1979,7 +1820,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().BoolVar(&diffChangesConfirm, "assumeYes", false, "Assume yes as answer for all user queries or prompts")
|
||||
command.Flags().BoolVar(&diffChanges, "preview-changes", false, "Preview difference against the target and live state before syncing app and wait for user confirmation")
|
||||
command.Flags().StringArrayVar(&projects, "project", []string{}, "Sync apps that belong to the specified projects. This option may be specified repeatedly.")
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree|tree=detailed")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -2143,7 +1983,7 @@ func checkResourceStatus(watch watchOpts, healthStatus string, syncStatus string
|
||||
} else if watch.degraded && watch.health {
|
||||
healthCheckPassed = healthStatus == string(health.HealthStatusHealthy) ||
|
||||
healthStatus == string(health.HealthStatusDegraded)
|
||||
// below are good
|
||||
//below are good
|
||||
} else if watch.suspended && watch.health {
|
||||
healthCheckPassed = healthStatus == string(health.HealthStatusHealthy) ||
|
||||
healthStatus == string(health.HealthStatusSuspended)
|
||||
@@ -2160,26 +2000,12 @@ func checkResourceStatus(watch watchOpts, healthStatus string, syncStatus string
|
||||
return synced && healthCheckPassed && operational
|
||||
}
|
||||
|
||||
// resourceParentChild gets the latest state of the app and the latest state of the app's resource tree and then
|
||||
// constructs the necessary data structures to print the app as a tree.
|
||||
func resourceParentChild(ctx context.Context, acdClient argocdclient.Client, appName string, appNs string) (map[string]argoappv1.ResourceNode, map[string][]string, map[string]struct{}, map[string]*resourceState) {
|
||||
_, appIf := acdClient.NewApplicationClientOrDie()
|
||||
mapUidToNode, mapParentToChild, parentNode := parentChildDetails(appIf, ctx, appName, appNs)
|
||||
app, err := appIf.Get(ctx, &application.ApplicationQuery{Name: pointer.String(appName), AppNamespace: pointer.String(appNs)})
|
||||
errors.CheckError(err)
|
||||
mapNodeNameToResourceState := make(map[string]*resourceState)
|
||||
for _, res := range getResourceStates(app, nil) {
|
||||
mapNodeNameToResourceState[res.Kind+"/"+res.Name] = res
|
||||
}
|
||||
return mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState
|
||||
}
|
||||
|
||||
const waitFormatString = "%s\t%5s\t%10s\t%10s\t%20s\t%8s\t%7s\t%10s\t%s\n"
|
||||
|
||||
// waitOnApplicationStatus watches an application and blocks until either the desired watch conditions
|
||||
// are fulfilled or we reach the timeout. Returns the app once desired conditions have been filled.
|
||||
// are fulfiled or we reach the timeout. Returns the app once desired conditions have been filled.
|
||||
// Additionally return the operationState at time of fulfilment (which may be different than returned app).
|
||||
func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, appName string, timeout uint, watch watchOpts, selectedResources []*argoappv1.SyncOperationResource, output string) (*argoappv1.Application, *argoappv1.OperationState, error) {
|
||||
func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, appName string, timeout uint, watch watchOpts, selectedResources []*argoappv1.SyncOperationResource) (*argoappv1.Application, *argoappv1.OperationState, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
@@ -2211,49 +2037,18 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client,
|
||||
printOperationResult(app.Status.OperationState)
|
||||
}
|
||||
|
||||
switch output {
|
||||
case "yaml", "json":
|
||||
err := PrintResource(app, output)
|
||||
errors.CheckError(err)
|
||||
case "wide", "":
|
||||
if len(app.Status.Resources) > 0 {
|
||||
fmt.Println()
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
printAppResources(w, app)
|
||||
_ = w.Flush()
|
||||
}
|
||||
case "tree":
|
||||
mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs)
|
||||
if len(mapUidToNode) > 0 {
|
||||
fmt.Println()
|
||||
printTreeView(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState)
|
||||
}
|
||||
case "tree=detailed":
|
||||
mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs)
|
||||
if len(mapUidToNode) > 0 {
|
||||
fmt.Println()
|
||||
printTreeViewDetailed(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState)
|
||||
}
|
||||
default:
|
||||
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
|
||||
if len(app.Status.Resources) > 0 {
|
||||
fmt.Println()
|
||||
w := tabwriter.NewWriter(os.Stdout, 5, 0, 2, ' ', 0)
|
||||
printAppResources(w, app)
|
||||
_ = w.Flush()
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
if timeout != 0 {
|
||||
time.AfterFunc(time.Duration(timeout)*time.Second, func() {
|
||||
_, appClient := acdClient.NewApplicationClientOrDie()
|
||||
app, err := appClient.Get(ctx, &application.ApplicationQuery{
|
||||
Name: &appRealName,
|
||||
AppNamespace: &appNs,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
fmt.Println()
|
||||
fmt.Println("This is the state of the app after `wait` timed out:")
|
||||
printFinalStatus(app)
|
||||
cancel()
|
||||
fmt.Println()
|
||||
fmt.Println("The command timed out waiting for the conditions to be met.")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2461,13 +2256,13 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr
|
||||
var (
|
||||
prune bool
|
||||
timeout uint
|
||||
output string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "rollback APPNAME [ID]",
|
||||
Short: "Rollback application to a previous deployed version by History ID, omitted will Rollback to the previous version",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
@@ -2501,13 +2296,12 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr
|
||||
|
||||
_, _, err = waitOnApplicationStatus(ctx, acdClient, app.QualifiedName(), timeout, watchOpts{
|
||||
operation: true,
|
||||
}, nil, output)
|
||||
}, nil)
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVar(&prune, "prune", false, "Allow deleting unexpected resources")
|
||||
command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds")
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree|tree=detailed")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -2582,8 +2376,7 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server})
|
||||
errors.CheckError(err)
|
||||
|
||||
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
|
||||
unstructureds = getLocalObjects(context.Background(), app, proj.Project, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod)
|
||||
unstructureds = getLocalObjects(context.Background(), app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod)
|
||||
} else if revision != "" {
|
||||
q := application.ApplicationManifestQuery{
|
||||
Name: &appName,
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
"os"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
@@ -34,22 +33,11 @@ type DisplayedAction struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
var (
|
||||
appActionExample = templates.Examples(`
|
||||
# List all the available actions for an application
|
||||
argocd app actions list APPNAME
|
||||
|
||||
# Run an available action for an application
|
||||
argocd app actions run APPNAME ACTION --kind KIND [--resource-name RESOURCE] [--namespace NAMESPACE] [--group GROUP]
|
||||
`)
|
||||
)
|
||||
|
||||
// NewApplicationResourceActionsCommand returns a new instance of an `argocd app actions` command
|
||||
func NewApplicationResourceActionsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "actions",
|
||||
Short: "Manage Resource actions",
|
||||
Example: appActionExample,
|
||||
Use: "actions",
|
||||
Short: "Manage Resource actions",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
@@ -70,10 +58,6 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt
|
||||
var command = &cobra.Command{
|
||||
Use: "list APPNAME",
|
||||
Short: "Lists available actions on a resource",
|
||||
Example: templates.Examples(`
|
||||
# List all the available actions for an application
|
||||
argocd app actions list APPNAME
|
||||
`),
|
||||
}
|
||||
command.Run = func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
@@ -152,10 +136,6 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti
|
||||
var command = &cobra.Command{
|
||||
Use: "run APPNAME ACTION",
|
||||
Short: "Runs an available action on resource(s)",
|
||||
Example: templates.Examples(`
|
||||
# Run an available action for an application
|
||||
argocd app actions run APPNAME ACTION --kind KIND [--resource-name RESOURCE] [--namespace NAMESPACE] [--group GROUP]
|
||||
`),
|
||||
}
|
||||
|
||||
command.Flags().StringVar(&resourceName, "resource-name", "", "Name of resource")
|
||||
|
||||
@@ -1,93 +1,13 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func TestPrintTreeViewAppResources(t *testing.T) {
|
||||
var nodes [3]v1alpha1.ResourceNode
|
||||
nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"}
|
||||
nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}}
|
||||
nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}
|
||||
nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}}
|
||||
nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}
|
||||
var nodeMapping = make(map[string]v1alpha1.ResourceNode)
|
||||
var mapParentToChild = make(map[string][]string)
|
||||
var parentNode = make(map[string]struct{})
|
||||
for _, node := range nodes {
|
||||
nodeMapping[node.UID] = node
|
||||
if len(node.ParentRefs) > 0 {
|
||||
_, ok := mapParentToChild[node.ParentRefs[0].UID]
|
||||
if !ok {
|
||||
var temp []string
|
||||
mapParentToChild[node.ParentRefs[0].UID] = temp
|
||||
}
|
||||
mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID)
|
||||
} else {
|
||||
parentNode[node.UID] = struct{}{}
|
||||
}
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0)
|
||||
|
||||
printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w)
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
output := buf.String()
|
||||
|
||||
assert.Contains(t, output, "Rollout")
|
||||
assert.Contains(t, output, "argoproj.io")
|
||||
}
|
||||
|
||||
func TestPrintTreeViewDetailedAppResources(t *testing.T) {
|
||||
var nodes [3]v1alpha1.ResourceNode
|
||||
nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"}
|
||||
nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}}
|
||||
nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}
|
||||
nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}}
|
||||
nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}
|
||||
nodes[2].Health = &v1alpha1.HealthStatus{
|
||||
Status: "Degraded",
|
||||
Message: "Readiness Gate failed",
|
||||
}
|
||||
|
||||
var nodeMapping = make(map[string]v1alpha1.ResourceNode)
|
||||
var mapParentToChild = make(map[string][]string)
|
||||
var parentNode = make(map[string]struct{})
|
||||
for _, node := range nodes {
|
||||
nodeMapping[node.UID] = node
|
||||
if len(node.ParentRefs) > 0 {
|
||||
_, ok := mapParentToChild[node.ParentRefs[0].UID]
|
||||
if !ok {
|
||||
var temp []string
|
||||
mapParentToChild[node.ParentRefs[0].UID] = temp
|
||||
}
|
||||
mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID)
|
||||
} else {
|
||||
parentNode[node.UID] = struct{}{}
|
||||
}
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0)
|
||||
|
||||
printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w)
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
output := buf.String()
|
||||
|
||||
assert.Contains(t, output, "Rollout")
|
||||
assert.Contains(t, output, "Degraded")
|
||||
assert.Contains(t, output, "Readiness Gate failed")
|
||||
}
|
||||
|
||||
func TestPrintResourcesTree(t *testing.T) {
|
||||
tree := v1alpha1.ApplicationTree{
|
||||
Nodes: []v1alpha1.ResourceNode{
|
||||
@@ -112,7 +32,7 @@ func TestPrintResourcesTree(t *testing.T) {
|
||||
},
|
||||
}
|
||||
output, _ := captureOutput(func() error {
|
||||
printResources(true, false, &tree, "")
|
||||
printResources(true, false, &tree)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ package commands
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -19,6 +19,8 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
@@ -29,7 +31,6 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
var kind string
|
||||
var group string
|
||||
var all bool
|
||||
var project string
|
||||
command := &cobra.Command{
|
||||
Use: "patch-resource APPNAME",
|
||||
Short: "Patch resource in an application",
|
||||
@@ -46,7 +47,6 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
command.Flags().StringVar(&group, "group", "", "Group")
|
||||
command.Flags().StringVar(&namespace, "namespace", "", "Namespace")
|
||||
command.Flags().BoolVar(&all, "all", false, "Indicates whether to patch multiple matching of resources")
|
||||
command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`)
|
||||
command.Run = func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -78,7 +78,6 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
Kind: pointer.String(gvk.Kind),
|
||||
Patch: pointer.String(patch),
|
||||
PatchType: pointer.String(patchType),
|
||||
Project: pointer.String(project),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
log.Infof("Resource '%s' patched", obj.GetName())
|
||||
@@ -96,7 +95,6 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
var force bool
|
||||
var orphan bool
|
||||
var all bool
|
||||
var project string
|
||||
command := &cobra.Command{
|
||||
Use: "delete-resource APPNAME",
|
||||
Short: "Delete resource in an application",
|
||||
@@ -111,7 +109,6 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
command.Flags().BoolVar(&force, "force", false, "Indicates whether to orphan the dependents of the deleted resource")
|
||||
command.Flags().BoolVar(&orphan, "orphan", false, "Indicates whether to force delete the resource")
|
||||
command.Flags().BoolVar(&all, "all", false, "Indicates whether to patch multiple matching of resources")
|
||||
command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`)
|
||||
command.Run = func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -143,7 +140,6 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
Kind: pointer.String(gvk.Kind),
|
||||
Force: &force,
|
||||
Orphan: &orphan,
|
||||
Project: pointer.String(project),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
log.Infof("Resource '%s' deleted", obj.GetName())
|
||||
@@ -153,114 +149,34 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
return command
|
||||
}
|
||||
|
||||
func parentChildInfo(nodes []v1alpha1.ResourceNode) (map[string]v1alpha1.ResourceNode, map[string][]string, map[string]struct{}) {
|
||||
mapUidToNode := make(map[string]v1alpha1.ResourceNode)
|
||||
mapParentToChild := make(map[string][]string)
|
||||
parentNode := make(map[string]struct{})
|
||||
|
||||
for _, node := range nodes {
|
||||
mapUidToNode[node.UID] = node
|
||||
|
||||
if len(node.ParentRefs) > 0 {
|
||||
_, ok := mapParentToChild[node.ParentRefs[0].UID]
|
||||
if !ok {
|
||||
var temp []string
|
||||
mapParentToChild[node.ParentRefs[0].UID] = temp
|
||||
}
|
||||
mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID)
|
||||
} else {
|
||||
parentNode[node.UID] = struct{}{}
|
||||
}
|
||||
}
|
||||
return mapUidToNode, mapParentToChild, parentNode
|
||||
}
|
||||
|
||||
func printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
|
||||
for uid := range parentNodes {
|
||||
detailedTreeViewAppResourcesNotOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func printDetailedTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
|
||||
for uid := range parentNodes {
|
||||
detailedTreeViewAppResourcesOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w)
|
||||
}
|
||||
}
|
||||
|
||||
func printTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
|
||||
for uid := range parentNodes {
|
||||
treeViewAppResourcesNotOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func printTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
|
||||
for uid := range parentNodes {
|
||||
treeViewAppResourcesOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w)
|
||||
}
|
||||
}
|
||||
|
||||
func printResources(listAll bool, orphaned bool, appResourceTree *v1alpha1.ApplicationTree, output string) {
|
||||
func printResources(listAll bool, orphaned bool, appResourceTree *v1alpha1.ApplicationTree) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
if output == "tree=detailed" {
|
||||
fmt.Fprintf(w, "GROUP\tKIND\tNAMESPACE\tNAME\tORPHANED\tAGE\tHEALTH\tREASON\n")
|
||||
|
||||
if !orphaned || listAll {
|
||||
mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.Nodes)
|
||||
printDetailedTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w)
|
||||
}
|
||||
|
||||
if orphaned || listAll {
|
||||
mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes)
|
||||
printDetailedTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w)
|
||||
}
|
||||
|
||||
} else if output == "tree" {
|
||||
fmt.Fprintf(w, "GROUP\tKIND\tNAMESPACE\tNAME\tORPHANED\n")
|
||||
|
||||
if !orphaned || listAll {
|
||||
mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.Nodes)
|
||||
printTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w)
|
||||
}
|
||||
|
||||
if orphaned || listAll {
|
||||
mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes)
|
||||
printTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
headers := []interface{}{"GROUP", "KIND", "NAMESPACE", "NAME", "ORPHANED"}
|
||||
fmtStr := "%s\t%s\t%s\t%s\t%s\n"
|
||||
_, _ = fmt.Fprintf(w, fmtStr, headers...)
|
||||
if !orphaned || listAll {
|
||||
for _, res := range appResourceTree.Nodes {
|
||||
if len(res.ParentRefs) == 0 {
|
||||
_, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "No")
|
||||
}
|
||||
headers := []interface{}{"GROUP", "KIND", "NAMESPACE", "NAME", "ORPHANED"}
|
||||
fmtStr := "%s\t%s\t%s\t%s\t%s\n"
|
||||
_, _ = fmt.Fprintf(w, fmtStr, headers...)
|
||||
if !orphaned || listAll {
|
||||
for _, res := range appResourceTree.Nodes {
|
||||
if len(res.ParentRefs) == 0 {
|
||||
_, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "No")
|
||||
}
|
||||
}
|
||||
if orphaned || listAll {
|
||||
for _, res := range appResourceTree.OrphanedNodes {
|
||||
_, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "Yes")
|
||||
}
|
||||
}
|
||||
if orphaned || listAll {
|
||||
for _, res := range appResourceTree.OrphanedNodes {
|
||||
_, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "Yes")
|
||||
}
|
||||
|
||||
}
|
||||
_ = w.Flush()
|
||||
|
||||
}
|
||||
|
||||
func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var orphaned bool
|
||||
var output string
|
||||
var project string
|
||||
var command = &cobra.Command{
|
||||
Use: "resources APPNAME",
|
||||
Short: "List resource of application",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
@@ -272,14 +188,11 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions)
|
||||
appResourceTree, err := appIf.ResourceTree(ctx, &applicationpkg.ResourcesQuery{
|
||||
ApplicationName: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Project: &project,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
printResources(listAll, orphaned, appResourceTree, output)
|
||||
printResources(listAll, orphaned, appResourceTree)
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVar(&orphaned, "orphaned", false, "Lists only orphaned resources")
|
||||
command.Flags().StringVar(&output, "output", "", "Provides the tree view of the resources")
|
||||
command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`)
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -115,86 +115,6 @@ func TestFindRevisionHistoryWithoutPassedId(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestPrintTreeViewAppGet(t *testing.T) {
|
||||
var nodes [3]v1alpha1.ResourceNode
|
||||
nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"}
|
||||
nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}}
|
||||
nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}
|
||||
nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}}
|
||||
nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}
|
||||
|
||||
var nodeMapping = make(map[string]v1alpha1.ResourceNode)
|
||||
var mapParentToChild = make(map[string][]string)
|
||||
var parentNode = make(map[string]struct{})
|
||||
|
||||
for _, node := range nodes {
|
||||
nodeMapping[node.UID] = node
|
||||
|
||||
if len(node.ParentRefs) > 0 {
|
||||
_, ok := mapParentToChild[node.ParentRefs[0].UID]
|
||||
if !ok {
|
||||
var temp []string
|
||||
mapParentToChild[node.ParentRefs[0].UID] = temp
|
||||
}
|
||||
mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID)
|
||||
} else {
|
||||
parentNode[node.UID] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
output, _ := captureOutput(func() error {
|
||||
printTreeView(nodeMapping, mapParentToChild, parentNode, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
assert.Contains(t, output, "Pod")
|
||||
assert.Contains(t, output, "ReplicaSet")
|
||||
assert.Contains(t, output, "Rollout")
|
||||
assert.Contains(t, output, "numalogic-rollout-demo-5dcd5457d5-6trpt")
|
||||
}
|
||||
|
||||
func TestPrintTreeViewDetailedAppGet(t *testing.T) {
|
||||
var nodes [3]v1alpha1.ResourceNode
|
||||
nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"}
|
||||
nodes[0].Health = &v1alpha1.HealthStatus{Status: "Degraded", Message: "Readiness Gate failed"}
|
||||
nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}}
|
||||
nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}
|
||||
nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}}
|
||||
nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}
|
||||
|
||||
var nodeMapping = make(map[string]v1alpha1.ResourceNode)
|
||||
var mapParentToChild = make(map[string][]string)
|
||||
var parentNode = make(map[string]struct{})
|
||||
|
||||
for _, node := range nodes {
|
||||
nodeMapping[node.UID] = node
|
||||
|
||||
if len(node.ParentRefs) > 0 {
|
||||
_, ok := mapParentToChild[node.ParentRefs[0].UID]
|
||||
if !ok {
|
||||
var temp []string
|
||||
mapParentToChild[node.ParentRefs[0].UID] = temp
|
||||
}
|
||||
mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID)
|
||||
} else {
|
||||
parentNode[node.UID] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
output, _ := captureOutput(func() error {
|
||||
printTreeViewDetailed(nodeMapping, mapParentToChild, parentNode, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
assert.Contains(t, output, "Pod")
|
||||
assert.Contains(t, output, "ReplicaSet")
|
||||
assert.Contains(t, output, "Rollout")
|
||||
assert.Contains(t, output, "numalogic-rollout-demo-5dcd5457d5-6trpt")
|
||||
assert.Contains(t, output, "Degraded")
|
||||
assert.Contains(t, output, "Readiness Gate failed")
|
||||
|
||||
}
|
||||
|
||||
func TestDefaultWaitOptions(t *testing.T) {
|
||||
watch := watchOpts{
|
||||
sync: false,
|
||||
@@ -387,8 +307,8 @@ func Test_groupObjsByKey(t *testing.T) {
|
||||
}
|
||||
|
||||
expected := map[kube.ResourceKey]*unstructured.Unstructured{
|
||||
{Group: "", Kind: "Pod", Namespace: "default", Name: "pod-name"}: localObjs[0],
|
||||
{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition", Namespace: "", Name: "certificates.cert-manager.io"}: localObjs[1],
|
||||
kube.ResourceKey{Group: "", Kind: "Pod", Namespace: "default", Name: "pod-name"}: localObjs[0],
|
||||
kube.ResourceKey{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition", Namespace: "", Name: "certificates.cert-manager.io"}: localObjs[1],
|
||||
}
|
||||
|
||||
objByKey := groupObjsByKey(localObjs, liveObjs, "default")
|
||||
|
||||
@@ -67,10 +67,6 @@ func NewApplicationSetGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
var command = &cobra.Command{
|
||||
Use: "get APPSETNAME",
|
||||
Short: "Get ApplicationSet details",
|
||||
Example: templates.Examples(`
|
||||
# Get ApplicationSets
|
||||
argocd appset get APPSETNAME
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
|
||||
@@ -40,12 +40,12 @@ func TestPrintApplicationSetTable(t *testing.T) {
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
v1alpha1.ApplicationSetGenerator{
|
||||
Git: &v1alpha1.GitGenerator{
|
||||
RepoURL: "https://github.com/argoproj/argo-cd.git",
|
||||
Revision: "head",
|
||||
Directories: []v1alpha1.GitDirectoryGeneratorItem{
|
||||
{
|
||||
v1alpha1.GitDirectoryGeneratorItem{
|
||||
Path: "applicationset/examples/git-generator-directory/cluster-addons/*",
|
||||
},
|
||||
},
|
||||
@@ -60,7 +60,7 @@ func TestPrintApplicationSetTable(t *testing.T) {
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Conditions: []v1alpha1.ApplicationSetCondition{
|
||||
{
|
||||
v1alpha1.ApplicationSetCondition{
|
||||
Status: v1alpha1.ApplicationSetConditionStatusTrue,
|
||||
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
|
||||
},
|
||||
@@ -75,12 +75,12 @@ func TestPrintApplicationSetTable(t *testing.T) {
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
v1alpha1.ApplicationSetGenerator{
|
||||
Git: &v1alpha1.GitGenerator{
|
||||
RepoURL: "https://github.com/argoproj/argo-cd.git",
|
||||
Revision: "head",
|
||||
Directories: []v1alpha1.GitDirectoryGeneratorItem{
|
||||
{
|
||||
v1alpha1.GitDirectoryGeneratorItem{
|
||||
Path: "applicationset/examples/git-generator-directory/cluster-addons/*",
|
||||
},
|
||||
},
|
||||
@@ -95,7 +95,7 @@ func TestPrintApplicationSetTable(t *testing.T) {
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Conditions: []v1alpha1.ApplicationSetCondition{
|
||||
{
|
||||
v1alpha1.ApplicationSetCondition{
|
||||
Status: v1alpha1.ApplicationSetConditionStatusTrue,
|
||||
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
|
||||
},
|
||||
@@ -118,12 +118,12 @@ func TestPrintAppSetSummaryTable(t *testing.T) {
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
v1alpha1.ApplicationSetGenerator{
|
||||
Git: &v1alpha1.GitGenerator{
|
||||
RepoURL: "https://github.com/argoproj/argo-cd.git",
|
||||
Revision: "head",
|
||||
Directories: []v1alpha1.GitDirectoryGeneratorItem{
|
||||
{
|
||||
v1alpha1.GitDirectoryGeneratorItem{
|
||||
Path: "applicationset/examples/git-generator-directory/cluster-addons/*",
|
||||
},
|
||||
},
|
||||
@@ -138,7 +138,7 @@ func TestPrintAppSetSummaryTable(t *testing.T) {
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Conditions: []v1alpha1.ApplicationSetCondition{
|
||||
{
|
||||
v1alpha1.ApplicationSetCondition{
|
||||
Status: v1alpha1.ApplicationSetConditionStatusTrue,
|
||||
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
|
||||
},
|
||||
|
||||
@@ -15,9 +15,7 @@ func NewBcryptCmd() *cobra.Command {
|
||||
)
|
||||
var bcryptCmd = &cobra.Command{
|
||||
Use: "bcrypt",
|
||||
Short: "Generate bcrypt hash for any password",
|
||||
Example: `# Generate bcrypt hash for any password
|
||||
argocd account bcrypt --password YOUR_PASSWORD`,
|
||||
Short: "Generate bcrypt hash for the admin password",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
bytePassword := []byte(password)
|
||||
// Hashing the password
|
||||
|
||||
@@ -130,7 +130,7 @@ func NewCertAddTLSCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
}
|
||||
},
|
||||
}
|
||||
command.Flags().StringVar(&fromFile, "from", "", "Read TLS certificate data from file (default is to read from stdin)")
|
||||
command.Flags().StringVar(&fromFile, "from", "", "read TLS certificate data from file (default is to read from stdin)")
|
||||
command.Flags().BoolVar(&upsert, "upsert", false, "Replace existing TLS certificate if certificate is different in input")
|
||||
return command
|
||||
}
|
||||
@@ -300,9 +300,9 @@ func NewCertListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
}
|
||||
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide")
|
||||
command.Flags().StringVar(&sortOrder, "sort", "", "Set display sort order for output format wide. One of: hostname|type")
|
||||
command.Flags().StringVar(&certType, "cert-type", "", "Only list certificates of given type, valid: 'ssh','https'")
|
||||
command.Flags().StringVar(&hostNamePattern, "hostname-pattern", "", "Only list certificates for hosts matching given glob-pattern")
|
||||
command.Flags().StringVar(&sortOrder, "sort", "", "set display sort order for output format wide. One of: hostname|type")
|
||||
command.Flags().StringVar(&certType, "cert-type", "", "only list certificates of given type, valid: 'ssh','https'")
|
||||
command.Flags().StringVar(&hostNamePattern, "hostname-pattern", "", "only list certificates for hosts matching given glob-pattern")
|
||||
return command
|
||||
}
|
||||
|
||||
|
||||
@@ -485,23 +485,6 @@ func NewClusterListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
|
||||
}
|
||||
},
|
||||
Example: `
|
||||
# List Clusters in Default "Wide" Format
|
||||
argocd cluster list
|
||||
|
||||
# List Cluster via specifing the server
|
||||
argocd cluster list --server <ARGOCD_SERVER_ADDRESS>
|
||||
|
||||
# List Clusters in JSON Format
|
||||
argocd cluster list -o json --server <ARGOCD_SERVER_ADDRESS>
|
||||
|
||||
# List Clusters in YAML Format
|
||||
argocd cluster list -o yaml --server <ARGOCD_SERVER_ADDRESS>
|
||||
|
||||
# List Clusters that have been added to your Argo CD
|
||||
argocd cluster list -o server <ARGOCD_SERVER_ADDRESS>
|
||||
|
||||
`,
|
||||
}
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|server")
|
||||
return command
|
||||
|
||||
@@ -211,13 +211,6 @@ compdef _argocd argocd
|
||||
Optionally, also add the following, in case you are getting errors involving compdef & compinit such as command not found: compdef:
|
||||
autoload -Uz compinit
|
||||
compinit
|
||||
`,
|
||||
Example: `# For bash
|
||||
$ source <(argocd completion bash)
|
||||
|
||||
# For zsh
|
||||
$ argocd completion zsh > _argocd
|
||||
$ source _argocd
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
|
||||
@@ -22,14 +22,6 @@ func NewContextCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
Use: "context [CONTEXT]",
|
||||
Aliases: []string{"ctx"},
|
||||
Short: "Switch between contexts",
|
||||
Example: `# List Argo CD Contexts
|
||||
argocd context
|
||||
|
||||
# Switch Argo CD context
|
||||
argocd context cd.argoproj.io
|
||||
|
||||
# Delete Argo CD context
|
||||
argocd context cd.argoproj.io --delete`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
|
||||
localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath)
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
)
|
||||
|
||||
// NewGPGCommand returns a new instance of an `argocd repo` command
|
||||
@@ -43,17 +42,6 @@ func NewGPGListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List configured GPG public keys",
|
||||
Example: templates.Examples(`
|
||||
# List all configured GPG public keys in wide format (default).
|
||||
argocd gpg list
|
||||
|
||||
# List all configured GPG public keys in JSON format.
|
||||
argocd gpg list -o json
|
||||
|
||||
# List all configured GPG public keys in YAML format.
|
||||
argocd gpg list -o yaml
|
||||
`),
|
||||
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -84,17 +72,6 @@ func NewGPGGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "get KEYID",
|
||||
Short: "Get the GPG public key with ID <KEYID> from the server",
|
||||
Example: templates.Examples(`
|
||||
# Get a GPG public key with the specified KEYID in wide format (default).
|
||||
argocd gpg get KEYID
|
||||
|
||||
# Get a GPG public key with the specified KEYID in JSON format.
|
||||
argocd gpg get KEYID -o json
|
||||
|
||||
# Get a GPG public key with the specified KEYID in YAML format.
|
||||
argocd gpg get KEYID -o yaml
|
||||
`),
|
||||
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -132,11 +109,6 @@ func NewGPGAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "Adds a GPG public key to the server's keyring",
|
||||
Example: templates.Examples(`
|
||||
# Add a GPG public key to the server's keyring from a file.
|
||||
argocd gpg add --from /path/to/keyfile
|
||||
`),
|
||||
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
@@ -39,14 +38,12 @@ import (
|
||||
)
|
||||
|
||||
type forwardCacheClient struct {
|
||||
namespace string
|
||||
context string
|
||||
init sync.Once
|
||||
client cache.CacheClient
|
||||
compression cache.RedisCompressionType
|
||||
err error
|
||||
redisHaProxyName string
|
||||
redisName string
|
||||
namespace string
|
||||
context string
|
||||
init sync.Once
|
||||
client cache.CacheClient
|
||||
compression cache.RedisCompressionType
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error {
|
||||
@@ -54,10 +51,8 @@ func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error)
|
||||
overrides := clientcmd.ConfigOverrides{
|
||||
CurrentContext: c.context,
|
||||
}
|
||||
redisHaProxyPodLabelSelector := common.LabelKeyAppName + "=" + c.redisHaProxyName
|
||||
redisPodLabelSelector := common.LabelKeyAppName + "=" + c.redisName
|
||||
redisPort, err := kubeutil.PortForward(6379, c.namespace, &overrides,
|
||||
redisHaProxyPodLabelSelector, redisPodLabelSelector)
|
||||
"app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis")
|
||||
if err != nil {
|
||||
c.err = err
|
||||
return
|
||||
@@ -103,12 +98,11 @@ func (c *forwardCacheClient) NotifyUpdated(key string) error {
|
||||
}
|
||||
|
||||
type forwardRepoClientset struct {
|
||||
namespace string
|
||||
context string
|
||||
init sync.Once
|
||||
repoClientset repoapiclient.Clientset
|
||||
err error
|
||||
repoServerName string
|
||||
namespace string
|
||||
context string
|
||||
init sync.Once
|
||||
repoClientset repoapiclient.Clientset
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.RepoServerServiceClient, error) {
|
||||
@@ -116,8 +110,7 @@ func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.R
|
||||
overrides := clientcmd.ConfigOverrides{
|
||||
CurrentContext: c.context,
|
||||
}
|
||||
repoServerPodLabelSelector := common.LabelKeyAppName + "=" + c.repoServerName
|
||||
repoServerPort, err := kubeutil.PortForward(8081, c.namespace, &overrides, repoServerPodLabelSelector)
|
||||
repoServerPort, err := kubeutil.PortForward(8081, c.namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server")
|
||||
if err != nil {
|
||||
c.err = err
|
||||
return
|
||||
@@ -134,45 +127,36 @@ func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.R
|
||||
func testAPI(ctx context.Context, clientOpts *apiclient.ClientOptions) error {
|
||||
apiClient, err := apiclient.NewClient(clientOpts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create API client: %w", err)
|
||||
return err
|
||||
}
|
||||
closer, versionClient, err := apiClient.NewVersionClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create version client: %w", err)
|
||||
return err
|
||||
}
|
||||
defer io.Close(closer)
|
||||
_, err = versionClient.Version(ctx, &empty.Empty{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get version: %w", err)
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// MaybeStartLocalServer allows executing command in a headless mode. If we're in core mode, starts the Argo CD API
|
||||
// server on the fly and changes provided client options to use started API server port.
|
||||
//
|
||||
// If the clientOpts enables core mode, but the local config does not have core mode enabled, this function will
|
||||
// not start the local server.
|
||||
func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType) error {
|
||||
// StartLocalServer allows executing command in a headless mode: on the fly starts Argo CD API server and
|
||||
// changes provided client options to use started API server port
|
||||
func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType) error {
|
||||
flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError)
|
||||
clientConfig := cli.AddKubectlFlagsToSet(flags)
|
||||
startInProcessAPI := clientOpts.Core
|
||||
if !startInProcessAPI {
|
||||
// Core mode is enabled on client options. Check the local config to see if we should start the API server.
|
||||
localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading local config: %w", err)
|
||||
return err
|
||||
}
|
||||
if localCfg != nil {
|
||||
configCtx, err := localCfg.ResolveContext(clientOpts.Context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error resolving context: %w", err)
|
||||
return err
|
||||
}
|
||||
// There was a local config file, so determine whether core mode is enabled per the config file.
|
||||
startInProcessAPI = configCtx.Server.Core
|
||||
}
|
||||
}
|
||||
// If we're in core mode, start the API server on the fly.
|
||||
if !startInProcessAPI {
|
||||
return nil
|
||||
}
|
||||
@@ -189,7 +173,7 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
|
||||
addr := fmt.Sprintf("%s:0", *address)
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen on %q: %w", addr, err)
|
||||
return err
|
||||
}
|
||||
port = &ln.Addr().(*net.TCPAddr).Port
|
||||
io.Close(ln)
|
||||
@@ -197,27 +181,27 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
|
||||
|
||||
restConfig, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating client config: %w", err)
|
||||
return err
|
||||
}
|
||||
appClientset, err := appclientset.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating app clientset: %w", err)
|
||||
return err
|
||||
}
|
||||
kubeClientset, err := kubernetes.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating kubernetes clientset: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting namespace: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
mr, err := miniredis.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running miniredis: %w", err)
|
||||
return err
|
||||
}
|
||||
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr, compression: compression, redisHaProxyName: clientOpts.RedisHaProxyName, redisName: clientOpts.RedisName}), time.Hour)
|
||||
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr, compression: compression}), time.Hour)
|
||||
srv := server.NewServer(ctx, server.ArgoCDServerOpts{
|
||||
EnableGZip: false,
|
||||
Namespace: namespace,
|
||||
@@ -229,14 +213,14 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
|
||||
KubeClientset: kubeClientset,
|
||||
Insecure: true,
|
||||
ListenHost: *address,
|
||||
RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr, repoServerName: clientOpts.RepoServerName},
|
||||
RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr},
|
||||
EnableProxyExtension: false,
|
||||
})
|
||||
srv.Init(ctx)
|
||||
|
||||
lns, err := srv.Listen()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen: %w", err)
|
||||
return err
|
||||
}
|
||||
go srv.Run(ctx, lns)
|
||||
clientOpts.ServerAddr = fmt.Sprintf("%s:%d", *address, *port)
|
||||
@@ -252,10 +236,7 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("all retries failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// NewClientOrDie creates a new API client from a set of config options, or fails fatally if the new client creation fails.
|
||||
@@ -263,9 +244,7 @@ func NewClientOrDie(opts *apiclient.ClientOptions, c *cobra.Command) apiclient.C
|
||||
ctx := c.Context()
|
||||
|
||||
ctxStr := initialize.RetrieveContextIfChanged(c.Flag("context"))
|
||||
// If we're in core mode, start the API server on the fly and configure the client `opts` to use it.
|
||||
// If we're not in core mode, this function call will do nothing.
|
||||
err := MaybeStartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone)
|
||||
err := StartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -18,10 +18,6 @@ func NewLogoutCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comma
|
||||
Use: "logout CONTEXT",
|
||||
Short: "Log out from Argo CD",
|
||||
Long: "Log out from Argo CD",
|
||||
Example: `# To log out of argocd
|
||||
$ argocd logout
|
||||
# This can be helpful for security reasons or when you want to switch between different Argo CD contexts or accounts.
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -26,7 +25,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
"github.com/argoproj/argo-cd/v2/util/gpg"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
)
|
||||
|
||||
type policyOpts struct {
|
||||
@@ -40,19 +38,6 @@ func NewProjectCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "proj",
|
||||
Short: "Manage projects",
|
||||
Example: templates.Examples(`
|
||||
# List all available projects
|
||||
argocd proj list
|
||||
|
||||
# Create a new project with name PROJECT
|
||||
argocd proj create PROJECT
|
||||
|
||||
# Delete the project with name PROJECT
|
||||
argocd proj delete PROJECT
|
||||
|
||||
# Edit the information on project with name PROJECT
|
||||
argocd proj edit PROJECT
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
@@ -102,13 +87,6 @@ func NewProjectCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
var command = &cobra.Command{
|
||||
Use: "create PROJECT",
|
||||
Short: "Create a project",
|
||||
Example: templates.Examples(`
|
||||
# Create a new project with name PROJECT
|
||||
argocd proj create PROJECT
|
||||
|
||||
# Create a new project with name PROJECT from a file or URL to a kubernetes manifest
|
||||
argocd proj create PROJECT -f FILE|URL
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -139,13 +117,6 @@ func NewProjectSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
var command = &cobra.Command{
|
||||
Use: "set PROJECT",
|
||||
Short: "Set project parameters",
|
||||
Example: templates.Examples(`
|
||||
# Set project parameters with some allowed cluster resources [RES1,RES2,...] for project with name PROJECT
|
||||
argocd proj set PROJECT --allow-cluster-resource [RES1,RES2,...]
|
||||
|
||||
# Set project parameters with some denied namespaced resources [RES1,RES2,...] for project with name PROJECT
|
||||
argocd proj set PROJECT ---deny-namespaced-resource [RES1,RES2,...]
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -179,10 +150,6 @@ func NewProjectAddSignatureKeyCommand(clientOpts *argocdclient.ClientOptions) *c
|
||||
var command = &cobra.Command{
|
||||
Use: "add-signature-key PROJECT KEY-ID",
|
||||
Short: "Add GnuPG signature key to project",
|
||||
Example: templates.Examples(`
|
||||
# Add GnuPG signature key KEY-ID to project PROJECT
|
||||
argocd proj add-signature-key PROJECT KEY-ID
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -221,10 +188,6 @@ func NewProjectRemoveSignatureKeyCommand(clientOpts *argocdclient.ClientOptions)
|
||||
var command = &cobra.Command{
|
||||
Use: "remove-signature-key PROJECT KEY-ID",
|
||||
Short: "Remove GnuPG signature key from project",
|
||||
Example: templates.Examples(`
|
||||
# Remove GnuPG signature key KEY-ID from project PROJECT
|
||||
argocd proj remove-signature-key PROJECT KEY-ID
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -275,13 +238,6 @@ func NewProjectAddDestinationCommand(clientOpts *argocdclient.ClientOptions) *co
|
||||
var command = &cobra.Command{
|
||||
Use: "add-destination PROJECT SERVER/NAME NAMESPACE",
|
||||
Short: "Add project destination",
|
||||
Example: templates.Examples(`
|
||||
# Add project destination using a server URL (SERVER) in the specified namespace (NAMESPACE) on the project with name PROJECT
|
||||
argocd proj add-destination PROJECT SERVER NAMESPACE
|
||||
|
||||
# Add project destination using a server name (NAME) in the specified namespace (NAMESPACE) on the project with name PROJECT
|
||||
argocd proj add-destination PROJECT NAME NAMESPACE --name
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -319,10 +275,6 @@ func NewProjectRemoveDestinationCommand(clientOpts *argocdclient.ClientOptions)
|
||||
var command = &cobra.Command{
|
||||
Use: "remove-destination PROJECT SERVER NAMESPACE",
|
||||
Short: "Remove project destination",
|
||||
Example: templates.Examples(`
|
||||
# Remove the destination (SERVER) from the specified namespace (NAMESPACE) on the project with name PROJECT
|
||||
argocd proj remove-destination PROJECT SERVER NAMESPACE
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -367,13 +319,6 @@ func NewProjectAddOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOptions)
|
||||
var command = &cobra.Command{
|
||||
Use: "add-orphaned-ignore PROJECT GROUP KIND",
|
||||
Short: "Add a resource to orphaned ignore list",
|
||||
Example: templates.Examples(`
|
||||
# Add a resource of the specified GROUP and KIND to orphaned ignore list on the project with name PROJECT
|
||||
argocd proj add-orphaned-ignore PROJECT GROUP KIND
|
||||
|
||||
# Add resources of the specified GROUP and KIND using a NAME pattern to orphaned ignore list on the project with name PROJECT
|
||||
argocd proj add-orphaned-ignore PROJECT GROUP KIND --name NAME
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -417,15 +362,8 @@ func NewProjectRemoveOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOption
|
||||
name string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "remove-orphaned-ignore PROJECT GROUP KIND",
|
||||
Use: "remove-orphaned-ignore PROJECT GROUP KIND NAME",
|
||||
Short: "Remove a resource from orphaned ignore list",
|
||||
Example: templates.Examples(`
|
||||
# Remove a resource of the specified GROUP and KIND from orphaned ignore list on the project with name PROJECT
|
||||
argocd proj remove-orphaned-ignore PROJECT GROUP KIND
|
||||
|
||||
# Remove resources of the specified GROUP and KIND using a NAME pattern from orphaned ignore list on the project with name PROJECT
|
||||
argocd proj remove-orphaned-ignore PROJECT GROUP KIND --name NAME
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -472,10 +410,6 @@ func NewProjectAddSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
var command = &cobra.Command{
|
||||
Use: "add-source PROJECT URL",
|
||||
Short: "Add project source repository",
|
||||
Example: templates.Examples(`
|
||||
# Add a source repository (URL) to the project with name PROJECT
|
||||
argocd proj add-source PROJECT URL
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -538,7 +472,7 @@ func modifyResourcesList(list *[]metav1.GroupKind, add bool, listDesc string, gr
|
||||
}
|
||||
}
|
||||
|
||||
func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdclient.ClientOptions, allow bool, namespacedList bool) *cobra.Command {
|
||||
func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.ClientOptions, allow bool, namespacedList bool) *cobra.Command {
|
||||
var (
|
||||
listType string
|
||||
defaultList string
|
||||
@@ -549,9 +483,8 @@ func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdc
|
||||
defaultList = "allow"
|
||||
}
|
||||
var command = &cobra.Command{
|
||||
Use: cmdUse,
|
||||
Short: cmdDesc,
|
||||
Example: templates.Examples(examples),
|
||||
Use: cmdUse,
|
||||
Short: cmdDesc,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -600,44 +533,28 @@ func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdc
|
||||
func NewProjectAllowNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
use := "allow-namespace-resource PROJECT GROUP KIND"
|
||||
desc := "Removes a namespaced API resource from the deny list or add a namespaced API resource to the allow list"
|
||||
examples := `
|
||||
# Removes a namespaced API resource with specified GROUP and KIND from the deny list or add a namespaced API resource to the allow list for project PROJECT
|
||||
argocd proj allow-namespace-resource PROJECT GROUP KIND
|
||||
`
|
||||
return modifyResourceListCmd(use, desc, examples, clientOpts, true, true)
|
||||
return modifyResourceListCmd(use, desc, clientOpts, true, true)
|
||||
}
|
||||
|
||||
// NewProjectDenyNamespaceResourceCommand returns a new instance of an `argocd proj deny-namespace-resource` command
|
||||
func NewProjectDenyNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
use := "deny-namespace-resource PROJECT GROUP KIND"
|
||||
desc := "Adds a namespaced API resource to the deny list or removes a namespaced API resource from the allow list"
|
||||
examples := `
|
||||
# Adds a namespaced API resource with specified GROUP and KIND from the deny list or removes a namespaced API resource from the allow list for project PROJECT
|
||||
argocd proj deny-namespace-resource PROJECT GROUP KIND
|
||||
`
|
||||
return modifyResourceListCmd(use, desc, examples, clientOpts, false, true)
|
||||
return modifyResourceListCmd(use, desc, clientOpts, false, true)
|
||||
}
|
||||
|
||||
// NewProjectDenyClusterResourceCommand returns a new instance of an `deny-cluster-resource` command
|
||||
func NewProjectDenyClusterResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
use := "deny-cluster-resource PROJECT GROUP KIND"
|
||||
desc := "Removes a cluster-scoped API resource from the allow list and adds it to deny list"
|
||||
examples := `
|
||||
# Removes a cluster-scoped API resource with specified GROUP and KIND from the allow list and adds it to deny list for project PROJECT
|
||||
argocd proj deny-cluster-resource PROJECT GROUP KIND
|
||||
`
|
||||
return modifyResourceListCmd(use, desc, examples, clientOpts, false, false)
|
||||
return modifyResourceListCmd(use, desc, clientOpts, false, false)
|
||||
}
|
||||
|
||||
// NewProjectAllowClusterResourceCommand returns a new instance of an `argocd proj allow-cluster-resource` command
|
||||
func NewProjectAllowClusterResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
use := "allow-cluster-resource PROJECT GROUP KIND"
|
||||
desc := "Adds a cluster-scoped API resource to the allow list and removes it from deny list"
|
||||
examples := `
|
||||
# Adds a cluster-scoped API resource with specified GROUP and KIND to the allow list and removes it from deny list for project PROJECT
|
||||
argocd proj allow-cluster-resource PROJECT GROUP KIND
|
||||
`
|
||||
return modifyResourceListCmd(use, desc, examples, clientOpts, true, false)
|
||||
return modifyResourceListCmd(use, desc, clientOpts, true, false)
|
||||
}
|
||||
|
||||
// NewProjectRemoveSourceCommand returns a new instance of an `argocd proj remove-src` command
|
||||
@@ -645,10 +562,6 @@ func NewProjectRemoveSourceCommand(clientOpts *argocdclient.ClientOptions) *cobr
|
||||
var command = &cobra.Command{
|
||||
Use: "remove-source PROJECT URL",
|
||||
Short: "Remove project source repository",
|
||||
Example: templates.Examples(`
|
||||
# Remove URL source repository to project PROJECT
|
||||
argocd proj remove-source PROJECT URL
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -689,10 +602,6 @@ func NewProjectDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
var command = &cobra.Command{
|
||||
Use: "delete PROJECT",
|
||||
Short: "Delete project",
|
||||
Example: templates.Examples(`
|
||||
# Delete the project with name PROJECT
|
||||
argocd proj delete PROJECT
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -736,13 +645,6 @@ func NewProjectListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
var command = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List projects",
|
||||
Example: templates.Examples(`
|
||||
# List all available projects
|
||||
argocd proj list
|
||||
|
||||
# List all available projects in yaml format
|
||||
argocd proj list -o yaml
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -908,14 +810,6 @@ func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
var command = &cobra.Command{
|
||||
Use: "get PROJECT",
|
||||
Short: "Get project details",
|
||||
Example: templates.Examples(`
|
||||
# Get details from project PROJECT
|
||||
argocd proj get PROJECT
|
||||
|
||||
# Get details from project PROJECT in yaml format
|
||||
argocd proj get PROJECT -o yaml
|
||||
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -924,7 +818,10 @@ func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
os.Exit(1)
|
||||
}
|
||||
projName := args[0]
|
||||
detailedProject := getProject(c, clientOpts, ctx, projName)
|
||||
conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
detailedProject, err := projIf.GetDetailedProject(ctx, &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
|
||||
switch output {
|
||||
case "yaml", "json":
|
||||
@@ -941,22 +838,10 @@ func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
return command
|
||||
}
|
||||
|
||||
func getProject(c *cobra.Command, clientOpts *argocdclient.ClientOptions, ctx context.Context, projName string) *projectpkg.DetailedProjectsResponse {
|
||||
conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
detailedProject, err := projIf.GetDetailedProject(ctx, &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
return detailedProject
|
||||
}
|
||||
|
||||
func NewProjectEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "edit PROJECT",
|
||||
Short: "Edit project",
|
||||
Example: templates.Examples(`
|
||||
# Edit the information on project with name PROJECT
|
||||
argocd proj edit PROJECT
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/jwt"
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -57,30 +56,6 @@ func NewProjectRoleAddPolicyCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
var command = &cobra.Command{
|
||||
Use: "add-policy PROJECT ROLE-NAME",
|
||||
Short: "Add a policy to a project role",
|
||||
Example: `# Before adding new policy
|
||||
$ argocd proj role get test-project test-role
|
||||
Role Name: test-role
|
||||
Description:
|
||||
Policies:
|
||||
p, proj:test-project:test-role, projects, get, test-project, allow
|
||||
JWT Tokens:
|
||||
ID ISSUED-AT EXPIRES-AT
|
||||
1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) <none>
|
||||
|
||||
# Add a new policy to allow update to the project
|
||||
$ argocd proj role add-policy test-project test-role -a update -p allow -o project
|
||||
|
||||
# Policy should be updated
|
||||
$ argocd proj role get test-project test-role
|
||||
Role Name: test-role
|
||||
Description:
|
||||
Policies:
|
||||
p, proj:test-project:test-role, projects, get, test-project, allow
|
||||
p, proj:test-project:test-role, applications, update, test-project/project, allow
|
||||
JWT Tokens:
|
||||
ID ISSUED-AT EXPIRES-AT
|
||||
1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) <none>
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -118,30 +93,6 @@ func NewProjectRoleRemovePolicyCommand(clientOpts *argocdclient.ClientOptions) *
|
||||
var command = &cobra.Command{
|
||||
Use: "remove-policy PROJECT ROLE-NAME",
|
||||
Short: "Remove a policy from a role within a project",
|
||||
Example: `List the policy of the test-role before removing a policy
|
||||
$ argocd proj role get test-project test-role
|
||||
Role Name: test-role
|
||||
Description:
|
||||
Policies:
|
||||
p, proj:test-project:test-role, projects, get, test-project, allow
|
||||
p, proj:test-project:test-role, applications, update, test-project/project, allow
|
||||
JWT Tokens:
|
||||
ID ISSUED-AT EXPIRES-AT
|
||||
1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) <none>
|
||||
|
||||
# Remove the policy to allow update to objects
|
||||
$ argocd proj role remove-policy test-project test-role -a update -p allow -o project
|
||||
|
||||
# The role should be removed now.
|
||||
$ argocd proj role get test-project test-role
|
||||
Role Name: test-role
|
||||
Description:
|
||||
Policies:
|
||||
p, proj:test-project:test-role, projects, get, test-project, allow
|
||||
JWT Tokens:
|
||||
ID ISSUED-AT EXPIRES-AT
|
||||
1696759698 2023-10-08T11:08:18+01:00 (4 hours ago) <none>
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -189,11 +140,6 @@ func NewProjectRoleCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
var command = &cobra.Command{
|
||||
Use: "create PROJECT ROLE-NAME",
|
||||
Short: "Create a project role",
|
||||
Example: templates.Examples(`
|
||||
# Create a project role in the "my-project" project with the name "my-role".
|
||||
argocd proj role create my-project my-role --description "My project role description"
|
||||
`),
|
||||
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -228,9 +174,8 @@ func NewProjectRoleCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
// NewProjectRoleDeleteCommand returns a new instance of an `argocd proj role delete` command
|
||||
func NewProjectRoleDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "delete PROJECT ROLE-NAME",
|
||||
Short: "Delete a project role",
|
||||
Example: `$ argocd proj role delete test-project test-role`,
|
||||
Use: "delete PROJECT ROLE-NAME",
|
||||
Short: "Delete a project role",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -278,15 +223,8 @@ func NewProjectRoleCreateTokenCommand(clientOpts *argocdclient.ClientOptions) *c
|
||||
tokenID string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "create-token PROJECT ROLE-NAME",
|
||||
Short: "Create a project token",
|
||||
Example: `$ argocd proj role create-token test-project test-role
|
||||
Create token succeeded for proj:test-project:test-role.
|
||||
ID: f316c466-40bd-4cfd-8a8c-1392e92255d4
|
||||
Issued At: 2023-10-08T15:21:40+01:00
|
||||
Expires At: Never
|
||||
Token: xxx
|
||||
`,
|
||||
Use: "create-token PROJECT ROLE-NAME",
|
||||
Short: "Create a project token",
|
||||
Aliases: []string{"token-create"},
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
@@ -350,13 +288,8 @@ func NewProjectRoleListTokensCommand(clientOpts *argocdclient.ClientOptions) *co
|
||||
useUnixTime bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "list-tokens PROJECT ROLE-NAME",
|
||||
Short: "List tokens for a given role.",
|
||||
Example: `$ argocd proj role list-tokens test-project test-role
|
||||
ID ISSUED AT EXPIRES AT
|
||||
f316c466-40bd-4cfd-8a8c-1392e92255d4 2023-10-08T15:21:40+01:00 Never
|
||||
fa9d3517-c52d-434c-9bff-215b38508842 2023-10-08T11:08:18+01:00 Never
|
||||
`,
|
||||
Use: "list-tokens PROJECT ROLE-NAME",
|
||||
Short: "List tokens for a given role.",
|
||||
Aliases: []string{"list-token", "token-list"},
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
@@ -406,35 +339,8 @@ fa9d3517-c52d-434c-9bff-215b38508842 2023-10-08T11:08:18+01:00 Never
|
||||
// NewProjectRoleDeleteTokenCommand returns a new instance of an `argocd proj role delete-token` command
|
||||
func NewProjectRoleDeleteTokenCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "delete-token PROJECT ROLE-NAME ISSUED-AT",
|
||||
Short: "Delete a project token",
|
||||
Example: `#Create project test-project
|
||||
$ argocd proj create test-project
|
||||
|
||||
# Create a role associated with test-project
|
||||
$ argocd proj role create test-project test-role
|
||||
Role 'test-role' created
|
||||
|
||||
# Create test-role associated with test-project
|
||||
$ argocd proj role create-token test-project test-role
|
||||
Create token succeeded for proj:test-project:test-role.
|
||||
ID: c312450e-12e1-4e0d-9f65-fac9cb027b32
|
||||
Issued At: 2023-10-08T13:58:57+01:00
|
||||
Expires At: Never
|
||||
Token: xxx
|
||||
|
||||
# Get test-role id to input into the delete-token command below
|
||||
$ argocd proj role get test-project test-role
|
||||
Role Name: test-role
|
||||
Description:
|
||||
Policies:
|
||||
p, proj:test-project:test-role, projects, get, test-project, allow
|
||||
JWT Tokens:
|
||||
ID ISSUED-AT EXPIRES-AT
|
||||
1696769937 2023-10-08T13:58:57+01:00 (6 minutes ago) <none>
|
||||
|
||||
$ argocd proj role delete-token test-project test-role 1696769937
|
||||
`,
|
||||
Use: "delete-token PROJECT ROLE-NAME ISSUED-AT",
|
||||
Short: "Delete a project token",
|
||||
Aliases: []string{"token-delete", "remove-token"},
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
@@ -483,15 +389,6 @@ func NewProjectRoleListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
var command = &cobra.Command{
|
||||
Use: "list PROJECT",
|
||||
Short: "List all the roles in a project",
|
||||
Example: templates.Examples(`
|
||||
# This command will list all the roles in argocd-project in a default table format.
|
||||
argocd proj role list PROJECT
|
||||
|
||||
# List the roles in the project in formats like json, yaml, wide, or name.
|
||||
argocd proj role list PROJECT --output json
|
||||
|
||||
`),
|
||||
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -527,16 +424,6 @@ func NewProjectRoleGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
var command = &cobra.Command{
|
||||
Use: "get PROJECT ROLE-NAME",
|
||||
Short: "Get the details of a specific role",
|
||||
Example: `$ argocd proj role get test-project test-role
|
||||
Role Name: test-role
|
||||
Description:
|
||||
Policies:
|
||||
p, proj:test-project:test-role, projects, get, test-project, allow
|
||||
JWT Tokens:
|
||||
ID ISSUED-AT EXPIRES-AT
|
||||
1696774900 2023-10-08T15:21:40+01:00 (4 minutes ago) <none>
|
||||
1696759698 2023-10-08T11:08:18+01:00 (4 hours ago) <none>
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
|
||||
@@ -125,23 +125,6 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) *
|
||||
var command = &cobra.Command{
|
||||
Use: "add PROJECT",
|
||||
Short: "Add a sync window to a project",
|
||||
Example: `# Add a 1 hour allow sync window
|
||||
argocd proj windows add PROJECT \
|
||||
--kind allow \
|
||||
--schedule "0 22 * * *" \
|
||||
--duration 1h \
|
||||
--applications "*"
|
||||
|
||||
# Add a deny sync window with the ability to manually sync.
|
||||
argocd proj windows add PROJECT \
|
||||
--kind deny \
|
||||
--schedule "30 10 * * *" \
|
||||
--duration 30m \
|
||||
--applications "prod-\\*,website" \
|
||||
--namespaces "default,\\*-prod" \
|
||||
--clusters "prod,staging" \
|
||||
--manual-sync
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -175,7 +158,7 @@ argocd proj windows add PROJECT \
|
||||
return command
|
||||
}
|
||||
|
||||
// NewProjectWindowsDeleteCommand returns a new instance of an `argocd proj windows delete` command
|
||||
// NewProjectWindowsAddWindowCommand returns a new instance of an `argocd proj windows delete` command
|
||||
func NewProjectWindowsDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "delete PROJECT ID",
|
||||
@@ -222,10 +205,6 @@ func NewProjectWindowsUpdateCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
Use: "update PROJECT ID",
|
||||
Short: "Update a project sync window",
|
||||
Long: "Update a project sync window. Requires ID which can be found by running \"argocd proj windows list PROJECT\"",
|
||||
Example: `# Change a sync window's schedule
|
||||
argocd proj windows update PROJECT ID \
|
||||
--schedule "0 20 * * *"
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -274,12 +253,6 @@ func NewProjectWindowsListCommand(clientOpts *argocdclient.ClientOptions) *cobra
|
||||
var command = &cobra.Command{
|
||||
Use: "list PROJECT",
|
||||
Short: "List project sync windows",
|
||||
Example: `# List project windows
|
||||
argocd proj windows list PROJECT
|
||||
|
||||
# List project windows in yaml format
|
||||
argocd proj windows list PROJECT -o yaml
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -312,8 +285,8 @@ argocd proj windows list PROJECT -o yaml
|
||||
func printSyncWindows(proj *v1alpha1.AppProject) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
var fmtStr string
|
||||
headers := []interface{}{"ID", "STATUS", "KIND", "SCHEDULE", "DURATION", "APPLICATIONS", "NAMESPACES", "CLUSTERS", "MANUALSYNC", "TIMEZONE"}
|
||||
fmtStr = strings.Repeat("%s\t", len(headers)) + "\n"
|
||||
headers := []interface{}{"ID", "STATUS", "KIND", "SCHEDULE", "DURATION", "APPLICATIONS", "NAMESPACES", "CLUSTERS", "MANUALSYNC"}
|
||||
fmtStr = "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
|
||||
fmt.Fprintf(w, fmtStr, headers...)
|
||||
if proj.Spec.SyncWindows.HasWindows() {
|
||||
for i, window := range proj.Spec.SyncWindows {
|
||||
@@ -327,7 +300,6 @@ func printSyncWindows(proj *v1alpha1.AppProject) {
|
||||
formatListOutput(window.Namespaces),
|
||||
formatListOutput(window.Clusters),
|
||||
formatManualOutput(window.ManualSync),
|
||||
window.TimeZone,
|
||||
}
|
||||
fmt.Fprintf(w, fmtStr, vals...)
|
||||
}
|
||||
|
||||
@@ -84,18 +84,6 @@ func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
errors.CheckError(err)
|
||||
fmt.Printf("Context '%s' updated\n", localCfg.CurrentContext)
|
||||
},
|
||||
Example: `
|
||||
# Reinitiates the login with previous contexts
|
||||
argocd relogin
|
||||
|
||||
# Reinitiates the login with password
|
||||
argocd relogin --password YOUR_PASSWORD
|
||||
|
||||
# Configure direct access using Kubernetes API server
|
||||
argocd login cd.argoproj.io --core
|
||||
|
||||
# If user logged in with - "argocd login cd.argoproj.io" with sso login
|
||||
# The command - "argocd relogin" will Reinitiates SSO login and updates the server context`,
|
||||
}
|
||||
command.Flags().StringVar(&password, "password", "", "The password of an account to authenticate")
|
||||
command.Flags().IntVar(&ssoPort, "sso-port", DefaultSSOLocalPort, "Port to run local OAuth2 login application")
|
||||
|
||||
@@ -29,19 +29,6 @@ func NewRepoCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
},
|
||||
Example: `
|
||||
# Add git repository connection parameters
|
||||
argocd repo add git@git.example.com:repos/repo
|
||||
|
||||
# Get a Configured Repository by URL
|
||||
argocd repo get https://github.com/yourusername/your-repo.git
|
||||
|
||||
# List Configured Repositories
|
||||
argocd repo list
|
||||
|
||||
# Remove Repository Credentials
|
||||
argocd repo rm https://github.com/yourusername/your-repo.git
|
||||
`,
|
||||
}
|
||||
|
||||
command.AddCommand(NewRepoAddCommand(clientOpts))
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
)
|
||||
|
||||
// NewRepoCredsCommand returns a new instance of an `argocd repocreds` command
|
||||
@@ -25,16 +24,6 @@ func NewRepoCredsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
var command = &cobra.Command{
|
||||
Use: "repocreds",
|
||||
Short: "Manage repository connection parameters",
|
||||
Example: templates.Examples(`
|
||||
# Add credentials with user/pass authentication to use for all repositories under the specified URL
|
||||
argocd repocreds add URL --username USERNAME --password PASSWORD
|
||||
|
||||
# List all the configured repository credentials
|
||||
argocd repocreds list
|
||||
|
||||
# Remove credentials for the repositories with speficied URL
|
||||
argocd repocreds rm URL
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
@@ -195,10 +184,6 @@ func NewRepoCredsRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
var command = &cobra.Command{
|
||||
Use: "rm CREDSURL",
|
||||
Short: "Remove repository credentials",
|
||||
Example: templates.Examples(`
|
||||
# Remove credentials for the repositories with URL https://git.example.com/repos
|
||||
argocd repocreds rm https://git.example.com/repos/
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -246,19 +231,6 @@ func NewRepoCredsListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
var command = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List configured repository credentials",
|
||||
Example: templates.Examples(`
|
||||
# List all repo urls
|
||||
argocd repocreds list
|
||||
|
||||
# List all repo urls in json format
|
||||
argocd repocreds list -o json
|
||||
|
||||
# List all repo urls in yaml format
|
||||
argocd repocreds list -o yaml
|
||||
|
||||
# List all repo urls in url format
|
||||
argocd repocreds list -o url
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/admin"
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/config"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
)
|
||||
@@ -59,7 +55,7 @@ func NewCommand() *cobra.Command {
|
||||
command.AddCommand(NewLogoutCommand(&clientOpts))
|
||||
command.AddCommand(initialize.InitCommand(NewCertCommand(&clientOpts)))
|
||||
command.AddCommand(initialize.InitCommand(NewGPGCommand(&clientOpts)))
|
||||
command.AddCommand(admin.NewAdminCommand(&clientOpts))
|
||||
command.AddCommand(admin.NewAdminCommand())
|
||||
|
||||
defaultLocalConfigPath, err := localconfig.DefaultLocalConfigPath()
|
||||
errors.CheckError(err)
|
||||
@@ -80,11 +76,6 @@ func NewCommand() *cobra.Command {
|
||||
command.PersistentFlags().StringVar(&clientOpts.PortForwardNamespace, "port-forward-namespace", config.GetFlag("port-forward-namespace", ""), "Namespace name which should be used for port forwarding")
|
||||
command.PersistentFlags().IntVar(&clientOpts.HttpRetryMax, "http-retry-max", 0, "Maximum number of retries to establish http connection to Argo CD server")
|
||||
command.PersistentFlags().BoolVar(&clientOpts.Core, "core", false, "If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server")
|
||||
command.PersistentFlags().StringVar(&clientOpts.ServerName, "server-name", env.StringFromEnv(common.EnvServerName, common.DefaultServerName), fmt.Sprintf("Name of the Argo CD API server; set this or the %s environment variable when the server's name label differs from the default, for example when installing via the Helm chart", common.EnvServerName))
|
||||
command.PersistentFlags().StringVar(&clientOpts.AppControllerName, "controller-name", env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName), fmt.Sprintf("Name of the Argo CD Application controller; set this or the %s environment variable when the controller's name label differs from the default, for example when installing via the Helm chart", common.EnvAppControllerName))
|
||||
command.PersistentFlags().StringVar(&clientOpts.RedisHaProxyName, "redis-haproxy-name", env.StringFromEnv(common.EnvRedisHaProxyName, common.DefaultRedisHaProxyName), fmt.Sprintf("Name of the Redis HA Proxy; set this or the %s environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart", common.EnvRedisHaProxyName))
|
||||
command.PersistentFlags().StringVar(&clientOpts.RedisName, "redis-name", env.StringFromEnv(common.EnvRedisName, common.DefaultRedisName), fmt.Sprintf("Name of the Redis deployment; set this or the %s environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart", common.EnvRedisName))
|
||||
command.PersistentFlags().StringVar(&clientOpts.RepoServerName, "repo-server-name", env.StringFromEnv(common.EnvRepoServerName, common.DefaultRepoServerName), fmt.Sprintf("Name of the Argo CD Repo server; set this or the %s environment variable when the server's name label differs from the default, for example when installing via the Helm chart", common.EnvRepoServerName))
|
||||
|
||||
clientOpts.KubeOverrides = &clientcmd.ConfigOverrides{}
|
||||
command.PersistentFlags().StringVar(&clientOpts.KubeOverrides.CurrentContext, "kube-context", "", "Directs the command to the given kube-context")
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"k8s.io/apimachinery/pkg/util/duration"
|
||||
)
|
||||
|
||||
const (
|
||||
firstElemPrefix = `├─`
|
||||
lastElemPrefix = `└─`
|
||||
indent = " "
|
||||
pipe = `│ `
|
||||
)
|
||||
|
||||
func extractHealthStatusAndReason(node v1alpha1.ResourceNode) (healthStatus health.HealthStatusCode, reason string) {
|
||||
if node.Health != nil {
|
||||
healthStatus = node.Health.Status
|
||||
reason = node.Health.Message
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func treeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentToChildMap map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState, w *tabwriter.Writer) {
|
||||
healthStatus, _ := extractHealthStatusAndReason(parent)
|
||||
if mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] != nil {
|
||||
value := mapNodeNameToResourceState[parent.Kind+"/"+parent.Name]
|
||||
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+value.Name, value.Status, value.Health, value.Message)
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+parent.Name, "", healthStatus, "")
|
||||
}
|
||||
chs := parentToChildMap[parent.UID]
|
||||
for i, childUid := range chs {
|
||||
var p string
|
||||
switch i {
|
||||
case len(chs) - 1:
|
||||
p = prefix + lastElemPrefix
|
||||
default:
|
||||
p = prefix + firstElemPrefix
|
||||
}
|
||||
treeViewAppGet(p, uidToNodeMap, parentToChildMap, uidToNodeMap[childUid], mapNodeNameToResourceState, w)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func detailedTreeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState, w *tabwriter.Writer) {
|
||||
healthStatus, reason := extractHealthStatusAndReason(parent)
|
||||
var age = "<unknown>"
|
||||
if parent.CreatedAt != nil {
|
||||
age = duration.HumanDuration(time.Since(parent.CreatedAt.Time))
|
||||
}
|
||||
|
||||
if mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] != nil {
|
||||
value := mapNodeNameToResourceState[parent.Kind+"/"+parent.Name]
|
||||
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+value.Name, value.Status, value.Health, age, value.Message, reason)
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+parent.Name, "", healthStatus, age, "", reason)
|
||||
|
||||
}
|
||||
chs := parentChildMap[parent.UID]
|
||||
for i, child := range chs {
|
||||
var p string
|
||||
switch i {
|
||||
case len(chs) - 1:
|
||||
p = prefix + lastElemPrefix
|
||||
default:
|
||||
p = prefix + firstElemPrefix
|
||||
}
|
||||
detailedTreeViewAppGet(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], mapNodeNameToResourceState, w)
|
||||
}
|
||||
}
|
||||
|
||||
func treeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) {
|
||||
if len(parent.ParentRefs) == 0 {
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "No")
|
||||
}
|
||||
chs := parentChildMap[parent.UID]
|
||||
for i, child := range chs {
|
||||
var p string
|
||||
switch i {
|
||||
case len(chs) - 1:
|
||||
p = prefix + lastElemPrefix
|
||||
default:
|
||||
p = prefix + firstElemPrefix
|
||||
}
|
||||
treeViewAppResourcesNotOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w)
|
||||
}
|
||||
}
|
||||
|
||||
func treeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) {
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes")
|
||||
chs := parentChildMap[parent.UID]
|
||||
for i, child := range chs {
|
||||
var p string
|
||||
switch i {
|
||||
case len(chs) - 1:
|
||||
p = prefix + lastElemPrefix
|
||||
default:
|
||||
p = prefix + firstElemPrefix
|
||||
}
|
||||
treeViewAppResourcesOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w)
|
||||
}
|
||||
}
|
||||
|
||||
func detailedTreeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) {
|
||||
|
||||
if len(parent.ParentRefs) == 0 {
|
||||
healthStatus, reason := extractHealthStatusAndReason(parent)
|
||||
var age = "<unknown>"
|
||||
if parent.CreatedAt != nil {
|
||||
age = duration.HumanDuration(time.Since(parent.CreatedAt.Time))
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "No", age, healthStatus, reason)
|
||||
}
|
||||
chs := parentChildMap[parent.UID]
|
||||
for i, child := range chs {
|
||||
var p string
|
||||
switch i {
|
||||
case len(chs) - 1:
|
||||
p = prefix + lastElemPrefix
|
||||
default:
|
||||
p = prefix + firstElemPrefix
|
||||
}
|
||||
detailedTreeViewAppResourcesNotOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w)
|
||||
}
|
||||
}
|
||||
|
||||
func detailedTreeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) {
|
||||
healthStatus, reason := extractHealthStatusAndReason(parent)
|
||||
var age = "<unknown>"
|
||||
if parent.CreatedAt != nil {
|
||||
age = duration.HumanDuration(time.Since(parent.CreatedAt.Time))
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes", age, healthStatus, reason)
|
||||
|
||||
chs := parentChildMap[parent.UID]
|
||||
for i, child := range chs {
|
||||
var p string
|
||||
switch i {
|
||||
case len(chs) - 1:
|
||||
p = prefix + lastElemPrefix
|
||||
default:
|
||||
p = prefix + firstElemPrefix
|
||||
}
|
||||
detailedTreeViewAppResourcesOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w)
|
||||
}
|
||||
}
|
||||
|
||||
func printPrefix(p string) string {
|
||||
|
||||
if strings.HasSuffix(p, firstElemPrefix) {
|
||||
p = strings.Replace(p, firstElemPrefix, pipe, strings.Count(p, firstElemPrefix)-1)
|
||||
} else {
|
||||
p = strings.ReplaceAll(p, firstElemPrefix, pipe)
|
||||
}
|
||||
|
||||
if strings.HasSuffix(p, lastElemPrefix) {
|
||||
p = strings.Replace(p, lastElemPrefix, strings.Repeat(" ", len([]rune(lastElemPrefix))), strings.Count(p, lastElemPrefix)-1)
|
||||
} else {
|
||||
p = strings.ReplaceAll(p, lastElemPrefix, strings.Repeat(" ", len([]rune(lastElemPrefix))))
|
||||
}
|
||||
return p
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user