Compare commits
1 Commits
commit-ser
...
temp-cherr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc50d58e72 |
5
.github/dependabot.yml
vendored
@@ -17,11 +17,6 @@ updates:
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/ui-test/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
|
||||
3
.github/workflows/README.md
vendored
@@ -6,10 +6,9 @@
|
||||
| codeql.yaml | CodeQL analysis |
|
||||
| image-reuse.yaml | Build, push, and Sign container images |
|
||||
| image.yaml | Build container image for PR's & publish for push events |
|
||||
| init-release.yaml | Build manifests and version then create a PR for release branch|
|
||||
| pr-title-check.yaml| Lint PR for semantic information |
|
||||
| init-release.yaml | Build manifests and version then create a PR for release branch|
|
||||
| release.yaml | Build images, cli-binaries, provenances, and post actions |
|
||||
| scorecard.yaml | Generate scorecard for supply-chain security |
|
||||
| update-snyk.yaml | Scheduled snyk reports |
|
||||
|
||||
# Reusable workflows
|
||||
|
||||
196
.github/workflows/ci-build.yaml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Integration tests
|
||||
on:
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
env:
|
||||
# Golang version to use across CI steps
|
||||
GOLANG_VERSION: '1.22'
|
||||
GOLANG_VERSION: '1.21'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -23,64 +23,36 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
backend: ${{ steps.filter.outputs.backend_any_changed }}
|
||||
frontend: ${{ steps.filter.outputs.frontend_any_changed }}
|
||||
steps:
|
||||
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- uses: tj-actions/changed-files@90a06d6ba9543371ab4df8eeca0be07ca6054959 # v42.0.2
|
||||
id: filter
|
||||
with:
|
||||
# Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file
|
||||
files_yaml: |
|
||||
backend:
|
||||
- '!ui/**'
|
||||
- '!**.md'
|
||||
- '!**/*.md'
|
||||
- '!docs/**'
|
||||
frontend:
|
||||
- 'ui/**'
|
||||
- Dockerfile
|
||||
docs:
|
||||
- 'docs/**'
|
||||
check-go:
|
||||
name: Ensure Go modules synchronicity
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Download all Go modules
|
||||
run: |
|
||||
go mod download
|
||||
- name: Check for tidiness of go.mod and go.sum
|
||||
- name: Check for tidyness of go.mod and go.sum
|
||||
run: |
|
||||
go mod tidy
|
||||
git diff --exit-code -- .
|
||||
|
||||
build-go:
|
||||
name: Build & cache Go code
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -95,42 +67,37 @@ jobs:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
|
||||
name: Lint Go code
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64 # v6.0.1
|
||||
uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
|
||||
with:
|
||||
version: v1.58.2
|
||||
args: --verbose
|
||||
version: v1.54.0
|
||||
args: --enable gofmt --timeout 10m --exclude SA5011 --verbose --max-issues-per-linter 0 --max-same-issues 0
|
||||
|
||||
test-go:
|
||||
name: Run unit tests for Go packages
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
steps:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -150,7 +117,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -171,35 +138,33 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-local
|
||||
- name: Generate code coverage artifacts
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: code-coverage
|
||||
path: coverage.out
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results/
|
||||
|
||||
test-go-race:
|
||||
name: Run unit tests with -race for Go packages
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
steps:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -219,7 +184,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -240,22 +205,19 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-race-local
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: race-results
|
||||
path: test-results/
|
||||
|
||||
codegen:
|
||||
name: Check changes to generated code
|
||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.docs == 'true'}}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Create symlink in GOPATH
|
||||
@@ -298,21 +260,17 @@ jobs:
|
||||
|
||||
build-ui:
|
||||
name: Build, test & lint UI code
|
||||
# We run UI logic for backend changes so that we have a complete set of coverage documents to send to codecov.
|
||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: '21.6.1'
|
||||
node-version: '20.7.0'
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -334,22 +292,20 @@ jobs:
|
||||
|
||||
analyze:
|
||||
name: Process & analyze test artifacts
|
||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- test-go
|
||||
- build-ui
|
||||
- changes
|
||||
env:
|
||||
sonar_secret: ${{ secrets.SONAR_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -359,22 +315,19 @@ jobs:
|
||||
- name: Create test-results directory
|
||||
run: |
|
||||
mkdir -p test-results
|
||||
- name: Get code coverage artifact
|
||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
||||
- name: Get code coverage artifiact
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: code-coverage
|
||||
- name: Get test result artifact
|
||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results
|
||||
- name: Upload code coverage information to codecov.io
|
||||
uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # v4.4.1
|
||||
uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
|
||||
with:
|
||||
file: coverage.out
|
||||
fail_ci_if_error: true
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Perform static code analysis using SonarCloud
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -383,37 +336,35 @@ jobs:
|
||||
SCANNER_PATH: /tmp/cache/scanner
|
||||
OS: linux
|
||||
run: |
|
||||
# We do not use the provided action, because it does contain an old
|
||||
# version of the scanner, and also takes time to build.
|
||||
set -e
|
||||
mkdir -p ${SCANNER_PATH}
|
||||
export SONAR_USER_HOME=${SCANNER_PATH}/.sonar
|
||||
if [[ ! -x "${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner" ]]; then
|
||||
curl -Ol https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip
|
||||
unzip -qq -o sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip -d ${SCANNER_PATH}
|
||||
fi
|
||||
|
||||
chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner
|
||||
chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/jre/bin/java
|
||||
|
||||
# Explicitly set NODE_MODULES
|
||||
export NODE_MODULES=${PWD}/ui/node_modules
|
||||
export NODE_PATH=${PWD}/ui/node_modules
|
||||
|
||||
${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner
|
||||
# We do not use the provided action, because it does contain an old
|
||||
# version of the scanner, and also takes time to build.
|
||||
set -e
|
||||
mkdir -p ${SCANNER_PATH}
|
||||
export SONAR_USER_HOME=${SCANNER_PATH}/.sonar
|
||||
if [[ ! -x "${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner" ]]; then
|
||||
curl -Ol https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip
|
||||
unzip -qq -o sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip -d ${SCANNER_PATH}
|
||||
fi
|
||||
|
||||
chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner
|
||||
chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/jre/bin/java
|
||||
|
||||
# Explicitly set NODE_MODULES
|
||||
export NODE_MODULES=${PWD}/ui/node_modules
|
||||
export NODE_PATH=${PWD}/ui/node_modules
|
||||
|
||||
${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner
|
||||
if: env.sonar_secret != ''
|
||||
|
||||
test-e2e:
|
||||
name: Run end-to-end tests
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
k3s-version: [v1.29.1, v1.28.6, v1.27.10, v1.26.13, v1.25.16]
|
||||
needs:
|
||||
k3s-version: [v1.28.2, v1.27.6, v1.26.9, v1.25.14]
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
env:
|
||||
GOPATH: /home/runner/go
|
||||
ARGOCD_FAKE_IN_CLUSTER: "true"
|
||||
@@ -426,12 +377,12 @@ jobs:
|
||||
ARGOCD_APPLICATION_NAMESPACES: "argocd-e2e-external,argocd-e2e-external-2"
|
||||
ARGOCD_SERVER: "127.0.0.1:8088"
|
||||
GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: GH actions workaround - Kill XSP4 process
|
||||
@@ -450,7 +401,7 @@ jobs:
|
||||
sudo chmod go-r $HOME/.kube/config
|
||||
kubectl version
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -476,7 +427,7 @@ jobs:
|
||||
git config --global user.email "john.doe@example.com"
|
||||
- name: Pull Docker image required for tests
|
||||
run: |
|
||||
docker pull ghcr.io/dexidp/dex:v2.38.0
|
||||
docker pull ghcr.io/dexidp/dex:v2.37.0
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:7.0.15-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
@@ -506,31 +457,8 @@ jobs:
|
||||
set -x
|
||||
make test-e2e-local
|
||||
- name: Upload e2e-server logs
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: e2e-server-k8s${{ matrix.k3s-version }}.log
|
||||
path: /tmp/e2e-server.log
|
||||
if: ${{ failure() }}
|
||||
|
||||
# workaround for status checks -- check this one job instead of each individual E2E job in the matrix
|
||||
# this allows us to skip the entire matrix when it doesn't need to run while still having accurate status checks
|
||||
# see:
|
||||
# https://github.com/argoproj/argo-workflows/pull/12006
|
||||
# https://github.com/orgs/community/discussions/9141#discussioncomment-2296809
|
||||
# https://github.com/orgs/community/discussions/26822#discussioncomment-3305794
|
||||
test-e2e-composite-result:
|
||||
name: E2E Tests - Composite result
|
||||
if: ${{ always() }}
|
||||
needs:
|
||||
- test-e2e
|
||||
- changes
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- run: |
|
||||
result="${{ needs.test-e2e.result }}"
|
||||
# mark as successful even if skipped
|
||||
if [[ $result == "success" || $result == "skipped" ]]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
10
.github/workflows/codeql.yml
vendored
@@ -29,17 +29,17 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
|
||||
# Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
|
||||
uses: github/codeql-action/init@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
|
||||
uses: github/codeql-action/autobuild@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -61,4 +61,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
|
||||
uses: github/codeql-action/analyze@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33
|
||||
|
||||
20
.github/workflows/image-reuse.yaml
vendored
@@ -58,25 +58,25 @@ jobs:
|
||||
image-digest: ${{ steps.image.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
if: ${{ github.ref_type == 'tag'}}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
if: ${{ github.ref_type != 'tag'}}
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
|
||||
with:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
|
||||
uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0
|
||||
|
||||
- uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
|
||||
- uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0
|
||||
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
|
||||
- name: Setup tags for container image as a CSV type
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
echo 'EOF' >> $GITHUB_ENV
|
||||
|
||||
- name: Login to Quay.io
|
||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
|
||||
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ secrets.quay_username }}
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
if: ${{ inputs.quay_image_name && inputs.push }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
|
||||
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.ghcr_username }}
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
if: ${{ inputs.ghcr_image_name && inputs.push }}
|
||||
|
||||
- name: Login to dockerhub Container Registry
|
||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
|
||||
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.docker_username }}
|
||||
password: ${{ secrets.docker_password }}
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
||||
uses: jlumbroso/free-disk-space@4d9e71b726748f254fe64fa44d273194bd18ec91
|
||||
with:
|
||||
large-packages: false
|
||||
docker-images: false
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
|
||||
- name: Build and push container image
|
||||
id: image
|
||||
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 #v5.3.0
|
||||
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 #v5.1.0
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ inputs.platforms }}
|
||||
|
||||
10
.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@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
|
||||
- name: Set image tag for ghcr
|
||||
run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
uses: ./.github/workflows/image-reuse.yaml
|
||||
with:
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
go-version: 1.22
|
||||
go-version: 1.21
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: false
|
||||
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
quay_image_name: quay.io/argoproj/argocd:latest
|
||||
ghcr_image_name: ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
go-version: 1.22
|
||||
go-version: 1.21
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: true
|
||||
secrets:
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
|
||||
with:
|
||||
image: ghcr.io/argoproj/argo-cd/argocd
|
||||
digest: ${{ needs.build-and-publish.outputs.image-digest }}
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
|
||||
env:
|
||||
TOKEN: ${{ secrets.TOKEN }}
|
||||
|
||||
4
.github/workflows/init-release.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
git stash pop
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5
|
||||
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2
|
||||
with:
|
||||
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
|
||||
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
|
||||
|
||||
2
.github/workflows/pr-title-check.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
name: Validate PR Title
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: thehanimo/pr-title-checker@1d8cd483a2b73118406a187f54dca8a9415f1375 # v1.4.2
|
||||
- uses: thehanimo/pr-title-checker@0cf5902181e78341bb97bb06646396e5bd354b3f # v1.4.0
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
configuration_path: ".github/pr-title-checker-config.json"
|
||||
|
||||
28
.github/workflows/release.yaml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
GOLANG_VERSION: '1.22' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
GOLANG_VERSION: '1.21' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
|
||||
jobs:
|
||||
argocd-image:
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
with:
|
||||
quay_image_name: quay.io/argoproj/argocd:${{ github.ref_name }}
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
go-version: 1.22
|
||||
go-version: 1.21
|
||||
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
|
||||
push: true
|
||||
secrets:
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
|
||||
with:
|
||||
image: quay.io/argoproj/argocd
|
||||
digest: ${{ needs.argocd-image.outputs.image-digest }}
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
||||
uses: jlumbroso/free-disk-space@4d9e71b726748f254fe64fa44d273194bd18ec91
|
||||
with:
|
||||
large-packages: false
|
||||
docker-images: false
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
tool-cache: false
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0
|
||||
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
|
||||
id: run-goreleaser
|
||||
with:
|
||||
version: latest
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
contents: write # Needed for release uploads
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
|
||||
with:
|
||||
base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
|
||||
provenance-name: "argocd-cli.intoto.jsonl"
|
||||
@@ -147,13 +147,13 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload SBOM
|
||||
uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 # v2.0.5
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
@@ -212,7 +212,7 @@ jobs:
|
||||
contents: write # Needed for release uploads
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
|
||||
with:
|
||||
base64-subjects: "${{ needs.generate-sbom.outputs.hashes }}"
|
||||
provenance-name: "argocd-sbom.intoto.jsonl"
|
||||
@@ -230,7 +230,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -295,7 +295,7 @@ jobs:
|
||||
if: ${{ env.UPDATE_VERSION == 'true' }}
|
||||
|
||||
- name: Create PR to update VERSION on master branch
|
||||
uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5
|
||||
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2
|
||||
with:
|
||||
commit-message: Bump version in master
|
||||
title: "chore: Bump version in master"
|
||||
|
||||
8
.github/workflows/scorecard.yaml
vendored
@@ -30,12 +30,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3
|
||||
uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
@@ -62,6 +62,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
|
||||
uses: github/codeql-action/upload-sarif@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2.2.1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
2
.github/workflows/update-snyk.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build reports
|
||||
|
||||
1
.gitignore
vendored
@@ -19,7 +19,6 @@ node_modules/
|
||||
./test/cmp/*.sock
|
||||
.envrc.remote
|
||||
.*.swp
|
||||
rerunreport.txt
|
||||
|
||||
# ignore built binaries
|
||||
cmd/argocd/argocd
|
||||
|
||||
2
.gitpod.Dockerfile
vendored
@@ -1,4 +1,4 @@
|
||||
FROM gitpod/workspace-full@sha256:8dd34e72ae5b9e6f60d267dd6287befc2cf5ad1a11c64e9d93daa60c952a2154
|
||||
FROM gitpod/workspace-full@sha256:511cecde4dc129ca9eb4cc4c479d61f95e5485ebe320a07f5b902f11899956a3
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
issues:
|
||||
exclude:
|
||||
- SA5011
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
linters:
|
||||
enable:
|
||||
- errcheck
|
||||
- gofmt
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- staticcheck
|
||||
- testifylint
|
||||
- unused
|
||||
linters-settings:
|
||||
testifylint:
|
||||
enable-all: true
|
||||
disable:
|
||||
- compares
|
||||
- empty
|
||||
- error-is-as
|
||||
- error-nil
|
||||
- expected-actual
|
||||
- float-compare
|
||||
- go-require
|
||||
- len
|
||||
- nil-compare
|
||||
- require-error
|
||||
run:
|
||||
timeout: 50m
|
||||
@@ -1,5 +1,3 @@
|
||||
version: 2
|
||||
|
||||
project_name: argocd
|
||||
|
||||
before:
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
** @argoproj/argocd-approvers
|
||||
|
||||
# Docs
|
||||
/docs/** @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/USERS.md @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/README.md @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/mkdocs.yml @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/docs/** @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/USERS.md @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/mkdocs.yml @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
|
||||
# CI
|
||||
/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||
|
||||
12
Dockerfile
@@ -1,10 +1,10 @@
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:24.04@sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508
|
||||
####################################################################################################
|
||||
# Builder image
|
||||
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
||||
# Also used as the image in CI jobs so needs all dependencies
|
||||
####################################################################################################
|
||||
FROM docker.io/library/golang:1.22.1@sha256:0b55ab82ac2a54a6f8f85ec8b943b9e470c39e32c109b766bbc1b801f3fa8d3b AS builder
|
||||
FROM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca AS builder
|
||||
|
||||
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
|
||||
@@ -28,7 +28,7 @@ WORKDIR /tmp
|
||||
COPY hack/install.sh hack/tool-versions.sh ./
|
||||
COPY hack/installers installers
|
||||
|
||||
RUN ./install.sh helm && \
|
||||
RUN ./install.sh helm-linux && \
|
||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize
|
||||
|
||||
####################################################################################################
|
||||
@@ -51,7 +51,7 @@ RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
||||
apt-get update && \
|
||||
apt-get dist-upgrade -y && \
|
||||
apt-get install -y \
|
||||
git git-lfs tini gpg tzdata connect-proxy && \
|
||||
git git-lfs tini gpg tzdata && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
@@ -83,7 +83,7 @@ WORKDIR /home/argocd
|
||||
####################################################################################################
|
||||
# Argo CD UI stage
|
||||
####################################################################################################
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/node:22.2.0@sha256:a8ba58f54e770a0f910ec36d25f8a4f1670e741a58c2e6358b2c30b575c84263 AS argocd-ui
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/node:20.6.1@sha256:14bd39208dbc0eb171cbfb26ccb9ac09fa1b2eba04ccd528ab5d12983fd9ee24 AS argocd-ui
|
||||
|
||||
WORKDIR /src
|
||||
COPY ["ui/package.json", "ui/yarn.lock", "./"]
|
||||
@@ -101,7 +101,7 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP
|
||||
####################################################################################################
|
||||
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
||||
####################################################################################################
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.22.1@sha256:0b55ab82ac2a54a6f8f85ec8b943b9e470c39e32c109b766bbc1b801f3fa8d3b AS argocd-build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca AS argocd-build
|
||||
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
|
||||
105
Makefile
@@ -3,7 +3,6 @@ CURRENT_DIR=$(shell pwd)
|
||||
DIST_DIR=${CURRENT_DIR}/dist
|
||||
CLI_NAME=argocd
|
||||
BIN_NAME=argocd
|
||||
CGO_FLAG=0
|
||||
|
||||
GEN_RESOURCES_CLI_NAME=argocd-resources-gen
|
||||
|
||||
@@ -23,21 +22,14 @@ KUBECTL_VERSION=$(shell go list -m k8s.io/client-go | head -n 1 | rev | cut -d'
|
||||
GOPATH?=$(shell if test -x `which go`; then go env GOPATH; else echo "$(HOME)/go"; fi)
|
||||
GOCACHE?=$(HOME)/.cache/go-build
|
||||
|
||||
# Docker command to use
|
||||
DOCKER?=docker
|
||||
ifeq ($(DOCKER),podman)
|
||||
PODMAN_ARGS=--userns keep-id
|
||||
else
|
||||
PODMAN_ARGS=
|
||||
endif
|
||||
|
||||
DOCKER_SRCDIR?=$(GOPATH)/src
|
||||
DOCKER_WORKDIR?=/go/src/github.com/argoproj/argo-cd
|
||||
|
||||
ARGOCD_PROCFILE?=Procfile
|
||||
|
||||
# pointing to python 3.7 to match https://github.com/argoproj/argo-cd/blob/master/.readthedocs.yml
|
||||
MKDOCS_DOCKER_IMAGE?=python:3.7-alpine
|
||||
# Strict mode has been disabled in latest versions of mkdocs-material.
|
||||
# Thus pointing to the older image of mkdocs-material matching the version used by argo-cd.
|
||||
MKDOCS_DOCKER_IMAGE?=squidfunk/mkdocs-material:4.1.1
|
||||
MKDOCS_RUN_ARGS?=
|
||||
|
||||
# Configuration for building argocd-test-tools image
|
||||
@@ -84,7 +76,7 @@ SUDO?=
|
||||
# Runs any command in the argocd-test-utils container in server mode
|
||||
# Server mode container will start with uid 0 and drop privileges during runtime
|
||||
define run-in-test-server
|
||||
$(SUDO) $(DOCKER) run --rm -it \
|
||||
$(SUDO) docker run --rm -it \
|
||||
--name argocd-test-server \
|
||||
-u $(CONTAINER_UID):$(CONTAINER_GID) \
|
||||
-e USER_ID=$(CONTAINER_UID) \
|
||||
@@ -109,14 +101,13 @@ define run-in-test-server
|
||||
-p ${ARGOCD_E2E_APISERVER_PORT}:8080 \
|
||||
-p 4000:4000 \
|
||||
-p 5000:5000 \
|
||||
$(PODMAN_ARGS) \
|
||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||
bash -c "$(1)"
|
||||
endef
|
||||
|
||||
# Runs any command in the argocd-test-utils container in client mode
|
||||
define run-in-test-client
|
||||
$(SUDO) $(DOCKER) run --rm -it \
|
||||
$(SUDO) docker run --rm -it \
|
||||
--name argocd-test-client \
|
||||
-u $(CONTAINER_UID):$(CONTAINER_GID) \
|
||||
-e HOME=/home/user \
|
||||
@@ -131,14 +122,13 @@ define run-in-test-client
|
||||
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
||||
-v /tmp:/tmp${VOLUME_MOUNT} \
|
||||
-w ${DOCKER_WORKDIR} \
|
||||
$(PODMAN_ARGS) \
|
||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||
bash -c "$(1)"
|
||||
endef
|
||||
|
||||
#
|
||||
define exec-in-test-server
|
||||
$(SUDO) $(DOCKER) exec -it -u $(CONTAINER_UID):$(CONTAINER_GID) -e ARGOCD_E2E_RECORD=$(ARGOCD_E2E_RECORD) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1)
|
||||
$(SUDO) docker exec -it -u $(CONTAINER_UID):$(CONTAINER_GID) -e ARGOCD_E2E_RECORD=$(ARGOCD_E2E_RECORD) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1)
|
||||
endef
|
||||
|
||||
PATH:=$(PATH):$(PWD)/hack
|
||||
@@ -185,21 +175,29 @@ endif
|
||||
.PHONY: all
|
||||
all: cli image
|
||||
|
||||
# We have some legacy requirements for being checked out within $GOPATH.
|
||||
# The ensure-gopath target can be used as dependency to ensure we are running
|
||||
# within these boundaries.
|
||||
.PHONY: ensure-gopath
|
||||
ensure-gopath:
|
||||
ifneq ("$(PWD)","$(LEGACY_PATH)")
|
||||
@echo "Due to legacy requirements for codegen, repository needs to be checked out within \$$GOPATH"
|
||||
@echo "Location of this repo should be '$(LEGACY_PATH)' but is '$(PWD)'"
|
||||
@exit 1
|
||||
endif
|
||||
|
||||
.PHONY: gogen
|
||||
gogen:
|
||||
gogen: ensure-gopath
|
||||
export GO111MODULE=off
|
||||
go generate ./...
|
||||
go generate ./util/argo/...
|
||||
|
||||
.PHONY: protogen
|
||||
protogen: mod-vendor-local protogen-fast
|
||||
|
||||
.PHONY: protogen-fast
|
||||
protogen-fast:
|
||||
protogen: ensure-gopath mod-vendor-local
|
||||
export GO111MODULE=off
|
||||
./hack/generate-proto.sh
|
||||
|
||||
.PHONY: openapigen
|
||||
openapigen:
|
||||
openapigen: ensure-gopath
|
||||
export GO111MODULE=off
|
||||
./hack/update-openapi.sh
|
||||
|
||||
@@ -214,22 +212,19 @@ notification-docs:
|
||||
|
||||
|
||||
.PHONY: clientgen
|
||||
clientgen:
|
||||
clientgen: ensure-gopath
|
||||
export GO111MODULE=off
|
||||
./hack/update-codegen.sh
|
||||
|
||||
.PHONY: clidocsgen
|
||||
clidocsgen:
|
||||
clidocsgen: ensure-gopath
|
||||
go run tools/cmd-docs/main.go
|
||||
|
||||
|
||||
.PHONY: codegen-local
|
||||
codegen-local: mod-vendor-local gogen protogen clientgen openapigen clidocsgen manifests-local notification-docs notification-catalog
|
||||
codegen-local: ensure-gopath mod-vendor-local gogen protogen clientgen openapigen clidocsgen manifests-local notification-docs notification-catalog
|
||||
rm -rf vendor/
|
||||
|
||||
.PHONY: codegen-local-fast
|
||||
codegen-local-fast: gogen protogen-fast clientgen openapigen clidocsgen manifests-local notification-docs notification-catalog
|
||||
|
||||
.PHONY: codegen
|
||||
codegen: test-tools-image
|
||||
$(call run-in-test-client,make codegen-local)
|
||||
@@ -240,11 +235,11 @@ cli: test-tools-image
|
||||
|
||||
.PHONY: cli-local
|
||||
cli-local: clean-debug
|
||||
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
|
||||
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
|
||||
|
||||
.PHONY: gen-resources-cli-local
|
||||
gen-resources-cli-local: clean-debug
|
||||
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${GEN_RESOURCES_CLI_NAME} ./hack/gen-resources/cmd
|
||||
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${GEN_RESOURCES_CLI_NAME} ./hack/gen-resources/cmd
|
||||
|
||||
.PHONY: release-cli
|
||||
release-cli: clean-debug build-ui
|
||||
@@ -259,8 +254,8 @@ release-cli: clean-debug build-ui
|
||||
.PHONY: test-tools-image
|
||||
test-tools-image:
|
||||
ifndef SKIP_TEST_TOOLS_IMAGE
|
||||
$(SUDO) $(DOCKER) build --build-arg UID=$(CONTAINER_UID) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile .
|
||||
$(SUDO) $(DOCKER) tag $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG)
|
||||
$(SUDO) docker build --build-arg UID=$(CONTAINER_UID) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile .
|
||||
$(SUDO) docker tag $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG)
|
||||
endif
|
||||
|
||||
.PHONY: manifests-local
|
||||
@@ -274,25 +269,25 @@ manifests: test-tools-image
|
||||
# consolidated binary for cli, util, server, repo-server, controller
|
||||
.PHONY: argocd-all
|
||||
argocd-all: clean-debug
|
||||
CGO_ENABLED=${CGO_FLAG} GOOS=${GOOS} GOARCH=${GOARCH} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
|
||||
CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
|
||||
|
||||
.PHONY: server
|
||||
server: clean-debug
|
||||
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd
|
||||
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd
|
||||
|
||||
.PHONY: repo-server
|
||||
repo-server:
|
||||
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd
|
||||
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd
|
||||
|
||||
.PHONY: controller
|
||||
controller:
|
||||
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
|
||||
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
|
||||
|
||||
.PHONY: build-ui
|
||||
build-ui:
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build -t argocd-ui --platform=$(TARGET_ARCH) --target argocd-ui .
|
||||
DOCKER_BUILDKIT=1 docker build -t argocd-ui --platform=$(TARGET_ARCH) --target argocd-ui .
|
||||
find ./ui/dist -type f -not -name gitkeep -delete
|
||||
$(DOCKER) run -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/'
|
||||
docker run -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/'
|
||||
|
||||
.PHONY: image
|
||||
ifeq ($(DEV_IMAGE), true)
|
||||
@@ -301,29 +296,29 @@ ifeq ($(DEV_IMAGE), true)
|
||||
# the dist directory is under .dockerignore.
|
||||
IMAGE_TAG="dev-$(shell git describe --always --dirty)"
|
||||
image: build-ui
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t argocd-base --target argocd-base .
|
||||
CGO_ENABLED=${CGO_FLAG} GOOS=linux GOARCH=amd64 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
|
||||
DOCKER_BUILDKIT=1 docker build --platform=$(TARGET_ARCH) -t argocd-base --target argocd-base .
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-application-controller
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-repo-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-cmp-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-dex
|
||||
cp Dockerfile.dev dist
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist
|
||||
DOCKER_BUILDKIT=1 docker build --platform=$(TARGET_ARCH) -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist
|
||||
else
|
||||
image:
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) --platform=$(TARGET_ARCH) .
|
||||
DOCKER_BUILDKIT=1 docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) --platform=$(TARGET_ARCH) .
|
||||
endif
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then $(DOCKER) push $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) ; fi
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: armimage
|
||||
armimage:
|
||||
$(DOCKER) build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm .
|
||||
docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm .
|
||||
|
||||
.PHONY: builder-image
|
||||
builder-image:
|
||||
$(DOCKER) build -t $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) --target builder .
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then $(DOCKER) push $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) ; fi
|
||||
docker build -t $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) --target builder .
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: mod-download
|
||||
mod-download: test-tools-image
|
||||
@@ -357,7 +352,7 @@ lint-local:
|
||||
golangci-lint --version
|
||||
# NOTE: If you get a "Killed" OOM message, try reducing the value of GOGC
|
||||
# See https://github.com/golangci/golangci-lint#memory-usage-of-golangci-lint
|
||||
GOGC=$(ARGOCD_LINT_GOGC) GOMAXPROCS=2 golangci-lint run --fix --verbose
|
||||
GOGC=$(ARGOCD_LINT_GOGC) GOMAXPROCS=2 golangci-lint run --enable gofmt --fix --verbose --timeout 3000s --max-issues-per-linter 0 --max-same-issues 0
|
||||
|
||||
.PHONY: lint-ui
|
||||
lint-ui: test-tools-image
|
||||
@@ -434,7 +429,7 @@ debug-test-client: test-tools-image
|
||||
# Starts e2e server in a container
|
||||
.PHONY: start-e2e
|
||||
start-e2e: test-tools-image
|
||||
$(DOCKER) version
|
||||
docker version
|
||||
mkdir -p ${GOCACHE}
|
||||
$(call run-in-test-server,make ARGOCD_PROCFILE=test/container/Procfile start-e2e-local)
|
||||
|
||||
@@ -481,7 +476,7 @@ clean: clean-debug
|
||||
|
||||
.PHONY: start
|
||||
start: test-tools-image
|
||||
$(DOCKER) version
|
||||
docker version
|
||||
$(call run-in-test-server,make ARGOCD_PROCFILE=test/container/Procfile start-local ARGOCD_START=${ARGOCD_START})
|
||||
|
||||
# Starts a local instance of ArgoCD
|
||||
@@ -494,7 +489,6 @@ start-local: mod-vendor-local dep-ui-local cli-local
|
||||
mkdir -p /tmp/argocd-local
|
||||
mkdir -p /tmp/argocd-local/gpg/keys && chmod 0700 /tmp/argocd-local/gpg/keys
|
||||
mkdir -p /tmp/argocd-local/gpg/source
|
||||
REDIS_PASSWORD=$(shell kubectl get secret argocd-redis -o jsonpath='{.data.auth}' | base64 -d) \
|
||||
ARGOCD_ZJWT_FEATURE_FLAG=always \
|
||||
ARGOCD_IN_CI=false \
|
||||
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
|
||||
@@ -532,7 +526,7 @@ build-docs-local:
|
||||
|
||||
.PHONY: build-docs
|
||||
build-docs:
|
||||
$(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install -r docs/requirements.txt; mkdocs build'
|
||||
docker run ${MKDOCS_RUN_ARGS} --rm -it -v ${CURRENT_DIR}:/docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install -r docs/requirements.txt; mkdocs build'
|
||||
|
||||
.PHONY: serve-docs-local
|
||||
serve-docs-local:
|
||||
@@ -540,7 +534,8 @@ serve-docs-local:
|
||||
|
||||
.PHONY: serve-docs
|
||||
serve-docs:
|
||||
$(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install -r docs/requirements.txt; mkdocs serve -a $$(ip route get 1 | awk '\''{print $$7}'\''):8000'
|
||||
docker run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}/site:/site -w /site --entrypoint "" ${MKDOCS_DOCKER_IMAGE} python3 -m http.server --bind 0.0.0.0 8000
|
||||
|
||||
|
||||
# Verify that kubectl can connect to your K8s cluster from Docker
|
||||
.PHONY: verify-kube-connect
|
||||
@@ -563,7 +558,7 @@ install-tools-local: install-test-tools-local install-codegen-tools-local instal
|
||||
.PHONY: install-test-tools-local
|
||||
install-test-tools-local:
|
||||
./hack/install.sh kustomize
|
||||
./hack/install.sh helm
|
||||
./hack/install.sh helm-linux
|
||||
./hack/install.sh gotestsum
|
||||
|
||||
# Installs all tools required for running codegen (Linux packages)
|
||||
@@ -592,7 +587,7 @@ list:
|
||||
|
||||
.PHONY: applicationset-controller
|
||||
applicationset-controller:
|
||||
GODEBUG="tarinsecurepath=0,zipinsecurepath=0" CGO_ENABLED=${CGO_FLAG} go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-applicationset-controller ./cmd
|
||||
GODEBUG="tarinsecurepath=0,zipinsecurepath=0" CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-applicationset-controller ./cmd
|
||||
|
||||
.PHONY: checksums
|
||||
checksums:
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
**Social:**
|
||||
[](https://twitter.com/argoproj)
|
||||
[](https://argoproj.github.io/community/join-slack)
|
||||
[](https://www.linkedin.com/company/argoproj/)
|
||||
|
||||
# Argo CD - Declarative Continuous Delivery for Kubernetes
|
||||
|
||||
@@ -86,5 +85,4 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
1. [Getting Started with ArgoCD for GitOps Deployments](https://youtu.be/AvLuplh1skA)
|
||||
1. [Using Argo CD & Datree for Stable Kubernetes CI/CD Deployments](https://youtu.be/17894DTru2Y)
|
||||
1. [How to create Argo CD Applications Automatically using ApplicationSet? "Automation of GitOps"](https://amralaayassen.medium.com/how-to-create-argocd-applications-automatically-using-applicationset-automation-of-the-gitops-59455eaf4f72)
|
||||
1. [Progressive Delivery with Service Mesh – Argo Rollouts with Istio](https://www.cncf.io/blog/2022/12/16/progressive-delivery-with-service-mesh-argo-rollouts-with-istio/)
|
||||
|
||||
|
||||
35
USERS.md
@@ -18,16 +18,12 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Albert Heijn](https://ah.nl/)
|
||||
1. [Alibaba Group](https://www.alibabagroup.com/)
|
||||
1. [Allianz Direct](https://www.allianzdirect.de/)
|
||||
1. [AlphaSense](https://www.alpha-sense.com/)
|
||||
1. [Amadeus IT Group](https://amadeus.com/)
|
||||
1. [Ambassador Labs](https://www.getambassador.io/)
|
||||
1. [Ancestry](https://www.ancestry.com/)
|
||||
1. [Andgo Systems](https://www.andgosystems.com/)
|
||||
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
|
||||
1. [Ant Group](https://www.antgroup.com/)
|
||||
1. [AppDirect](https://www.appdirect.com)
|
||||
1. [Arctiq Inc.](https://www.arctiq.ca)
|
||||
2. [Arturia](https://www.arturia.com)
|
||||
1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/)
|
||||
1. [Autodesk](https://www.autodesk.com)
|
||||
1. [Axians ACSP](https://www.axians.fr)
|
||||
@@ -37,39 +33,35 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
|
||||
1. [Beat](https://thebeat.co/en/)
|
||||
1. [Beez Innovation Labs](https://www.beezlabs.com/)
|
||||
1. [Bedag Informatik AG](https://www.bedag.ch/)
|
||||
1. [Beleza Na Web](https://www.belezanaweb.com.br/)
|
||||
1. [BigPanda](https://bigpanda.io)
|
||||
1. [BioBox Analytics](https://biobox.io)
|
||||
1. [BMW Group](https://www.bmwgroup.com/)
|
||||
1. [Boozt](https://www.booztgroup.com/)
|
||||
1. [Boticario](https://www.boticario.com.br/)
|
||||
1. [Broker Consulting, a.s.](https://www.bcas.cz/en/)
|
||||
1. [Bulder Bank](https://bulderbank.no)
|
||||
1. [CAM](https://cam-inc.co.jp)
|
||||
1. [Camptocamp](https://camptocamp.com)
|
||||
1. [Candis](https://www.candis.io)
|
||||
1. [Capital One](https://www.capitalone.com)
|
||||
1. [CARFAX Europe](https://www.carfax.eu)
|
||||
1. [CARFAX](https://www.carfax.com)
|
||||
1. [CARFAX Europe](https://www.carfax.eu)
|
||||
1. [Carrefour Group](https://www.carrefour.com)
|
||||
1. [Casavo](https://casavo.com)
|
||||
1. [Celonis](https://www.celonis.com/)
|
||||
1. [CERN](https://home.cern/)
|
||||
1. [Chainnodes](https://chainnodes.org)
|
||||
1. [Chargetrip](https://chargetrip.com)
|
||||
1. [Chainnodes](https://chainnodes.org)
|
||||
1. [Chime](https://www.chime.com)
|
||||
1. [Cisco ET&I](https://eti.cisco.com/)
|
||||
1. [Cloud Posse](https://www.cloudposse.com/)
|
||||
1. [Cloud Scale](https://cloudscaleinc.com/)
|
||||
1. [CloudGeometry](https://www.cloudgeometry.io/)
|
||||
1. [Cloudmate](https://cloudmt.co.kr/)
|
||||
1. [Cloudogu](https://cloudogu.com/)
|
||||
1. [Cobalt](https://www.cobalt.io/)
|
||||
1. [Codefresh](https://www.codefresh.io/)
|
||||
1. [Codility](https://www.codility.com/)
|
||||
1. [Commonbond](https://commonbond.co/)
|
||||
1. [Contlo](https://contlo.com/)
|
||||
1. [Coralogix](https://coralogix.com/)
|
||||
1. [Crédit Agricole CIB](https://www.ca-cib.com)
|
||||
1. [CROZ d.o.o.](https://croz.net/)
|
||||
@@ -102,7 +94,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Fave](https://myfave.com)
|
||||
1. [Flexport](https://www.flexport.com/)
|
||||
1. [Flip](https://flip.id)
|
||||
1. [Fly Security](https://www.flysecurity.com.br/)
|
||||
1. [Fonoa](https://www.fonoa.com/)
|
||||
1. [Fortra](https://www.fortra.com)
|
||||
1. [freee](https://corp.freee.co.jp/en/company/)
|
||||
@@ -121,8 +112,8 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [GlueOps](https://glueops.dev)
|
||||
1. [GMETRI](https://gmetri.com/)
|
||||
1. [Gojek](https://www.gojek.io/)
|
||||
1. [GoTo Financial](https://gotofinancial.com/)
|
||||
1. [GoTo](https://www.goto.com/)
|
||||
1. [GoTo Financial](https://gotofinancial.com/)
|
||||
1. [Greenpass](https://www.greenpass.com.br/)
|
||||
1. [Gridfuse](https://gridfuse.com/)
|
||||
1. [Groww](https://groww.in)
|
||||
@@ -135,12 +126,9 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Hiya](https://hiya.com)
|
||||
1. [Honestbank](https://honestbank.com)
|
||||
1. [Hostinger](https://www.hostinger.com)
|
||||
1. [IABAI](https://www.iab.ai)
|
||||
1. [IBM](https://www.ibm.com/)
|
||||
1. [Ibotta](https://home.ibotta.com)
|
||||
1. [IFS](https://www.ifs.com)
|
||||
1. [IITS-Consulting](https://iits-consulting.de)
|
||||
1. [IllumiDesk](https://www.illumidesk.com)
|
||||
1. [imaware](https://imaware.health)
|
||||
1. [Indeed](https://indeed.com)
|
||||
1. [Index Exchange](https://www.indexexchange.com/)
|
||||
@@ -166,7 +154,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [KubeSphere](https://github.com/kubesphere)
|
||||
1. [Kurly](https://www.kurly.com/)
|
||||
1. [Kvist](https://kvistsolutions.com)
|
||||
1. [Kyriba](https://www.kyriba.com/)
|
||||
1. [LexisNexis](https://www.lexisnexis.com/)
|
||||
1. [Lian Chu Securities](https://lczq.com)
|
||||
1. [Liatrio](https://www.liatrio.com)
|
||||
@@ -187,7 +174,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Meilleurs Agents](https://www.meilleursagents.com/)
|
||||
1. [Mercedes-Benz Tech Innovation](https://www.mercedes-benz-techinnovation.com/)
|
||||
1. [Mercedes-Benz.io](https://www.mercedes-benz.io/)
|
||||
1. [Metacore Games](https://metacoregames.com/)
|
||||
1. [Metanet](http://www.metanet.co.kr/en/)
|
||||
1. [MindSpore](https://mindspore.cn)
|
||||
1. [Mirantis](https://mirantis.com/)
|
||||
@@ -200,7 +186,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Natura &Co](https://naturaeco.com/)
|
||||
1. [Nethopper](https://nethopper.io)
|
||||
1. [New Relic](https://newrelic.com/)
|
||||
1. [Nextbasket](https://nextbasket.com)
|
||||
1. [Nextdoor](https://nextdoor.com/)
|
||||
1. [Nikkei](https://www.nikkei.co.jp/nikkeiinfo/en/)
|
||||
1. [Nitro](https://gonitro.com)
|
||||
@@ -208,11 +193,9 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Objective](https://www.objective.com.br/)
|
||||
1. [OCCMundial](https://occ.com.mx)
|
||||
1. [Octadesk](https://octadesk.com)
|
||||
1. [Octopus Deploy](https://octopus.com)
|
||||
1. [Olfeo](https://www.olfeo.com/)
|
||||
1. [omegaUp](https://omegaUp.com)
|
||||
1. [Omni](https://omni.se/)
|
||||
1. [Oncourse Home Solutions](https://oncoursehome.com/)
|
||||
1. [openEuler](https://openeuler.org)
|
||||
1. [openGauss](https://opengauss.org/)
|
||||
1. [OpenGov](https://opengov.com)
|
||||
@@ -229,17 +212,13 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [PagerDuty](https://www.pagerduty.com/)
|
||||
1. [Pandosearch](https://www.pandosearch.com/en/home)
|
||||
1. [Patreon](https://www.patreon.com/)
|
||||
1. [PayIt](https://payitgov.com/)
|
||||
1. [PayPay](https://paypay.ne.jp/)
|
||||
1. [Peloton Interactive](https://www.onepeloton.com/)
|
||||
1. [Percona](https://percona.com/)
|
||||
1. [PGS](https://www.pgs.com)
|
||||
1. [Pigment](https://www.gopigment.com/)
|
||||
1. [Pipedrive](https://www.pipedrive.com/)
|
||||
1. [Pipefy](https://www.pipefy.com/)
|
||||
1. [Pipekit](https://pipekit.io/)
|
||||
1. [Pismo](https://pismo.io/)
|
||||
1. [PITS Globale Datenrettungsdienste](https://www.pitsdatenrettung.de/)
|
||||
1. [Platform9 Systems](https://platform9.com/)
|
||||
1. [Polarpoint.io](https://polarpoint.io)
|
||||
1. [PostFinance](https://github.com/postfinance)
|
||||
@@ -256,17 +235,15 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Quipper](https://www.quipper.com/)
|
||||
1. [RapidAPI](https://www.rapidapi.com/)
|
||||
1. [rebuy](https://www.rebuy.de/)
|
||||
1. [Recreation.gov](https://www.recreation.gov/)
|
||||
1. [Red Hat](https://www.redhat.com/)
|
||||
1. [Redpill Linpro](https://www.redpill-linpro.com/)
|
||||
1. [Reenigne Cloud](https://reenigne.ca)
|
||||
1. [reev.com](https://www.reev.com/)
|
||||
1. [RightRev](https://rightrev.com/)
|
||||
1. [Rijkswaterstaat](https://www.rijkswaterstaat.nl/en)
|
||||
1. [Rise](https://www.risecard.eu/)
|
||||
1. [Riskified](https://www.riskified.com/)
|
||||
1. [Robotinfra](https://www.robotinfra.com)
|
||||
1. [Rocket.Chat](https://rocket.chat)
|
||||
1. [Rogo](https://rogodata.com)
|
||||
1. [Rubin Observatory](https://www.lsst.org)
|
||||
1. [Saildrone](https://www.saildrone.com/)
|
||||
1. [Salad Technologies](https://salad.com/)
|
||||
@@ -277,7 +254,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [SCRM Lidl International Hub](https://scrm.lidl)
|
||||
1. [SEEK](https://seek.com.au)
|
||||
1. [Semgrep](https://semgrep.com)
|
||||
1. [Shield](https://shield.com)
|
||||
1. [SI Analytics](https://si-analytics.ai)
|
||||
1. [Skit](https://skit.ai/)
|
||||
1. [Skyscanner](https://www.skyscanner.net/)
|
||||
@@ -293,7 +269,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Splunk](https://splunk.com/)
|
||||
1. [Spores Labs](https://spores.app)
|
||||
1. [Statsig](https://statsig.com)
|
||||
1. [SternumIOT](https://sternumiot.com)
|
||||
1. [StreamNative](https://streamnative.io)
|
||||
1. [Stuart](https://stuart.com/)
|
||||
1. [Sumo Logic](https://sumologic.com/)
|
||||
@@ -307,7 +282,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Tamkeen Technologies](https://tamkeentech.sa/)
|
||||
1. [Techcombank](https://www.techcombank.com.vn/trang-chu)
|
||||
1. [Technacy](https://www.technacy.it/)
|
||||
1. [Telavita](https://www.telavita.com.br/)
|
||||
1. [Tesla](https://tesla.com/)
|
||||
1. [The Scale Factory](https://www.scalefactory.com/)
|
||||
1. [ThousandEyes](https://www.thousandeyes.com/)
|
||||
@@ -331,7 +305,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Urbantz](https://urbantz.com/)
|
||||
1. [Vectra](https://www.vectra.ai)
|
||||
1. [Veepee](https://www.veepee.com)
|
||||
1. [Verkada](https://www.verkada.com)
|
||||
1. [Viaduct](https://www.viaduct.ai/)
|
||||
1. [VietMoney](https://vietmoney.vn/)
|
||||
1. [Vinted](https://vinted.com/)
|
||||
|
||||
@@ -18,11 +18,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
log "github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -42,6 +39,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/generators"
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
@@ -111,19 +109,15 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
|
||||
// Do not attempt to further reconcile the ApplicationSet if it is being deleted.
|
||||
if applicationSetInfo.ObjectMeta.DeletionTimestamp != nil {
|
||||
appsetName := applicationSetInfo.ObjectMeta.Name
|
||||
logCtx.Debugf("DeletionTimestamp is set on %s", appsetName)
|
||||
deleteAllowed := utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete()
|
||||
if !deleteAllowed {
|
||||
logCtx.Debugf("ApplicationSet policy does not allow to delete")
|
||||
if err := r.removeOwnerReferencesOnDeleteAppSet(ctx, applicationSetInfo); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
logCtx.Debugf("ownerReferences referring %s is deleted from generated applications", appsetName)
|
||||
}
|
||||
controllerutil.RemoveFinalizer(&applicationSetInfo, argov1alpha1.ResourcesFinalizerName)
|
||||
if err := r.Update(ctx, &applicationSetInfo); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
controllerutil.RemoveFinalizer(&applicationSetInfo, argov1alpha1.ResourcesFinalizerName)
|
||||
if err := r.Update(ctx, &applicationSetInfo); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
@@ -131,20 +125,18 @@ 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, generatorsErr := r.generateApplications(logCtx, applicationSetInfo)
|
||||
if generatorsErr != nil {
|
||||
desiredApplications, applicationSetReason, err := r.generateApplications(logCtx, applicationSetInfo)
|
||||
if err != nil {
|
||||
_ = r.setApplicationSetStatusCondition(ctx,
|
||||
&applicationSetInfo,
|
||||
argov1alpha1.ApplicationSetCondition{
|
||||
Type: argov1alpha1.ApplicationSetConditionErrorOccurred,
|
||||
Message: generatorsErr.Error(),
|
||||
Message: err.Error(),
|
||||
Reason: string(applicationSetReason),
|
||||
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
|
||||
}, parametersGenerated,
|
||||
)
|
||||
if len(desiredApplications) < 1 {
|
||||
return ctrl.Result{}, generatorsErr
|
||||
}
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
parametersGenerated = true
|
||||
@@ -172,16 +164,6 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
return ctrl.Result{RequeueAfter: ReconcileRequeueOnValidationError}, nil
|
||||
}
|
||||
|
||||
currentApplications, err := r.getCurrentApplications(ctx, applicationSetInfo)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err)
|
||||
}
|
||||
|
||||
err = r.updateResourcesStatus(ctx, logCtx, &applicationSetInfo, currentApplications)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("failed to get update resources status for application set: %w", err)
|
||||
}
|
||||
|
||||
// appMap is a name->app collection of Applications in this ApplicationSet.
|
||||
appMap := map[string]argov1alpha1.Application{}
|
||||
// appSyncMap tracks which apps will be synced during this reconciliation.
|
||||
@@ -198,11 +180,16 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
}
|
||||
} else if applicationSetInfo.Spec.Strategy != nil {
|
||||
// appset uses progressive sync
|
||||
for _, app := range currentApplications {
|
||||
applications, err := r.getCurrentApplications(ctx, applicationSetInfo)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err)
|
||||
}
|
||||
|
||||
for _, app := range applications {
|
||||
appMap[app.Name] = app
|
||||
}
|
||||
|
||||
appSyncMap, err = r.performProgressiveSyncs(ctx, logCtx, applicationSetInfo, currentApplications, desiredApplications, appMap)
|
||||
appSyncMap, err = r.performProgressiveSyncs(ctx, logCtx, applicationSetInfo, applications, desiredApplications, appMap)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("failed to perform progressive sync reconciliation for application set: %w", err)
|
||||
}
|
||||
@@ -323,7 +310,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
|
||||
requeueAfter := r.getMinRequeueAfter(&applicationSetInfo)
|
||||
|
||||
if len(validateErrors) == 0 && generatorsErr == nil {
|
||||
if len(validateErrors) == 0 {
|
||||
if err := r.setApplicationSetStatusCondition(ctx,
|
||||
&applicationSetInfo,
|
||||
argov1alpha1.ApplicationSetCondition{
|
||||
@@ -623,7 +610,7 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProg
|
||||
Owns(&argov1alpha1.Application{}, builder.WithPredicates(ownsHandler)).
|
||||
WithEventFilter(ignoreNotAllowedNamespaces(r.ApplicationSetNamespaces)).
|
||||
Watches(
|
||||
&corev1.Secret{},
|
||||
&source.Kind{Type: &corev1.Secret{}},
|
||||
&clusterSecretEventHandler{
|
||||
Client: mgr.GetClient(),
|
||||
Log: log.WithField("type", "createSecretEventHandler"),
|
||||
@@ -728,17 +715,6 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve post-delete finalizers:
|
||||
// https://github.com/argoproj/argo-cd/issues/17181
|
||||
for _, finalizer := range found.ObjectMeta.Finalizers {
|
||||
if strings.HasPrefix(finalizer, argov1alpha1.PostDeleteFinalizerName) {
|
||||
if generatedApp.Finalizers == nil {
|
||||
generatedApp.Finalizers = []string{}
|
||||
}
|
||||
generatedApp.Finalizers = append(generatedApp.Finalizers, finalizer)
|
||||
}
|
||||
}
|
||||
|
||||
found.ObjectMeta.Annotations = generatedApp.Annotations
|
||||
|
||||
found.ObjectMeta.Finalizers = generatedApp.Finalizers
|
||||
@@ -1175,7 +1151,6 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
||||
Message: "No Application status found, defaulting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Step: fmt.Sprint(appStepMap[app.Name] + 1),
|
||||
TargetRevisions: app.Status.GetRevisions(),
|
||||
}
|
||||
} else {
|
||||
// we have an existing AppStatus
|
||||
@@ -1193,25 +1168,20 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
||||
currentAppStatus.Status = "Waiting"
|
||||
currentAppStatus.Message = "Application has pending changes, setting status to Waiting."
|
||||
currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1)
|
||||
currentAppStatus.TargetRevisions = app.Status.GetRevisions()
|
||||
}
|
||||
|
||||
if currentAppStatus.Status == "Pending" {
|
||||
if operationPhaseString == "Succeeded" {
|
||||
revisions := []string{}
|
||||
if len(app.Status.OperationState.SyncResult.Revisions) > 0 {
|
||||
revisions = app.Status.OperationState.SyncResult.Revisions
|
||||
} else if app.Status.OperationState.SyncResult.Revision != "" {
|
||||
revisions = append(revisions, app.Status.OperationState.SyncResult.Revision)
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(currentAppStatus.TargetRevisions, revisions) {
|
||||
logCtx.Infof("Application %v has completed a sync successfully, updating its ApplicationSet status to Progressing", app.Name)
|
||||
currentAppStatus.LastTransitionTime = &now
|
||||
currentAppStatus.Status = "Progressing"
|
||||
currentAppStatus.Message = "Application resource completed a sync successfully, updating status from Pending to Progressing."
|
||||
currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1)
|
||||
// check for successful syncs started less than 10s before the Application transitioned to Pending
|
||||
// this covers race conditions where syncs initiated by RollingSync miraculously have a sync time before the transition to Pending state occurred (could be a few seconds)
|
||||
if operationPhaseString == "Succeeded" && app.Status.OperationState.StartedAt.Add(time.Duration(10)*time.Second).After(currentAppStatus.LastTransitionTime.Time) {
|
||||
if !app.Status.OperationState.StartedAt.After(currentAppStatus.LastTransitionTime.Time) {
|
||||
logCtx.Warnf("Application %v was synced less than 10s prior to entering Pending status, we'll assume the AppSet controller triggered this sync and update its status to Progressing", app.Name)
|
||||
}
|
||||
logCtx.Infof("Application %v has completed a sync successfully, updating its ApplicationSet status to Progressing", app.Name)
|
||||
currentAppStatus.LastTransitionTime = &now
|
||||
currentAppStatus.Status = "Progressing"
|
||||
currentAppStatus.Message = "Application resource completed a sync successfully, updating status from Pending to Progressing."
|
||||
currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1)
|
||||
} else if operationPhaseString == "Running" || healthStatusString == "Progressing" {
|
||||
logCtx.Infof("Application %v has entered Progressing status, updating its ApplicationSet status to Progressing", app.Name)
|
||||
currentAppStatus.LastTransitionTime = &now
|
||||
@@ -1380,87 +1350,7 @@ func findApplicationStatusIndex(appStatuses []argov1alpha1.ApplicationSetApplica
|
||||
return -1
|
||||
}
|
||||
|
||||
func (r *ApplicationSetReconciler) updateResourcesStatus(ctx context.Context, logCtx *log.Entry, appset *argov1alpha1.ApplicationSet, apps []argov1alpha1.Application) error {
|
||||
statusMap := getResourceStatusMap(appset)
|
||||
statusMap = buildResourceStatus(statusMap, apps)
|
||||
|
||||
statuses := []argov1alpha1.ResourceStatus{}
|
||||
for _, status := range statusMap {
|
||||
statuses = append(statuses, status)
|
||||
}
|
||||
appset.Status.Resources = statuses
|
||||
|
||||
namespacedName := types.NamespacedName{Namespace: appset.Namespace, Name: appset.Name}
|
||||
err := r.Client.Status().Update(ctx, appset)
|
||||
if err != nil {
|
||||
|
||||
logCtx.Errorf("unable to set application set status: %v", err)
|
||||
return fmt.Errorf("unable to set application set status: %v", err)
|
||||
}
|
||||
|
||||
if err := r.Get(ctx, namespacedName, appset); err != nil {
|
||||
if client.IgnoreNotFound(err) != nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("error fetching updated application set: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildResourceStatus(statusMap map[string]argov1alpha1.ResourceStatus, apps []argov1alpha1.Application) map[string]argov1alpha1.ResourceStatus {
|
||||
appMap := map[string]argov1alpha1.Application{}
|
||||
for _, app := range apps {
|
||||
appCopy := app
|
||||
appMap[app.Name] = app
|
||||
|
||||
gvk := app.GroupVersionKind()
|
||||
// Create status if it does not exist
|
||||
status, ok := statusMap[app.Name]
|
||||
if !ok {
|
||||
status = argov1alpha1.ResourceStatus{
|
||||
Group: gvk.Group,
|
||||
Version: gvk.Version,
|
||||
Kind: gvk.Kind,
|
||||
Name: app.Name,
|
||||
Namespace: app.Namespace,
|
||||
Status: app.Status.Sync.Status,
|
||||
Health: &appCopy.Status.Health,
|
||||
}
|
||||
}
|
||||
|
||||
status.Group = gvk.Group
|
||||
status.Version = gvk.Version
|
||||
status.Kind = gvk.Kind
|
||||
status.Name = app.Name
|
||||
status.Namespace = app.Namespace
|
||||
status.Status = app.Status.Sync.Status
|
||||
status.Health = &appCopy.Status.Health
|
||||
|
||||
statusMap[app.Name] = status
|
||||
}
|
||||
cleanupDeletedApplicationStatuses(statusMap, appMap)
|
||||
|
||||
return statusMap
|
||||
}
|
||||
|
||||
func getResourceStatusMap(appset *argov1alpha1.ApplicationSet) map[string]argov1alpha1.ResourceStatus {
|
||||
statusMap := map[string]argov1alpha1.ResourceStatus{}
|
||||
for _, status := range appset.Status.Resources {
|
||||
statusMap[status.Name] = status
|
||||
}
|
||||
return statusMap
|
||||
}
|
||||
|
||||
func cleanupDeletedApplicationStatuses(statusMap map[string]argov1alpha1.ResourceStatus, apps map[string]argov1alpha1.Application) {
|
||||
for name := range statusMap {
|
||||
if _, ok := apps[name]; !ok {
|
||||
delete(statusMap, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setApplicationSetApplicationStatus updates the ApplicationSet's status field
|
||||
// setApplicationSetApplicationStatus updates the ApplicatonSet's status field
|
||||
// with any new/changed Application statuses.
|
||||
func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, applicationStatuses []argov1alpha1.ApplicationSetApplicationStatus) error {
|
||||
needToUpdateStatus := false
|
||||
@@ -1637,14 +1527,10 @@ func shouldRequeueApplicationSet(appOld *argov1alpha1.Application, appNew *argov
|
||||
}
|
||||
|
||||
// the applicationset controller owns the application spec, labels, annotations, and finalizers on the applications
|
||||
// reflect.DeepEqual considers nil slices/maps not equal to empty slices/maps
|
||||
// https://pkg.go.dev/reflect#DeepEqual
|
||||
// ApplicationDestination has an unexported field so we can just use the == for comparison
|
||||
if !cmp.Equal(appOld.Spec, appNew.Spec, cmpopts.EquateEmpty(), cmpopts.EquateComparable(argov1alpha1.ApplicationDestination{})) ||
|
||||
!cmp.Equal(appOld.ObjectMeta.GetAnnotations(), appNew.ObjectMeta.GetAnnotations(), cmpopts.EquateEmpty()) ||
|
||||
!cmp.Equal(appOld.ObjectMeta.GetLabels(), appNew.ObjectMeta.GetLabels(), cmpopts.EquateEmpty()) ||
|
||||
!cmp.Equal(appOld.ObjectMeta.GetFinalizers(), appNew.ObjectMeta.GetFinalizers(), cmpopts.EquateEmpty()) {
|
||||
|
||||
if !reflect.DeepEqual(appOld.Spec, appNew.Spec) ||
|
||||
!reflect.DeepEqual(appOld.ObjectMeta.GetAnnotations(), appNew.ObjectMeta.GetAnnotations()) ||
|
||||
!reflect.DeepEqual(appOld.ObjectMeta.GetLabels(), appNew.ObjectMeta.GetLabels()) ||
|
||||
!reflect.DeepEqual(appOld.ObjectMeta.GetFinalizers(), appNew.ObjectMeta.GetFinalizers()) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"k8s.io/client-go/tools/record"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
crtcache "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"
|
||||
@@ -67,7 +66,7 @@ type fakeCache struct {
|
||||
cache.Cache
|
||||
}
|
||||
|
||||
func (f *fakeCache) GetInformer(ctx context.Context, obj crtclient.Object, opt ...crtcache.InformerGetOption) (cache.Informer, error) {
|
||||
func (f *fakeCache) GetInformer(ctx context.Context, obj crtclient.Object) (cache.Informer, error) {
|
||||
return &fakeInformer{}, nil
|
||||
}
|
||||
|
||||
@@ -1282,71 +1281,6 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "Ensure that argocd post-delete finalizers are preserved from an existing app",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingApps: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: application.ApplicationKind,
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "2",
|
||||
Finalizers: []string{
|
||||
v1alpha1.PostDeleteFinalizerName,
|
||||
v1alpha1.PostDeleteFinalizerName + "/mystage",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
desiredApps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: application.ApplicationKind,
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "2",
|
||||
Finalizers: []string{
|
||||
v1alpha1.PostDeleteFinalizerName,
|
||||
v1alpha1.PostDeleteFinalizerName + "/mystage",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
|
||||
@@ -2440,7 +2374,7 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{&project}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
|
||||
badCluster := v1alpha1.Cluster{Server: "https://bad-cluster", Name: "bad-cluster"}
|
||||
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
|
||||
@@ -2489,91 +2423,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestReconcilerCreateAppsRecoveringRenderError(t *testing.T) {
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
assert.Nil(t, err)
|
||||
err = v1alpha1.AddToScheme(scheme)
|
||||
assert.Nil(t, err)
|
||||
|
||||
project := v1alpha1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"},
|
||||
}
|
||||
appSet := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
List: &v1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{{
|
||||
Raw: []byte(`{"name": "very-good-app"}`),
|
||||
}, {
|
||||
Raw: []byte(`{"name": "bad-app"}`),
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||
Name: "{{ index (splitList \"-\" .name ) 2 }}",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"},
|
||||
Project: "default",
|
||||
Destination: v1alpha1.ApplicationDestination{Server: "https://kubernetes.default.svc"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
kubeclientset := kubefake.NewSimpleClientset()
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{&project}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
KubeClientset: kubeclientset,
|
||||
Policy: v1alpha1.ApplicationsSyncPolicySync,
|
||||
ArgoCDNamespace: "argocd",
|
||||
}
|
||||
|
||||
req := ctrl.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Namespace: "argocd",
|
||||
Name: "name",
|
||||
},
|
||||
}
|
||||
|
||||
// Verify that on generatorsError, no error is returned, but the object is requeued
|
||||
res, err := r.Reconcile(context.Background(), req)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, res.RequeueAfter == ReconcileRequeueOnValidationError)
|
||||
|
||||
var app v1alpha1.Application
|
||||
|
||||
// make sure good app got created
|
||||
err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "app"}, &app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, app.Name, "app")
|
||||
}
|
||||
|
||||
func TestSetApplicationSetStatusCondition(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
@@ -2679,7 +2528,7 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{&defaultProject}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
|
||||
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
|
||||
argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{
|
||||
@@ -2849,7 +2698,7 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{&defaultProject}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
|
||||
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
|
||||
argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{
|
||||
@@ -3170,7 +3019,7 @@ func TestPolicies(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
@@ -3333,7 +3182,7 @@ func TestSetApplicationSetApplicationStatus(t *testing.T) {
|
||||
|
||||
t.Run(cc.name, func(t *testing.T) {
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
@@ -4113,7 +3962,7 @@ func TestBuildAppDependencyList(t *testing.T) {
|
||||
}
|
||||
|
||||
appDependencyList, appStepMap, err := r.buildAppDependencyList(log.NewEntry(log.StandardLogger()), cc.appSet, cc.apps)
|
||||
assert.Equal(t, err, nil, "expected no errors, but errors occurred")
|
||||
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")
|
||||
})
|
||||
@@ -4707,7 +4556,7 @@ func TestBuildAppSyncMap(t *testing.T) {
|
||||
}
|
||||
|
||||
appSyncMap, err := r.buildAppSyncMap(context.TODO(), cc.appSet, cc.appDependencyList, cc.appMap)
|
||||
assert.Equal(t, err, nil, "expected no errors, but errors occurred")
|
||||
assert.Equal(t, err, nil, "expected no errors, but errors occured")
|
||||
assert.Equal(t, cc.expectedMap, appSyncMap, "expected appSyncMap did not match actual")
|
||||
})
|
||||
}
|
||||
@@ -4780,11 +4629,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource is already Healthy, updating status from Waiting to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{},
|
||||
Application: "app1",
|
||||
Message: "Application resource is already Healthy, updating status from Waiting to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -4823,11 +4671,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource is already Healthy, updating status from Waiting to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{},
|
||||
Application: "app1",
|
||||
Message: "Application resource is already Healthy, updating status from Waiting to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -4847,18 +4694,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Previous"},
|
||||
},
|
||||
{
|
||||
Application: "app2-multisource",
|
||||
Message: "",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Previous", "OtherPrevious"},
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -4870,37 +4709,17 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeOutOfSync,
|
||||
Revision: "Next",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app2-multisource",
|
||||
},
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeOutOfSync,
|
||||
Revisions: []string{"Next", "OtherNext"},
|
||||
Status: v1alpha1.SyncStatusCodeOutOfSync,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application has pending changes, setting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
},
|
||||
{
|
||||
Application: "app2-multisource",
|
||||
Message: "Application has pending changes, setting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next", "OtherNext"},
|
||||
Application: "app1",
|
||||
Message: "Application has pending changes, setting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5127,13 +4946,9 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
OperationState: &v1alpha1.OperationState{
|
||||
Phase: common.OperationSucceeded,
|
||||
SyncResult: &v1alpha1.SyncOperationResult{
|
||||
Revision: "Previous",
|
||||
},
|
||||
},
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeOutOfSync,
|
||||
Revision: "Next",
|
||||
Status: v1alpha1.SyncStatusCodeOutOfSync,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5144,16 +4959,15 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "No Application status found, defaulting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Step: "2",
|
||||
TargetRevisions: []string{"Next"},
|
||||
Application: "app1",
|
||||
Message: "No Application status found, defaulting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Step: "2",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "progresses a pending application with a successful sync triggered by controller to progressing",
|
||||
name: "progresses a pending application with a successful sync to progressing",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
@@ -5172,10 +4986,9 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
LastTransitionTime: &metav1.Time{
|
||||
Time: time.Now().Add(time.Duration(-1) * time.Minute),
|
||||
},
|
||||
Message: "",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
Message: "",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5194,35 +5007,24 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
StartedAt: metav1.Time{
|
||||
Time: time.Now(),
|
||||
},
|
||||
Operation: v1alpha1.Operation{
|
||||
InitiatedBy: v1alpha1.OperationInitiator{
|
||||
Username: "applicationset-controller",
|
||||
Automated: true,
|
||||
},
|
||||
},
|
||||
SyncResult: &v1alpha1.SyncOperationResult{
|
||||
Revision: "Next",
|
||||
},
|
||||
},
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
Revision: "Next",
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource completed a sync successfully, updating status from Pending to Progressing.",
|
||||
Status: "Progressing",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
Application: "app1",
|
||||
Message: "Application resource completed a sync successfully, updating status from Pending to Progressing.",
|
||||
Status: "Progressing",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "progresses a pending application with a successful sync trigger by applicationset-controller <1s ago to progressing",
|
||||
name: "progresses a pending application with a successful sync <1s ago to progressing",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
@@ -5241,10 +5043,9 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
LastTransitionTime: &metav1.Time{
|
||||
Time: time.Now(),
|
||||
},
|
||||
Message: "",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
Message: "",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5263,35 +5064,24 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
StartedAt: metav1.Time{
|
||||
Time: time.Now().Add(time.Duration(-1) * time.Second),
|
||||
},
|
||||
Operation: v1alpha1.Operation{
|
||||
InitiatedBy: v1alpha1.OperationInitiator{
|
||||
Username: "applicationset-controller",
|
||||
Automated: true,
|
||||
},
|
||||
},
|
||||
SyncResult: &v1alpha1.SyncOperationResult{
|
||||
Revision: "Next",
|
||||
},
|
||||
},
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
Revision: "Next",
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource completed a sync successfully, updating status from Pending to Progressing.",
|
||||
Status: "Progressing",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
Application: "app1",
|
||||
Message: "Application resource completed a sync successfully, updating status from Pending to Progressing.",
|
||||
Status: "Progressing",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "does not progresses a pending application with a successful sync triggered by controller with invalid revision to progressing",
|
||||
name: "does not progresses a pending application with an old successful sync to progressing",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
@@ -5308,12 +5098,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
{
|
||||
Application: "app1",
|
||||
LastTransitionTime: &metav1.Time{
|
||||
Time: time.Now().Add(time.Duration(-1) * time.Minute),
|
||||
Time: time.Now(),
|
||||
},
|
||||
Message: "",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
Message: "Application moved to Pending status, watching for the Application resource to start Progressing.",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5330,16 +5119,7 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
OperationState: &v1alpha1.OperationState{
|
||||
Phase: common.OperationSucceeded,
|
||||
StartedAt: metav1.Time{
|
||||
Time: time.Now(),
|
||||
},
|
||||
Operation: v1alpha1.Operation{
|
||||
InitiatedBy: v1alpha1.OperationInitiator{
|
||||
Username: "applicationset-controller",
|
||||
Automated: true,
|
||||
},
|
||||
},
|
||||
SyncResult: &v1alpha1.SyncOperationResult{
|
||||
Revision: "Previous",
|
||||
Time: time.Now().Add(time.Duration(-11) * time.Second),
|
||||
},
|
||||
},
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
@@ -5350,11 +5130,10 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
Application: "app1",
|
||||
Message: "Application moved to Pending status, watching for the Application resource to start Progressing.",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5423,7 +5202,7 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
@@ -5443,7 +5222,7 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
appStatuses[i].LastTransitionTime = nil
|
||||
}
|
||||
|
||||
assert.Equal(t, err, nil, "expected no errors, but errors occurred")
|
||||
assert.Equal(t, err, nil, "expected no errors, but errors occured")
|
||||
assert.Equal(t, cc.expectedAppStatus, appStatuses, "expected appStatuses did not match actual")
|
||||
})
|
||||
}
|
||||
@@ -6177,7 +5956,7 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) {
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build()
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
@@ -6197,225 +5976,12 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) {
|
||||
appStatuses[i].LastTransitionTime = nil
|
||||
}
|
||||
|
||||
assert.Equal(t, err, nil, "expected no errors, but errors occurred")
|
||||
assert.Equal(t, err, nil, "expected no errors, but errors occured")
|
||||
assert.Equal(t, cc.expectedAppStatus, appStatuses, "expected appStatuses did not match actual")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateResourceStatus(t *testing.T) {
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = v1alpha1.AddToScheme(scheme)
|
||||
assert.Nil(t, err)
|
||||
|
||||
for _, cc := range []struct {
|
||||
name string
|
||||
appSet v1alpha1.ApplicationSet
|
||||
apps []v1alpha1.Application
|
||||
expectedResources []v1alpha1.ResourceStatus
|
||||
}{
|
||||
{
|
||||
name: "handles an empty application list",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Resources: []v1alpha1.ResourceStatus{},
|
||||
},
|
||||
},
|
||||
apps: []v1alpha1.Application{},
|
||||
expectedResources: nil,
|
||||
},
|
||||
{
|
||||
name: "adds status if no existing statuses",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{},
|
||||
},
|
||||
},
|
||||
apps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
},
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
},
|
||||
Health: v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
Message: "OK",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResources: []v1alpha1.ResourceStatus{
|
||||
{
|
||||
Name: "app1",
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
Health: &v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
Message: "OK",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "handles an applicationset with existing and up-to-date status",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Resources: []v1alpha1.ResourceStatus{
|
||||
{
|
||||
Name: "app1",
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
Health: &v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
Message: "OK",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
apps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
},
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
},
|
||||
Health: v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
Message: "OK",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResources: []v1alpha1.ResourceStatus{
|
||||
{
|
||||
Name: "app1",
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
Health: &v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
Message: "OK",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updates an applicationset with existing and out of date status",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Resources: []v1alpha1.ResourceStatus{
|
||||
{
|
||||
Name: "app1",
|
||||
Status: v1alpha1.SyncStatusCodeOutOfSync,
|
||||
Health: &v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusProgressing,
|
||||
Message: "Progressing",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
apps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
},
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
},
|
||||
Health: v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
Message: "OK",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResources: []v1alpha1.ResourceStatus{
|
||||
{
|
||||
Name: "app1",
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
Health: &v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
Message: "OK",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deletes an applicationset status if the application no longer exists",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Resources: []v1alpha1.ResourceStatus{
|
||||
{
|
||||
Name: "app1",
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
Health: &v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
Message: "OK",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
apps: []v1alpha1.Application{},
|
||||
expectedResources: nil,
|
||||
},
|
||||
} {
|
||||
|
||||
t.Run(cc.name, func(t *testing.T) {
|
||||
|
||||
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithStatusSubresource(&cc.appSet).WithObjects(&cc.appSet).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
KubeClientset: kubeclientset,
|
||||
}
|
||||
|
||||
err := r.updateResourcesStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps)
|
||||
|
||||
assert.Equal(t, err, nil, "expected no errors, but errors occurred")
|
||||
assert.Equal(t, cc.expectedResources, cc.appSet.Status.Resources, "expected resources did not match actual")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOwnsHandler(t *testing.T) {
|
||||
// progressive syncs do not affect create, delete, or generic
|
||||
ownsHandler := getOwnsHandlerPredicates(true)
|
||||
@@ -6521,70 +6087,14 @@ func TestOwnsHandler(t *testing.T) {
|
||||
ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}}},
|
||||
ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"bar": "foo"}}},
|
||||
}}, want: true},
|
||||
{name: "DifferentApplicationLabelsNil", args: args{e: event.UpdateEvent{
|
||||
ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
|
||||
ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: nil}},
|
||||
}}, want: false},
|
||||
{name: "DifferentApplicationAnnotations", args: args{e: event.UpdateEvent{
|
||||
ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"foo": "bar"}}},
|
||||
ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"bar": "foo"}}},
|
||||
}}, want: true},
|
||||
{name: "DifferentApplicationAnnotationsNil", args: args{e: event.UpdateEvent{
|
||||
ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}},
|
||||
ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: nil}},
|
||||
}}, want: false},
|
||||
{name: "DifferentApplicationFinalizers", args: args{e: event.UpdateEvent{
|
||||
ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"argo"}}},
|
||||
ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"none"}}},
|
||||
}}, want: true},
|
||||
{name: "DifferentApplicationFinalizersNil", args: args{e: event.UpdateEvent{
|
||||
ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
|
||||
ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Finalizers: nil}},
|
||||
}}, want: false},
|
||||
{name: "ApplicationDestinationSame", args: args{e: event.UpdateEvent{
|
||||
ObjectOld: &v1alpha1.Application{
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "server",
|
||||
Namespace: "ns",
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
ObjectNew: &v1alpha1.Application{
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "server",
|
||||
Namespace: "ns",
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
enableProgressiveSyncs: true,
|
||||
}, want: false},
|
||||
{name: "ApplicationDestinationDiff", args: args{e: event.UpdateEvent{
|
||||
ObjectOld: &v1alpha1.Application{
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "server",
|
||||
Namespace: "ns",
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
ObjectNew: &v1alpha1.Application{
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "notSameServer",
|
||||
Namespace: "ns",
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
enableProgressiveSyncs: true,
|
||||
}, want: true},
|
||||
{name: "NotAnAppOld", args: args{e: event.UpdateEvent{
|
||||
ObjectOld: &v1alpha1.AppProject{},
|
||||
ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"bar": "foo"}}},
|
||||
|
||||
@@ -24,20 +24,20 @@ type clusterSecretEventHandler struct {
|
||||
Client client.Client
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.Object)
|
||||
func (h *clusterSecretEventHandler) Create(e event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(q, e.Object)
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Update(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.ObjectNew)
|
||||
func (h *clusterSecretEventHandler) Update(e event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(q, e.ObjectNew)
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Delete(ctx context.Context, e event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.Object)
|
||||
func (h *clusterSecretEventHandler) Delete(e event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(q, e.Object)
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Generic(ctx context.Context, e event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.Object)
|
||||
func (h *clusterSecretEventHandler) Generic(e event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(q, e.Object)
|
||||
}
|
||||
|
||||
// addRateLimitingInterface defines the Add method of workqueue.RateLimitingInterface, allow us to easily mock
|
||||
@@ -46,7 +46,7 @@ type addRateLimitingInterface interface {
|
||||
Add(item interface{})
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Context, q addRateLimitingInterface, object client.Object) {
|
||||
func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingInterface, object client.Object) {
|
||||
// Check for label, lookup all ApplicationSets that might match the cluster, queue them all
|
||||
if object.GetLabels()[generators.ArgoCDSecretTypeLabel] != generators.ArgoCDSecretTypeCluster {
|
||||
return
|
||||
@@ -58,7 +58,7 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Contex
|
||||
}).Info("processing event for cluster secret")
|
||||
|
||||
appSetList := &argoprojiov1alpha1.ApplicationSetList{}
|
||||
err := h.Client.List(ctx, appSetList)
|
||||
err := h.Client.List(context.Background(), appSetList)
|
||||
if err != nil {
|
||||
h.Log.WithError(err).Error("unable to list ApplicationSets")
|
||||
return
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -551,7 +550,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
|
||||
mockAddRateLimitingInterface := mockAddRateLimitingInterface{}
|
||||
|
||||
handler.queueRelatedAppGenerators(context.Background(), &mockAddRateLimitingInterface, &test.secret)
|
||||
handler.queueRelatedAppGenerators(&mockAddRateLimitingInterface, &test.secret)
|
||||
|
||||
assert.False(t, mockAddRateLimitingInterface.errorOccurred)
|
||||
assert.ElementsMatch(t, mockAddRateLimitingInterface.addedItems, test.expectedRequests)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.40.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.25.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -17,10 +17,6 @@ type Repos struct {
|
||||
func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) {
|
||||
ret := _m.Called(ctx, repoURL, revision, noRevisionCache)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetDirectories")
|
||||
}
|
||||
|
||||
var r0 []string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) ([]string, error)); ok {
|
||||
@@ -47,10 +43,6 @@ func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision st
|
||||
func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) {
|
||||
ret := _m.Called(ctx, repoURL, revision, pattern, noRevisionCache)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetFiles")
|
||||
}
|
||||
|
||||
var r0 map[string][]byte
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool) (map[string][]byte, error)); ok {
|
||||
@@ -73,12 +65,13 @@ func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string,
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewRepos(t interface {
|
||||
type mockConstructorTestingTNewRepos interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Repos {
|
||||
}
|
||||
|
||||
// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewRepos(t mockConstructorTestingTNewRepos) *Repos {
|
||||
mock := &Repos{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.40.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.21.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -19,10 +19,6 @@ type RepositoryDB struct {
|
||||
func (_m *RepositoryDB) GetRepository(ctx context.Context, url string) (*v1alpha1.Repository, error) {
|
||||
ret := _m.Called(ctx, url)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetRepository")
|
||||
}
|
||||
|
||||
var r0 *v1alpha1.Repository
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (*v1alpha1.Repository, error)); ok {
|
||||
@@ -45,12 +41,13 @@ func (_m *RepositoryDB) GetRepository(ctx context.Context, url string) (*v1alpha
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewRepositoryDB creates a new instance of RepositoryDB. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewRepositoryDB(t interface {
|
||||
type mockConstructorTestingTNewRepositoryDB interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *RepositoryDB {
|
||||
}
|
||||
|
||||
// NewRepositoryDB creates a new instance of RepositoryDB. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewRepositoryDB(t mockConstructorTestingTNewRepositoryDB) *RepositoryDB {
|
||||
mock := &RepositoryDB{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.40.2 --name=RepositoryDB
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.25.1 --name=RepositoryDB
|
||||
|
||||
// RepositoryDB Is a lean facade for ArgoDB,
|
||||
// Using a lean interface makes it easier to test the functionality of the git generator
|
||||
@@ -27,7 +27,7 @@ type argoCDService struct {
|
||||
newFileGlobbingEnabled bool
|
||||
}
|
||||
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.40.2 --name=Repos
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.25.1 --name=Repos
|
||||
|
||||
type Repos interface {
|
||||
|
||||
|
||||
@@ -8,17 +8,15 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"k8s.io/utils/ptr"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
azureMock "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/azure_devops/git/mocks"
|
||||
"github.com/microsoft/azure-devops-go-api/azuredevops"
|
||||
azureGit "github.com/microsoft/azure-devops-go-api/azuredevops/git"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.40.2 --srcpkg=github.com/microsoft/azure-devops-go-api/azuredevops/git --name=Client --output=azure_devops/git/mocks --outpkg=mocks
|
||||
|
||||
func s(input string) *string {
|
||||
return ptr.To(input)
|
||||
return pointer.String(input)
|
||||
}
|
||||
|
||||
func TestAzureDevopsRepoHasPath(t *testing.T) {
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/utils/ptr"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
// The contents of this file are from
|
||||
@@ -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 = ptr.To(int64(val))
|
||||
shard = pointer.Int64(int64(val))
|
||||
}
|
||||
}
|
||||
cluster := appv1.Cluster{
|
||||
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
@@ -384,7 +384,7 @@
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names\n+optional",
|
||||
"description": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#names\n+optional",
|
||||
"name": "application.metadata.name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
@@ -975,25 +975,6 @@
|
||||
"type": "string",
|
||||
"name": "project",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"name": "sourcePositions",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"name": "revisions",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -2030,43 +2011,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/applicationsets/{name}/resource-tree": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"ApplicationSetService"
|
||||
],
|
||||
"summary": "ResourceTree returns resource tree",
|
||||
"operationId": "ApplicationSetService_ResourceTree",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The application set namespace. Default empty is argocd control plane namespace.",
|
||||
"name": "appsetNamespace",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1alpha1ApplicationSetTree"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/runtimeError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/certificates": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -2968,7 +2912,7 @@
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names\n+optional",
|
||||
"description": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#names\n+optional",
|
||||
"name": "project.metadata.name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
@@ -4275,19 +4219,6 @@
|
||||
"revision": {
|
||||
"type": "string"
|
||||
},
|
||||
"revisions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"sourcePositions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"$ref": "#/definitions/v1alpha1SyncStrategy"
|
||||
},
|
||||
@@ -5403,8 +5334,8 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "key is the label key that the selector applies to.",
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"title": "key is the label key that the selector applies to.\n+patchMergeKey=key\n+patchStrategy=merge"
|
||||
},
|
||||
"operator": {
|
||||
"description": "operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist.",
|
||||
@@ -5454,10 +5385,6 @@
|
||||
"type": "string",
|
||||
"title": "IP is set for load-balancer ingress points that are IP based\n(typically GCE or OpenStack load-balancers)\n+optional"
|
||||
},
|
||||
"ipMode": {
|
||||
"type": "string",
|
||||
"title": "IPMode specifies how the load-balancer IP behaves, and may only be specified when the ip field is specified.\nSetting this to \"VIP\" indicates that traffic is delivered to the node with\nthe destination set to the load-balancer's IP and port.\nSetting this to \"Proxy\" indicates that traffic is delivered to the node or pod with\nthe destination set to the node's IP and node port or the pod's IP and port.\nService implementations may use this information to adjust traffic routing.\n+optional"
|
||||
},
|
||||
"ports": {
|
||||
"type": "array",
|
||||
"title": "Ports is a list of records of service ports\nIf used, every port defined in the service should have an entry in it\n+listType=atomic\n+optional",
|
||||
@@ -5567,7 +5494,7 @@
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"title": "Annotations is an unstructured key value map stored with a resource that may be\nset by external tools to store and retrieve arbitrary metadata. They are not\nqueryable and should be preserved when modifying objects.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations\n+optional",
|
||||
"title": "Annotations is an unstructured key value map stored with a resource that may be\nset by external tools to store and retrieve arbitrary metadata. They are not\nqueryable and should be preserved when modifying objects.\nMore info: http://kubernetes.io/docs/user-guide/annotations\n+optional",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -5601,7 +5528,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"title": "Map of string keys and values that can be used to organize and categorize\n(scope and select) objects. May match selectors of replication controllers\nand services.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels\n+optional",
|
||||
"title": "Map of string keys and values that can be used to organize and categorize\n(scope and select) objects. May match selectors of replication controllers\nand services.\nMore info: http://kubernetes.io/docs/user-guide/labels\n+optional",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -5615,10 +5542,10 @@
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names\n+optional"
|
||||
"title": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#names\n+optional"
|
||||
},
|
||||
"namespace": {
|
||||
"description": "Namespace defines the space within which each name must be unique. An empty namespace is\nequivalent to the \"default\" namespace, but \"default\" is the canonical representation.\nNot all objects are required to be scoped to a namespace - the value of this field for\nthose objects will be empty.\n\nMust be a DNS_LABEL.\nCannot be updated.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces\n+optional",
|
||||
"description": "Namespace defines the space within which each name must be unique. An empty namespace is\nequivalent to the \"default\" namespace, but \"default\" is the canonical representation.\nNot all objects are required to be scoped to a namespace - the value of this field for\nthose objects will be empty.\n\nMust be a DNS_LABEL.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/namespaces\n+optional",
|
||||
"type": "string"
|
||||
},
|
||||
"ownerReferences": {
|
||||
@@ -5637,7 +5564,7 @@
|
||||
"title": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.\n+optional"
|
||||
},
|
||||
"uid": {
|
||||
"description": "UID is the unique in time and space value for this object. It is typically generated by\nthe server on successful creation of a resource and is not allowed to change on PUT\noperations.\n\nPopulated by the system.\nRead-only.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids\n+optional",
|
||||
"description": "UID is the unique in time and space value for this object. It is typically generated by\nthe server on successful creation of a resource and is not allowed to change on PUT\noperations.\n\nPopulated by the system.\nRead-only.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#uids\n+optional",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -5698,11 +5625,11 @@
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "Name of the referent.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names"
|
||||
"title": "Name of the referent.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#names"
|
||||
},
|
||||
"uid": {
|
||||
"type": "string",
|
||||
"title": "UID of the referent.\nMore info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids"
|
||||
"title": "UID of the referent.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#uids"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -5737,10 +5664,6 @@
|
||||
"type": "string",
|
||||
"title": "ClusterName contains AWS cluster name"
|
||||
},
|
||||
"profile": {
|
||||
"description": "Profile contains optional role ARN. If set then AWS IAM Authenticator uses the profile to perform cluster operations instead of the default AWS credential provider chain.",
|
||||
"type": "string"
|
||||
},
|
||||
"roleARN": {
|
||||
"description": "RoleARN contains optional role ARN. If set then AWS IAM Authenticator assume a role to perform cluster operations instead of the default AWS credential provider chain.",
|
||||
"type": "string"
|
||||
@@ -5879,7 +5802,7 @@
|
||||
},
|
||||
"v1alpha1Application": {
|
||||
"type": "object",
|
||||
"title": "Application is a definition of Application resource.\n+genclient\n+genclient:noStatus\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+kubebuilder:resource:path=applications,shortName=app;apps\n+kubebuilder:printcolumn:name=\"Sync Status\",type=string,JSONPath=`.status.sync.status`\n+kubebuilder:printcolumn:name=\"Health Status\",type=string,JSONPath=`.status.health.status`\n+kubebuilder:printcolumn:name=\"Revision\",type=string,JSONPath=`.status.sync.revision`,priority=10\n+kubebuilder:printcolumn:name=\"Project\",type=string,JSONPath=`.spec.project`,priority=10",
|
||||
"title": "Application is a definition of Application resource.\n+genclient\n+genclient:noStatus\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+kubebuilder:resource:path=applications,shortName=app;apps\n+kubebuilder:printcolumn:name=\"Sync Status\",type=string,JSONPath=`.status.sync.status`\n+kubebuilder:printcolumn:name=\"Health Status\",type=string,JSONPath=`.status.health.status`\n+kubebuilder:printcolumn:name=\"Revision\",type=string,JSONPath=`.status.sync.revision`,priority=10",
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"$ref": "#/definitions/v1ObjectMeta"
|
||||
@@ -6016,19 +5939,12 @@
|
||||
"step": {
|
||||
"type": "string",
|
||||
"title": "Step tracks which step this Application should be updated in"
|
||||
},
|
||||
"targetrevisions": {
|
||||
"description": "TargetRevision tracks the desired revisions the Application should be synced to.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1ApplicationSetCondition": {
|
||||
"type": "object",
|
||||
"title": "ApplicationSetCondition contains details about an applicationset condition, which is usually an error or warning",
|
||||
"title": "ApplicationSetCondition contains details about an applicationset condition, which is usally an error or warning",
|
||||
"properties": {
|
||||
"lastTransitionTime": {
|
||||
"$ref": "#/definitions/v1Time"
|
||||
@@ -6249,13 +6165,6 @@
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1ApplicationSetCondition"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"description": "Resources is a list of Applications resources managed by this application set.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1ResourceStatus"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6327,19 +6236,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1ApplicationSetTree": {
|
||||
"type": "object",
|
||||
"title": "ApplicationSetTree holds nodes which belongs to the application\nUsed to build a tree of an ApplicationSet and its children",
|
||||
"properties": {
|
||||
"nodes": {
|
||||
"type": "array",
|
||||
"title": "Nodes contains list of nodes which are directly managed by the applicationset",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1ResourceNode"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1ApplicationSource": {
|
||||
"type": "object",
|
||||
"title": "ApplicationSource contains all required information about the source of an application",
|
||||
@@ -6524,10 +6420,6 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"labelWithoutSelector": {
|
||||
"type": "boolean",
|
||||
"title": "LabelWithoutSelector specifies whether to apply common labels to resource selectors or not"
|
||||
},
|
||||
"namePrefix": {
|
||||
"type": "string",
|
||||
"title": "NamePrefix is a prefix appended to resources for Kustomize apps"
|
||||
@@ -8608,9 +8500,6 @@
|
||||
"format": "int64",
|
||||
"title": "ID is an auto incrementing identifier of the RevisionHistory"
|
||||
},
|
||||
"initiatedBy": {
|
||||
"$ref": "#/definitions/v1alpha1OperationInitiator"
|
||||
},
|
||||
"revision": {
|
||||
"type": "string",
|
||||
"title": "Revision holds the revision the sync was performed against"
|
||||
|
||||
@@ -220,7 +220,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)")
|
||||
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that applications are allowed to be reconciled from")
|
||||
command.Flags().BoolVar(&persistResourceHealth, "persist-resource-health", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH", true), "Enables storing the managed resources health in the Application CRD")
|
||||
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", env.StringFromEnv(common.EnvControllerShardingAlgorithm, common.DefaultShardingAlgorithm), "Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin, consistent-hashing] ")
|
||||
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", env.StringFromEnv(common.EnvControllerShardingAlgorithm, common.DefaultShardingAlgorithm), "Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] ")
|
||||
// global queue rate limit config
|
||||
command.Flags().Int64Var(&workqueueRateLimit.BucketSize, "wq-bucket-size", env.ParseInt64FromEnv("WORKQUEUE_BUCKET_SIZE", 500, 1, math.MaxInt64), "Set Workqueue Rate Limiter Bucket Size, default 500")
|
||||
command.Flags().Float64Var(&workqueueRateLimit.BucketQPS, "wq-bucket-qps", env.ParseFloat64FromEnv("WORKQUEUE_BUCKET_QPS", math.MaxFloat64, 1, math.MaxFloat64), "Set Workqueue Rate Limiter Bucket QPS, default set to MaxFloat64 which disables the bucket limiter")
|
||||
@@ -233,10 +233,8 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().BoolVar(&enableDynamicClusterDistribution, "dynamic-cluster-distribution-enabled", env.ParseBoolFromEnv(common.EnvEnableDynamicClusterDistribution, false), "Enables dynamic cluster distribution.")
|
||||
command.Flags().BoolVar(&serverSideDiff, "server-side-diff-enabled", env.ParseBoolFromEnv(common.EnvServerSideDiff, false), "Feature flag to enable ServerSide diff. Default (\"false\")")
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout-seconds", env.ParseDurationFromEnv("ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT", 0*time.Second, 0, math.MaxInt64), "Set ignore normalizer JQ execution timeout")
|
||||
cacheSource = appstatecache.AddCacheFlagsToCmd(&command, cacheutil.Options{
|
||||
OnClientCreated: func(client *redis.Client) {
|
||||
redisClient = client
|
||||
},
|
||||
cacheSource = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
})
|
||||
return &command
|
||||
}
|
||||
|
||||
@@ -30,9 +30,6 @@ import (
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services"
|
||||
appv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -116,28 +113,15 @@ func NewCommand() *cobra.Command {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var cacheOpt ctrlcache.Options
|
||||
|
||||
if watchedNamespace != "" {
|
||||
cacheOpt = ctrlcache.Options{
|
||||
DefaultNamespaces: map[string]ctrlcache.Config{
|
||||
watchedNamespace: {},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Metrics: metricsserver.Options{
|
||||
BindAddress: metricsAddr,
|
||||
},
|
||||
Cache: cacheOpt,
|
||||
Scheme: scheme,
|
||||
MetricsBindAddress: metricsAddr,
|
||||
Namespace: watchedNamespace,
|
||||
HealthProbeBindAddress: probeBindAddr,
|
||||
Port: 9443,
|
||||
LeaderElection: enableLeaderElection,
|
||||
LeaderElectionID: "58ac56fa.applicationsets.argoproj.io",
|
||||
Client: ctrlclient.Options{
|
||||
DryRun: &dryRun,
|
||||
},
|
||||
DryRunClient: dryRun,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -37,14 +37,13 @@ func newAWSCommand() *cobra.Command {
|
||||
var (
|
||||
clusterName string
|
||||
roleARN string
|
||||
profile string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "aws",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
presignedURLString, err := getSignedRequestWithRetry(ctx, time.Minute, 5*time.Second, clusterName, roleARN, profile, getSignedRequest)
|
||||
presignedURLString, err := getSignedRequestWithRetry(ctx, time.Minute, 5*time.Second, clusterName, roleARN, getSignedRequest)
|
||||
errors.CheckError(err)
|
||||
token := v1Prefix + base64.RawURLEncoding.EncodeToString([]byte(presignedURLString))
|
||||
// Set token expiration to 1 minute before the presigned URL expires for some cushion
|
||||
@@ -54,17 +53,16 @@ func newAWSCommand() *cobra.Command {
|
||||
}
|
||||
command.Flags().StringVar(&clusterName, "cluster-name", "", "AWS Cluster name")
|
||||
command.Flags().StringVar(&roleARN, "role-arn", "", "AWS Role ARN")
|
||||
command.Flags().StringVar(&profile, "profile", "", "AWS Profile")
|
||||
return command
|
||||
}
|
||||
|
||||
type getSignedRequestFunc func(clusterName, roleARN string, profile string) (string, error)
|
||||
type getSignedRequestFunc func(clusterName, roleARN string) (string, error)
|
||||
|
||||
func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Duration, clusterName, roleARN string, profile string, fn getSignedRequestFunc) (string, error) {
|
||||
func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Duration, clusterName, roleARN string, fn getSignedRequestFunc) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
for {
|
||||
signed, err := fn(clusterName, roleARN, profile)
|
||||
signed, err := fn(clusterName, roleARN)
|
||||
if err == nil {
|
||||
return signed, nil
|
||||
}
|
||||
@@ -76,10 +74,8 @@ func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Durat
|
||||
}
|
||||
}
|
||||
|
||||
func getSignedRequest(clusterName, roleARN string, profile string) (string, error) {
|
||||
sess, err := session.NewSessionWithOptions(session.Options{
|
||||
Profile: profile,
|
||||
})
|
||||
func getSignedRequest(clusterName, roleARN string) (string, error) {
|
||||
sess, err := session.NewSession()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating new AWS session: %s", err)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestGetSignedRequestWithRetry(t *testing.T) {
|
||||
}
|
||||
|
||||
// when
|
||||
signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", "", mock.getSignedRequestMock)
|
||||
signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", mock.getSignedRequestMock)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -41,7 +41,7 @@ func TestGetSignedRequestWithRetry(t *testing.T) {
|
||||
}
|
||||
|
||||
// when
|
||||
signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", "", mock.getSignedRequestMock)
|
||||
signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", mock.getSignedRequestMock)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -57,7 +57,7 @@ func TestGetSignedRequestWithRetry(t *testing.T) {
|
||||
}
|
||||
|
||||
// when
|
||||
signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", "", mock.getSignedRequestMock)
|
||||
signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", mock.getSignedRequestMock)
|
||||
|
||||
// then
|
||||
assert.Error(t, err)
|
||||
@@ -70,7 +70,7 @@ type signedRequestMock struct {
|
||||
returnFunc func(m *signedRequestMock) (string, error)
|
||||
}
|
||||
|
||||
func (m *signedRequestMock) getSignedRequestMock(clusterName, roleARN string, profile string) (string, error) {
|
||||
func (m *signedRequestMock) getSignedRequestMock(clusterName, roleARN string) (string, error) {
|
||||
m.getSignedRequestCalls++
|
||||
return m.returnFunc(m)
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ func NewCommand() *cobra.Command {
|
||||
helmManifestMaxExtractedSize string
|
||||
helmRegistryMaxIndexSize string
|
||||
disableManifestMaxExtractedSize bool
|
||||
includeHiddenDirectories bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -131,7 +130,6 @@ func NewCommand() *cobra.Command {
|
||||
StreamedManifestMaxTarSize: streamedManifestMaxTarSizeQuantity.ToDec().Value(),
|
||||
HelmManifestMaxExtractedSize: helmManifestMaxExtractedSizeQuantity.ToDec().Value(),
|
||||
HelmRegistryMaxIndexSize: helmRegistryMaxIndexSizeQuantity.ToDec().Value(),
|
||||
IncludeHiddenDirectories: includeHiddenDirectories,
|
||||
}, askPassServer)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -217,12 +215,9 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&helmManifestMaxExtractedSize, "helm-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of helm manifest archives when extracted")
|
||||
command.Flags().StringVar(&helmRegistryMaxIndexSize, "helm-registry-max-index-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_INDEX_SIZE", "1G"), "Maximum size of registry index file")
|
||||
command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted")
|
||||
command.Flags().BoolVar(&includeHiddenDirectories, "include-hidden-directories", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_INCLUDE_HIDDEN_DIRECTORIES", false), "Include hidden directories from Git")
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, cacheutil.Options{
|
||||
OnClientCreated: func(client *redis.Client) {
|
||||
redisClient = client
|
||||
},
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
})
|
||||
return &command
|
||||
}
|
||||
|
||||
@@ -19,10 +19,8 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
reposervercache "github.com/argoproj/argo-cd/v2/reposerver/cache"
|
||||
"github.com/argoproj/argo-cd/v2/server"
|
||||
servercache "github.com/argoproj/argo-cd/v2/server/cache"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/dex"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
@@ -68,7 +66,6 @@ func NewCommand() *cobra.Command {
|
||||
enableGZip bool
|
||||
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
|
||||
cacheSrc func() (*servercache.Cache, error)
|
||||
repoServerCacheSrc func() (*reposervercache.Cache, error)
|
||||
frameOptions string
|
||||
contentSecurityPolicy string
|
||||
repoServerPlaintext bool
|
||||
@@ -110,8 +107,6 @@ func NewCommand() *cobra.Command {
|
||||
errors.CheckError(err)
|
||||
cache, err := cacheSrc()
|
||||
errors.CheckError(err)
|
||||
repoServerCache, err := repoServerCacheSrc()
|
||||
errors.CheckError(err)
|
||||
|
||||
kubeclientset := kubernetes.NewForConfigOrDie(config)
|
||||
|
||||
@@ -196,7 +191,6 @@ func NewCommand() *cobra.Command {
|
||||
EnableGZip: enableGZip,
|
||||
TLSConfigCustomizer: tlsConfigCustomizer,
|
||||
Cache: cache,
|
||||
RepoServerCache: repoServerCache,
|
||||
XFrameOptions: frameOptions,
|
||||
ContentSecurityPolicy: contentSecurityPolicy,
|
||||
RedisClient: redisClient,
|
||||
@@ -269,11 +263,8 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces where application resources can be managed in")
|
||||
command.Flags().BoolVar(&enableProxyExtension, "enable-proxy-extension", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_PROXY_EXTENSION", false), "Enable Proxy Extension feature")
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command)
|
||||
cacheSrc = servercache.AddCacheFlagsToCmd(command, cacheutil.Options{
|
||||
OnClientCreated: func(client *redis.Client) {
|
||||
redisClient = client
|
||||
},
|
||||
cacheSrc = servercache.AddCacheFlagsToCmd(command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
})
|
||||
repoServerCacheSrc = reposervercache.AddCacheFlagsToCmd(command, cacheutil.Options{FlagPrefix: "repo-server-"})
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -48,9 +48,84 @@ func NewAdminCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
Example: `# Access the Argo CD web UI
|
||||
Example: `# List all clusters
|
||||
$ argocd admin cluster list
|
||||
|
||||
# Add a new cluster
|
||||
$ argocd admin cluster add my-cluster --name my-cluster --in-cluster-context
|
||||
|
||||
# Remove a cluster
|
||||
argocd admin cluster remove my-cluster
|
||||
|
||||
# List all projects
|
||||
$ argocd admin project list
|
||||
|
||||
# Create a new project
|
||||
$argocd admin project create my-project --src-namespace my-source-namespace --dest-namespace my-dest-namespace
|
||||
|
||||
# Update a project
|
||||
$ argocd admin project update my-project --src-namespace my-updated-source-namespace --dest-namespace my-updated-dest-namespace
|
||||
|
||||
# Delete a project
|
||||
$ argocd admin project delete my-project
|
||||
|
||||
# List all settings
|
||||
$ argocd admin settings list
|
||||
|
||||
# Get the current settings
|
||||
$ argocd admin settings get
|
||||
|
||||
# Update settings
|
||||
$ argocd admin settings update --repository.resync --value 15
|
||||
|
||||
# List all applications
|
||||
$ argocd admin app list
|
||||
|
||||
# Get application details
|
||||
$ argocd admin app get my-app
|
||||
|
||||
# Sync an application
|
||||
$ argocd admin app sync my-app
|
||||
|
||||
# Pause an application
|
||||
$ argocd admin app pause my-app
|
||||
|
||||
# Resume an application
|
||||
$ argocd admin app resume my-app
|
||||
|
||||
# List all repositories
|
||||
$ argocd admin repo list
|
||||
|
||||
# Add a repository
|
||||
$ argocd admin repo add https://github.com/argoproj/my-repo.git
|
||||
|
||||
# Remove a repository
|
||||
$ argocd admin repo remove https://github.com/argoproj/my-repo.git
|
||||
|
||||
# Import an application from a YAML file
|
||||
$ argocd admin app import -f my-app.yaml
|
||||
|
||||
# Export an application to a YAML file
|
||||
$ argocd admin app export my-app -o my-exported-app.yaml
|
||||
|
||||
# Access the Argo CD web UI
|
||||
$ argocd admin dashboard
|
||||
|
||||
# List notifications
|
||||
$ argocd admin notification list
|
||||
|
||||
# Get notification details
|
||||
$ argocd admin notification get my-notification
|
||||
|
||||
# Create a new notification
|
||||
$ argocd admin notification create my-notification -f notification-config.yaml
|
||||
|
||||
# Update a notification
|
||||
$ argocd admin notification update my-notification -f updated-notification-config.yaml
|
||||
|
||||
# Delete a notification
|
||||
$ argocd admin notification delete my-notification
|
||||
|
||||
# Reset the initial admin password
|
||||
$ argocd admin initial-password reset
|
||||
`,
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/controller"
|
||||
"github.com/argoproj/argo-cd/v2/controller/cache"
|
||||
"github.com/argoproj/argo-cd/v2/controller/metrics"
|
||||
"github.com/argoproj/argo-cd/v2/controller/sharding"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
@@ -272,26 +271,18 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
|
||||
var result []appReconcileResult
|
||||
if refresh {
|
||||
appClientset := appclientset.NewForConfigOrDie(cfg)
|
||||
kubeClientset := kubernetes.NewForConfigOrDie(cfg)
|
||||
if repoServerAddress == "" {
|
||||
printLine("Repo server is not provided, trying to port-forward to argocd-repo-server pod.")
|
||||
overrides := clientcmd.ConfigOverrides{}
|
||||
repoServerName := clientOpts.RepoServerName
|
||||
repoServerServiceLabelSelector := common.LabelKeyComponentRepoServer + "=" + common.LabelValueComponentRepoServer
|
||||
repoServerServices, err := kubeClientset.CoreV1().Services(namespace).List(context.Background(), v1.ListOptions{LabelSelector: repoServerServiceLabelSelector})
|
||||
errors.CheckError(err)
|
||||
if len(repoServerServices.Items) > 0 {
|
||||
if repoServerServicelabel, ok := repoServerServices.Items[0].Labels[common.LabelKeyAppName]; ok && repoServerServicelabel != "" {
|
||||
repoServerName = repoServerServicelabel
|
||||
}
|
||||
}
|
||||
repoServerPodLabelSelector := common.LabelKeyAppName + "=" + repoServerName
|
||||
repoServerPodLabelSelector := common.LabelKeyAppName + "=" + clientOpts.RepoServerName
|
||||
repoServerPort, err := kubeutil.PortForward(8081, namespace, &overrides, repoServerPodLabelSelector)
|
||||
errors.CheckError(err)
|
||||
repoServerAddress = fmt.Sprintf("localhost:%d", repoServerPort)
|
||||
}
|
||||
repoServerClient := reposerverclient.NewRepoServerClientset(repoServerAddress, 60, reposerverclient.TLSConfiguration{DisableTLS: false, StrictValidation: false})
|
||||
|
||||
appClientset := appclientset.NewForConfigOrDie(cfg)
|
||||
kubeClientset := kubernetes.NewForConfigOrDie(cfg)
|
||||
result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache, serverSideDiff, ignoreNormalizerOpts)
|
||||
errors.CheckError(err)
|
||||
} else {
|
||||
@@ -449,5 +440,5 @@ func reconcileApplications(
|
||||
}
|
||||
|
||||
func newLiveStateCache(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache {
|
||||
return cache.NewLiveStateCache(argoDB, appInformer, settingsMgr, kubeutil.NewKubectl(), server, func(managedByApp map[string]bool, ref apiv1.ObjectReference) {}, &sharding.ClusterSharding{}, argo.NewResourceTracking())
|
||||
return cache.NewLiveStateCache(argoDB, appInformer, settingsMgr, kubeutil.NewKubectl(), server, func(managedByApp map[string]bool, ref apiv1.ObjectReference) {}, nil, argo.NewResourceTracking())
|
||||
}
|
||||
|
||||
@@ -19,13 +19,14 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/utils/ptr"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/controller/sharding"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
@@ -71,7 +72,7 @@ argocd admin cluster namespaces my-cluster `,
|
||||
}
|
||||
|
||||
type ClusterWithInfo struct {
|
||||
v1alpha1.Cluster
|
||||
argoappv1.Cluster
|
||||
// Shard holds controller shard number that handles the cluster
|
||||
Shard int
|
||||
// Namespaces holds list of namespaces managed by Argo CD in the cluster
|
||||
@@ -86,12 +87,8 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clusterShardingCache := sharding.NewClusterSharding(argoDB, shard, replicas, shardingAlgorithm)
|
||||
clusterShardingCache.Init(clustersList, appItems)
|
||||
clusterShardingCache.Init(clustersList)
|
||||
clusterShards := clusterShardingCache.GetDistribution()
|
||||
|
||||
var cache *appstatecache.Cache
|
||||
@@ -117,6 +114,10 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
|
||||
}
|
||||
}
|
||||
|
||||
appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apps := appItems.Items
|
||||
for i, app := range apps {
|
||||
err := argo.ValidateDestination(ctx, &app.Spec.Destination, argoDB)
|
||||
@@ -129,6 +130,12 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
|
||||
|
||||
batchSize := 10
|
||||
batchesCount := int(math.Ceil(float64(len(clusters)) / float64(batchSize)))
|
||||
clusterSharding := &sharding.ClusterSharding{
|
||||
Shard: shard,
|
||||
Replicas: replicas,
|
||||
Shards: make(map[string]int),
|
||||
Clusters: make(map[string]*v1alpha1.Cluster),
|
||||
}
|
||||
for batchNum := 0; batchNum < batchesCount; batchNum++ {
|
||||
batchStart := batchSize * batchNum
|
||||
batchEnd := batchSize * (batchNum + 1)
|
||||
@@ -140,8 +147,10 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
|
||||
clusterShard := 0
|
||||
cluster := batch[i]
|
||||
if replicas > 0 {
|
||||
clusterShard = clusterShards[cluster.Server]
|
||||
cluster.Shard = ptr.To(int64(clusterShard))
|
||||
distributionFunction := sharding.GetDistributionFunction(clusterSharding.GetClusterAccessor(), common.DefaultShardingAlgorithm, replicas)
|
||||
distributionFunction(&cluster)
|
||||
clusterShard := clusterShards[cluster.Server]
|
||||
cluster.Shard = pointer.Int64(int64(clusterShard))
|
||||
log.Infof("Cluster with uid: %s will be processed by shard %d", cluster.ID, clusterShard)
|
||||
}
|
||||
if shard != -1 && clusterShard != shard {
|
||||
@@ -219,7 +228,7 @@ func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().IntVar(&shard, "shard", -1, "Cluster shard filter")
|
||||
command.Flags().IntVar(&replicas, "replicas", 0, "Application controller replicas count. Inferred from number of running controller pods if not specified")
|
||||
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", common.DefaultShardingAlgorithm, "Sharding method. Defaults: legacy. Supported sharding methods are : [legacy, round-robin, consistent-hashing] ")
|
||||
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", common.DefaultShardingAlgorithm, "Sharding method. Defaults: legacy. Supported sharding methods are : [legacy, round-robin] ")
|
||||
command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?")
|
||||
|
||||
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command)
|
||||
@@ -514,7 +523,7 @@ argocd admin cluster stats target-cluster`,
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().IntVar(&shard, "shard", -1, "Cluster shard filter")
|
||||
command.Flags().IntVar(&replicas, "replicas", 0, "Application controller replicas count. Inferred from number of running controller pods if not specified")
|
||||
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", common.DefaultShardingAlgorithm, "Sharding method. Defaults: legacy. Supported sharding methods are : [legacy, round-robin, consistent-hashing] ")
|
||||
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", common.DefaultShardingAlgorithm, "Sharding method. Defaults: legacy. Supported sharding methods are : [legacy, round-robin] ")
|
||||
command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?")
|
||||
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command)
|
||||
|
||||
@@ -617,16 +626,15 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command
|
||||
errors.CheckError(err)
|
||||
kubeClientset := fake.NewSimpleClientset()
|
||||
|
||||
var awsAuthConf *v1alpha1.AWSAuthConfig
|
||||
var execProviderConf *v1alpha1.ExecProviderConfig
|
||||
var awsAuthConf *argoappv1.AWSAuthConfig
|
||||
var execProviderConf *argoappv1.ExecProviderConfig
|
||||
if clusterOpts.AwsClusterName != "" {
|
||||
awsAuthConf = &v1alpha1.AWSAuthConfig{
|
||||
awsAuthConf = &argoappv1.AWSAuthConfig{
|
||||
ClusterName: clusterOpts.AwsClusterName,
|
||||
RoleARN: clusterOpts.AwsRoleArn,
|
||||
Profile: clusterOpts.AwsProfile,
|
||||
}
|
||||
} else if clusterOpts.ExecProviderCommand != "" {
|
||||
execProviderConf = &v1alpha1.ExecProviderConfig{
|
||||
execProviderConf = &argoappv1.ExecProviderConfig{
|
||||
Command: clusterOpts.ExecProviderCommand,
|
||||
Args: clusterOpts.ExecProviderArgs,
|
||||
Env: clusterOpts.ExecProviderEnv,
|
||||
@@ -650,7 +658,7 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command
|
||||
|
||||
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, bearerToken, awsAuthConf, execProviderConf, labelsMap, annotationsMap)
|
||||
if clusterOpts.InClusterEndpoint() {
|
||||
clst.Server = v1alpha1.KubernetesInternalAPIServerAddr
|
||||
clst.Server = argoappv1.KubernetesInternalAPIServerAddr
|
||||
}
|
||||
if clusterOpts.ClusterEndpoint == string(cmdutil.KubePublicEndpoint) {
|
||||
// Ignore `kube-public` cluster endpoints, since this command is intended to run without invoking any network connections.
|
||||
|
||||
@@ -21,12 +21,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/rbac"
|
||||
)
|
||||
|
||||
type actionTraitMap map[string]rbacTrait
|
||||
|
||||
type rbacTrait struct {
|
||||
allowPath bool
|
||||
}
|
||||
|
||||
// Provide a mapping of short-hand resource names to their RBAC counterparts
|
||||
var resourceMap map[string]string = map[string]string{
|
||||
"account": rbacpolicy.ResourceAccounts,
|
||||
@@ -38,7 +32,6 @@ var resourceMap map[string]string = map[string]string{
|
||||
"certs": rbacpolicy.ResourceCertificates,
|
||||
"certificate": rbacpolicy.ResourceCertificates,
|
||||
"cluster": rbacpolicy.ResourceClusters,
|
||||
"extension": rbacpolicy.ResourceExtensions,
|
||||
"gpgkey": rbacpolicy.ResourceGPGKeys,
|
||||
"key": rbacpolicy.ResourceGPGKeys,
|
||||
"log": rbacpolicy.ResourceLogs,
|
||||
@@ -53,53 +46,28 @@ var resourceMap map[string]string = map[string]string{
|
||||
}
|
||||
|
||||
// List of allowed RBAC resources
|
||||
var validRBACResourcesActions map[string]actionTraitMap = map[string]actionTraitMap{
|
||||
rbacpolicy.ResourceAccounts: accountsActions,
|
||||
rbacpolicy.ResourceApplications: applicationsActions,
|
||||
rbacpolicy.ResourceApplicationSets: defaultCRUDActions,
|
||||
rbacpolicy.ResourceCertificates: defaultCRDActions,
|
||||
rbacpolicy.ResourceClusters: defaultCRUDActions,
|
||||
rbacpolicy.ResourceExtensions: extensionActions,
|
||||
rbacpolicy.ResourceGPGKeys: defaultCRDActions,
|
||||
rbacpolicy.ResourceLogs: logsActions,
|
||||
rbacpolicy.ResourceExec: execActions,
|
||||
rbacpolicy.ResourceProjects: defaultCRUDActions,
|
||||
rbacpolicy.ResourceRepositories: defaultCRUDActions,
|
||||
var validRBACResources map[string]bool = map[string]bool{
|
||||
rbacpolicy.ResourceAccounts: true,
|
||||
rbacpolicy.ResourceApplications: true,
|
||||
rbacpolicy.ResourceApplicationSets: true,
|
||||
rbacpolicy.ResourceCertificates: true,
|
||||
rbacpolicy.ResourceClusters: true,
|
||||
rbacpolicy.ResourceGPGKeys: true,
|
||||
rbacpolicy.ResourceLogs: true,
|
||||
rbacpolicy.ResourceExec: true,
|
||||
rbacpolicy.ResourceProjects: true,
|
||||
rbacpolicy.ResourceRepositories: true,
|
||||
}
|
||||
|
||||
// List of allowed RBAC actions
|
||||
var defaultCRUDActions = actionTraitMap{
|
||||
rbacpolicy.ActionCreate: rbacTrait{},
|
||||
rbacpolicy.ActionGet: rbacTrait{},
|
||||
rbacpolicy.ActionUpdate: rbacTrait{},
|
||||
rbacpolicy.ActionDelete: rbacTrait{},
|
||||
}
|
||||
var defaultCRDActions = actionTraitMap{
|
||||
rbacpolicy.ActionCreate: rbacTrait{},
|
||||
rbacpolicy.ActionGet: rbacTrait{},
|
||||
rbacpolicy.ActionDelete: rbacTrait{},
|
||||
}
|
||||
var applicationsActions = actionTraitMap{
|
||||
rbacpolicy.ActionCreate: rbacTrait{},
|
||||
rbacpolicy.ActionGet: rbacTrait{},
|
||||
rbacpolicy.ActionUpdate: rbacTrait{allowPath: true},
|
||||
rbacpolicy.ActionDelete: rbacTrait{allowPath: true},
|
||||
rbacpolicy.ActionAction: rbacTrait{allowPath: true},
|
||||
rbacpolicy.ActionOverride: rbacTrait{},
|
||||
rbacpolicy.ActionSync: rbacTrait{},
|
||||
}
|
||||
var accountsActions = actionTraitMap{
|
||||
rbacpolicy.ActionCreate: rbacTrait{},
|
||||
rbacpolicy.ActionUpdate: rbacTrait{},
|
||||
}
|
||||
var execActions = actionTraitMap{
|
||||
rbacpolicy.ActionCreate: rbacTrait{},
|
||||
}
|
||||
var logsActions = actionTraitMap{
|
||||
rbacpolicy.ActionGet: rbacTrait{},
|
||||
}
|
||||
var extensionActions = actionTraitMap{
|
||||
rbacpolicy.ActionInvoke: rbacTrait{},
|
||||
var validRBACActions map[string]bool = map[string]bool{
|
||||
rbacpolicy.ActionAction: true,
|
||||
rbacpolicy.ActionCreate: true,
|
||||
rbacpolicy.ActionDelete: true,
|
||||
rbacpolicy.ActionGet: true,
|
||||
rbacpolicy.ActionOverride: true,
|
||||
rbacpolicy.ActionSync: true,
|
||||
rbacpolicy.ActionUpdate: true,
|
||||
}
|
||||
|
||||
// NewRBACCommand is the command for 'rbac'
|
||||
@@ -253,8 +221,8 @@ argocd admin settings rbac validate --policy-file policy.csv
|
||||
# i.e. 'policy.csv' and (optionally) 'policy.default'
|
||||
argocd admin settings rbac validate --policy-file argocd-rbac-cm.yaml
|
||||
|
||||
# If --policy-file is not given, and instead --namespace is giventhe ConfigMap 'argocd-rbac-cm'
|
||||
# from K8s is used.
|
||||
# If --policy-file is not given, and instead --namespace is giventhe ConfigMap 'argocd-rbac-cm'
|
||||
# from K8s is used.
|
||||
argocd admin settings rbac validate --namespace argocd
|
||||
|
||||
# Either --policy-file or --namespace must be given.
|
||||
@@ -408,9 +376,11 @@ func checkPolicy(subject, action, resource, subResource, builtinPolicy, userPoli
|
||||
// If in strict mode, validate that given RBAC resource and action are
|
||||
// actually valid tokens.
|
||||
if strict {
|
||||
if err := validateRBACResourceAction(realResource, action); err != nil {
|
||||
log.Fatalf("error in RBAC request: %v", err)
|
||||
return false
|
||||
if !isValidRBACResource(realResource) {
|
||||
log.Fatalf("error in RBAC request: '%s' is not a valid resource name", realResource)
|
||||
}
|
||||
if !isValidRBACAction(action) {
|
||||
log.Fatalf("error in RBAC request: '%s' is not a valid action name", action)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,18 +406,17 @@ func resolveRBACResourceName(name string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// validateRBACResourceAction checks whether a given resource is a valid RBAC resource.
|
||||
// If it is, it validates that the action is a valid RBAC action for this resource.
|
||||
func validateRBACResourceAction(resource, action string) error {
|
||||
validActions, ok := validRBACResourcesActions[resource]
|
||||
if !ok {
|
||||
return fmt.Errorf("'%s' is not a valid resource name", resource)
|
||||
// isValidRBACAction checks whether a given action is a valid RBAC action
|
||||
func isValidRBACAction(action string) bool {
|
||||
if strings.HasPrefix(action, rbacpolicy.ActionAction+"/") {
|
||||
return true
|
||||
}
|
||||
|
||||
realAction, _, hasPath := strings.Cut(action, "/")
|
||||
actionTrait, ok := validActions[realAction]
|
||||
if !ok || hasPath && !actionTrait.allowPath {
|
||||
return fmt.Errorf("'%s' is not a valid action for %s", action, resource)
|
||||
}
|
||||
return nil
|
||||
_, ok := validRBACActions[action]
|
||||
return ok
|
||||
}
|
||||
|
||||
// isValidRBACResource checks whether a given resource is a valid RBAC resource
|
||||
func isValidRBACResource(resource string) bool {
|
||||
_, ok := validRBACResources[resource]
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/v2/util/assets"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -42,75 +41,35 @@ func (f *FakeClientConfig) ConfigAccess() clientcmd.ConfigAccess {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_validateRBACResourceAction(t *testing.T) {
|
||||
type args struct {
|
||||
resource string
|
||||
action string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
name: "Test valid resource and action",
|
||||
args: args{
|
||||
resource: rbacpolicy.ResourceApplications,
|
||||
action: rbacpolicy.ActionCreate,
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Test invalid resource",
|
||||
args: args{
|
||||
resource: "invalid",
|
||||
},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "Test invalid action",
|
||||
args: args{
|
||||
resource: rbacpolicy.ResourceApplications,
|
||||
action: "invalid",
|
||||
},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "Test invalid action for resource",
|
||||
args: args{
|
||||
resource: rbacpolicy.ResourceLogs,
|
||||
action: rbacpolicy.ActionCreate,
|
||||
},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "Test valid action with path",
|
||||
args: args{
|
||||
resource: rbacpolicy.ResourceApplications,
|
||||
action: rbacpolicy.ActionAction + "/apps/Deployment/restart",
|
||||
},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Test invalid action with path",
|
||||
args: args{
|
||||
resource: rbacpolicy.ResourceApplications,
|
||||
action: rbacpolicy.ActionGet + "/apps/Deployment/restart",
|
||||
},
|
||||
valid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := validateRBACResourceAction(tt.args.resource, tt.args.action)
|
||||
if tt.valid {
|
||||
assert.NoError(t, result)
|
||||
} else {
|
||||
assert.NotNil(t, result)
|
||||
}
|
||||
func Test_isValidRBACAction(t *testing.T) {
|
||||
for k := range validRBACActions {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
ok := isValidRBACAction(k)
|
||||
assert.True(t, ok)
|
||||
})
|
||||
}
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
ok := isValidRBACAction("invalid")
|
||||
assert.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_isValidRBACAction_ActionAction(t *testing.T) {
|
||||
ok := isValidRBACAction("action/apps/Deployment/restart")
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
func Test_isValidRBACResource(t *testing.T) {
|
||||
for k := range validRBACResources {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
ok := isValidRBACResource(k)
|
||||
assert.True(t, ok)
|
||||
})
|
||||
}
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
ok := isValidRBACResource("invalid")
|
||||
assert.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PolicyFromCSV(t *testing.T) {
|
||||
|
||||
@@ -4,17 +4,16 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
"os"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/utils/ptr"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
|
||||
@@ -97,11 +96,11 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt
|
||||
availActionsForResource, err := appIf.ListResourceActions(ctx, &applicationpkg.ApplicationResourceRequest{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Namespace: ptr.To(obj.GetNamespace()),
|
||||
ResourceName: ptr.To(obj.GetName()),
|
||||
Group: ptr.To(gvk.Group),
|
||||
Kind: ptr.To(gvk.Kind),
|
||||
Version: ptr.To(gvk.Version),
|
||||
Namespace: pointer.String(obj.GetNamespace()),
|
||||
ResourceName: pointer.String(obj.GetName()),
|
||||
Group: pointer.String(gvk.Group),
|
||||
Kind: pointer.String(gvk.Kind),
|
||||
Version: pointer.String(gvk.Version),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
for _, action := range availActionsForResource.Actions {
|
||||
@@ -196,12 +195,12 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti
|
||||
_, err := appIf.RunResourceAction(ctx, &applicationpkg.ResourceActionRunRequest{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Namespace: ptr.To(obj.GetNamespace()),
|
||||
ResourceName: ptr.To(objResourceName),
|
||||
Group: ptr.To(gvk.Group),
|
||||
Kind: ptr.To(gvk.Kind),
|
||||
Version: ptr.To(gvk.GroupVersion().Version),
|
||||
Action: ptr.To(actionName),
|
||||
Namespace: pointer.String(obj.GetNamespace()),
|
||||
ResourceName: pointer.String(objResourceName),
|
||||
Group: pointer.String(gvk.Group),
|
||||
Kind: pointer.String(gvk.Kind),
|
||||
Version: pointer.String(gvk.GroupVersion().Version),
|
||||
Action: pointer.String(actionName),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/utils/ptr"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
@@ -71,14 +71,14 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
_, err = appIf.PatchResource(ctx, &applicationpkg.ApplicationResourcePatchRequest{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Namespace: ptr.To(obj.GetNamespace()),
|
||||
ResourceName: ptr.To(obj.GetName()),
|
||||
Version: ptr.To(gvk.Version),
|
||||
Group: ptr.To(gvk.Group),
|
||||
Kind: ptr.To(gvk.Kind),
|
||||
Patch: ptr.To(patch),
|
||||
PatchType: ptr.To(patchType),
|
||||
Project: ptr.To(project),
|
||||
Namespace: pointer.String(obj.GetNamespace()),
|
||||
ResourceName: pointer.String(obj.GetName()),
|
||||
Version: pointer.String(gvk.Version),
|
||||
Group: pointer.String(gvk.Group),
|
||||
Kind: pointer.String(gvk.Kind),
|
||||
Patch: pointer.String(patch),
|
||||
PatchType: pointer.String(patchType),
|
||||
Project: pointer.String(project),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
log.Infof("Resource '%s' patched", obj.GetName())
|
||||
@@ -108,8 +108,8 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
errors.CheckError(err)
|
||||
command.Flags().StringVar(&group, "group", "", "Group")
|
||||
command.Flags().StringVar(&namespace, "namespace", "", "Namespace")
|
||||
command.Flags().BoolVar(&force, "force", false, "Indicates whether to force delete the resource")
|
||||
command.Flags().BoolVar(&orphan, "orphan", false, "Indicates whether to orphan the dependents of the deleted resource")
|
||||
command.Flags().BoolVar(&force, "force", false, "Indicates whether to orphan the dependents of the deleted resource")
|
||||
command.Flags().BoolVar(&orphan, "orphan", false, "Indicates whether to force delete the resource")
|
||||
command.Flags().BoolVar(&all, "all", false, "Indicates whether to patch multiple matching of resources")
|
||||
command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`)
|
||||
command.Run = func(c *cobra.Command, args []string) {
|
||||
@@ -136,14 +136,14 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
_, err = appIf.DeleteResource(ctx, &applicationpkg.ApplicationResourceDeleteRequest{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Namespace: ptr.To(obj.GetNamespace()),
|
||||
ResourceName: ptr.To(obj.GetName()),
|
||||
Version: ptr.To(gvk.Version),
|
||||
Group: ptr.To(gvk.Group),
|
||||
Kind: ptr.To(gvk.Kind),
|
||||
Namespace: pointer.String(obj.GetNamespace()),
|
||||
ResourceName: pointer.String(obj.GetName()),
|
||||
Version: pointer.String(gvk.Version),
|
||||
Group: pointer.String(gvk.Group),
|
||||
Kind: pointer.String(gvk.Kind),
|
||||
Force: &force,
|
||||
Orphan: &orphan,
|
||||
Project: ptr.To(project),
|
||||
Project: pointer.String(project),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
log.Infof("Resource '%s' deleted", obj.GetName())
|
||||
|
||||
@@ -1,43 +1,23 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
accountpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/account"
|
||||
applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
|
||||
applicationsetpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/applicationset"
|
||||
certificatepkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/certificate"
|
||||
clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
|
||||
gpgkeypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/gpgkey"
|
||||
notificationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/notification"
|
||||
projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
|
||||
repocredspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repocreds"
|
||||
repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
|
||||
sessionpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/session"
|
||||
settingspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings"
|
||||
versionpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/version"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/oauth2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
k8swatch "k8s.io/apimachinery/pkg/watch"
|
||||
)
|
||||
|
||||
func Test_getInfos(t *testing.T) {
|
||||
@@ -223,10 +203,10 @@ func TestDefaultWaitOptions(t *testing.T) {
|
||||
suspended: false,
|
||||
}
|
||||
opts := getWatchOpts(watch)
|
||||
assert.True(t, opts.sync)
|
||||
assert.True(t, opts.health)
|
||||
assert.True(t, opts.operation)
|
||||
assert.False(t, opts.suspended)
|
||||
assert.Equal(t, true, opts.sync)
|
||||
assert.Equal(t, true, opts.health)
|
||||
assert.Equal(t, true, opts.operation)
|
||||
assert.Equal(t, false, opts.suspended)
|
||||
}
|
||||
|
||||
func TestOverrideWaitOptions(t *testing.T) {
|
||||
@@ -237,10 +217,10 @@ func TestOverrideWaitOptions(t *testing.T) {
|
||||
suspended: false,
|
||||
}
|
||||
opts := getWatchOpts(watch)
|
||||
assert.True(t, opts.sync)
|
||||
assert.False(t, opts.health)
|
||||
assert.False(t, opts.operation)
|
||||
assert.False(t, opts.suspended)
|
||||
assert.Equal(t, true, opts.sync)
|
||||
assert.Equal(t, false, opts.health)
|
||||
assert.Equal(t, false, opts.operation)
|
||||
assert.Equal(t, false, opts.suspended)
|
||||
}
|
||||
|
||||
func TestFindRevisionHistoryWithoutPassedIdAndEmptyHistoryList(t *testing.T) {
|
||||
@@ -422,8 +402,8 @@ func TestFormatSyncPolicy(t *testing.T) {
|
||||
|
||||
policy := formatSyncPolicy(app)
|
||||
|
||||
if policy != "Manual" {
|
||||
t.Fatalf("Incorrect policy %q, should be Manual", policy)
|
||||
if policy != "<none>" {
|
||||
t.Fatalf("Incorrect policy %q, should be <none>", policy)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -557,21 +537,18 @@ func TestPrintApplicationHistoryTable(t *testing.T) {
|
||||
ID: 1,
|
||||
Source: v1alpha1.ApplicationSource{
|
||||
TargetRevision: "1",
|
||||
RepoURL: "test",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Source: v1alpha1.ApplicationSource{
|
||||
TargetRevision: "2",
|
||||
RepoURL: "test",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 3,
|
||||
Source: v1alpha1.ApplicationSource{
|
||||
TargetRevision: "3",
|
||||
RepoURL: "test",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -581,86 +558,7 @@ func TestPrintApplicationHistoryTable(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
expectation := "SOURCE test\nID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1\n2 0001-01-01 00:00:00 +0000 UTC 2\n3 0001-01-01 00:00:00 +0000 UTC 3\n"
|
||||
|
||||
if output != expectation {
|
||||
t.Fatalf("Incorrect print operation output %q, should be %q", output, expectation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintApplicationHistoryTableWithMultipleSources(t *testing.T) {
|
||||
histories := []v1alpha1.RevisionHistory{
|
||||
{
|
||||
ID: 0,
|
||||
Source: v1alpha1.ApplicationSource{
|
||||
TargetRevision: "0",
|
||||
RepoURL: "test",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 1,
|
||||
Revisions: []string{
|
||||
"1a",
|
||||
"1b",
|
||||
},
|
||||
//added Source just for testing the fuction
|
||||
Source: v1alpha1.ApplicationSource{
|
||||
TargetRevision: "-1",
|
||||
RepoURL: "ignore",
|
||||
},
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
v1alpha1.ApplicationSource{
|
||||
RepoURL: "test-1",
|
||||
TargetRevision: "1a",
|
||||
},
|
||||
v1alpha1.ApplicationSource{
|
||||
RepoURL: "test-2",
|
||||
TargetRevision: "1b",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Revisions: []string{
|
||||
"2a",
|
||||
"2b",
|
||||
},
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
v1alpha1.ApplicationSource{
|
||||
RepoURL: "test-1",
|
||||
TargetRevision: "2a",
|
||||
},
|
||||
v1alpha1.ApplicationSource{
|
||||
RepoURL: "test-2",
|
||||
TargetRevision: "2b",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 3,
|
||||
Revisions: []string{
|
||||
"3a",
|
||||
"3b",
|
||||
},
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
v1alpha1.ApplicationSource{
|
||||
RepoURL: "test-1",
|
||||
TargetRevision: "3a",
|
||||
},
|
||||
v1alpha1.ApplicationSource{
|
||||
RepoURL: "test-2",
|
||||
TargetRevision: "3b",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
output, _ := captureOutput(func() error {
|
||||
printApplicationHistoryTable(histories)
|
||||
return nil
|
||||
})
|
||||
|
||||
expectation := "SOURCE test\nID DATE REVISION\n0 0001-01-01 00:00:00 +0000 UTC 0\n\nSOURCE test-1\nID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1a\n2 0001-01-01 00:00:00 +0000 UTC 2a\n3 0001-01-01 00:00:00 +0000 UTC 3a\n\nSOURCE test-2\nID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1b\n2 0001-01-01 00:00:00 +0000 UTC 2b\n3 0001-01-01 00:00:00 +0000 UTC 3b\n"
|
||||
expectation := "ID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1\n2 0001-01-01 00:00:00 +0000 UTC 2\n3 0001-01-01 00:00:00 +0000 UTC 3\n"
|
||||
|
||||
if output != expectation {
|
||||
t.Fatalf("Incorrect print operation output %q, should be %q", output, expectation)
|
||||
@@ -741,110 +639,11 @@ Project: default
|
||||
Server: local
|
||||
Namespace: argocd
|
||||
URL: url
|
||||
Source:
|
||||
- Repo: test
|
||||
Target: master
|
||||
Path: /test
|
||||
Helm Values: path1,path2
|
||||
Name Prefix: prefix
|
||||
SyncWindow: Sync Denied
|
||||
Assigned Windows: allow:0 0 * * *:24h,deny:0 0 * * *:24h,allow:0 0 * * *:24h
|
||||
Sync Policy: Automated (Prune)
|
||||
Sync Status: OutOfSync from master
|
||||
Health Status: Progressing (health-message)
|
||||
`
|
||||
assert.Equalf(t, expectation, output, "Incorrect print app summary output %q, should be %q", output, expectation)
|
||||
}
|
||||
|
||||
func TestPrintAppSummaryTable_MultipleSources(t *testing.T) {
|
||||
output, _ := captureOutput(func() error {
|
||||
app := &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SyncPolicy: &v1alpha1.SyncPolicy{
|
||||
Automated: &v1alpha1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
},
|
||||
},
|
||||
Project: "default",
|
||||
Destination: v1alpha1.ApplicationDestination{Server: "local", Namespace: "argocd"},
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "test",
|
||||
TargetRevision: "master",
|
||||
Path: "/test",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"path1", "path2"},
|
||||
},
|
||||
Kustomize: &v1alpha1.ApplicationSourceKustomize{NamePrefix: "prefix"},
|
||||
}, {
|
||||
RepoURL: "test2",
|
||||
TargetRevision: "master2",
|
||||
Path: "/test2",
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeOutOfSync,
|
||||
},
|
||||
Health: v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusProgressing,
|
||||
Message: "health-message",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
windows := &v1alpha1.SyncWindows{
|
||||
{
|
||||
Kind: "allow",
|
||||
Schedule: "0 0 * * *",
|
||||
Duration: "24h",
|
||||
Applications: []string{
|
||||
"*-prod",
|
||||
},
|
||||
ManualSync: true,
|
||||
},
|
||||
{
|
||||
Kind: "deny",
|
||||
Schedule: "0 0 * * *",
|
||||
Duration: "24h",
|
||||
Namespaces: []string{
|
||||
"default",
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: "allow",
|
||||
Schedule: "0 0 * * *",
|
||||
Duration: "24h",
|
||||
Clusters: []string{
|
||||
"in-cluster",
|
||||
"cluster1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
printAppSummaryTable(app, "url", windows)
|
||||
return nil
|
||||
})
|
||||
|
||||
expectation := `Name: argocd/test
|
||||
Project: default
|
||||
Server: local
|
||||
Namespace: argocd
|
||||
URL: url
|
||||
Sources:
|
||||
- Repo: test
|
||||
Target: master
|
||||
Path: /test
|
||||
Helm Values: path1,path2
|
||||
Name Prefix: prefix
|
||||
- Repo: test2
|
||||
Target: master2
|
||||
Path: /test2
|
||||
Repo: test
|
||||
Target: master
|
||||
Path: /test
|
||||
Helm Values: path1,path2
|
||||
Name Prefix: prefix
|
||||
SyncWindow: Sync Denied
|
||||
Assigned Windows: allow:0 0 * * *:24h,deny:0 0 * * *:24h,allow:0 0 * * *:24h
|
||||
Sync Policy: Automated (Prune)
|
||||
@@ -1007,14 +806,6 @@ func TestTargetObjects_invalid(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestCheckForDeleteEvent(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
fakeClient := new(fakeAcdClient)
|
||||
|
||||
checkForDeleteEvent(ctx, fakeClient, "testApp")
|
||||
}
|
||||
|
||||
func TestPrintApplicationNames(t *testing.T) {
|
||||
output, _ := captureOutput(func() error {
|
||||
app := &v1alpha1.Application{
|
||||
@@ -1163,18 +954,18 @@ func Test_unset(t *testing.T) {
|
||||
assert.False(t, updated)
|
||||
assert.False(t, nothingToUnset)
|
||||
|
||||
assert.True(t, helmSource.Helm.IgnoreMissingValueFiles)
|
||||
assert.Equal(t, true, helmSource.Helm.IgnoreMissingValueFiles)
|
||||
updated, nothingToUnset = unset(helmSource, unsetOpts{ignoreMissingValueFiles: true})
|
||||
assert.False(t, helmSource.Helm.IgnoreMissingValueFiles)
|
||||
assert.Equal(t, false, helmSource.Helm.IgnoreMissingValueFiles)
|
||||
assert.True(t, updated)
|
||||
assert.False(t, nothingToUnset)
|
||||
updated, nothingToUnset = unset(helmSource, unsetOpts{ignoreMissingValueFiles: true})
|
||||
assert.False(t, updated)
|
||||
assert.False(t, nothingToUnset)
|
||||
|
||||
assert.True(t, helmSource.Helm.PassCredentials)
|
||||
assert.Equal(t, true, helmSource.Helm.PassCredentials)
|
||||
updated, nothingToUnset = unset(helmSource, unsetOpts{passCredentials: true})
|
||||
assert.False(t, helmSource.Helm.PassCredentials)
|
||||
assert.Equal(t, false, helmSource.Helm.PassCredentials)
|
||||
assert.True(t, updated)
|
||||
assert.False(t, nothingToUnset)
|
||||
updated, nothingToUnset = unset(helmSource, unsetOpts{passCredentials: true})
|
||||
@@ -1357,47 +1148,47 @@ func TestFilterAppResources(t *testing.T) {
|
||||
expectedResult []*v1alpha1.SyncOperationResource
|
||||
}{
|
||||
// --resource apps:ReplicaSet:replicaSet-name1 --resource *:Service:*
|
||||
{testName: "Include ReplicaSet replicaSet-name1 resource and all service resources",
|
||||
{testName: "Include ReplicaSet replicaSet-name1 resouce and all service resources",
|
||||
selectedResources: []*v1alpha1.SyncOperationResource{&includeAllServiceResources, &includeReplicaSet1Resource},
|
||||
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &service1, &service2},
|
||||
},
|
||||
// --resource apps:ReplicaSet:replicaSet-name1 --resource !*:Service:*
|
||||
{testName: "Include ReplicaSet replicaSet-name1 resource and exclude all service resources",
|
||||
{testName: "Include ReplicaSet replicaSet-name1 resouce and exclude all service resources",
|
||||
selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllServiceResources, &includeReplicaSet1Resource},
|
||||
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment},
|
||||
},
|
||||
// --resource !apps:ReplicaSet:replicaSet-name2 --resource !*:Service:*
|
||||
{testName: "Exclude ReplicaSet replicaSet-name2 resource and all service resources",
|
||||
{testName: "Exclude ReplicaSet replicaSet-name2 resouce and all service resources",
|
||||
selectedResources: []*v1alpha1.SyncOperationResource{&excludeReplicaSet2Resource, &excludeAllServiceResources},
|
||||
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment},
|
||||
},
|
||||
// --resource !apps:ReplicaSet:replicaSet-name2
|
||||
{testName: "Exclude ReplicaSet replicaSet-name2 resource",
|
||||
{testName: "Exclude ReplicaSet replicaSet-name2 resouce",
|
||||
selectedResources: []*v1alpha1.SyncOperationResource{&excludeReplicaSet2Resource},
|
||||
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &job, &service1, &service2, &deployment},
|
||||
},
|
||||
// --resource apps:ReplicaSet:replicaSet-name1
|
||||
{testName: "Include ReplicaSet replicaSet-name1 resource",
|
||||
{testName: "Include ReplicaSet replicaSet-name1 resouce",
|
||||
selectedResources: []*v1alpha1.SyncOperationResource{&includeReplicaSet1Resource},
|
||||
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1},
|
||||
},
|
||||
// --resource !*:Service:*
|
||||
{testName: "Exclude Service resources",
|
||||
{testName: "Exclude Service resouces",
|
||||
selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllServiceResources},
|
||||
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment},
|
||||
},
|
||||
// --resource *:Service:*
|
||||
{testName: "Include Service resources",
|
||||
{testName: "Include Service resouces",
|
||||
selectedResources: []*v1alpha1.SyncOperationResource{&includeAllServiceResources},
|
||||
expectedResult: []*v1alpha1.SyncOperationResource{&service1, &service2},
|
||||
},
|
||||
// --resource !*:*:*
|
||||
{testName: "Exclude all resources",
|
||||
{testName: "Exclude all resouces",
|
||||
selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllResources},
|
||||
expectedResult: nil,
|
||||
},
|
||||
// --resource *:*:*
|
||||
{testName: "Include all resources",
|
||||
{testName: "Include all resouces",
|
||||
selectedResources: []*v1alpha1.SyncOperationResource{&includeAllResources},
|
||||
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment},
|
||||
},
|
||||
@@ -1510,7 +1301,7 @@ func TestPrintApplicationTableNotWide(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
expectation := "NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual <none>\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual <none>\n"
|
||||
expectation := "NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS\napp-name http://localhost:8080 default prj OutOfSync Healthy <none> <none>\napp-name http://localhost:8080 default prj OutOfSync Healthy <none> <none>\n"
|
||||
assert.Equal(t, output, expectation)
|
||||
}
|
||||
|
||||
@@ -1546,7 +1337,7 @@ func TestPrintApplicationTableWide(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
expectation := "NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual <none> https://github.com/argoproj/argocd-example-apps guestbook 123\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual <none> https://github.com/argoproj/argocd-example-apps guestbook 123\n"
|
||||
expectation := "NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET\napp-name http://localhost:8080 default prj OutOfSync Healthy <none> <none> https://github.com/argoproj/argocd-example-apps guestbook 123\napp-name http://localhost:8080 default prj OutOfSync Healthy <none> <none> https://github.com/argoproj/argocd-example-apps guestbook 123\n"
|
||||
assert.Equal(t, output, expectation)
|
||||
}
|
||||
|
||||
@@ -1808,104 +1599,3 @@ func testApp(name, project string, labels map[string]string, annotations map[str
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type fakeAcdClient struct{}
|
||||
|
||||
func (c *fakeAcdClient) ClientOptions() argocdclient.ClientOptions {
|
||||
return argocdclient.ClientOptions{}
|
||||
}
|
||||
func (c *fakeAcdClient) HTTPClient() (*http.Client, error) { return nil, nil }
|
||||
func (c *fakeAcdClient) OIDCConfig(context.Context, *settingspkg.Settings) (*oauth2.Config, *oidc.Provider, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewRepoClient() (io.Closer, repositorypkg.RepositoryServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewRepoClientOrDie() (io.Closer, repositorypkg.RepositoryServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewRepoCredsClient() (io.Closer, repocredspkg.RepoCredsServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewRepoCredsClientOrDie() (io.Closer, repocredspkg.RepoCredsServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewCertClient() (io.Closer, certificatepkg.CertificateServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewCertClientOrDie() (io.Closer, certificatepkg.CertificateServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewClusterClient() (io.Closer, clusterpkg.ClusterServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewClusterClientOrDie() (io.Closer, clusterpkg.ClusterServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewGPGKeyClient() (io.Closer, gpgkeypkg.GPGKeyServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewGPGKeyClientOrDie() (io.Closer, gpgkeypkg.GPGKeyServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewApplicationClient() (io.Closer, applicationpkg.ApplicationServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewApplicationSetClient() (io.Closer, applicationsetpkg.ApplicationSetServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewApplicationClientOrDie() (io.Closer, applicationpkg.ApplicationServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewApplicationSetClientOrDie() (io.Closer, applicationsetpkg.ApplicationSetServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewNotificationClient() (io.Closer, notificationpkg.NotificationServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewNotificationClientOrDie() (io.Closer, notificationpkg.NotificationServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewSessionClient() (io.Closer, sessionpkg.SessionServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewSessionClientOrDie() (io.Closer, sessionpkg.SessionServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewSettingsClient() (io.Closer, settingspkg.SettingsServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewSettingsClientOrDie() (io.Closer, settingspkg.SettingsServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewVersionClient() (io.Closer, versionpkg.VersionServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewVersionClientOrDie() (io.Closer, versionpkg.VersionServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewProjectClient() (io.Closer, projectpkg.ProjectServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewProjectClientOrDie() (io.Closer, projectpkg.ProjectServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewAccountClient() (io.Closer, accountpkg.AccountServiceClient, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) NewAccountClientOrDie() (io.Closer, accountpkg.AccountServiceClient) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *fakeAcdClient) WatchApplicationWithRetry(ctx context.Context, appName string, revision string) chan *v1alpha1.ApplicationWatchEvent {
|
||||
appEventsCh := make(chan *v1alpha1.ApplicationWatchEvent)
|
||||
|
||||
go func() {
|
||||
modifiedEvent := new(v1alpha1.ApplicationWatchEvent)
|
||||
modifiedEvent.Type = k8swatch.Modified
|
||||
appEventsCh <- modifiedEvent
|
||||
deletedEvent := new(v1alpha1.ApplicationWatchEvent)
|
||||
deletedEvent.Type = k8swatch.Deleted
|
||||
appEventsCh <- deletedEvent
|
||||
}()
|
||||
return appEventsCh
|
||||
}
|
||||
|
||||
@@ -350,11 +350,9 @@ func printAppSetSummaryTable(appSet *arogappsetv1.ApplicationSet) {
|
||||
fmt.Printf(printOpFmtStr, "Project:", appSet.Spec.Template.Spec.GetProject())
|
||||
fmt.Printf(printOpFmtStr, "Server:", getServerForAppSet(appSet))
|
||||
fmt.Printf(printOpFmtStr, "Namespace:", appSet.Spec.Template.Spec.Destination.Namespace)
|
||||
if !appSet.Spec.Template.Spec.HasMultipleSources() {
|
||||
fmt.Println("Source:")
|
||||
} else {
|
||||
fmt.Println("Sources:")
|
||||
}
|
||||
fmt.Printf(printOpFmtStr, "Repo:", source.RepoURL)
|
||||
fmt.Printf(printOpFmtStr, "Target:", source.TargetRevision)
|
||||
fmt.Printf(printOpFmtStr, "Path:", source.Path)
|
||||
printAppSourceDetails(&source)
|
||||
|
||||
var (
|
||||
|
||||
@@ -180,9 +180,9 @@ func TestPrintAppSetSummaryTable(t *testing.T) {
|
||||
Project: default
|
||||
Server:
|
||||
Namespace:
|
||||
Source:
|
||||
- Repo:
|
||||
Target:
|
||||
Repo:
|
||||
Target:
|
||||
Path:
|
||||
SyncPolicy: <none>
|
||||
`,
|
||||
},
|
||||
@@ -193,9 +193,9 @@ SyncPolicy: <none>
|
||||
Project: default
|
||||
Server:
|
||||
Namespace:
|
||||
Source:
|
||||
- Repo:
|
||||
Target:
|
||||
Repo:
|
||||
Target:
|
||||
Path:
|
||||
SyncPolicy: Automated
|
||||
`,
|
||||
},
|
||||
@@ -206,9 +206,9 @@ SyncPolicy: Automated
|
||||
Project: default
|
||||
Server:
|
||||
Namespace:
|
||||
Source:
|
||||
- Repo:
|
||||
Target:
|
||||
Repo:
|
||||
Target:
|
||||
Path:
|
||||
SyncPolicy: Automated
|
||||
`,
|
||||
},
|
||||
|
||||
@@ -111,7 +111,6 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
|
||||
awsAuthConf = &argoappv1.AWSAuthConfig{
|
||||
ClusterName: clusterOpts.AwsClusterName,
|
||||
RoleARN: clusterOpts.AwsRoleArn,
|
||||
Profile: clusterOpts.AwsProfile,
|
||||
}
|
||||
} else if clusterOpts.ExecProviderCommand != "" {
|
||||
execProviderConf = &argoappv1.ExecProviderConfig{
|
||||
@@ -490,7 +489,7 @@ func NewClusterListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
# List Clusters in Default "Wide" Format
|
||||
argocd cluster list
|
||||
|
||||
# List Cluster via specifying the server
|
||||
# List Cluster via specifing the server
|
||||
argocd cluster list --server <ARGOCD_SERVER_ADDRESS>
|
||||
|
||||
# List Clusters in JSON Format
|
||||
|
||||
@@ -196,8 +196,8 @@ __argocd_custom_func() {
|
||||
func NewCompletionCommand() *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "completion SHELL",
|
||||
Short: "output shell completion code for the specified shell (bash, zsh or fish)",
|
||||
Long: `Write bash, zsh or fish shell completion code to standard output.
|
||||
Short: "output shell completion code for the specified shell (bash or zsh)",
|
||||
Long: `Write bash or zsh shell completion code to standard output.
|
||||
|
||||
For bash, ensure you have bash completions installed and enabled.
|
||||
To access completions in your current shell, run
|
||||
@@ -218,11 +218,6 @@ $ source <(argocd completion bash)
|
||||
# For zsh
|
||||
$ argocd completion zsh > _argocd
|
||||
$ source _argocd
|
||||
|
||||
# For fish
|
||||
$ argocd completion fish > ~/.config/fish/completions/argocd.fish
|
||||
$ source ~/.config/fish/completions/argocd.fish
|
||||
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
@@ -232,17 +227,16 @@ $ source ~/.config/fish/completions/argocd.fish
|
||||
shell := args[0]
|
||||
rootCommand := NewCommand()
|
||||
rootCommand.BashCompletionFunction = bashCompletionFunc
|
||||
availableCompletions := map[string]func(out io.Writer, cmd *cobra.Command) error{
|
||||
"bash": runCompletionBash,
|
||||
"zsh": runCompletionZsh,
|
||||
"fish": runCompletionFish,
|
||||
availableCompletions := map[string]func(io.Writer) error{
|
||||
"bash": rootCommand.GenBashCompletion,
|
||||
"zsh": rootCommand.GenZshCompletion,
|
||||
}
|
||||
completion, ok := availableCompletions[shell]
|
||||
if !ok {
|
||||
fmt.Printf("Invalid shell '%s'. The supported shells are bash, zsh and fish.\n", shell)
|
||||
fmt.Printf("Invalid shell '%s'. The supported shells are bash and zsh.\n", shell)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := completion(os.Stdout, rootCommand); err != nil {
|
||||
if err := completion(os.Stdout); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
@@ -250,15 +244,3 @@ $ source ~/.config/fish/completions/argocd.fish
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
func runCompletionBash(out io.Writer, cmd *cobra.Command) error {
|
||||
return cmd.GenBashCompletion(out)
|
||||
}
|
||||
|
||||
func runCompletionZsh(out io.Writer, cmd *cobra.Command) error {
|
||||
return cmd.GenZshCompletion(out)
|
||||
}
|
||||
|
||||
func runCompletionFish(out io.Writer, cmd *cobra.Command) error {
|
||||
return cmd.GenFishCompletion(out, true)
|
||||
}
|
||||
|
||||
@@ -18,12 +18,11 @@ import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/pflag"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
cache2 "k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/utils/ptr"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -79,12 +78,6 @@ func (c *forwardCacheClient) Set(item *cache.Item) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) Rename(oldKey string, newKey string, expiration time.Duration) error {
|
||||
return c.doLazy(func(client cache.CacheClient) error {
|
||||
return client.Rename(oldKey, newKey, expiration)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) Get(key string, obj interface{}) error {
|
||||
return c.doLazy(func(client cache.CacheClient) error {
|
||||
return client.Get(key, obj)
|
||||
@@ -116,7 +109,6 @@ type forwardRepoClientset struct {
|
||||
repoClientset repoapiclient.Clientset
|
||||
err error
|
||||
repoServerName string
|
||||
kubeClientset kubernetes.Interface
|
||||
}
|
||||
|
||||
func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.RepoServerServiceClient, error) {
|
||||
@@ -124,19 +116,7 @@ func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.R
|
||||
overrides := clientcmd.ConfigOverrides{
|
||||
CurrentContext: c.context,
|
||||
}
|
||||
repoServerName := c.repoServerName
|
||||
repoServererviceLabelSelector := common.LabelKeyComponentRepoServer + "=" + common.LabelValueComponentRepoServer
|
||||
repoServerServices, err := c.kubeClientset.CoreV1().Services(c.namespace).List(context.Background(), v1.ListOptions{LabelSelector: repoServererviceLabelSelector})
|
||||
if err != nil {
|
||||
c.err = err
|
||||
return
|
||||
}
|
||||
if len(repoServerServices.Items) > 0 {
|
||||
if repoServerServicelabel, ok := repoServerServices.Items[0].Labels[common.LabelKeyAppName]; ok && repoServerServicelabel != "" {
|
||||
repoServerName = repoServerServicelabel
|
||||
}
|
||||
}
|
||||
repoServerPodLabelSelector := common.LabelKeyAppName + "=" + repoServerName
|
||||
repoServerPodLabelSelector := common.LabelKeyAppName + "=" + c.repoServerName
|
||||
repoServerPort, err := kubeutil.PortForward(8081, c.namespace, &overrides, repoServerPodLabelSelector)
|
||||
if err != nil {
|
||||
c.err = err
|
||||
@@ -205,7 +185,7 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
|
||||
log.SetLevel(log.ErrorLevel)
|
||||
os.Setenv(v1alpha1.EnvVarFakeInClusterConfig, "true")
|
||||
if address == nil {
|
||||
address = ptr.To("localhost")
|
||||
address = pointer.String("localhost")
|
||||
}
|
||||
if port == nil || *port == 0 {
|
||||
addr := fmt.Sprintf("%s:0", *address)
|
||||
@@ -251,7 +231,7 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
|
||||
KubeClientset: kubeClientset,
|
||||
Insecure: true,
|
||||
ListenHost: *address,
|
||||
RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr, repoServerName: clientOpts.RepoServerName, kubeClientset: kubeClientset},
|
||||
RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr, repoServerName: clientOpts.RepoServerName},
|
||||
EnableProxyExtension: false,
|
||||
})
|
||||
srv.Init(ctx)
|
||||
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
oidcutil "github.com/argoproj/argo-cd/v2/util/oidc"
|
||||
"github.com/argoproj/argo-cd/v2/util/rand"
|
||||
oidcconfig "github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
// NewLoginCommand returns a new instance of `argocd login` command
|
||||
@@ -307,7 +306,6 @@ func oauth2Login(
|
||||
fmt.Printf("Opening browser for authentication\n")
|
||||
|
||||
var url string
|
||||
var oidcconfig oidcconfig.OIDCConfig
|
||||
grantType := oidcutil.InferGrantType(oidcConf)
|
||||
opts := []oauth2.AuthCodeOption{oauth2.AccessTypeOffline}
|
||||
if claimsRequested := oidcSettings.GetIDTokenClaims(); claimsRequested != nil {
|
||||
@@ -318,9 +316,6 @@ func oauth2Login(
|
||||
case oidcutil.GrantTypeAuthorizationCode:
|
||||
opts = append(opts, oauth2.SetAuthURLParam("code_challenge", codeChallenge))
|
||||
opts = append(opts, oauth2.SetAuthURLParam("code_challenge_method", "S256"))
|
||||
if oidcconfig.DomainHint != "" {
|
||||
opts = append(opts, oauth2.SetAuthURLParam("domain_hint", oidcconfig.DomainHint))
|
||||
}
|
||||
url = oauth2conf.AuthCodeURL(stateNonce, opts...)
|
||||
case oidcutil.GrantTypeImplicit:
|
||||
url, err = oidcutil.ImplicitFlowURL(oauth2conf, stateNonce, opts...)
|
||||
|
||||
@@ -78,8 +78,6 @@ func NewProjectCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
command.AddCommand(NewProjectWindowsCommand(clientOpts))
|
||||
command.AddCommand(NewProjectAddOrphanedIgnoreCommand(clientOpts))
|
||||
command.AddCommand(NewProjectRemoveOrphanedIgnoreCommand(clientOpts))
|
||||
command.AddCommand(NewProjectAddSourceNamespace(clientOpts))
|
||||
command.AddCommand(NewProjectRemoveSourceNamespace(clientOpts))
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -511,88 +509,6 @@ func NewProjectAddSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
return command
|
||||
}
|
||||
|
||||
// NewProjectAddSourceNamespace returns a new instance of an `argocd proj add-source-namespace` command
|
||||
func NewProjectAddSourceNamespace(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "add-source-namespace PROJECT NAMESPACE",
|
||||
Short: "Add source namespace to the AppProject",
|
||||
Example: templates.Examples(`
|
||||
# Add Kubernetes namespace as source namespace to the AppProject where application resources are allowed to be created in.
|
||||
argocd proj add-source-namespace PROJECT NAMESPACE
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
if len(args) != 2 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
projName := args[0]
|
||||
srcNamespace := args[1]
|
||||
conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(ctx, &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
|
||||
for _, item := range proj.Spec.SourceNamespaces {
|
||||
if item == "*" || item == srcNamespace {
|
||||
fmt.Printf("Source namespace '*' already allowed in project\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
proj.Spec.SourceNamespaces = append(proj.Spec.SourceNamespaces, srcNamespace)
|
||||
_, err = projIf.Update(ctx, &projectpkg.ProjectUpdateRequest{Project: proj})
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
// NewProjectRemoveSourceNamespace returns a new instance of an `argocd proj remove-source-namespace` command
|
||||
func NewProjectRemoveSourceNamespace(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "remove-source-namespace PROJECT NAMESPACE",
|
||||
Short: "Removes the source namespace from the AppProject",
|
||||
Example: templates.Examples(`
|
||||
# Remove source NAMESPACE in PROJECT
|
||||
argocd proj remove-source-namespace PROJECT NAMESPACE
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
if len(args) != 2 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
projName := args[0]
|
||||
srcNamespace := args[1]
|
||||
conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(ctx, &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
|
||||
index := -1
|
||||
for i, item := range proj.Spec.SourceNamespaces {
|
||||
if item == srcNamespace && item != "*" {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if index == -1 {
|
||||
fmt.Printf("Source namespace '%s' does not exist in project or cannot be removed\n", srcNamespace)
|
||||
} else {
|
||||
proj.Spec.SourceNamespaces = append(proj.Spec.SourceNamespaces[:index], proj.Spec.SourceNamespaces[index+1:]...)
|
||||
_, err = projIf.Update(ctx, &projectpkg.ProjectUpdateRequest{Project: proj})
|
||||
errors.CheckError(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
func modifyResourcesList(list *[]metav1.GroupKind, add bool, listDesc string, group string, kind string) bool {
|
||||
if add {
|
||||
for _, item := range *list {
|
||||
|
||||
@@ -58,7 +58,7 @@ func NewProjectWindowsDisableManualSyncCommand(clientOpts *argocdclient.ClientOp
|
||||
#Disable manual sync for a sync window for the Project
|
||||
argocd proj windows disable-manual-sync PROJECT ID
|
||||
|
||||
#Disabling manual sync for a windows set on the default project with Id 0
|
||||
#Disbaling manual sync for a windows set on the default project with Id 0
|
||||
argocd proj windows disable-manual-sync default 0`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
|
||||
@@ -64,12 +64,6 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
# Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here
|
||||
argocd repo add ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa
|
||||
|
||||
# Add a Git repository via SSH using socks5 proxy with no proxy credentials
|
||||
argocd repo add ssh://git@github.com/argoproj/argocd-example-apps --ssh-private-key-path ~/id_rsa --proxy socks5://your.proxy.server.ip:1080
|
||||
|
||||
# Add a Git repository via SSH using socks5 proxy with proxy credentials
|
||||
argocd repo add ssh://git@github.com/argoproj/argocd-example-apps --ssh-private-key-path ~/id_rsa --proxy socks5://username:password@your.proxy.server.ip:1080
|
||||
|
||||
# Add a private Git repository via HTTPS using username/password and TLS client certificates:
|
||||
argocd repo add https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key
|
||||
|
||||
|
||||
@@ -75,11 +75,11 @@ func NewCommand() *cobra.Command {
|
||||
command.PersistentFlags().StringVar(&clientOpts.GRPCWebRootPath, "grpc-web-root-path", config.GetFlag("grpc-web-root-path", ""), "Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root.")
|
||||
command.PersistentFlags().StringVar(&cmdutil.LogFormat, "logformat", config.GetFlag("logformat", "text"), "Set the logging format. One of: text|json")
|
||||
command.PersistentFlags().StringVar(&cmdutil.LogLevel, "loglevel", config.GetFlag("loglevel", "info"), "Set the logging level. One of: debug|info|warn|error")
|
||||
command.PersistentFlags().StringSliceVarP(&clientOpts.Headers, "header", "H", config.GetStringSliceFlag("header", []string{}), "Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers)")
|
||||
command.PersistentFlags().StringSliceVarP(&clientOpts.Headers, "header", "H", []string{}, "Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers)")
|
||||
command.PersistentFlags().BoolVar(&clientOpts.PortForward, "port-forward", config.GetBoolFlag("port-forward"), "Connect to a random argocd-server port using port forwarding")
|
||||
command.PersistentFlags().StringVar(&clientOpts.PortForwardNamespace, "port-forward-namespace", config.GetFlag("port-forward-namespace", ""), "Namespace name which should be used for port forwarding")
|
||||
command.PersistentFlags().IntVar(&clientOpts.HttpRetryMax, "http-retry-max", config.GetIntFlag("http-retry-max", 0), "Maximum number of retries to establish http connection to Argo CD server")
|
||||
command.PersistentFlags().BoolVar(&clientOpts.Core, "core", config.GetBoolFlag("core"), "If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server")
|
||||
command.PersistentFlags().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))
|
||||
|
||||
259
cmd/util/app.go
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/ptr"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -68,7 +68,6 @@ type AppOptions struct {
|
||||
kustomizeVersion string
|
||||
kustomizeCommonLabels []string
|
||||
kustomizeCommonAnnotations []string
|
||||
kustomizeLabelWithoutSelector bool
|
||||
kustomizeForceCommonLabels bool
|
||||
kustomizeForceCommonAnnotations bool
|
||||
kustomizeNamespace string
|
||||
@@ -80,7 +79,6 @@ type AppOptions struct {
|
||||
retryBackoffDuration time.Duration
|
||||
retryBackoffMaxDuration time.Duration
|
||||
retryBackoffFactor int64
|
||||
ref string
|
||||
}
|
||||
|
||||
func AddAppFlags(command *cobra.Command, opts *AppOptions) {
|
||||
@@ -105,7 +103,7 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
|
||||
command.Flags().StringArrayVar(&opts.helmSetFiles, "helm-set-file", []string{}, "Helm set values from respective files specified via the command line (can be repeated to set several values: --helm-set-file key1=path1 --helm-set-file key2=path2)")
|
||||
command.Flags().BoolVar(&opts.helmSkipCrds, "helm-skip-crds", false, "Skip helm crd installation step")
|
||||
command.Flags().StringVar(&opts.project, "project", "", "Application project name")
|
||||
command.Flags().StringVar(&opts.syncPolicy, "sync-policy", "", "Set the sync policy (one of: manual (aliases of manual: none), automated (aliases of automated: auto, automatic))")
|
||||
command.Flags().StringVar(&opts.syncPolicy, "sync-policy", "", "Set the sync policy (one of: none, automated (aliases of automated: auto, automatic))")
|
||||
command.Flags().StringArrayVar(&opts.syncOptions, "sync-option", []string{}, "Add or remove a sync option, e.g add `Prune=false`. Remove using `!` prefix, e.g. `!Prune=false`")
|
||||
command.Flags().BoolVar(&opts.autoPrune, "auto-prune", false, "Set automatic pruning when sync is automated")
|
||||
command.Flags().BoolVar(&opts.selfHeal, "self-heal", false, "Set self healing when sync is automated")
|
||||
@@ -126,7 +124,6 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
|
||||
command.Flags().BoolVar(&opts.Validate, "validate", true, "Validation of repo and cluster")
|
||||
command.Flags().StringArrayVar(&opts.kustomizeCommonLabels, "kustomize-common-label", []string{}, "Set common labels in Kustomize")
|
||||
command.Flags().StringArrayVar(&opts.kustomizeCommonAnnotations, "kustomize-common-annotation", []string{}, "Set common labels in Kustomize")
|
||||
command.Flags().BoolVar(&opts.kustomizeLabelWithoutSelector, "kustomize-label-without-selector", false, "Do not apply common label to selectors or templates")
|
||||
command.Flags().BoolVar(&opts.kustomizeForceCommonLabels, "kustomize-force-common-label", false, "Force common labels in Kustomize")
|
||||
command.Flags().BoolVar(&opts.kustomizeForceCommonAnnotations, "kustomize-force-common-annotation", false, "Force common annotations in Kustomize")
|
||||
command.Flags().StringVar(&opts.kustomizeNamespace, "kustomize-namespace", "", "Kustomize namespace")
|
||||
@@ -136,37 +133,81 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
|
||||
command.Flags().DurationVar(&opts.retryBackoffDuration, "sync-retry-backoff-duration", argoappv1.DefaultSyncRetryDuration, "Sync retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h)")
|
||||
command.Flags().DurationVar(&opts.retryBackoffMaxDuration, "sync-retry-backoff-max-duration", argoappv1.DefaultSyncRetryMaxDuration, "Max sync retry backoff duration. Input needs to be a duration (e.g. 2m, 1h)")
|
||||
command.Flags().Int64Var(&opts.retryBackoffFactor, "sync-retry-backoff-factor", argoappv1.DefaultSyncRetryFactor, "Factor multiplies the base duration after each failed sync retry")
|
||||
command.Flags().StringVar(&opts.ref, "ref", "", "Ref is reference to another source within sources field")
|
||||
}
|
||||
|
||||
func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, appOpts *AppOptions, sourcePosition int) int {
|
||||
func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, appOpts *AppOptions) int {
|
||||
visited := 0
|
||||
if flags == nil {
|
||||
return visited
|
||||
}
|
||||
source := spec.GetSourcePtrByPosition(sourcePosition)
|
||||
if source == nil {
|
||||
source = &argoappv1.ApplicationSource{}
|
||||
}
|
||||
source, visited = ConstructSource(source, *appOpts, flags)
|
||||
if spec.HasMultipleSources() {
|
||||
if sourcePosition == 0 {
|
||||
spec.Sources[sourcePosition] = *source
|
||||
} else if sourcePosition > 0 {
|
||||
spec.Sources[sourcePosition-1] = *source
|
||||
} else {
|
||||
spec.Sources = append(spec.Sources, *source)
|
||||
}
|
||||
} else {
|
||||
spec.Source = source
|
||||
}
|
||||
flags.Visit(func(f *pflag.Flag) {
|
||||
visited++
|
||||
|
||||
source := spec.GetSourcePtr()
|
||||
if source == nil {
|
||||
source = &argoappv1.ApplicationSource{}
|
||||
}
|
||||
switch f.Name {
|
||||
case "repo":
|
||||
source.RepoURL = appOpts.repoURL
|
||||
case "path":
|
||||
source.Path = appOpts.appPath
|
||||
case "helm-chart":
|
||||
source.Chart = appOpts.chart
|
||||
case "revision":
|
||||
source.TargetRevision = appOpts.revision
|
||||
case "revision-history-limit":
|
||||
i := int64(appOpts.revisionHistoryLimit)
|
||||
spec.RevisionHistoryLimit = &i
|
||||
case "values":
|
||||
setHelmOpt(source, helmOpts{valueFiles: appOpts.valuesFiles})
|
||||
case "ignore-missing-value-files":
|
||||
setHelmOpt(source, helmOpts{ignoreMissingValueFiles: appOpts.ignoreMissingValueFiles})
|
||||
case "values-literal-file":
|
||||
var data []byte
|
||||
|
||||
// read uri
|
||||
parsedURL, err := url.ParseRequestURI(appOpts.values)
|
||||
if err != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") {
|
||||
data, err = os.ReadFile(appOpts.values)
|
||||
} else {
|
||||
data, err = config.ReadRemoteFile(appOpts.values)
|
||||
}
|
||||
errors.CheckError(err)
|
||||
setHelmOpt(source, helmOpts{values: string(data)})
|
||||
case "release-name":
|
||||
setHelmOpt(source, helmOpts{releaseName: appOpts.releaseName})
|
||||
case "helm-version":
|
||||
setHelmOpt(source, helmOpts{version: appOpts.helmVersion})
|
||||
case "helm-pass-credentials":
|
||||
setHelmOpt(source, helmOpts{passCredentials: appOpts.helmPassCredentials})
|
||||
case "helm-set":
|
||||
setHelmOpt(source, helmOpts{helmSets: appOpts.helmSets})
|
||||
case "helm-set-string":
|
||||
setHelmOpt(source, helmOpts{helmSetStrings: appOpts.helmSetStrings})
|
||||
case "helm-set-file":
|
||||
setHelmOpt(source, helmOpts{helmSetFiles: appOpts.helmSetFiles})
|
||||
case "helm-skip-crds":
|
||||
setHelmOpt(source, helmOpts{skipCrds: appOpts.helmSkipCrds})
|
||||
case "directory-recurse":
|
||||
if source.Directory != nil {
|
||||
source.Directory.Recurse = appOpts.directoryRecurse
|
||||
} else {
|
||||
source.Directory = &argoappv1.ApplicationSourceDirectory{Recurse: appOpts.directoryRecurse}
|
||||
}
|
||||
case "directory-exclude":
|
||||
if source.Directory != nil {
|
||||
source.Directory.Exclude = appOpts.directoryExclude
|
||||
} else {
|
||||
source.Directory = &argoappv1.ApplicationSourceDirectory{Exclude: appOpts.directoryExclude}
|
||||
}
|
||||
case "directory-include":
|
||||
if source.Directory != nil {
|
||||
source.Directory.Include = appOpts.directoryInclude
|
||||
} else {
|
||||
source.Directory = &argoappv1.ApplicationSourceDirectory{Include: appOpts.directoryInclude}
|
||||
}
|
||||
case "config-management-plugin":
|
||||
source.Plugin = &argoappv1.ApplicationSourcePlugin{Name: appOpts.configManagementPlugin}
|
||||
case "dest-name":
|
||||
spec.Destination.Name = appOpts.destName
|
||||
case "dest-server":
|
||||
@@ -175,9 +216,45 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
spec.Destination.Namespace = appOpts.destNamespace
|
||||
case "project":
|
||||
spec.Project = appOpts.project
|
||||
case "nameprefix":
|
||||
setKustomizeOpt(source, kustomizeOpts{namePrefix: appOpts.namePrefix})
|
||||
case "namesuffix":
|
||||
setKustomizeOpt(source, kustomizeOpts{nameSuffix: appOpts.nameSuffix})
|
||||
case "kustomize-image":
|
||||
setKustomizeOpt(source, kustomizeOpts{images: appOpts.kustomizeImages})
|
||||
case "kustomize-replica":
|
||||
setKustomizeOpt(source, kustomizeOpts{replicas: appOpts.kustomizeReplicas})
|
||||
case "kustomize-version":
|
||||
setKustomizeOpt(source, kustomizeOpts{version: appOpts.kustomizeVersion})
|
||||
case "kustomize-namespace":
|
||||
setKustomizeOpt(source, kustomizeOpts{namespace: appOpts.kustomizeNamespace})
|
||||
case "kustomize-common-label":
|
||||
parsedLabels, err := label.Parse(appOpts.kustomizeCommonLabels)
|
||||
errors.CheckError(err)
|
||||
setKustomizeOpt(source, kustomizeOpts{commonLabels: parsedLabels})
|
||||
case "kustomize-common-annotation":
|
||||
parsedAnnotations, err := label.Parse(appOpts.kustomizeCommonAnnotations)
|
||||
errors.CheckError(err)
|
||||
setKustomizeOpt(source, kustomizeOpts{commonAnnotations: parsedAnnotations})
|
||||
case "kustomize-force-common-label":
|
||||
setKustomizeOpt(source, kustomizeOpts{forceCommonLabels: appOpts.kustomizeForceCommonLabels})
|
||||
case "kustomize-force-common-annotation":
|
||||
setKustomizeOpt(source, kustomizeOpts{forceCommonAnnotations: appOpts.kustomizeForceCommonAnnotations})
|
||||
case "jsonnet-tla-str":
|
||||
setJsonnetOpt(source, appOpts.jsonnetTlaStr, false)
|
||||
case "jsonnet-tla-code":
|
||||
setJsonnetOpt(source, appOpts.jsonnetTlaCode, true)
|
||||
case "jsonnet-ext-var-str":
|
||||
setJsonnetOptExtVar(source, appOpts.jsonnetExtVarStr, false)
|
||||
case "jsonnet-ext-var-code":
|
||||
setJsonnetOptExtVar(source, appOpts.jsonnetExtVarCode, true)
|
||||
case "jsonnet-libs":
|
||||
setJsonnetOptLibs(source, appOpts.jsonnetLibs)
|
||||
case "plugin-env":
|
||||
setPluginOptEnvs(source, appOpts.pluginEnvs)
|
||||
case "sync-policy":
|
||||
switch appOpts.syncPolicy {
|
||||
case "none", "manual":
|
||||
case "none":
|
||||
if spec.SyncPolicy != nil {
|
||||
spec.SyncPolicy.Automated = nil
|
||||
}
|
||||
@@ -218,7 +295,7 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
Backoff: &argoappv1.Backoff{
|
||||
Duration: appOpts.retryBackoffDuration.String(),
|
||||
MaxDuration: appOpts.retryBackoffMaxDuration.String(),
|
||||
Factor: ptr.To(appOpts.retryBackoffFactor),
|
||||
Factor: pointer.Int64(appOpts.retryBackoffFactor),
|
||||
},
|
||||
}
|
||||
} else if appOpts.retryLimit == 0 {
|
||||
@@ -231,6 +308,7 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
log.Fatalf("Invalid sync-retry-limit [%d]", appOpts.retryLimit)
|
||||
}
|
||||
}
|
||||
spec.Source = source
|
||||
})
|
||||
if flags.Changed("auto-prune") {
|
||||
if spec.SyncPolicy == nil || spec.SyncPolicy.Automated == nil {
|
||||
@@ -262,7 +340,6 @@ type kustomizeOpts struct {
|
||||
version string
|
||||
commonLabels map[string]string
|
||||
commonAnnotations map[string]string
|
||||
labelWithoutSelector bool
|
||||
forceCommonLabels bool
|
||||
forceCommonAnnotations bool
|
||||
namespace string
|
||||
@@ -290,9 +367,6 @@ func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
|
||||
if opts.commonAnnotations != nil {
|
||||
src.Kustomize.CommonAnnotations = opts.commonAnnotations
|
||||
}
|
||||
if opts.labelWithoutSelector {
|
||||
src.Kustomize.LabelWithoutSelector = opts.labelWithoutSelector
|
||||
}
|
||||
if opts.forceCommonLabels {
|
||||
src.Kustomize.ForceCommonLabels = opts.forceCommonLabels
|
||||
}
|
||||
@@ -424,11 +498,11 @@ func setJsonnetOptLibs(src *argoappv1.ApplicationSource, libs []string) {
|
||||
// SetParameterOverrides updates an existing or appends a new parameter override in the application
|
||||
// The app is assumed to be a helm app and is expected to be in the form:
|
||||
// param=value
|
||||
func SetParameterOverrides(app *argoappv1.Application, parameters []string, index int) {
|
||||
func SetParameterOverrides(app *argoappv1.Application, parameters []string) {
|
||||
if len(parameters) == 0 {
|
||||
return
|
||||
}
|
||||
source := app.Spec.GetSourcePtrByIndex(index)
|
||||
source := app.Spec.GetSource()
|
||||
var sourceType argoappv1.ApplicationSourceType
|
||||
if st, _ := source.ExplicitType(); st != nil {
|
||||
sourceType = *st
|
||||
@@ -540,8 +614,8 @@ func constructAppsBaseOnName(appName string, labels, annotations, args []string,
|
||||
Source: &argoappv1.ApplicationSource{},
|
||||
},
|
||||
}
|
||||
SetAppSpecOptions(flags, &app.Spec, &appOpts, 0)
|
||||
SetParameterOverrides(app, appOpts.Parameters, 0)
|
||||
SetAppSpecOptions(flags, &app.Spec, &appOpts)
|
||||
SetParameterOverrides(app, appOpts.Parameters)
|
||||
mergeLabels(app, labels)
|
||||
setAnnotations(app, annotations)
|
||||
return []*argoappv1.Application{
|
||||
@@ -566,15 +640,10 @@ func constructAppsFromFileUrl(fileURL, appName string, labels, annotations, args
|
||||
if app.Name == "" {
|
||||
return nil, fmt.Errorf("app.Name is empty. --name argument can be used to provide app.Name")
|
||||
}
|
||||
|
||||
SetAppSpecOptions(flags, &app.Spec, &appOpts)
|
||||
SetParameterOverrides(app, appOpts.Parameters)
|
||||
mergeLabels(app, labels)
|
||||
setAnnotations(app, annotations)
|
||||
|
||||
// do not allow overrides for applications with multiple sources
|
||||
if !app.Spec.HasMultipleSources() {
|
||||
SetAppSpecOptions(flags, &app.Spec, &appOpts, 0)
|
||||
SetParameterOverrides(app, appOpts.Parameters, 0)
|
||||
}
|
||||
}
|
||||
return apps, nil
|
||||
}
|
||||
@@ -585,117 +654,9 @@ func ConstructApps(fileURL, appName string, labels, annotations, args []string,
|
||||
} else if fileURL != "" {
|
||||
return constructAppsFromFileUrl(fileURL, appName, labels, annotations, args, appOpts, flags)
|
||||
}
|
||||
|
||||
return constructAppsBaseOnName(appName, labels, annotations, args, appOpts, flags)
|
||||
}
|
||||
|
||||
func ConstructSource(source *argoappv1.ApplicationSource, appOpts AppOptions, flags *pflag.FlagSet) (*argoappv1.ApplicationSource, int) {
|
||||
visited := 0
|
||||
flags.Visit(func(f *pflag.Flag) {
|
||||
visited++
|
||||
switch f.Name {
|
||||
case "repo":
|
||||
source.RepoURL = appOpts.repoURL
|
||||
case "path":
|
||||
source.Path = appOpts.appPath
|
||||
case "helm-chart":
|
||||
source.Chart = appOpts.chart
|
||||
case "revision":
|
||||
source.TargetRevision = appOpts.revision
|
||||
case "values":
|
||||
setHelmOpt(source, helmOpts{valueFiles: appOpts.valuesFiles})
|
||||
case "ignore-missing-value-files":
|
||||
setHelmOpt(source, helmOpts{ignoreMissingValueFiles: appOpts.ignoreMissingValueFiles})
|
||||
case "values-literal-file":
|
||||
var data []byte
|
||||
// read uri
|
||||
parsedURL, err := url.ParseRequestURI(appOpts.values)
|
||||
if err != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") {
|
||||
data, err = os.ReadFile(appOpts.values)
|
||||
} else {
|
||||
data, err = config.ReadRemoteFile(appOpts.values)
|
||||
}
|
||||
errors.CheckError(err)
|
||||
setHelmOpt(source, helmOpts{values: string(data)})
|
||||
case "release-name":
|
||||
setHelmOpt(source, helmOpts{releaseName: appOpts.releaseName})
|
||||
case "helm-version":
|
||||
setHelmOpt(source, helmOpts{version: appOpts.helmVersion})
|
||||
case "helm-pass-credentials":
|
||||
setHelmOpt(source, helmOpts{passCredentials: appOpts.helmPassCredentials})
|
||||
case "helm-set":
|
||||
setHelmOpt(source, helmOpts{helmSets: appOpts.helmSets})
|
||||
case "helm-set-string":
|
||||
setHelmOpt(source, helmOpts{helmSetStrings: appOpts.helmSetStrings})
|
||||
case "helm-set-file":
|
||||
setHelmOpt(source, helmOpts{helmSetFiles: appOpts.helmSetFiles})
|
||||
case "helm-skip-crds":
|
||||
setHelmOpt(source, helmOpts{skipCrds: appOpts.helmSkipCrds})
|
||||
case "directory-recurse":
|
||||
if source.Directory != nil {
|
||||
source.Directory.Recurse = appOpts.directoryRecurse
|
||||
} else {
|
||||
source.Directory = &argoappv1.ApplicationSourceDirectory{Recurse: appOpts.directoryRecurse}
|
||||
}
|
||||
case "directory-exclude":
|
||||
if source.Directory != nil {
|
||||
source.Directory.Exclude = appOpts.directoryExclude
|
||||
} else {
|
||||
source.Directory = &argoappv1.ApplicationSourceDirectory{Exclude: appOpts.directoryExclude}
|
||||
}
|
||||
case "directory-include":
|
||||
if source.Directory != nil {
|
||||
source.Directory.Include = appOpts.directoryInclude
|
||||
} else {
|
||||
source.Directory = &argoappv1.ApplicationSourceDirectory{Include: appOpts.directoryInclude}
|
||||
}
|
||||
case "config-management-plugin":
|
||||
source.Plugin = &argoappv1.ApplicationSourcePlugin{Name: appOpts.configManagementPlugin}
|
||||
case "nameprefix":
|
||||
setKustomizeOpt(source, kustomizeOpts{namePrefix: appOpts.namePrefix})
|
||||
case "namesuffix":
|
||||
setKustomizeOpt(source, kustomizeOpts{nameSuffix: appOpts.nameSuffix})
|
||||
case "kustomize-image":
|
||||
setKustomizeOpt(source, kustomizeOpts{images: appOpts.kustomizeImages})
|
||||
case "kustomize-replica":
|
||||
setKustomizeOpt(source, kustomizeOpts{replicas: appOpts.kustomizeReplicas})
|
||||
case "kustomize-version":
|
||||
setKustomizeOpt(source, kustomizeOpts{version: appOpts.kustomizeVersion})
|
||||
case "kustomize-namespace":
|
||||
setKustomizeOpt(source, kustomizeOpts{namespace: appOpts.kustomizeNamespace})
|
||||
case "kustomize-common-label":
|
||||
parsedLabels, err := label.Parse(appOpts.kustomizeCommonLabels)
|
||||
errors.CheckError(err)
|
||||
setKustomizeOpt(source, kustomizeOpts{commonLabels: parsedLabels})
|
||||
case "kustomize-common-annotation":
|
||||
parsedAnnotations, err := label.Parse(appOpts.kustomizeCommonAnnotations)
|
||||
errors.CheckError(err)
|
||||
setKustomizeOpt(source, kustomizeOpts{commonAnnotations: parsedAnnotations})
|
||||
case "kustomize-label-without-selector":
|
||||
setKustomizeOpt(source, kustomizeOpts{labelWithoutSelector: appOpts.kustomizeLabelWithoutSelector})
|
||||
case "kustomize-force-common-label":
|
||||
setKustomizeOpt(source, kustomizeOpts{forceCommonLabels: appOpts.kustomizeForceCommonLabels})
|
||||
case "kustomize-force-common-annotation":
|
||||
setKustomizeOpt(source, kustomizeOpts{forceCommonAnnotations: appOpts.kustomizeForceCommonAnnotations})
|
||||
case "jsonnet-tla-str":
|
||||
setJsonnetOpt(source, appOpts.jsonnetTlaStr, false)
|
||||
case "jsonnet-tla-code":
|
||||
setJsonnetOpt(source, appOpts.jsonnetTlaCode, true)
|
||||
case "jsonnet-ext-var-str":
|
||||
setJsonnetOptExtVar(source, appOpts.jsonnetExtVarStr, false)
|
||||
case "jsonnet-ext-var-code":
|
||||
setJsonnetOptExtVar(source, appOpts.jsonnetExtVarCode, true)
|
||||
case "jsonnet-libs":
|
||||
setJsonnetOptLibs(source, appOpts.jsonnetLibs)
|
||||
case "plugin-env":
|
||||
setPluginOptEnvs(source, appOpts.pluginEnvs)
|
||||
case "ref":
|
||||
source.Ref = appOpts.ref
|
||||
}
|
||||
})
|
||||
return source, visited
|
||||
}
|
||||
|
||||
func mergeLabels(app *argoappv1.Application, labels []string) {
|
||||
mapLabels, err := label.Parse(labels)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -27,7 +27,7 @@ func Test_setHelmOpt(t *testing.T) {
|
||||
t.Run("IgnoreMissingValueFiles", func(t *testing.T) {
|
||||
src := v1alpha1.ApplicationSource{}
|
||||
setHelmOpt(&src, helmOpts{ignoreMissingValueFiles: true})
|
||||
assert.True(t, src.Helm.IgnoreMissingValueFiles)
|
||||
assert.Equal(t, true, src.Helm.IgnoreMissingValueFiles)
|
||||
})
|
||||
t.Run("ReleaseName", func(t *testing.T) {
|
||||
src := v1alpha1.ApplicationSource{}
|
||||
@@ -57,12 +57,12 @@ func Test_setHelmOpt(t *testing.T) {
|
||||
t.Run("HelmPassCredentials", func(t *testing.T) {
|
||||
src := v1alpha1.ApplicationSource{}
|
||||
setHelmOpt(&src, helmOpts{passCredentials: true})
|
||||
assert.True(t, src.Helm.PassCredentials)
|
||||
assert.Equal(t, true, src.Helm.PassCredentials)
|
||||
})
|
||||
t.Run("HelmSkipCrds", func(t *testing.T) {
|
||||
src := v1alpha1.ApplicationSource{}
|
||||
setHelmOpt(&src, helmOpts{skipCrds: true})
|
||||
assert.True(t, src.Helm.SkipCrds)
|
||||
assert.Equal(t, true, src.Helm.SkipCrds)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -123,11 +123,6 @@ func Test_setKustomizeOpt(t *testing.T) {
|
||||
setKustomizeOpt(&src, kustomizeOpts{commonAnnotations: map[string]string{"foo1": "bar1", "foo2": "bar2"}})
|
||||
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{CommonAnnotations: map[string]string{"foo1": "bar1", "foo2": "bar2"}}, src.Kustomize)
|
||||
})
|
||||
t.Run("Label Without Selector", func(t *testing.T) {
|
||||
src := v1alpha1.ApplicationSource{}
|
||||
setKustomizeOpt(&src, kustomizeOpts{commonLabels: map[string]string{"foo1": "bar1", "foo2": "bar2"}, labelWithoutSelector: true})
|
||||
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{CommonLabels: map[string]string{"foo1": "bar1", "foo2": "bar2"}, LabelWithoutSelector: true}, src.Kustomize)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_setJsonnetOpt(t *testing.T) {
|
||||
@@ -170,16 +165,7 @@ func (f *appOptionsFixture) SetFlag(key, value string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = SetAppSpecOptions(f.command.Flags(), f.spec, f.options, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *appOptionsFixture) SetFlagWithSourcePosition(key, value string, sourcePosition int) error {
|
||||
err := f.command.Flags().Set(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = SetAppSpecOptions(f.command.Flags(), f.spec, f.options, sourcePosition)
|
||||
_ = SetAppSpecOptions(f.command.Flags(), f.spec, f.options)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -234,54 +220,6 @@ func Test_setAppSpecOptions(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func newMultiSourceAppOptionsFixture() *appOptionsFixture {
|
||||
fixture := &appOptionsFixture{
|
||||
spec: &v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
v1alpha1.ApplicationSource{},
|
||||
v1alpha1.ApplicationSource{},
|
||||
},
|
||||
},
|
||||
command: &cobra.Command{},
|
||||
options: &AppOptions{},
|
||||
}
|
||||
AddAppFlags(fixture.command, fixture.options)
|
||||
return fixture
|
||||
}
|
||||
|
||||
func Test_setAppSpecOptionsMultiSourceApp(t *testing.T) {
|
||||
f := newMultiSourceAppOptionsFixture()
|
||||
sourcePosition := 0
|
||||
sourcePosition1 := 1
|
||||
sourcePosition2 := 2
|
||||
t.Run("SyncPolicy", func(t *testing.T) {
|
||||
assert.NoError(t, f.SetFlagWithSourcePosition("sync-policy", "automated", sourcePosition1))
|
||||
assert.NotNil(t, f.spec.SyncPolicy.Automated)
|
||||
|
||||
f.spec.SyncPolicy = nil
|
||||
assert.NoError(t, f.SetFlagWithSourcePosition("sync-policy", "automatic", sourcePosition1))
|
||||
assert.NotNil(t, f.spec.SyncPolicy.Automated)
|
||||
})
|
||||
t.Run("Helm - SourcePosition 0", func(t *testing.T) {
|
||||
assert.NoError(t, f.SetFlagWithSourcePosition("helm-version", "v2", sourcePosition))
|
||||
assert.Equal(t, len(f.spec.GetSources()), 2)
|
||||
assert.Equal(t, f.spec.GetSources()[sourcePosition].Helm.Version, "v2")
|
||||
})
|
||||
t.Run("Kustomize", func(t *testing.T) {
|
||||
assert.NoError(t, f.SetFlagWithSourcePosition("kustomize-replica", "my-deployment=2", sourcePosition1))
|
||||
assert.Equal(t, f.spec.Sources[sourcePosition1-1].Kustomize.Replicas, v1alpha1.KustomizeReplicas{{Name: "my-deployment", Count: intstr.FromInt(2)}})
|
||||
assert.NoError(t, f.SetFlagWithSourcePosition("kustomize-replica", "my-deployment=4", sourcePosition2))
|
||||
assert.Equal(t, f.spec.Sources[sourcePosition2-1].Kustomize.Replicas, v1alpha1.KustomizeReplicas{{Name: "my-deployment", Count: intstr.FromInt(4)}})
|
||||
})
|
||||
t.Run("Helm", func(t *testing.T) {
|
||||
assert.NoError(t, f.SetFlagWithSourcePosition("helm-version", "v2", sourcePosition1))
|
||||
assert.NoError(t, f.SetFlagWithSourcePosition("helm-version", "v3", sourcePosition2))
|
||||
assert.Equal(t, len(f.spec.GetSources()), 2)
|
||||
assert.Equal(t, f.spec.GetSources()[sourcePosition1-1].Helm.Version, "v2")
|
||||
assert.Equal(t, f.spec.GetSources()[sourcePosition2-1].Helm.Version, "v3")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_setAnnotations(t *testing.T) {
|
||||
t.Run("Annotations", func(t *testing.T) {
|
||||
app := v1alpha1.Application{}
|
||||
|
||||
@@ -144,7 +144,6 @@ type ClusterOptions struct {
|
||||
Upsert bool
|
||||
ServiceAccount string
|
||||
AwsRoleArn string
|
||||
AwsProfile string
|
||||
AwsClusterName string
|
||||
SystemNamespace string
|
||||
Namespaces []string
|
||||
@@ -170,7 +169,6 @@ func AddClusterFlags(command *cobra.Command, opts *ClusterOptions) {
|
||||
command.Flags().BoolVar(&opts.InCluster, "in-cluster", false, "Indicates Argo CD resides inside this cluster and should connect using the internal k8s hostname (kubernetes.default.svc)")
|
||||
command.Flags().StringVar(&opts.AwsClusterName, "aws-cluster-name", "", "AWS Cluster name if set then aws cli eks token command will be used to access cluster")
|
||||
command.Flags().StringVar(&opts.AwsRoleArn, "aws-role-arn", "", "Optional AWS role arn. If set then AWS IAM Authenticator assumes a role to perform cluster operations instead of the default AWS credential provider chain.")
|
||||
command.Flags().StringVar(&opts.AwsProfile, "aws-profile", "", "Optional AWS profile. If set then AWS IAM Authenticator uses this profile to perform cluster operations instead of the default AWS credential provider chain.")
|
||||
command.Flags().StringArrayVar(&opts.Namespaces, "namespace", nil, "List of namespaces which are allowed to manage")
|
||||
command.Flags().BoolVar(&opts.ClusterResources, "cluster-resources", false, "Indicates if cluster level resources should be managed. The setting is used only if list of managed namespaces is not empty.")
|
||||
command.Flags().StringVar(&opts.Name, "name", "", "Overwrite the cluster name")
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/ptr"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -115,7 +115,7 @@ func GetOrphanedResourcesSettings(flagSet *pflag.FlagSet, opts ProjectOpts) *v1a
|
||||
if opts.orphanedResourcesEnabled || warnChanged {
|
||||
settings := v1alpha1.OrphanedResourcesMonitorSettings{}
|
||||
if warnChanged {
|
||||
settings.Warn = ptr.To(opts.orphanedResourcesWarn)
|
||||
settings.Warn = pointer.Bool(opts.orphanedResourcesWarn)
|
||||
}
|
||||
return &settings
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@ package apiclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
@@ -17,9 +14,9 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
var (
|
||||
const (
|
||||
// MaxGRPCMessageSize contains max grpc message size
|
||||
MaxGRPCMessageSize = env.ParseNumFromEnv(common.EnvGRPCMaxSizeMB, 100, 0, math.MaxInt32) * 1024 * 1024
|
||||
MaxGRPCMessageSize = 100 * 1024 * 1024
|
||||
)
|
||||
|
||||
// Clientset represents config management plugin server api clients
|
||||
|
||||
@@ -128,8 +128,8 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
|
||||
if len(output) == 0 {
|
||||
logCtx.Warn("Plugin command returned zero output")
|
||||
} else {
|
||||
// Log stderr even on successful commands to help develop plugins
|
||||
logCtx.Info("Plugin command successful")
|
||||
// Log stderr even on successfull commands to help develop plugins
|
||||
logCtx.Info("Plugin command successfull")
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(output, "\n"), nil
|
||||
|
||||
@@ -369,7 +369,7 @@ func TestRunCommandEmptyCommand(t *testing.T) {
|
||||
assert.ErrorContains(t, err, "Command is empty")
|
||||
}
|
||||
|
||||
// TestRunCommandContextTimeoutWithCleanup makes sure that the process is given enough time to cleanup before sending SIGKILL.
|
||||
// TestRunCommandContextTimeoutWithGracefulTermination makes sure that the process is given enough time to cleanup before sending SIGKILL.
|
||||
func TestRunCommandContextTimeoutWithCleanup(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 900*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
@@ -113,17 +113,11 @@ const (
|
||||
|
||||
// LegacyShardingAlgorithm is the default value for Sharding Algorithm it uses an `uid` based distribution (non-uniform)
|
||||
LegacyShardingAlgorithm = "legacy"
|
||||
// RoundRobinShardingAlgorithm is a flag value that can be opted for Sharding Algorithm it uses an equal distribution across all shards
|
||||
// RoundRobinShardingAlgorithm is a flag value that can be opted for Sharding Algorithm it uses an equal distribution accross all shards
|
||||
RoundRobinShardingAlgorithm = "round-robin"
|
||||
// AppControllerHeartbeatUpdateRetryCount is the retry count for updating the Shard Mapping to the Shard Mapping ConfigMap used by Application Controller
|
||||
AppControllerHeartbeatUpdateRetryCount = 3
|
||||
|
||||
// ConsistentHashingWithBoundedLoadsAlgorithm uses an algorithm that tries to use an equal distribution across
|
||||
// all shards but is optimised to handle sharding and/or cluster addition or removal. In case of sharding or
|
||||
// cluster changes, this algorithm minimises the changes between shard and clusters assignments.
|
||||
ConsistentHashingWithBoundedLoadsAlgorithm = "consistent-hashing"
|
||||
|
||||
DefaultShardingAlgorithm = LegacyShardingAlgorithm
|
||||
DefaultShardingAlgorithm = LegacyShardingAlgorithm
|
||||
)
|
||||
|
||||
// Dex related constants
|
||||
@@ -155,14 +149,10 @@ const (
|
||||
LabelKeyAppInstance = "app.kubernetes.io/instance"
|
||||
// LabelKeyAppName is the label key to use to uniquely identify the name of the Kubernetes application
|
||||
LabelKeyAppName = "app.kubernetes.io/name"
|
||||
// LabelKeyAutoLabelClusterInfo if set to true will automatically add extra labels from the cluster info (currently it only adds a k8s version label)
|
||||
LabelKeyAutoLabelClusterInfo = "argocd.argoproj.io/auto-label-cluster-info"
|
||||
// LabelKeyLegacyApplicationName is the legacy label (v0.10 and below) and is superseded by 'app.kubernetes.io/instance'
|
||||
LabelKeyLegacyApplicationName = "applications.argoproj.io/app-name"
|
||||
// LabelKeySecretType contains the type of argocd secret (currently: 'cluster', 'repository', 'repo-config' or 'repo-creds')
|
||||
LabelKeySecretType = "argocd.argoproj.io/secret-type"
|
||||
// LabelKeyClusterKubernetesVersion contains the kubernetes version of the cluster secret if it has been enabled
|
||||
LabelKeyClusterKubernetesVersion = "argocd.argoproj.io/kubernetes-version"
|
||||
// LabelValueSecretTypeCluster indicates a secret type of cluster
|
||||
LabelValueSecretTypeCluster = "cluster"
|
||||
// LabelValueSecretTypeRepository indicates a secret type of repository
|
||||
@@ -194,10 +184,6 @@ const (
|
||||
// AnnotationKeyAppSkipReconcile tells the Application to skip the Application controller reconcile.
|
||||
// Skip reconcile when the value is "true" or any other string values that can be strconv.ParseBool() to be true.
|
||||
AnnotationKeyAppSkipReconcile = "argocd.argoproj.io/skip-reconcile"
|
||||
// LabelKeyComponentRepoServer is the label key to identify the component as repo-server
|
||||
LabelKeyComponentRepoServer = "app.kubernetes.io/component"
|
||||
// LabelValueComponentRepoServer is the label value for the repo-server component
|
||||
LabelValueComponentRepoServer = "repo-server"
|
||||
)
|
||||
|
||||
// Environment variables for tuning and debugging Argo CD
|
||||
@@ -212,7 +198,7 @@ const (
|
||||
EnvVarTLSDataPath = "ARGOCD_TLS_DATA_PATH"
|
||||
// EnvGitAttemptsCount specifies number of git remote operations attempts count
|
||||
EnvGitAttemptsCount = "ARGOCD_GIT_ATTEMPTS_COUNT"
|
||||
// EnvGitRetryMaxDuration specifies max duration of git remote operation retry
|
||||
// EnvGitRetryMaxDuration specifices max duration of git remote operation retry
|
||||
EnvGitRetryMaxDuration = "ARGOCD_GIT_RETRY_MAX_DURATION"
|
||||
// EnvGitRetryDuration specifies duration of git remote operation retry
|
||||
EnvGitRetryDuration = "ARGOCD_GIT_RETRY_DURATION"
|
||||
@@ -252,8 +238,6 @@ const (
|
||||
EnvLogFormat = "ARGOCD_LOG_FORMAT"
|
||||
// EnvLogLevel log level that is defined by `--loglevel` option
|
||||
EnvLogLevel = "ARGOCD_LOG_LEVEL"
|
||||
// EnvLogFormatEnableFullTimestamp enables the FullTimestamp option in logs
|
||||
EnvLogFormatEnableFullTimestamp = "ARGOCD_LOG_FORMAT_ENABLE_FULL_TIMESTAMP"
|
||||
// EnvMaxCookieNumber max number of chunks a cookie can be broken into
|
||||
EnvMaxCookieNumber = "ARGOCD_MAX_COOKIE_NUMBER"
|
||||
// EnvPluginSockFilePath allows to override the pluginSockFilePath for repo server and cmp server
|
||||
@@ -279,8 +263,6 @@ const (
|
||||
// EnvServerSideDiff defines the env var used to enable ServerSide Diff feature.
|
||||
// If defined, value must be "true" or "false".
|
||||
EnvServerSideDiff = "ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF"
|
||||
// EnvGRPCMaxSizeMB is the environment variable to look for a max GRPC message size
|
||||
EnvGRPCMaxSizeMB = "ARGOCD_GRPC_MAX_SIZE_MB"
|
||||
)
|
||||
|
||||
// Config Management Plugin related constants
|
||||
@@ -359,7 +341,7 @@ func GetCMPChunkSize() int {
|
||||
}
|
||||
|
||||
// GetCMPWorkDir will return the full path of the work directory used by the CMP server.
|
||||
// This directory and all it's contents will be deleted during CMP bootstrap.
|
||||
// This directory and all it's contents will be deleted durring CMP bootstrap.
|
||||
func GetCMPWorkDir() string {
|
||||
if workDir := os.Getenv(EnvCMPWorkDir); workDir != "" {
|
||||
return filepath.Join(workDir, DefaultCMPWorkDirName)
|
||||
|
||||
@@ -48,6 +48,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/controller/sharding"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
argov1alpha "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions/application/v1alpha1"
|
||||
applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
|
||||
@@ -97,15 +98,6 @@ func (a CompareWith) Pointer() *CompareWith {
|
||||
return &a
|
||||
}
|
||||
|
||||
func getAppLog(app *appv1.Application) *log.Entry {
|
||||
return log.WithFields(log.Fields{
|
||||
"application": app.Name,
|
||||
"app-namespace": app.Namespace,
|
||||
"app-qualified-name": app.QualifiedName(),
|
||||
"project": app.Spec.Project,
|
||||
})
|
||||
}
|
||||
|
||||
// ApplicationController is the controller for application resources.
|
||||
type ApplicationController struct {
|
||||
cache *appstatecache.Cache
|
||||
@@ -422,11 +414,10 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b
|
||||
continue
|
||||
}
|
||||
|
||||
logCtx := getAppLog(app)
|
||||
// Enforce application's permission for the source namespace
|
||||
_, err = ctrl.getAppProj(app)
|
||||
if err != nil {
|
||||
logCtx.Errorf("Unable to determine project for app '%s': %v", app.QualifiedName(), err)
|
||||
log.Errorf("Unable to determine project for app '%s': %v", app.QualifiedName(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -439,14 +430,15 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b
|
||||
if ref.Namespace == "" {
|
||||
namespace = "(cluster-scoped)"
|
||||
}
|
||||
logCtx.WithFields(log.Fields{
|
||||
"comparison-level": level,
|
||||
"namespace": namespace,
|
||||
"name": ref.Name,
|
||||
"api-version": ref.APIVersion,
|
||||
"kind": ref.Kind,
|
||||
"server": app.Spec.Destination.Server,
|
||||
"cluster-name": app.Spec.Destination.Name,
|
||||
log.WithFields(log.Fields{
|
||||
"application": appKey,
|
||||
"level": level,
|
||||
"namespace": namespace,
|
||||
"name": ref.Name,
|
||||
"api-version": ref.APIVersion,
|
||||
"kind": ref.Kind,
|
||||
"server": app.Spec.Destination.Server,
|
||||
"cluster-name": app.Spec.Destination.Name,
|
||||
}).Debug("Requesting app refresh caused by object update")
|
||||
|
||||
ctrl.requestAppRefresh(app.QualifiedName(), &level, nil)
|
||||
@@ -523,13 +515,13 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal live state of managed resources: %w", err)
|
||||
}
|
||||
var target = &unstructured.Unstructured{}
|
||||
err = json.Unmarshal([]byte(managedResource.TargetState), &target)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal target state of managed resources: %w", err)
|
||||
}
|
||||
|
||||
if live == nil {
|
||||
var target = &unstructured.Unstructured{}
|
||||
err = json.Unmarshal([]byte(managedResource.TargetState), &target)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal target state of managed resources: %w", err)
|
||||
}
|
||||
nodes = append(nodes, appv1.ResourceNode{
|
||||
ResourceRef: appv1.ResourceRef{
|
||||
Version: target.GroupVersionKind().Version,
|
||||
@@ -809,13 +801,7 @@ func (ctrl *ApplicationController) Run(ctx context.Context, statusProcessors int
|
||||
if err != nil {
|
||||
log.Warnf("Cannot init sharding. Error while querying clusters list from database: %v", err)
|
||||
} else {
|
||||
appItems, err := ctrl.getAppList(metav1.ListOptions{})
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Cannot init sharding. Error while querying application list from database: %v", err)
|
||||
} else {
|
||||
ctrl.clusterSharding.Init(clusters, appItems)
|
||||
}
|
||||
ctrl.clusterSharding.Init(clusters)
|
||||
}
|
||||
|
||||
errors.CheckError(ctrl.stateCache.Init())
|
||||
@@ -923,7 +909,7 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b
|
||||
// We cannot rely on informer since applications might be updated by both application controller and api server.
|
||||
freshApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.ObjectMeta.Namespace).Get(context.Background(), app.ObjectMeta.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
getAppLog(app).Errorf("Failed to retrieve latest application state: %v", err)
|
||||
log.Errorf("Failed to retrieve latest application state: %v", err)
|
||||
return
|
||||
}
|
||||
app = freshApp
|
||||
@@ -1069,25 +1055,24 @@ func (ctrl *ApplicationController) getPermittedAppLiveObjects(app *appv1.Applica
|
||||
return objsMap, nil
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) isValidDestination(app *appv1.Application) (bool, *appv1.Cluster) {
|
||||
logCtx := getAppLog(app)
|
||||
func (ctrl *ApplicationController) isValidDestination(app *appv1.Application) (bool, *argov1alpha.Cluster) {
|
||||
// Validate the cluster using the Application destination's `name` field, if applicable,
|
||||
// and set the Server field, if needed.
|
||||
if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil {
|
||||
logCtx.Warnf("Unable to validate destination of the Application being deleted: %v", err)
|
||||
log.Warnf("Unable to validate destination of the Application being deleted: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
logCtx.Warnf("Unable to locate cluster URL for Application being deleted: %v", err)
|
||||
log.Warnf("Unable to locate cluster URL for Application being deleted: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
return true, cluster
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Application, projectClusters func(project string) ([]*appv1.Cluster, error)) error {
|
||||
logCtx := getAppLog(app)
|
||||
logCtx := log.WithField("application", app.QualifiedName())
|
||||
// Get refreshed application info, since informer app copy might be stale
|
||||
app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(context.Background(), app.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
@@ -1235,7 +1220,6 @@ func (ctrl *ApplicationController) updateFinalizers(app *appv1.Application) erro
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) setAppCondition(app *appv1.Application, condition appv1.ApplicationCondition) {
|
||||
logCtx := getAppLog(app)
|
||||
// do nothing if app already has same condition
|
||||
for _, c := range app.Status.Conditions {
|
||||
if c.Message == condition.Message && c.Type == condition.Type {
|
||||
@@ -1255,12 +1239,12 @@ func (ctrl *ApplicationController) setAppCondition(app *appv1.Application, condi
|
||||
_, err = ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{})
|
||||
}
|
||||
if err != nil {
|
||||
logCtx.Errorf("Unable to set application condition: %v", err)
|
||||
log.Errorf("Unable to set application condition: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Application) {
|
||||
logCtx := getAppLog(app)
|
||||
logCtx := log.WithField("application", app.QualifiedName())
|
||||
var state *appv1.OperationState
|
||||
// Recover from any unexpected panics and automatically set the status to be failed
|
||||
defer func() {
|
||||
@@ -1367,7 +1351,7 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
|
||||
// sync/health information
|
||||
if _, err := cache.MetaNamespaceKeyFunc(app); err == nil {
|
||||
// force app refresh with using CompareWithLatest comparison type and trigger app reconciliation loop
|
||||
ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatestForceResolve.Pointer(), nil)
|
||||
ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatest.Pointer(), nil)
|
||||
} else {
|
||||
logCtx.Warnf("Fails to requeue application: %v", err)
|
||||
}
|
||||
@@ -1375,7 +1359,8 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) setOperationState(app *appv1.Application, state *appv1.OperationState) {
|
||||
logCtx := getAppLog(app)
|
||||
logCtx := log.WithFields(log.Fields{"application": app.Name, "appNamespace": app.Namespace, "project": app.Spec.Project})
|
||||
|
||||
if state.Phase == "" {
|
||||
// expose any bugs where we neglect to set phase
|
||||
panic("no phase was set")
|
||||
@@ -1453,7 +1438,7 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta
|
||||
// writeBackToInformer writes a just recently updated App back into the informer cache.
|
||||
// This prevents the situation where the controller operates on a stale app and repeats work
|
||||
func (ctrl *ApplicationController) writeBackToInformer(app *appv1.Application) {
|
||||
logCtx := getAppLog(app).WithField("informer-writeBack", true)
|
||||
logCtx := log.WithFields(log.Fields{"application": app.Name, "appNamespace": app.Namespace, "project": app.Spec.Project, "informer-writeBack": true})
|
||||
err := ctrl.appInformer.GetStore().Update(app)
|
||||
if err != nil {
|
||||
logCtx.Errorf("failed to update informer store: %v", err)
|
||||
@@ -1507,11 +1492,12 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
|
||||
return
|
||||
}
|
||||
app := origApp.DeepCopy()
|
||||
logCtx := getAppLog(app).WithFields(log.Fields{
|
||||
"comparison-level": comparisonLevel,
|
||||
"dest-server": origApp.Spec.Destination.Server,
|
||||
"dest-name": origApp.Spec.Destination.Name,
|
||||
"dest-namespace": origApp.Spec.Destination.Namespace,
|
||||
logCtx := log.WithFields(log.Fields{
|
||||
"application": app.QualifiedName(),
|
||||
"level": comparisonLevel,
|
||||
"dest-server": origApp.Spec.Destination.Server,
|
||||
"dest-name": origApp.Spec.Destination.Name,
|
||||
"dest-namespace": origApp.Spec.Destination.Namespace,
|
||||
})
|
||||
|
||||
startTime := time.Now()
|
||||
@@ -1551,10 +1537,10 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
|
||||
patchMs = ctrl.persistAppStatus(origApp, &app.Status)
|
||||
|
||||
if err := ctrl.cache.SetAppResourcesTree(app.InstanceName(ctrl.namespace), &appv1.ApplicationTree{}); err != nil {
|
||||
logCtx.Warnf("failed to set app resource tree: %v", err)
|
||||
log.Warnf("failed to set app resource tree: %v", err)
|
||||
}
|
||||
if err := ctrl.cache.SetAppManagedResources(app.InstanceName(ctrl.namespace), nil); err != nil {
|
||||
logCtx.Warnf("failed to set app managed resources tree: %v", err)
|
||||
log.Warnf("failed to set app managed resources tree: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1678,7 +1664,7 @@ func currentSourceEqualsSyncedSource(app *appv1.Application) bool {
|
||||
// Additionally, it returns whether full refresh was requested or not.
|
||||
// If full refresh is requested then target and live state should be reconciled, else only live state tree should be updated.
|
||||
func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application, statusRefreshTimeout, statusHardRefreshTimeout time.Duration) (bool, appv1.RefreshType, CompareWith) {
|
||||
logCtx := getAppLog(app)
|
||||
logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()})
|
||||
var reason string
|
||||
compareWith := CompareWithLatest
|
||||
refreshType := appv1.RefreshTypeNormal
|
||||
@@ -1755,8 +1741,8 @@ func (ctrl *ApplicationController) refreshAppConditions(app *appv1.Application)
|
||||
|
||||
// normalizeApplication normalizes an application.spec and additionally persists updates if it changed
|
||||
func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Application) {
|
||||
logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()})
|
||||
app.Spec = *argo.NormalizeApplicationSpec(&app.Spec)
|
||||
logCtx := getAppLog(app)
|
||||
|
||||
patch, modified, err := diff.CreateTwoWayMergePatch(orig, app, appv1.Application{})
|
||||
|
||||
@@ -1774,7 +1760,7 @@ func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Applica
|
||||
|
||||
// persistAppStatus persists updates to application status. If no changes were made, it is a no-op
|
||||
func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, newStatus *appv1.ApplicationStatus) (patchMs time.Duration) {
|
||||
logCtx := getAppLog(orig)
|
||||
logCtx := log.WithFields(log.Fields{"application": orig.QualifiedName()})
|
||||
if orig.Status.Sync.Status != newStatus.Sync.Status {
|
||||
message := fmt.Sprintf("Updated sync status: %s -> %s", orig.Status.Sync.Status, newStatus.Sync.Status)
|
||||
ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message, "")
|
||||
@@ -1821,7 +1807,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
|
||||
if app.Spec.SyncPolicy == nil || app.Spec.SyncPolicy.Automated == nil {
|
||||
return nil, 0
|
||||
}
|
||||
logCtx := getAppLog(app)
|
||||
logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()})
|
||||
|
||||
if app.Operation != nil {
|
||||
logCtx.Infof("Skipping auto-sync: another operation is in progress")
|
||||
@@ -1932,15 +1918,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
|
||||
} else {
|
||||
ctrl.writeBackToInformer(updatedApp)
|
||||
}
|
||||
|
||||
var target string
|
||||
if updatedApp.Spec.HasMultipleSources() {
|
||||
target = strings.Join(desiredCommitSHAsMS, ", ")
|
||||
} else {
|
||||
target = desiredCommitSHA
|
||||
}
|
||||
message := fmt.Sprintf("Initiated automated sync to '%s'", target)
|
||||
|
||||
message := fmt.Sprintf("Initiated automated sync to '%s'", desiredCommitSHA)
|
||||
ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonOperationStarted, Type: v1.EventTypeNormal}, message, "")
|
||||
logCtx.Info(message)
|
||||
return nil, setOpTime
|
||||
@@ -2019,7 +1997,7 @@ func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
|
||||
|
||||
if annotations := app.GetAnnotations(); annotations != nil {
|
||||
if skipVal, ok := annotations[common.AnnotationKeyAppSkipReconcile]; ok {
|
||||
logCtx := getAppLog(app)
|
||||
logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()})
|
||||
if skipReconcile, err := strconv.ParseBool(skipVal); err == nil {
|
||||
if skipReconcile {
|
||||
logCtx.Debugf("Skipping Application reconcile based on annotation %s", common.AnnotationKeyAppSkipReconcile)
|
||||
@@ -2133,10 +2111,6 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
|
||||
ctrl.appRefreshQueue.AddRateLimited(key)
|
||||
ctrl.appOperationQueue.AddRateLimited(key)
|
||||
}
|
||||
newApp, newOK := obj.(*appv1.Application)
|
||||
if err == nil && newOK {
|
||||
ctrl.clusterSharding.AddApp(newApp)
|
||||
}
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
if !ctrl.canProcessApp(new) {
|
||||
@@ -2155,7 +2129,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
|
||||
newApp, newOK := new.(*appv1.Application)
|
||||
if oldOK && newOK {
|
||||
if automatedSyncEnabled(oldApp, newApp) {
|
||||
getAppLog(newApp).Info("Enabled automated sync")
|
||||
log.WithField("application", newApp.QualifiedName()).Info("Enabled automated sync")
|
||||
compareWith = CompareWithLatest.Pointer()
|
||||
}
|
||||
if ctrl.statusRefreshJitter != 0 && oldApp.ResourceVersion == newApp.ResourceVersion {
|
||||
@@ -2167,7 +2141,6 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
|
||||
|
||||
ctrl.requestAppRefresh(newApp.QualifiedName(), compareWith, delay)
|
||||
ctrl.appOperationQueue.AddRateLimited(key)
|
||||
ctrl.clusterSharding.UpdateApp(newApp)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
if !ctrl.canProcessApp(obj) {
|
||||
@@ -2180,10 +2153,6 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
|
||||
// for deletes, we immediately add to the refresh queue
|
||||
ctrl.appRefreshQueue.Add(key)
|
||||
}
|
||||
delApp, delOK := obj.(*appv1.Application)
|
||||
if err == nil && delOK {
|
||||
ctrl.clusterSharding.DeleteApp(delApp)
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -2259,26 +2228,4 @@ func (ctrl *ApplicationController) toAppQualifiedName(appName, appNamespace stri
|
||||
return fmt.Sprintf("%s/%s", appNamespace, appName)
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) getAppList(options metav1.ListOptions) (*appv1.ApplicationList, error) {
|
||||
watchNamespace := ctrl.namespace
|
||||
// If we have at least one additional namespace configured, we need to
|
||||
// watch on them all.
|
||||
if len(ctrl.applicationNamespaces) > 0 {
|
||||
watchNamespace = ""
|
||||
}
|
||||
|
||||
appList, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(watchNamespace).List(context.TODO(), options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newItems := []appv1.Application{}
|
||||
for _, app := range appList.Items {
|
||||
if ctrl.isAppNamespaceAllowed(&app) {
|
||||
newItems = append(newItems, app)
|
||||
}
|
||||
}
|
||||
appList.Items = newItems
|
||||
return appList, nil
|
||||
}
|
||||
|
||||
type ClusterFilterFunction func(c *appv1.Cluster, distributionFunction sharding.DistributionFunction) bool
|
||||
type ClusterFilterFunction func(c *argov1alpha.Cluster, distributionFunction sharding.DistributionFunction) bool
|
||||
|
||||
@@ -54,15 +54,14 @@ type namespacedResource struct {
|
||||
}
|
||||
|
||||
type fakeData struct {
|
||||
apps []runtime.Object
|
||||
manifestResponse *apiclient.ManifestResponse
|
||||
manifestResponses []*apiclient.ManifestResponse
|
||||
managedLiveObjs map[kube.ResourceKey]*unstructured.Unstructured
|
||||
namespacedResources map[kube.ResourceKey]namespacedResource
|
||||
configMapData map[string]string
|
||||
metricsCacheExpiration time.Duration
|
||||
applicationNamespaces []string
|
||||
updateRevisionForPathsResponse *apiclient.UpdateRevisionForPathsResponse
|
||||
apps []runtime.Object
|
||||
manifestResponse *apiclient.ManifestResponse
|
||||
manifestResponses []*apiclient.ManifestResponse
|
||||
managedLiveObjs map[kube.ResourceKey]*unstructured.Unstructured
|
||||
namespacedResources map[kube.ResourceKey]namespacedResource
|
||||
configMapData map[string]string
|
||||
metricsCacheExpiration time.Duration
|
||||
applicationNamespaces []string
|
||||
}
|
||||
|
||||
type MockKubectl struct {
|
||||
@@ -108,8 +107,6 @@ func newFakeController(data *fakeData, repoErr error) *ApplicationController {
|
||||
}
|
||||
}
|
||||
|
||||
mockRepoClient.On("UpdateRevisionForPaths", mock.Anything, mock.Anything).Return(data.updateRevisionForPathsResponse, nil)
|
||||
|
||||
mockRepoClientset := mockrepoclient.Clientset{RepoServerServiceClient: &mockRepoClient}
|
||||
|
||||
secret := corev1.Secret{
|
||||
@@ -1718,36 +1715,6 @@ func TestProcessRequestedAppOperation_HasRetriesTerminated(t *testing.T) {
|
||||
assert.Equal(t, string(synccommon.OperationFailed), phase)
|
||||
}
|
||||
|
||||
func TestProcessRequestedAppOperation_Successful(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
app.Spec.Project = "default"
|
||||
app.Operation = &v1alpha1.Operation{
|
||||
Sync: &v1alpha1.SyncOperation{},
|
||||
}
|
||||
ctrl := newFakeController(&fakeData{
|
||||
apps: []runtime.Object{app, &defaultProj},
|
||||
manifestResponses: []*apiclient.ManifestResponse{{
|
||||
Manifests: []string{},
|
||||
}},
|
||||
}, nil)
|
||||
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
|
||||
receivedPatch := map[string]interface{}{}
|
||||
fakeAppCs.PrependReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
if patchAction, ok := action.(kubetesting.PatchAction); ok {
|
||||
assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch))
|
||||
}
|
||||
return true, &v1alpha1.Application{}, nil
|
||||
})
|
||||
|
||||
ctrl.processRequestedAppOperation(app)
|
||||
|
||||
phase, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "phase")
|
||||
assert.Equal(t, string(synccommon.OperationSucceeded), phase)
|
||||
ok, level := ctrl.isRefreshRequested(ctrl.toAppKey(app.Name))
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, CompareWithLatestForceResolve, level)
|
||||
}
|
||||
|
||||
func TestGetAppHosts(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
data := &fakeData{
|
||||
|
||||
11
controller/cache/cache.go
vendored
@@ -41,8 +41,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.40.2 --name=LiveStateCache
|
||||
|
||||
const (
|
||||
// EnvClusterCacheResyncDuration is the env variable that holds cluster cache re-sync duration
|
||||
EnvClusterCacheResyncDuration = "ARGOCD_CLUSTER_CACHE_RESYNC_DURATION"
|
||||
@@ -381,14 +379,9 @@ func isRetryableError(err error) bool {
|
||||
isResourceQuotaConflictErr(err) ||
|
||||
isTransientNetworkErr(err) ||
|
||||
isExceededQuotaErr(err) ||
|
||||
isHTTP2GoawayErr(err) ||
|
||||
errors.Is(err, syscall.ECONNRESET)
|
||||
}
|
||||
|
||||
func isHTTP2GoawayErr(err error) bool {
|
||||
return strings.Contains(err.Error(), "http2: server sent GOAWAY and closed the connection")
|
||||
}
|
||||
|
||||
func isExceededQuotaErr(err error) bool {
|
||||
return kerrors.IsForbidden(err) && strings.Contains(err.Error(), "exceeded quota")
|
||||
}
|
||||
@@ -446,10 +439,6 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
|
||||
return nil, fmt.Errorf("error getting cluster: %w", err)
|
||||
}
|
||||
|
||||
if c.clusterSharding == nil {
|
||||
return nil, fmt.Errorf("unable to handle cluster %s: cluster sharding is not configured", cluster.Server)
|
||||
}
|
||||
|
||||
if !c.canHandleCluster(cluster) {
|
||||
return nil, fmt.Errorf("controller is configured to ignore cluster %s", cluster.Server)
|
||||
}
|
||||
|
||||
83
controller/cache/mocks/LiveStateCache.go
vendored
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.40.2. DO NOT EDIT.
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -29,15 +29,7 @@ type LiveStateCache struct {
|
||||
func (_m *LiveStateCache) GetClusterCache(server string) (cache.ClusterCache, error) {
|
||||
ret := _m.Called(server)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetClusterCache")
|
||||
}
|
||||
|
||||
var r0 cache.ClusterCache
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string) (cache.ClusterCache, error)); ok {
|
||||
return rf(server)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string) cache.ClusterCache); ok {
|
||||
r0 = rf(server)
|
||||
} else {
|
||||
@@ -46,6 +38,7 @@ func (_m *LiveStateCache) GetClusterCache(server string) (cache.ClusterCache, er
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(server)
|
||||
} else {
|
||||
@@ -59,10 +52,6 @@ func (_m *LiveStateCache) GetClusterCache(server string) (cache.ClusterCache, er
|
||||
func (_m *LiveStateCache) GetClustersInfo() []cache.ClusterInfo {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetClustersInfo")
|
||||
}
|
||||
|
||||
var r0 []cache.ClusterInfo
|
||||
if rf, ok := ret.Get(0).(func() []cache.ClusterInfo); ok {
|
||||
r0 = rf()
|
||||
@@ -79,15 +68,7 @@ func (_m *LiveStateCache) GetClustersInfo() []cache.ClusterInfo {
|
||||
func (_m *LiveStateCache) GetManagedLiveObjs(a *v1alpha1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error) {
|
||||
ret := _m.Called(a, targetObjs)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetManagedLiveObjs")
|
||||
}
|
||||
|
||||
var r0 map[kube.ResourceKey]*unstructured.Unstructured
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(*v1alpha1.Application, []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error)); ok {
|
||||
return rf(a, targetObjs)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(*v1alpha1.Application, []*unstructured.Unstructured) map[kube.ResourceKey]*unstructured.Unstructured); ok {
|
||||
r0 = rf(a, targetObjs)
|
||||
} else {
|
||||
@@ -96,6 +77,7 @@ func (_m *LiveStateCache) GetManagedLiveObjs(a *v1alpha1.Application, targetObjs
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*v1alpha1.Application, []*unstructured.Unstructured) error); ok {
|
||||
r1 = rf(a, targetObjs)
|
||||
} else {
|
||||
@@ -109,15 +91,7 @@ func (_m *LiveStateCache) GetManagedLiveObjs(a *v1alpha1.Application, targetObjs
|
||||
func (_m *LiveStateCache) GetNamespaceTopLevelResources(server string, namespace string) (map[kube.ResourceKey]v1alpha1.ResourceNode, error) {
|
||||
ret := _m.Called(server, namespace)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetNamespaceTopLevelResources")
|
||||
}
|
||||
|
||||
var r0 map[kube.ResourceKey]v1alpha1.ResourceNode
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string, string) (map[kube.ResourceKey]v1alpha1.ResourceNode, error)); ok {
|
||||
return rf(server, namespace)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, string) map[kube.ResourceKey]v1alpha1.ResourceNode); ok {
|
||||
r0 = rf(server, namespace)
|
||||
} else {
|
||||
@@ -126,6 +100,7 @@ func (_m *LiveStateCache) GetNamespaceTopLevelResources(server string, namespace
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
r1 = rf(server, namespace)
|
||||
} else {
|
||||
@@ -139,22 +114,14 @@ func (_m *LiveStateCache) GetNamespaceTopLevelResources(server string, namespace
|
||||
func (_m *LiveStateCache) GetVersionsInfo(serverURL string) (string, []kube.APIResourceInfo, error) {
|
||||
ret := _m.Called(serverURL)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetVersionsInfo")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 []kube.APIResourceInfo
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(string) (string, []kube.APIResourceInfo, error)); ok {
|
||||
return rf(serverURL)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string) string); ok {
|
||||
r0 = rf(serverURL)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
var r1 []kube.APIResourceInfo
|
||||
if rf, ok := ret.Get(1).(func(string) []kube.APIResourceInfo); ok {
|
||||
r1 = rf(serverURL)
|
||||
} else {
|
||||
@@ -163,6 +130,7 @@ func (_m *LiveStateCache) GetVersionsInfo(serverURL string) (string, []kube.APIR
|
||||
}
|
||||
}
|
||||
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(2).(func(string) error); ok {
|
||||
r2 = rf(serverURL)
|
||||
} else {
|
||||
@@ -176,10 +144,6 @@ func (_m *LiveStateCache) GetVersionsInfo(serverURL string) (string, []kube.APIR
|
||||
func (_m *LiveStateCache) Init() error {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Init")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
@@ -194,21 +158,14 @@ func (_m *LiveStateCache) Init() error {
|
||||
func (_m *LiveStateCache) IsNamespaced(server string, gk schema.GroupKind) (bool, error) {
|
||||
ret := _m.Called(server, gk)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for IsNamespaced")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string, schema.GroupKind) (bool, error)); ok {
|
||||
return rf(server, gk)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, schema.GroupKind) bool); ok {
|
||||
r0 = rf(server, gk)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, schema.GroupKind) error); ok {
|
||||
r1 = rf(server, gk)
|
||||
} else {
|
||||
@@ -222,10 +179,6 @@ func (_m *LiveStateCache) IsNamespaced(server string, gk schema.GroupKind) (bool
|
||||
func (_m *LiveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(v1alpha1.ResourceNode, string) bool) error {
|
||||
ret := _m.Called(server, key, action)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for IterateHierarchy")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, kube.ResourceKey, func(v1alpha1.ResourceNode, string) bool) error); ok {
|
||||
r0 = rf(server, key, action)
|
||||
@@ -240,10 +193,6 @@ func (_m *LiveStateCache) IterateHierarchy(server string, key kube.ResourceKey,
|
||||
func (_m *LiveStateCache) IterateResources(server string, callback func(*cache.Resource, *controllercache.ResourceInfo)) error {
|
||||
ret := _m.Called(server, callback)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for IterateResources")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, func(*cache.Resource, *controllercache.ResourceInfo)) error); ok {
|
||||
r0 = rf(server, callback)
|
||||
@@ -258,10 +207,6 @@ func (_m *LiveStateCache) IterateResources(server string, callback func(*cache.R
|
||||
func (_m *LiveStateCache) Run(ctx context.Context) error {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Run")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
|
||||
r0 = rf(ctx)
|
||||
@@ -271,17 +216,3 @@ func (_m *LiveStateCache) Run(ctx context.Context) error {
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewLiveStateCache creates a new instance of LiveStateCache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewLiveStateCache(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *LiveStateCache {
|
||||
mock := &LiveStateCache{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package controller
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
@@ -102,11 +101,8 @@ func (c *clusterInfoUpdater) updateClusters() {
|
||||
}
|
||||
_ = kube.RunAllAsync(len(clustersFiltered), func(i int) error {
|
||||
cluster := clustersFiltered[i]
|
||||
clusterInfo := infoByServer[cluster.Server]
|
||||
if err := c.updateClusterInfo(ctx, cluster, clusterInfo); err != nil {
|
||||
log.Warnf("Failed to save cluster info: %v", err)
|
||||
} else if err := updateClusterLabels(ctx, clusterInfo, cluster, c.db.UpdateCluster); err != nil {
|
||||
log.Warnf("Failed to update cluster labels: %v", err)
|
||||
if err := c.updateClusterInfo(ctx, cluster, infoByServer[cluster.Server]); err != nil {
|
||||
log.Warnf("Failed to save clusters info: %v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -118,12 +114,6 @@ func (c *clusterInfoUpdater) updateClusterInfo(ctx context.Context, cluster appv
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while fetching the apps list: %w", err)
|
||||
}
|
||||
|
||||
updated := c.getUpdatedClusterInfo(ctx, apps, cluster, info, metav1.Now())
|
||||
return c.cache.SetClusterInfo(cluster.Server, &updated)
|
||||
}
|
||||
|
||||
func (c *clusterInfoUpdater) getUpdatedClusterInfo(ctx context.Context, apps []*appv1.Application, cluster appv1.Cluster, info *cache.ClusterInfo, now metav1.Time) appv1.ClusterInfo {
|
||||
var appCount int64
|
||||
for _, a := range apps {
|
||||
if c.projGetter != nil {
|
||||
@@ -139,6 +129,7 @@ func (c *clusterInfoUpdater) getUpdatedClusterInfo(ctx context.Context, apps []*
|
||||
appCount += 1
|
||||
}
|
||||
}
|
||||
now := metav1.Now()
|
||||
clusterInfo := appv1.ClusterInfo{
|
||||
ConnectionState: appv1.ConnectionState{ModifiedAt: &now},
|
||||
ApplicationsCount: appCount,
|
||||
@@ -165,15 +156,5 @@ func (c *clusterInfoUpdater) getUpdatedClusterInfo(ctx context.Context, apps []*
|
||||
}
|
||||
}
|
||||
|
||||
return clusterInfo
|
||||
}
|
||||
|
||||
func updateClusterLabels(ctx context.Context, clusterInfo *cache.ClusterInfo, cluster appv1.Cluster, updateCluster func(context.Context, *appv1.Cluster) (*appv1.Cluster, error)) error {
|
||||
if clusterInfo != nil && cluster.Labels[common.LabelKeyAutoLabelClusterInfo] == "true" && cluster.Labels[common.LabelKeyClusterKubernetesVersion] != clusterInfo.K8SVersion {
|
||||
cluster.Labels[common.LabelKeyClusterKubernetesVersion] = clusterInfo.K8SVersion
|
||||
_, err := updateCluster(ctx, &cluster)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return c.cache.SetClusterInfo(cluster.Server, &clusterInfo)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -99,92 +98,3 @@ func TestClusterSecretUpdater(t *testing.T) {
|
||||
assert.Equal(t, test.ExpectedStatus, clusterInfo.ConnectionState.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateClusterLabels(t *testing.T) {
|
||||
shouldNotBeInvoked := func(ctx context.Context, cluster *v1alpha1.Cluster) (*v1alpha1.Cluster, error) {
|
||||
shouldNotHappen := errors.New("if an error happens here, something's wrong")
|
||||
assert.NoError(t, shouldNotHappen)
|
||||
return nil, shouldNotHappen
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
clusterInfo *clustercache.ClusterInfo
|
||||
cluster v1alpha1.Cluster
|
||||
updateCluster func(context.Context, *v1alpha1.Cluster) (*v1alpha1.Cluster, error)
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
"enableClusterInfoLabels = false",
|
||||
&clustercache.ClusterInfo{
|
||||
Server: "kubernetes.svc.local",
|
||||
K8SVersion: "1.28",
|
||||
},
|
||||
v1alpha1.Cluster{
|
||||
Server: "kubernetes.svc.local",
|
||||
Labels: nil,
|
||||
},
|
||||
shouldNotBeInvoked,
|
||||
assert.NoError,
|
||||
},
|
||||
{
|
||||
"clusterInfo = nil",
|
||||
nil,
|
||||
v1alpha1.Cluster{
|
||||
Server: "kubernetes.svc.local",
|
||||
Labels: map[string]string{"argocd.argoproj.io/auto-label-cluster-info": "true"},
|
||||
},
|
||||
shouldNotBeInvoked,
|
||||
assert.NoError,
|
||||
},
|
||||
{
|
||||
"clusterInfo.k8sversion == cluster k8s label",
|
||||
&clustercache.ClusterInfo{
|
||||
Server: "kubernetes.svc.local",
|
||||
K8SVersion: "1.28",
|
||||
},
|
||||
v1alpha1.Cluster{
|
||||
Server: "kubernetes.svc.local",
|
||||
Labels: map[string]string{"argocd.argoproj.io/kubernetes-version": "1.28", "argocd.argoproj.io/auto-label-cluster-info": "true"},
|
||||
},
|
||||
shouldNotBeInvoked,
|
||||
assert.NoError,
|
||||
},
|
||||
{
|
||||
"clusterInfo.k8sversion != cluster k8s label, no error",
|
||||
&clustercache.ClusterInfo{
|
||||
Server: "kubernetes.svc.local",
|
||||
K8SVersion: "1.28",
|
||||
},
|
||||
v1alpha1.Cluster{
|
||||
Server: "kubernetes.svc.local",
|
||||
Labels: map[string]string{"argocd.argoproj.io/kubernetes-version": "1.27", "argocd.argoproj.io/auto-label-cluster-info": "true"},
|
||||
},
|
||||
func(ctx context.Context, cluster *v1alpha1.Cluster) (*v1alpha1.Cluster, error) {
|
||||
assert.Equal(t, cluster.Labels["argocd.argoproj.io/kubernetes-version"], "1.28")
|
||||
return nil, nil
|
||||
},
|
||||
assert.NoError,
|
||||
},
|
||||
{
|
||||
"clusterInfo.k8sversion != cluster k8s label, some error",
|
||||
&clustercache.ClusterInfo{
|
||||
Server: "kubernetes.svc.local",
|
||||
K8SVersion: "1.28",
|
||||
},
|
||||
v1alpha1.Cluster{
|
||||
Server: "kubernetes.svc.local",
|
||||
Labels: map[string]string{"argocd.argoproj.io/kubernetes-version": "1.27", "argocd.argoproj.io/auto-label-cluster-info": "true"},
|
||||
},
|
||||
func(ctx context.Context, cluster *v1alpha1.Cluster) (*v1alpha1.Cluster, error) {
|
||||
assert.Equal(t, cluster.Labels["argocd.argoproj.io/kubernetes-version"], "1.28")
|
||||
return nil, errors.New("some error happened while saving")
|
||||
},
|
||||
assert.Error,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.wantErr(t, updateClusterLabels(context.Background(), tt.clusterInfo, tt.cluster, tt.updateCluster), fmt.Sprintf("updateClusterLabels(%v, %v, %v)", context.Background(), tt.clusterInfo, tt.cluster))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func setApplicationHealth(resources []managedResource, statuses []appv1.Resource
|
||||
app.Status.ResourceHealthSource = appv1.ResourceHealthLocationAppTree
|
||||
}
|
||||
if savedErr != nil && errCount > 1 {
|
||||
savedErr = fmt.Errorf("see application-controller logs for %d other errors; most recent error was: %w", errCount-1, savedErr)
|
||||
savedErr = fmt.Errorf("see applicaton-controller logs for %d other errors; most recent error was: %w", errCount-1, savedErr)
|
||||
}
|
||||
return &appHealth, savedErr
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ var (
|
||||
reconcileHistogram = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "argocd_app_reconcile",
|
||||
Help: "Application reconciliation performance in seconds.",
|
||||
Help: "Application reconciliation performance.",
|
||||
// Buckets chosen after observing a ~2100ms mean reconcile time
|
||||
Buckets: []float64{0.25, .5, 1, 2, 4, 8, 16},
|
||||
},
|
||||
|
||||
@@ -374,7 +374,7 @@ func assertMetricsPrinted(t *testing.T, expectedLines, body string) {
|
||||
}
|
||||
}
|
||||
|
||||
// assertMetricsNotPrinted
|
||||
// assertMetricNotPrinted
|
||||
func assertMetricsNotPrinted(t *testing.T, expectedLines, body string) {
|
||||
for _, line := range strings.Split(expectedLines, "\n") {
|
||||
if line == "" {
|
||||
@@ -391,7 +391,7 @@ func TestReconcileMetrics(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
appReconcileMetrics := `
|
||||
# HELP argocd_app_reconcile Application reconciliation performance in seconds.
|
||||
# HELP argocd_app_reconcile Application reconciliation performance.
|
||||
# TYPE argocd_app_reconcile histogram
|
||||
argocd_app_reconcile_bucket{dest_server="https://localhost:6443",namespace="argocd",le="0.25"} 0
|
||||
argocd_app_reconcile_bucket{dest_server="https://localhost:6443",namespace="argocd",le="0.5"} 0
|
||||
|
||||
@@ -9,16 +9,12 @@ import (
|
||||
)
|
||||
|
||||
type ClusterShardingCache interface {
|
||||
Init(clusters *v1alpha1.ClusterList, apps *v1alpha1.ApplicationList)
|
||||
Init(clusters *v1alpha1.ClusterList)
|
||||
Add(c *v1alpha1.Cluster)
|
||||
Delete(clusterServer string)
|
||||
Update(oldCluster *v1alpha1.Cluster, newCluster *v1alpha1.Cluster)
|
||||
AddApp(a *v1alpha1.Application)
|
||||
DeleteApp(a *v1alpha1.Application)
|
||||
UpdateApp(a *v1alpha1.Application)
|
||||
IsManagedCluster(c *v1alpha1.Cluster) bool
|
||||
GetDistribution() map[string]int
|
||||
GetAppDistribution() map[string]int
|
||||
}
|
||||
|
||||
type ClusterSharding struct {
|
||||
@@ -26,7 +22,6 @@ type ClusterSharding struct {
|
||||
Replicas int
|
||||
Shards map[string]int
|
||||
Clusters map[string]*v1alpha1.Cluster
|
||||
Apps map[string]*v1alpha1.Application
|
||||
lock sync.RWMutex
|
||||
getClusterShard DistributionFunction
|
||||
}
|
||||
@@ -38,12 +33,11 @@ func NewClusterSharding(_ db.ArgoDB, shard, replicas int, shardingAlgorithm stri
|
||||
Replicas: replicas,
|
||||
Shards: make(map[string]int),
|
||||
Clusters: make(map[string]*v1alpha1.Cluster),
|
||||
Apps: make(map[string]*v1alpha1.Application),
|
||||
}
|
||||
distributionFunction := NoShardingDistributionFunction()
|
||||
if replicas > 1 {
|
||||
log.Debugf("Processing clusters from shard %d: Using filter function: %s", shard, shardingAlgorithm)
|
||||
distributionFunction = GetDistributionFunction(clusterSharding.getClusterAccessor(), clusterSharding.getAppAccessor(), shardingAlgorithm, replicas)
|
||||
distributionFunction = GetDistributionFunction(clusterSharding.GetClusterAccessor(), shardingAlgorithm, replicas)
|
||||
} else {
|
||||
log.Info("Processing all cluster shards")
|
||||
}
|
||||
@@ -51,7 +45,7 @@ func NewClusterSharding(_ db.ArgoDB, shard, replicas int, shardingAlgorithm stri
|
||||
return clusterSharding
|
||||
}
|
||||
|
||||
// IsManagedCluster returns whether or not the cluster should be processed by a given shard.
|
||||
// IsManagedCluster returns wheter or not the cluster should be processed by a given shard.
|
||||
func (s *ClusterSharding) IsManagedCluster(c *v1alpha1.Cluster) bool {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
@@ -68,7 +62,7 @@ func (s *ClusterSharding) IsManagedCluster(c *v1alpha1.Cluster) bool {
|
||||
return clusterShard == s.Shard
|
||||
}
|
||||
|
||||
func (sharding *ClusterSharding) Init(clusters *v1alpha1.ClusterList, apps *v1alpha1.ApplicationList) {
|
||||
func (sharding *ClusterSharding) Init(clusters *v1alpha1.ClusterList) {
|
||||
sharding.lock.Lock()
|
||||
defer sharding.lock.Unlock()
|
||||
newClusters := make(map[string]*v1alpha1.Cluster, len(clusters.Items))
|
||||
@@ -77,13 +71,6 @@ func (sharding *ClusterSharding) Init(clusters *v1alpha1.ClusterList, apps *v1al
|
||||
newClusters[c.Server] = &cluster
|
||||
}
|
||||
sharding.Clusters = newClusters
|
||||
|
||||
newApps := make(map[string]*v1alpha1.Application, len(apps.Items))
|
||||
for i := range apps.Items {
|
||||
app := apps.Items[i]
|
||||
newApps[app.Name] = &app
|
||||
}
|
||||
sharding.Apps = newApps
|
||||
sharding.updateDistribution()
|
||||
}
|
||||
|
||||
@@ -186,8 +173,7 @@ func hasShardingUpdates(old, new *v1alpha1.Cluster) bool {
|
||||
return old.Shard == nil || new.Shard == nil || int64(*old.Shard) != int64(*new.Shard)
|
||||
}
|
||||
|
||||
// A read lock should be acquired before calling getClusterAccessor.
|
||||
func (d *ClusterSharding) getClusterAccessor() clusterAccessor {
|
||||
func (d *ClusterSharding) GetClusterAccessor() clusterAccessor {
|
||||
return func() []*v1alpha1.Cluster {
|
||||
// no need to lock, as this is only called from the updateDistribution function
|
||||
clusters := make([]*v1alpha1.Cluster, 0, len(d.Clusters))
|
||||
@@ -197,68 +183,3 @@ func (d *ClusterSharding) getClusterAccessor() clusterAccessor {
|
||||
return clusters
|
||||
}
|
||||
}
|
||||
|
||||
// A read lock should be acquired before calling getAppAccessor.
|
||||
func (d *ClusterSharding) getAppAccessor() appAccessor {
|
||||
return func() []*v1alpha1.Application {
|
||||
apps := make([]*v1alpha1.Application, 0, len(d.Apps))
|
||||
for _, a := range d.Apps {
|
||||
apps = append(apps, a)
|
||||
}
|
||||
return apps
|
||||
}
|
||||
}
|
||||
|
||||
func (sharding *ClusterSharding) AddApp(a *v1alpha1.Application) {
|
||||
sharding.lock.Lock()
|
||||
defer sharding.lock.Unlock()
|
||||
|
||||
_, ok := sharding.Apps[a.Name]
|
||||
sharding.Apps[a.Name] = a
|
||||
if !ok {
|
||||
sharding.updateDistribution()
|
||||
} else {
|
||||
log.Debugf("Skipping sharding distribution update. App already added")
|
||||
}
|
||||
}
|
||||
|
||||
func (sharding *ClusterSharding) DeleteApp(a *v1alpha1.Application) {
|
||||
sharding.lock.Lock()
|
||||
defer sharding.lock.Unlock()
|
||||
if _, ok := sharding.Apps[a.Name]; ok {
|
||||
delete(sharding.Apps, a.Name)
|
||||
sharding.updateDistribution()
|
||||
}
|
||||
}
|
||||
|
||||
func (sharding *ClusterSharding) UpdateApp(a *v1alpha1.Application) {
|
||||
sharding.lock.Lock()
|
||||
defer sharding.lock.Unlock()
|
||||
|
||||
_, ok := sharding.Apps[a.Name]
|
||||
sharding.Apps[a.Name] = a
|
||||
if !ok {
|
||||
sharding.updateDistribution()
|
||||
} else {
|
||||
log.Debugf("Skipping sharding distribution update. No relevant changes")
|
||||
}
|
||||
}
|
||||
|
||||
// GetAppDistribution should be not be called from a DestributionFunction because
|
||||
// it could cause a deadlock when updateDistribution is called.
|
||||
func (sharding *ClusterSharding) GetAppDistribution() map[string]int {
|
||||
sharding.lock.RLock()
|
||||
clusters := sharding.Clusters
|
||||
apps := sharding.Apps
|
||||
sharding.lock.RUnlock()
|
||||
|
||||
appDistribution := make(map[string]int, len(clusters))
|
||||
|
||||
for _, a := range apps {
|
||||
if _, ok := appDistribution[a.Spec.Destination.Server]; !ok {
|
||||
appDistribution[a.Spec.Destination.Server] = 0
|
||||
}
|
||||
appDistribution[a.Spec.Destination.Server]++
|
||||
}
|
||||
return appDistribution
|
||||
}
|
||||
|
||||
@@ -139,12 +139,6 @@ func TestClusterSharding_Delete(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&v1alpha1.ApplicationList{
|
||||
Items: []v1alpha1.Application{
|
||||
createApp("app2", "https://127.0.0.1:6443"),
|
||||
createApp("app1", "https://kubernetes.default.svc"),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
sharding.Delete("https://kubernetes.default.svc")
|
||||
@@ -170,12 +164,6 @@ func TestClusterSharding_Update(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&v1alpha1.ApplicationList{
|
||||
Items: []v1alpha1.Application{
|
||||
createApp("app2", "https://127.0.0.1:6443"),
|
||||
createApp("app1", "https://kubernetes.default.svc"),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
distributionBefore := sharding.GetDistribution()
|
||||
@@ -219,12 +207,6 @@ func TestClusterSharding_UpdateServerName(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&v1alpha1.ApplicationList{
|
||||
Items: []v1alpha1.Application{
|
||||
createApp("app2", "https://127.0.0.1:6443"),
|
||||
createApp("app1", "https://kubernetes.default.svc"),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
distributionBefore := sharding.GetDistribution()
|
||||
@@ -269,12 +251,6 @@ func TestClusterSharding_IsManagedCluster(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&v1alpha1.ApplicationList{
|
||||
Items: []v1alpha1.Application{
|
||||
createApp("app2", "https://127.0.0.1:6443"),
|
||||
createApp("app1", "https://kubernetes.default.svc"),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.True(t, sharding0.IsManagedCluster(&v1alpha1.Cluster{
|
||||
@@ -302,12 +278,6 @@ func TestClusterSharding_IsManagedCluster(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&v1alpha1.ApplicationList{
|
||||
Items: []v1alpha1.Application{
|
||||
createApp("app2", "https://127.0.0.1:6443"),
|
||||
createApp("app1", "https://kubernetes.default.svc"),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.False(t, sharding1.IsManagedCluster(&v1alpha1.Cluster{
|
||||
@@ -357,12 +327,6 @@ func TestClusterSharding_ClusterShardOfResourceShouldNotBeChanged(t *testing.T)
|
||||
*clusterWithToBigValue,
|
||||
},
|
||||
},
|
||||
&v1alpha1.ApplicationList{
|
||||
Items: []v1alpha1.Application{
|
||||
createApp("app2", "https://127.0.0.1:6443"),
|
||||
createApp("app1", "https://kubernetes.default.svc"),
|
||||
},
|
||||
},
|
||||
)
|
||||
distribution := sharding.GetDistribution()
|
||||
assert.Equal(t, 3, len(distribution))
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
// An implementation of Consistent Hashing and
|
||||
// Consistent Hashing With Bounded Loads.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Consistent_hashing
|
||||
//
|
||||
// https://research.googleblog.com/2017/04/consistent-hashing-with-bounded-loads.html
|
||||
package consistent
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/google/btree"
|
||||
|
||||
blake2b "github.com/minio/blake2b-simd"
|
||||
)
|
||||
|
||||
// OptimalExtraCapacityFactor extra factor capacity (1 + ε). The ideal balance
|
||||
// between keeping the shards uniform while also keeping consistency when
|
||||
// changing shard numbers.
|
||||
const OptimalExtraCapacityFactor = 1.25
|
||||
|
||||
var ErrNoHosts = errors.New("no hosts added")
|
||||
|
||||
type Host struct {
|
||||
Name string
|
||||
Load int64
|
||||
}
|
||||
|
||||
type Consistent struct {
|
||||
servers map[uint64]string
|
||||
clients *btree.BTree
|
||||
loadMap map[string]*Host
|
||||
totalLoad int64
|
||||
replicationFactor int
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type item struct {
|
||||
value uint64
|
||||
}
|
||||
|
||||
func (i item) Less(than btree.Item) bool {
|
||||
return i.value < than.(item).value
|
||||
}
|
||||
|
||||
func New() *Consistent {
|
||||
return &Consistent{
|
||||
servers: map[uint64]string{},
|
||||
clients: btree.New(2),
|
||||
loadMap: map[string]*Host{},
|
||||
replicationFactor: 1000,
|
||||
}
|
||||
}
|
||||
|
||||
func NewWithReplicationFactor(replicationFactor int) *Consistent {
|
||||
return &Consistent{
|
||||
servers: map[uint64]string{},
|
||||
clients: btree.New(2),
|
||||
loadMap: map[string]*Host{},
|
||||
replicationFactor: replicationFactor,
|
||||
}
|
||||
}
|
||||
func (c *Consistent) Add(server string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if _, ok := c.loadMap[server]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
c.loadMap[server] = &Host{Name: server, Load: 0}
|
||||
for i := 0; i < c.replicationFactor; i++ {
|
||||
h := c.hash(fmt.Sprintf("%s%d", server, i))
|
||||
c.servers[h] = server
|
||||
c.clients.ReplaceOrInsert(item{h})
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the server that owns the given client.
|
||||
// As described in https://en.wikipedia.org/wiki/Consistent_hashing
|
||||
// It returns ErrNoHosts if the ring has no servers in it.
|
||||
func (c *Consistent) Get(client string) (string, error) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
if c.clients.Len() == 0 {
|
||||
return "", ErrNoHosts
|
||||
}
|
||||
|
||||
h := c.hash(client)
|
||||
var foundItem btree.Item
|
||||
c.clients.AscendGreaterOrEqual(item{h}, func(i btree.Item) bool {
|
||||
foundItem = i
|
||||
return false // stop the iteration
|
||||
})
|
||||
|
||||
if foundItem == nil {
|
||||
// If no host found, wrap around to the first one.
|
||||
foundItem = c.clients.Min()
|
||||
}
|
||||
|
||||
host := c.servers[foundItem.(item).value]
|
||||
|
||||
return host, nil
|
||||
}
|
||||
|
||||
// GetLeast returns the least loaded host that can serve the key.
|
||||
// It uses Consistent Hashing With Bounded loads.
|
||||
// https://research.googleblog.com/2017/04/consistent-hashing-with-bounded-loads.html
|
||||
// It returns ErrNoHosts if the ring has no hosts in it.
|
||||
func (c *Consistent) GetLeast(client string) (string, error) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
if c.clients.Len() == 0 {
|
||||
return "", ErrNoHosts
|
||||
}
|
||||
h := c.hash(client)
|
||||
for {
|
||||
var foundItem btree.Item
|
||||
c.clients.AscendGreaterOrEqual(item{h}, func(bItem btree.Item) bool {
|
||||
if h != bItem.(item).value {
|
||||
foundItem = bItem
|
||||
return false // stop the iteration
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if foundItem == nil {
|
||||
// If no host found, wrap around to the first one.
|
||||
foundItem = c.clients.Min()
|
||||
}
|
||||
key := c.clients.Get(foundItem)
|
||||
if key != nil {
|
||||
host := c.servers[key.(item).value]
|
||||
if c.loadOK(host) {
|
||||
return host, nil
|
||||
}
|
||||
h = key.(item).value
|
||||
} else {
|
||||
return client, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the load of `server` to the given `load`
|
||||
func (c *Consistent) UpdateLoad(server string, load int64) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if _, ok := c.loadMap[server]; !ok {
|
||||
return
|
||||
}
|
||||
c.totalLoad -= c.loadMap[server].Load
|
||||
c.loadMap[server].Load = load
|
||||
c.totalLoad += load
|
||||
}
|
||||
|
||||
// Increments the load of host by 1
|
||||
//
|
||||
// should only be used with if you obtained a host with GetLeast
|
||||
func (c *Consistent) Inc(server string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if _, ok := c.loadMap[server]; !ok {
|
||||
return
|
||||
}
|
||||
atomic.AddInt64(&c.loadMap[server].Load, 1)
|
||||
atomic.AddInt64(&c.totalLoad, 1)
|
||||
}
|
||||
|
||||
// Decrements the load of host by 1
|
||||
//
|
||||
// should only be used with if you obtained a host with GetLeast
|
||||
func (c *Consistent) Done(server string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if _, ok := c.loadMap[server]; !ok {
|
||||
return
|
||||
}
|
||||
atomic.AddInt64(&c.loadMap[server].Load, -1)
|
||||
atomic.AddInt64(&c.totalLoad, -1)
|
||||
}
|
||||
|
||||
// Deletes host from the ring
|
||||
func (c *Consistent) Remove(server string) bool {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
for i := 0; i < c.replicationFactor; i++ {
|
||||
h := c.hash(fmt.Sprintf("%s%d", server, i))
|
||||
delete(c.servers, h)
|
||||
c.delSlice(h)
|
||||
}
|
||||
delete(c.loadMap, server)
|
||||
return true
|
||||
}
|
||||
|
||||
// Return the list of servers in the ring
|
||||
func (c *Consistent) Servers() (servers []string) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
for k := range c.loadMap {
|
||||
servers = append(servers, k)
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
// Returns the loads of all the hosts
|
||||
func (c *Consistent) GetLoads() map[string]int64 {
|
||||
loads := map[string]int64{}
|
||||
|
||||
for k, v := range c.loadMap {
|
||||
loads[k] = v.Load
|
||||
}
|
||||
return loads
|
||||
}
|
||||
|
||||
// Returns the maximum load of the single host
|
||||
// which is:
|
||||
// (total_load/number_of_hosts)*1.25
|
||||
// total_load = is the total number of active requests served by hosts
|
||||
// for more info:
|
||||
// https://research.googleblog.com/2017/04/consistent-hashing-with-bounded-loads.html
|
||||
func (c *Consistent) MaxLoad() int64 {
|
||||
if c.totalLoad == 0 {
|
||||
c.totalLoad = 1
|
||||
}
|
||||
var avgLoadPerNode float64
|
||||
avgLoadPerNode = float64(c.totalLoad / int64(len(c.loadMap)))
|
||||
if avgLoadPerNode == 0 {
|
||||
avgLoadPerNode = 1
|
||||
}
|
||||
avgLoadPerNode = math.Ceil(avgLoadPerNode * OptimalExtraCapacityFactor)
|
||||
return int64(avgLoadPerNode)
|
||||
}
|
||||
|
||||
func (c *Consistent) loadOK(server string) bool {
|
||||
// a safety check if someone performed c.Done more than needed
|
||||
if c.totalLoad < 0 {
|
||||
c.totalLoad = 0
|
||||
}
|
||||
|
||||
var avgLoadPerNode float64
|
||||
avgLoadPerNode = float64((c.totalLoad + 1) / int64(len(c.loadMap)))
|
||||
if avgLoadPerNode == 0 {
|
||||
avgLoadPerNode = 1
|
||||
}
|
||||
avgLoadPerNode = math.Ceil(avgLoadPerNode * 1.25)
|
||||
|
||||
bserver, ok := c.loadMap[server]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("given host(%s) not in loadsMap", bserver.Name))
|
||||
}
|
||||
|
||||
return float64(bserver.Load)+1 <= avgLoadPerNode
|
||||
}
|
||||
|
||||
func (c *Consistent) delSlice(val uint64) {
|
||||
c.clients.Delete(item{val})
|
||||
}
|
||||
|
||||
func (c *Consistent) hash(key string) uint64 {
|
||||
out := blake2b.Sum512([]byte(key))
|
||||
return binary.LittleEndian.Uint64(out[:])
|
||||
}
|
||||
@@ -14,9 +14,7 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/controller/sharding/consistent"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
slices "golang.org/x/exp/slices"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
@@ -45,7 +43,6 @@ const ShardControllerMappingKey = "shardControllerMapping"
|
||||
type DistributionFunction func(c *v1alpha1.Cluster) int
|
||||
type ClusterFilterFunction func(c *v1alpha1.Cluster) bool
|
||||
type clusterAccessor func() []*v1alpha1.Cluster
|
||||
type appAccessor func() []*v1alpha1.Application
|
||||
|
||||
// shardApplicationControllerMapping stores the mapping of Shard Number to Application Controller in ConfigMap.
|
||||
// It also stores the heartbeat of last synced time of the application controller.
|
||||
@@ -56,7 +53,7 @@ type shardApplicationControllerMapping struct {
|
||||
}
|
||||
|
||||
// GetClusterFilter returns a ClusterFilterFunction which is a function taking a cluster as a parameter
|
||||
// and returns whether or not the cluster should be processed by a given shard. It calls the distributionFunction
|
||||
// and returns wheter or not the cluster should be processed by a given shard. It calls the distributionFunction
|
||||
// to determine which shard will process the cluster, and if the given shard is equal to the calculated shard
|
||||
// the function will return true.
|
||||
func GetClusterFilter(db db.ArgoDB, distributionFunction DistributionFunction, replicas, shard int) ClusterFilterFunction {
|
||||
@@ -78,7 +75,7 @@ func GetClusterFilter(db db.ArgoDB, distributionFunction DistributionFunction, r
|
||||
|
||||
// GetDistributionFunction returns which DistributionFunction should be used based on the passed algorithm and
|
||||
// the current datas.
|
||||
func GetDistributionFunction(clusters clusterAccessor, apps appAccessor, shardingAlgorithm string, replicasCount int) DistributionFunction {
|
||||
func GetDistributionFunction(clusters clusterAccessor, shardingAlgorithm string, replicasCount int) DistributionFunction {
|
||||
log.Debugf("Using filter function: %s", shardingAlgorithm)
|
||||
distributionFunction := LegacyDistributionFunction(replicasCount)
|
||||
switch shardingAlgorithm {
|
||||
@@ -86,8 +83,6 @@ func GetDistributionFunction(clusters clusterAccessor, apps appAccessor, shardin
|
||||
distributionFunction = RoundRobinDistributionFunction(clusters, replicasCount)
|
||||
case common.LegacyShardingAlgorithm:
|
||||
distributionFunction = LegacyDistributionFunction(replicasCount)
|
||||
case common.ConsistentHashingWithBoundedLoadsAlgorithm:
|
||||
distributionFunction = ConsistentHashingWithBoundedLoadsDistributionFunction(clusters, apps, replicasCount)
|
||||
default:
|
||||
log.Warnf("distribution type %s is not supported, defaulting to %s", shardingAlgorithm, common.DefaultShardingAlgorithm)
|
||||
}
|
||||
@@ -132,13 +127,13 @@ func LegacyDistributionFunction(replicas int) DistributionFunction {
|
||||
// for a given cluster the function will return the shard number based on the modulo of the cluster rank in
|
||||
// the cluster's list sorted by uid on the shard number.
|
||||
// This function ensures an homogenous distribution: each shards got assigned the same number of
|
||||
// clusters +/-1 , but with the drawback of a reshuffling of clusters across shards in case of some changes
|
||||
// clusters +/-1 , but with the drawback of a reshuffling of clusters accross shards in case of some changes
|
||||
// in the cluster list
|
||||
|
||||
func RoundRobinDistributionFunction(clusters clusterAccessor, replicas int) DistributionFunction {
|
||||
return func(c *v1alpha1.Cluster) int {
|
||||
if replicas > 0 {
|
||||
if c == nil { // in-cluster does not necessarily have a secret assigned. So we are receiving a nil cluster here.
|
||||
if c == nil { // in-cluster does not necessarly have a secret assigned. So we are receiving a nil cluster here.
|
||||
return 0
|
||||
}
|
||||
// if Shard is manually set and the assigned value is lower than the number of replicas,
|
||||
@@ -162,92 +157,6 @@ func RoundRobinDistributionFunction(clusters clusterAccessor, replicas int) Dist
|
||||
}
|
||||
}
|
||||
|
||||
// ConsistentHashingWithBoundedLoadsDistributionFunction returns a DistributionFunction using an almost homogeneous distribution algorithm:
|
||||
// for a given cluster the function will return the shard number based on a consistent hashing with bounded loads algorithm.
|
||||
// This function ensures an almost homogenous distribution: each shards got assigned the fairly similar number of
|
||||
// clusters +/-10% , but with it is resilient to sharding and/or number of clusters changes.
|
||||
func ConsistentHashingWithBoundedLoadsDistributionFunction(clusters clusterAccessor, apps appAccessor, replicas int) DistributionFunction {
|
||||
return func(c *v1alpha1.Cluster) int {
|
||||
if replicas > 0 {
|
||||
if c == nil { // in-cluster does not necessarily have a secret assigned. So we are receiving a nil cluster here.
|
||||
return 0
|
||||
}
|
||||
|
||||
// if Shard is manually set and the assigned value is lower than the number of replicas,
|
||||
// then its value is returned otherwise it is the default calculated value
|
||||
if c.Shard != nil && int(*c.Shard) < replicas {
|
||||
return int(*c.Shard)
|
||||
} else {
|
||||
// if the cluster is not in the clusters list anymore, we should unassign it from any shard, so we
|
||||
// return the reserved value of -1
|
||||
if !slices.Contains(clusters(), c) {
|
||||
log.Warnf("Cluster with id=%s not found in cluster map.", c.ID)
|
||||
return -1
|
||||
}
|
||||
shardIndexedByCluster := createConsistentHashingWithBoundLoads(replicas, clusters, apps)
|
||||
shard, ok := shardIndexedByCluster[c.ID]
|
||||
if !ok {
|
||||
log.Warnf("Cluster with id=%s not found in cluster map.", c.ID)
|
||||
return -1
|
||||
}
|
||||
log.Debugf("Cluster with id=%s will be processed by shard %d", c.ID, shard)
|
||||
return shard
|
||||
}
|
||||
}
|
||||
log.Warnf("The number of replicas (%d) is lower than 1", replicas)
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
func createConsistentHashingWithBoundLoads(replicas int, getCluster clusterAccessor, getApp appAccessor) map[string]int {
|
||||
clusters := getSortedClustersList(getCluster)
|
||||
appDistribution := getAppDistribution(getCluster, getApp)
|
||||
shardIndexedByCluster := make(map[string]int)
|
||||
appsIndexedByShard := make(map[string]int64)
|
||||
consistentHashing := consistent.New()
|
||||
// Adding a shard with id "-1" as a reserved value for clusters that does not have an assigned shard
|
||||
// this happens for clusters that are removed for the clusters list
|
||||
//consistentHashing.Add("-1")
|
||||
for i := 0; i < replicas; i++ {
|
||||
shard := strconv.Itoa(i)
|
||||
consistentHashing.Add(shard)
|
||||
appsIndexedByShard[shard] = 0
|
||||
}
|
||||
|
||||
for _, c := range clusters {
|
||||
clusterIndex, err := consistentHashing.GetLeast(c.ID)
|
||||
if err != nil {
|
||||
log.Warnf("Cluster with id=%s not found in cluster map.", c.ID)
|
||||
}
|
||||
shardIndexedByCluster[c.ID], err = strconv.Atoi(clusterIndex)
|
||||
if err != nil {
|
||||
log.Errorf("Consistent Hashing was supposed to return a shard index but it returned %d", err)
|
||||
}
|
||||
numApps, ok := appDistribution[c.Server]
|
||||
if !ok {
|
||||
numApps = 0
|
||||
}
|
||||
appsIndexedByShard[clusterIndex] += numApps
|
||||
consistentHashing.UpdateLoad(clusterIndex, appsIndexedByShard[clusterIndex])
|
||||
}
|
||||
|
||||
return shardIndexedByCluster
|
||||
}
|
||||
|
||||
func getAppDistribution(getCluster clusterAccessor, getApps appAccessor) map[string]int64 {
|
||||
apps := getApps()
|
||||
clusters := getCluster()
|
||||
appDistribution := make(map[string]int64, len(clusters))
|
||||
|
||||
for _, a := range apps {
|
||||
if _, ok := appDistribution[a.Spec.Destination.Server]; !ok {
|
||||
appDistribution[a.Spec.Destination.Server] = 0
|
||||
}
|
||||
appDistribution[a.Spec.Destination.Server]++
|
||||
}
|
||||
return appDistribution
|
||||
}
|
||||
|
||||
// NoShardingDistributionFunction returns a DistributionFunction that will process all cluster by shard 0
|
||||
// the function is created for API compatibility purposes and is not supposed to be activated.
|
||||
func NoShardingDistributionFunction() DistributionFunction {
|
||||
@@ -465,13 +374,13 @@ func GetClusterSharding(kubeClient kubernetes.Interface, settingsMgr *settings.S
|
||||
|
||||
// if app controller deployment is not found when dynamic cluster distribution is enabled error out
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("(dynamic cluster distribution) failed to get app controller deployment: %v", err)
|
||||
return nil, fmt.Errorf("(dymanic cluster distribution) failed to get app controller deployment: %v", err)
|
||||
}
|
||||
|
||||
if appControllerDeployment != nil && appControllerDeployment.Spec.Replicas != nil {
|
||||
replicasCount = int(*appControllerDeployment.Spec.Replicas)
|
||||
} else {
|
||||
return nil, fmt.Errorf("(dynamic cluster distribution) failed to get app controller deployment replica count")
|
||||
return nil, fmt.Errorf("(dymanic cluster distribution) failed to get app controller deployment replica count")
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func TestGetShardByID_NotEmptyID(t *testing.T) {
|
||||
@@ -102,14 +101,13 @@ func TestGetClusterFilterLegacy(t *testing.T) {
|
||||
|
||||
func TestGetClusterFilterUnknown(t *testing.T) {
|
||||
clusterAccessor, db, cluster1, cluster2, cluster3, cluster4, _ := createTestClusters()
|
||||
appAccessor, _, _, _, _, _ := createTestApps()
|
||||
// Test with replicas set to 0
|
||||
t.Setenv(common.EnvControllerReplicas, "2")
|
||||
os.Unsetenv(common.EnvControllerShardingAlgorithm)
|
||||
t.Setenv(common.EnvControllerShardingAlgorithm, "unknown")
|
||||
replicasCount := 2
|
||||
db.On("GetApplicationControllerReplicas").Return(replicasCount)
|
||||
distributionFunction := GetDistributionFunction(clusterAccessor, appAccessor, "unknown", replicasCount)
|
||||
distributionFunction := GetDistributionFunction(clusterAccessor, "unknown", replicasCount)
|
||||
assert.Equal(t, 0, distributionFunction(nil))
|
||||
assert.Equal(t, 0, distributionFunction(&cluster1))
|
||||
assert.Equal(t, 1, distributionFunction(&cluster2))
|
||||
@@ -121,10 +119,9 @@ func TestLegacyGetClusterFilterWithFixedShard(t *testing.T) {
|
||||
//shardIndex := 1 // ensuring that a shard with index 1 will process all the clusters with an "even" id (2,4,6,...)
|
||||
t.Setenv(common.EnvControllerReplicas, "5")
|
||||
clusterAccessor, db, cluster1, cluster2, cluster3, cluster4, _ := createTestClusters()
|
||||
appAccessor, _, _, _, _, _ := createTestApps()
|
||||
replicasCount := 5
|
||||
db.On("GetApplicationControllerReplicas").Return(replicasCount)
|
||||
filter := GetDistributionFunction(clusterAccessor, appAccessor, common.DefaultShardingAlgorithm, replicasCount)
|
||||
filter := GetDistributionFunction(clusterAccessor, common.DefaultShardingAlgorithm, replicasCount)
|
||||
assert.Equal(t, 0, filter(nil))
|
||||
assert.Equal(t, 4, filter(&cluster1))
|
||||
assert.Equal(t, 1, filter(&cluster2))
|
||||
@@ -134,13 +131,13 @@ func TestLegacyGetClusterFilterWithFixedShard(t *testing.T) {
|
||||
var fixedShard int64 = 4
|
||||
cluster5 := &v1alpha1.Cluster{ID: "5", Shard: &fixedShard}
|
||||
clusterAccessor = getClusterAccessor([]v1alpha1.Cluster{cluster1, cluster2, cluster2, cluster4, *cluster5})
|
||||
filter = GetDistributionFunction(clusterAccessor, appAccessor, common.DefaultShardingAlgorithm, replicasCount)
|
||||
filter = GetDistributionFunction(clusterAccessor, common.DefaultShardingAlgorithm, replicasCount)
|
||||
assert.Equal(t, int(fixedShard), filter(cluster5))
|
||||
|
||||
fixedShard = 1
|
||||
cluster5.Shard = &fixedShard
|
||||
clusterAccessor = getClusterAccessor([]v1alpha1.Cluster{cluster1, cluster2, cluster2, cluster4, *cluster5})
|
||||
filter = GetDistributionFunction(clusterAccessor, appAccessor, common.DefaultShardingAlgorithm, replicasCount)
|
||||
filter = GetDistributionFunction(clusterAccessor, common.DefaultShardingAlgorithm, replicasCount)
|
||||
assert.Equal(t, int(fixedShard), filter(&v1alpha1.Cluster{ID: "4", Shard: &fixedShard}))
|
||||
}
|
||||
|
||||
@@ -148,11 +145,10 @@ func TestRoundRobinGetClusterFilterWithFixedShard(t *testing.T) {
|
||||
//shardIndex := 1 // ensuring that a shard with index 1 will process all the clusters with an "even" id (2,4,6,...)
|
||||
t.Setenv(common.EnvControllerReplicas, "4")
|
||||
clusterAccessor, db, cluster1, cluster2, cluster3, cluster4, _ := createTestClusters()
|
||||
appAccessor, _, _, _, _, _ := createTestApps()
|
||||
replicasCount := 4
|
||||
db.On("GetApplicationControllerReplicas").Return(replicasCount)
|
||||
|
||||
filter := GetDistributionFunction(clusterAccessor, appAccessor, common.RoundRobinShardingAlgorithm, replicasCount)
|
||||
filter := GetDistributionFunction(clusterAccessor, common.RoundRobinShardingAlgorithm, replicasCount)
|
||||
assert.Equal(t, filter(nil), 0)
|
||||
assert.Equal(t, filter(&cluster1), 0)
|
||||
assert.Equal(t, filter(&cluster2), 1)
|
||||
@@ -165,14 +161,14 @@ func TestRoundRobinGetClusterFilterWithFixedShard(t *testing.T) {
|
||||
cluster5 := v1alpha1.Cluster{Name: "cluster5", ID: "5", Shard: &fixedShard}
|
||||
clusters := []v1alpha1.Cluster{cluster1, cluster2, cluster3, cluster4, cluster5}
|
||||
clusterAccessor = getClusterAccessor(clusters)
|
||||
filter = GetDistributionFunction(clusterAccessor, appAccessor, common.RoundRobinShardingAlgorithm, replicasCount)
|
||||
filter = GetDistributionFunction(clusterAccessor, common.RoundRobinShardingAlgorithm, replicasCount)
|
||||
assert.Equal(t, int(fixedShard), filter(&cluster5))
|
||||
|
||||
fixedShard = 1
|
||||
cluster5 = v1alpha1.Cluster{Name: "cluster5", ID: "5", Shard: &fixedShard}
|
||||
clusters = []v1alpha1.Cluster{cluster1, cluster2, cluster3, cluster4, cluster5}
|
||||
clusterAccessor = getClusterAccessor(clusters)
|
||||
filter = GetDistributionFunction(clusterAccessor, appAccessor, common.RoundRobinShardingAlgorithm, replicasCount)
|
||||
filter = GetDistributionFunction(clusterAccessor, common.RoundRobinShardingAlgorithm, replicasCount)
|
||||
assert.Equal(t, int(fixedShard), filter(&v1alpha1.Cluster{Name: "cluster4", ID: "4", Shard: &fixedShard}))
|
||||
}
|
||||
|
||||
@@ -275,110 +271,6 @@ func TestGetShardByIndexModuloReplicasCountDistributionFunctionWhenClusterIsAdde
|
||||
assert.Equal(t, -1, distributionFunction(&cluster6))
|
||||
}
|
||||
|
||||
func TestConsistentHashingWhenClusterIsAddedAndRemoved(t *testing.T) {
|
||||
db := dbmocks.ArgoDB{}
|
||||
clusterCount := 133
|
||||
prefix := "cluster"
|
||||
|
||||
clusters := []v1alpha1.Cluster{}
|
||||
for i := 0; i < clusterCount; i++ {
|
||||
id := fmt.Sprintf("%06d", i)
|
||||
cluster := fmt.Sprintf("%s-%s", prefix, id)
|
||||
clusters = append(clusters, createCluster(cluster, id))
|
||||
}
|
||||
clusterAccessor := getClusterAccessor(clusters)
|
||||
appAccessor, _, _, _, _, _ := createTestApps()
|
||||
clusterList := &v1alpha1.ClusterList{Items: clusters}
|
||||
db.On("ListClusters", mock.Anything).Return(clusterList, nil)
|
||||
// Test with replicas set to 3
|
||||
replicasCount := 3
|
||||
db.On("GetApplicationControllerReplicas").Return(replicasCount)
|
||||
distributionFunction := ConsistentHashingWithBoundedLoadsDistributionFunction(clusterAccessor, appAccessor, replicasCount)
|
||||
assert.Equal(t, 0, distributionFunction(nil))
|
||||
distributionMap := map[int]int{}
|
||||
assignementMap := map[string]int{}
|
||||
for i := 0; i < clusterCount; i++ {
|
||||
assignedShard := distributionFunction(&clusters[i])
|
||||
assignementMap[clusters[i].ID] = assignedShard
|
||||
distributionMap[assignedShard]++
|
||||
|
||||
}
|
||||
|
||||
// We check that the distribution does not differ for more than 20%
|
||||
var sum float64
|
||||
sum = 0
|
||||
for shard, count := range distributionMap {
|
||||
if shard != -1 {
|
||||
sum = (sum + float64(count))
|
||||
}
|
||||
}
|
||||
average := sum / float64(replicasCount)
|
||||
failedTests := false
|
||||
for shard, count := range distributionMap {
|
||||
if shard != -1 {
|
||||
if float64(count) > average*float64(1.1) || float64(count) < average*float64(0.9) {
|
||||
fmt.Printf("Cluster distribution differs for more than 20%%: %d for shard %d (average: %f)\n", count, shard, average)
|
||||
failedTests = true
|
||||
}
|
||||
if failedTests {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we will decrease the number of replicas to 2, and we should see only clusters that were attached to shard 2 to be reassigned
|
||||
replicasCount = 2
|
||||
distributionFunction = ConsistentHashingWithBoundedLoadsDistributionFunction(getClusterAccessor(clusterList.Items), appAccessor, replicasCount)
|
||||
removedCluster := clusterList.Items[len(clusterList.Items)-1]
|
||||
for i := 0; i < clusterCount; i++ {
|
||||
c := &clusters[i]
|
||||
assignedShard := distributionFunction(c)
|
||||
prevıouslyAssignedShard := assignementMap[clusters[i].ID]
|
||||
if prevıouslyAssignedShard != 2 && prevıouslyAssignedShard != assignedShard {
|
||||
fmt.Printf("Previously assigned %s cluster has moved from replica %d to %d", c.ID, prevıouslyAssignedShard, assignedShard)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// Now, we remove the last added cluster, it should be unassigned
|
||||
removedCluster = clusterList.Items[len(clusterList.Items)-1]
|
||||
clusterList.Items = clusterList.Items[:len(clusterList.Items)-1]
|
||||
distributionFunction = ConsistentHashingWithBoundedLoadsDistributionFunction(getClusterAccessor(clusterList.Items), appAccessor, replicasCount)
|
||||
assert.Equal(t, -1, distributionFunction(&removedCluster))
|
||||
}
|
||||
|
||||
func TestConsistentHashingWhenClusterWithZeroReplicas(t *testing.T) {
|
||||
db := dbmocks.ArgoDB{}
|
||||
clusters := []v1alpha1.Cluster{createCluster("cluster-01", "01")}
|
||||
clusterAccessor := getClusterAccessor(clusters)
|
||||
clusterList := &v1alpha1.ClusterList{Items: clusters}
|
||||
db.On("ListClusters", mock.Anything).Return(clusterList, nil)
|
||||
appAccessor, _, _, _, _, _ := createTestApps()
|
||||
// Test with replicas set to 0
|
||||
replicasCount := 0
|
||||
db.On("GetApplicationControllerReplicas").Return(replicasCount)
|
||||
distributionFunction := ConsistentHashingWithBoundedLoadsDistributionFunction(clusterAccessor, appAccessor, replicasCount)
|
||||
assert.Equal(t, -1, distributionFunction(nil))
|
||||
}
|
||||
|
||||
func TestConsistentHashingWhenClusterWithFixedShard(t *testing.T) {
|
||||
db := dbmocks.ArgoDB{}
|
||||
var fixedShard int64 = 1
|
||||
cluster := &v1alpha1.Cluster{ID: "1", Shard: &fixedShard}
|
||||
clusters := []v1alpha1.Cluster{*cluster}
|
||||
|
||||
clusterAccessor := getClusterAccessor(clusters)
|
||||
clusterList := &v1alpha1.ClusterList{Items: clusters}
|
||||
db.On("ListClusters", mock.Anything).Return(clusterList, nil)
|
||||
|
||||
// Test with replicas set to 5
|
||||
replicasCount := 5
|
||||
db.On("GetApplicationControllerReplicas").Return(replicasCount)
|
||||
appAccessor, _, _, _, _, _ := createTestApps()
|
||||
distributionFunction := ConsistentHashingWithBoundedLoadsDistributionFunction(clusterAccessor, appAccessor, replicasCount)
|
||||
assert.Equal(t, fixedShard, int64(distributionFunction(cluster)))
|
||||
|
||||
}
|
||||
|
||||
func TestGetShardByIndexModuloReplicasCountDistributionFunction(t *testing.T) {
|
||||
clusters, db, cluster1, cluster2, _, _, _ := createTestClusters()
|
||||
replicasCount := 2
|
||||
@@ -950,7 +842,7 @@ func TestGetClusterSharding(t *testing.T) {
|
||||
useDynamicSharding: true,
|
||||
expectedShard: 0,
|
||||
expectedReplicas: 1,
|
||||
expectedErr: fmt.Errorf("(dynamic cluster distribution) failed to get app controller deployment: deployments.apps \"missing-deployment\" not found"),
|
||||
expectedErr: fmt.Errorf("(dymanic cluster distribution) failed to get app controller deployment: deployments.apps \"missing-deployment\" not found"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -978,81 +870,3 @@ func TestGetClusterSharding(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppAwareCache(t *testing.T) {
|
||||
_, db, cluster1, cluster2, cluster3, cluster4, cluster5 := createTestClusters()
|
||||
_, app1, app2, app3, app4, app5 := createTestApps()
|
||||
|
||||
clusterSharding := NewClusterSharding(db, 0, 1, "legacy")
|
||||
|
||||
clusterList := &v1alpha1.ClusterList{Items: []v1alpha1.Cluster{cluster1, cluster2, cluster3, cluster4, cluster5}}
|
||||
appList := &v1alpha1.ApplicationList{Items: []v1alpha1.Application{app1, app2, app3, app4, app5}}
|
||||
clusterSharding.Init(clusterList, appList)
|
||||
|
||||
appDistribution := clusterSharding.GetAppDistribution()
|
||||
|
||||
assert.Equal(t, 2, appDistribution["cluster1"])
|
||||
assert.Equal(t, 2, appDistribution["cluster2"])
|
||||
assert.Equal(t, 1, appDistribution["cluster3"])
|
||||
|
||||
app6 := createApp("app6", "cluster4")
|
||||
clusterSharding.AddApp(&app6)
|
||||
|
||||
app1Update := createApp("app1", "cluster2")
|
||||
clusterSharding.UpdateApp(&app1Update)
|
||||
|
||||
clusterSharding.DeleteApp(&app3)
|
||||
|
||||
appDistribution = clusterSharding.GetAppDistribution()
|
||||
|
||||
assert.Equal(t, 1, appDistribution["cluster1"])
|
||||
assert.Equal(t, 2, appDistribution["cluster2"])
|
||||
assert.Equal(t, 1, appDistribution["cluster3"])
|
||||
assert.Equal(t, 1, appDistribution["cluster4"])
|
||||
}
|
||||
|
||||
func createTestApps() (appAccessor, v1alpha1.Application, v1alpha1.Application, v1alpha1.Application, v1alpha1.Application, v1alpha1.Application) {
|
||||
app1 := createApp("app1", "cluster1")
|
||||
app2 := createApp("app2", "cluster1")
|
||||
app3 := createApp("app3", "cluster2")
|
||||
app4 := createApp("app4", "cluster2")
|
||||
app5 := createApp("app5", "cluster3")
|
||||
|
||||
apps := []v1alpha1.Application{app1, app2, app3, app4, app5}
|
||||
|
||||
return getAppAccessor(apps), app1, app2, app3, app4, app5
|
||||
}
|
||||
|
||||
func getAppAccessor(apps []v1alpha1.Application) appAccessor {
|
||||
// Convert the array to a slice of pointers
|
||||
appPointers := getAppPointers(apps)
|
||||
appAccessor := func() []*v1alpha1.Application { return appPointers }
|
||||
return appAccessor
|
||||
}
|
||||
|
||||
func getAppPointers(apps []v1alpha1.Application) []*v1alpha1.Application {
|
||||
var appPointers []*v1alpha1.Application
|
||||
for i := range apps {
|
||||
appPointers = append(appPointers, &apps[i])
|
||||
}
|
||||
return appPointers
|
||||
}
|
||||
|
||||
func createApp(name string, server string) v1alpha1.Application {
|
||||
var testApp = `
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: ` + name + `
|
||||
spec:
|
||||
destination:
|
||||
server: ` + server + `
|
||||
`
|
||||
|
||||
var app v1alpha1.Application
|
||||
err := yaml.Unmarshal([]byte(testApp), &app)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/app/path"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
@@ -197,38 +196,6 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
|
||||
return nil, nil, fmt.Errorf("failed to get Kustomize options for source %d of %d: %w", i+1, len(sources), err)
|
||||
}
|
||||
|
||||
syncedRevision := app.Status.Sync.Revision
|
||||
if app.Spec.HasMultipleSources() {
|
||||
if i < len(app.Status.Sync.Revisions) {
|
||||
syncedRevision = app.Status.Sync.Revisions[i]
|
||||
} else {
|
||||
syncedRevision = ""
|
||||
}
|
||||
}
|
||||
|
||||
val, ok := app.Annotations[v1alpha1.AnnotationKeyManifestGeneratePaths]
|
||||
if !source.IsHelm() && syncedRevision != "" && ok && val != "" {
|
||||
// Validate the manifest-generate-path annotation to avoid generating manifests if it has not changed.
|
||||
_, err = repoClient.UpdateRevisionForPaths(context.Background(), &apiclient.UpdateRevisionForPathsRequest{
|
||||
Repo: repo,
|
||||
Revision: revisions[i],
|
||||
SyncedRevision: syncedRevision,
|
||||
Paths: path.GetAppRefreshPaths(app),
|
||||
AppLabelKey: appLabelKey,
|
||||
AppName: app.InstanceName(m.namespace),
|
||||
Namespace: app.Spec.Destination.Namespace,
|
||||
ApplicationSource: &source,
|
||||
KubeVersion: serverVersion,
|
||||
ApiVersions: argo.APIResourcesToStrings(apiResources, true),
|
||||
TrackingMethod: string(argo.GetTrackingMethod(m.settingsMgr)),
|
||||
RefSources: refSources,
|
||||
HasMultipleSources: app.Spec.HasMultipleSources(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to compare revisions for source %d of %d: %w", i+1, len(sources), err)
|
||||
}
|
||||
}
|
||||
|
||||
ts.AddCheckpoint("version_ms")
|
||||
log.Debugf("Generating Manifest for source %s revision %s", source, revisions[i])
|
||||
manifestInfo, err := repoClient.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
@@ -915,16 +882,7 @@ func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sou
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *appStateManager) persistRevisionHistory(
|
||||
app *v1alpha1.Application,
|
||||
revision string,
|
||||
source v1alpha1.ApplicationSource,
|
||||
revisions []string,
|
||||
sources []v1alpha1.ApplicationSource,
|
||||
hasMultipleSources bool,
|
||||
startedAt metav1.Time,
|
||||
initiatedBy v1alpha1.OperationInitiator,
|
||||
) error {
|
||||
func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, revisions []string, sources []v1alpha1.ApplicationSource, hasMultipleSources bool, startedAt metav1.Time) error {
|
||||
var nextID int64
|
||||
if len(app.Status.History) > 0 {
|
||||
nextID = app.Status.History.LastRevisionHistory().ID + 1
|
||||
@@ -937,7 +895,6 @@ func (m *appStateManager) persistRevisionHistory(
|
||||
ID: nextID,
|
||||
Sources: sources,
|
||||
Revisions: revisions,
|
||||
InitiatedBy: initiatedBy,
|
||||
})
|
||||
} else {
|
||||
app.Status.History = append(app.Status.History, v1alpha1.RevisionHistory{
|
||||
@@ -946,7 +903,6 @@ func (m *appStateManager) persistRevisionHistory(
|
||||
DeployStartedAt: &startedAt,
|
||||
ID: nextID,
|
||||
Source: source,
|
||||
InitiatedBy: initiatedBy,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/controller/testdata"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
mockrepoclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
)
|
||||
@@ -652,37 +649,6 @@ var defaultProj = argoappv1.AppProject{
|
||||
},
|
||||
}
|
||||
|
||||
// TestCompareAppStateWithManifestGeneratePath tests that it compares revisions when the manifest-generate-path annotation is set.
|
||||
func TestCompareAppStateWithManifestGeneratePath(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
app.SetAnnotations(map[string]string{argoappv1.AnnotationKeyManifestGeneratePaths: "."})
|
||||
app.Status.Sync = argoappv1.SyncStatus{
|
||||
Revision: "abc123",
|
||||
Status: argoappv1.SyncStatusCodeSynced,
|
||||
}
|
||||
|
||||
data := fakeData{
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
Manifests: []string{},
|
||||
Namespace: test.FakeDestNamespace,
|
||||
Server: test.FakeClusterURL,
|
||||
Revision: "abc123",
|
||||
},
|
||||
updateRevisionForPathsResponse: &apiclient.UpdateRevisionForPathsResponse{},
|
||||
}
|
||||
|
||||
ctrl := newFakeController(&data, nil)
|
||||
revisions := make([]string, 0)
|
||||
revisions = append(revisions, "abc123")
|
||||
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, false)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, compRes)
|
||||
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
|
||||
assert.Equal(t, "abc123", compRes.syncStatus.Revision)
|
||||
ctrl.repoClientset.(*mockrepoclient.Clientset).RepoServerServiceClient.(*mockrepoclient.RepoServerServiceClient).AssertNumberOfCalls(t, "UpdateRevisionForPaths", 1)
|
||||
}
|
||||
|
||||
func TestSetHealth(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
deployment := kube.MustToUnstructured(&v1.Deployment{
|
||||
@@ -872,7 +838,7 @@ func Test_appStateManager_persistRevisionHistory(t *testing.T) {
|
||||
app.Spec.RevisionHistoryLimit = &i
|
||||
}
|
||||
addHistory := func() {
|
||||
err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1.Time{}, v1alpha1.OperationInitiator{})
|
||||
err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1.Time{})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
addHistory()
|
||||
@@ -908,7 +874,7 @@ func Test_appStateManager_persistRevisionHistory(t *testing.T) {
|
||||
assert.Len(t, app.Status.History, 9)
|
||||
|
||||
metav1NowTime := metav1.NewTime(time.Now())
|
||||
err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1NowTime, v1alpha1.OperationInitiator{})
|
||||
err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1NowTime)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, app.Status.History.LastRevisionHistory().DeployStartedAt, &metav1NowTime)
|
||||
}
|
||||
@@ -1542,17 +1508,6 @@ func TestUseDiffCache(t *testing.T) {
|
||||
expectedUseCache: true,
|
||||
serverSideDiff: false,
|
||||
},
|
||||
{
|
||||
testName: "will use diff cache with sync policy",
|
||||
noCache: false,
|
||||
manifestInfos: manifestInfos("rev1"),
|
||||
sources: sources(),
|
||||
app: test.YamlToApplication(testdata.DiffCacheYaml),
|
||||
manifestRevisions: []string{"rev1"},
|
||||
statusRefreshTimeout: time.Hour * 24,
|
||||
expectedUseCache: true,
|
||||
serverSideDiff: true,
|
||||
},
|
||||
{
|
||||
testName: "will use diff cache for multisource",
|
||||
noCache: false,
|
||||
|
||||
@@ -104,7 +104,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
if syncOp.SyncOptions.HasOption("FailOnSharedResource=true") &&
|
||||
hasSharedResource {
|
||||
state.Phase = common.OperationFailed
|
||||
state.Message = fmt.Sprintf("Shared resource found: %s", sharedResourceMessage)
|
||||
state.Message = fmt.Sprintf("Shared resouce found: %s", sharedResourceMessage)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -162,12 +162,6 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
state.Phase = common.OperationError
|
||||
state.Message = fmt.Sprintf("Failed to load application project: %v", err)
|
||||
return
|
||||
} else if syncWindowPreventsSync(app, proj) {
|
||||
// If the operation is currently running, simply let the user know the sync is blocked by a current sync window
|
||||
if state.Phase == common.OperationRunning {
|
||||
state.Message = "Sync operation blocked by sync window"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if app.Spec.HasMultipleSources() {
|
||||
@@ -398,7 +392,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
logEntry.WithField("duration", time.Since(start)).Info("sync/terminate complete")
|
||||
|
||||
if !syncOp.DryRun && len(syncOp.Resources) == 0 && state.Phase.Successful() {
|
||||
err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source, compareResult.syncStatus.Revisions, compareResult.syncStatus.ComparedTo.Sources, app.Spec.HasMultipleSources(), state.StartedAt, state.Operation.InitiatedBy)
|
||||
err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source, compareResult.syncStatus.Revisions, compareResult.syncStatus.ComparedTo.Sources, app.Spec.HasMultipleSources(), state.StartedAt)
|
||||
if err != nil {
|
||||
state.Phase = common.OperationError
|
||||
state.Message = fmt.Sprintf("failed to record sync to history: %v", err)
|
||||
@@ -530,12 +524,3 @@ func delayBetweenSyncWaves(phase common.SyncPhase, wave int, finalWave bool) err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func syncWindowPreventsSync(app *v1alpha1.Application, proj *v1alpha1.AppProject) bool {
|
||||
window := proj.Spec.SyncWindows.Matches(app)
|
||||
isManual := false
|
||||
if app.Status.OperationState != nil {
|
||||
isManual = !app.Status.OperationState.Operation.InitiatedBy.Automated
|
||||
}
|
||||
return !window.CanSync(isManual)
|
||||
}
|
||||
|
||||
@@ -255,75 +255,6 @@ func TestAppStateManager_SyncAppState(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSyncWindowDeniesSync(t *testing.T) {
|
||||
type fixture struct {
|
||||
project *v1alpha1.AppProject
|
||||
application *v1alpha1.Application
|
||||
controller *ApplicationController
|
||||
}
|
||||
|
||||
setup := func() *fixture {
|
||||
app := newFakeApp()
|
||||
app.Status.OperationState = nil
|
||||
app.Status.History = nil
|
||||
|
||||
project := &v1alpha1.AppProject{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: test.FakeArgoCDNamespace,
|
||||
Name: "default",
|
||||
},
|
||||
Spec: v1alpha1.AppProjectSpec{
|
||||
SyncWindows: v1alpha1.SyncWindows{{
|
||||
Kind: "deny",
|
||||
Schedule: "0 0 * * *",
|
||||
Duration: "24h",
|
||||
Clusters: []string{"*"},
|
||||
Namespaces: []string{"*"},
|
||||
Applications: []string{"*"},
|
||||
}},
|
||||
},
|
||||
}
|
||||
data := fakeData{
|
||||
apps: []runtime.Object{app, project},
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
Manifests: []string{},
|
||||
Namespace: test.FakeDestNamespace,
|
||||
Server: test.FakeClusterURL,
|
||||
Revision: "abc123",
|
||||
},
|
||||
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
|
||||
}
|
||||
ctrl := newFakeController(&data, nil)
|
||||
|
||||
return &fixture{
|
||||
project: project,
|
||||
application: app,
|
||||
controller: ctrl,
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("will keep the sync progressing if a sync window prevents the sync", func(t *testing.T) {
|
||||
// given a project with an active deny sync window and an operation in progress
|
||||
t.Parallel()
|
||||
f := setup()
|
||||
opMessage := "Sync operation blocked by sync window"
|
||||
|
||||
opState := &v1alpha1.OperationState{Operation: v1alpha1.Operation{
|
||||
Sync: &v1alpha1.SyncOperation{
|
||||
Source: &v1alpha1.ApplicationSource{},
|
||||
}},
|
||||
Phase: common.OperationRunning,
|
||||
}
|
||||
// when
|
||||
f.controller.appStateManager.SyncAppState(f.application, opState)
|
||||
|
||||
//then
|
||||
assert.Equal(t, common.OperationRunning, opState.Phase)
|
||||
assert.Contains(t, opState.Message, opMessage)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestNormalizeTargetResources(t *testing.T) {
|
||||
type fixture struct {
|
||||
comparisonResult *comparisonResult
|
||||
|
||||
3
controller/testdata/data.go
vendored
@@ -12,9 +12,6 @@ var (
|
||||
//go:embed target-deployment-new-entries.yaml
|
||||
TargetDeploymentNewEntries string
|
||||
|
||||
//go:embed diff-cache.yaml
|
||||
DiffCacheYaml string
|
||||
|
||||
//go:embed live-httpproxy.yaml
|
||||
LiveHTTPProxy string
|
||||
|
||||
|
||||
498
controller/testdata/diff-cache.yaml
vendored
@@ -1,498 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
annotations:
|
||||
argocd-image-updater.argoproj.io/allow-tags: any
|
||||
argocd-image-updater.argoproj.io/ignore-tags: ""
|
||||
argocd-image-updater.argoproj.io/image-list-disabled-hack: ""
|
||||
argocd-image-updater.argoproj.io/update-strategy: semver
|
||||
argocd-image-updater.argoproj.io/write-back-method: git
|
||||
argocd-image-updater.argoproj.io/write-back-target: kustomization
|
||||
argocd-notif-onDeployed.slack-disabled: ""
|
||||
argocd-notif-onHealthDegraded.slack-disabled: ""
|
||||
argocd-notif-onSyncFailed.slack-disabled: ""
|
||||
argocd-notif-onSyncRunning.slack-disabled: ""
|
||||
argocd-notif-onSyncStatusUnknown.slack-disabled: ""
|
||||
argocd-notif-onSyncSucceeded.slack-disabled: ""
|
||||
argocd.argoproj.io/compare-options: ServerSideDiff=true
|
||||
argocd.argoproj.io/manifest-generate-paths: .;/chart
|
||||
creationTimestamp: "2024-03-04T21:30:33Z"
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
generation: 263
|
||||
labels:
|
||||
cloud_provider: gcp
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
foo: bar
|
||||
preview: "true"
|
||||
project: sre
|
||||
service_class: alpha
|
||||
stack: gke-v2
|
||||
name: velero-test
|
||||
namespace: argo-cd
|
||||
ownerReferences:
|
||||
- apiVersion: argoproj.io/v1alpha1
|
||||
blockOwnerDeletion: true
|
||||
controller: true
|
||||
kind: ApplicationSet
|
||||
name: velero
|
||||
uid: 86cdfba4-8697-47b3-8489-71fab7f4a805
|
||||
resourceVersion: "722811357"
|
||||
uid: 94978696-4fd4-40b3-a1de-38d9df9e9316
|
||||
spec:
|
||||
destination:
|
||||
name: gke-alpha-01-europe-west1
|
||||
namespace: test-lla
|
||||
project: sre
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
syncPolicy:
|
||||
retry:
|
||||
backoff:
|
||||
duration: 5s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
limit: 10
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ApplyOutOfSyncOnly=true
|
||||
- RespectIgnoreDifferences=false
|
||||
- ServerSideApply=true
|
||||
- Validate=true
|
||||
status:
|
||||
controllerNamespace: argo-cd
|
||||
health:
|
||||
status: Healthy
|
||||
history:
|
||||
- deployStartedAt: "2024-03-04T22:00:05Z"
|
||||
deployedAt: "2024-03-04T22:00:06Z"
|
||||
id: 14
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
- deployStartedAt: "2024-03-04T22:08:29Z"
|
||||
deployedAt: "2024-03-04T22:08:30Z"
|
||||
id: 15
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
- deployStartedAt: "2024-03-04T22:09:16Z"
|
||||
deployedAt: "2024-03-04T22:09:16Z"
|
||||
id: 16
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
- deployStartedAt: "2024-03-04T22:11:41Z"
|
||||
deployedAt: "2024-03-04T22:11:41Z"
|
||||
id: 17
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
- deployStartedAt: "2024-03-04T22:50:55Z"
|
||||
deployedAt: "2024-03-04T22:50:55Z"
|
||||
id: 18
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
- deployStartedAt: "2024-03-04T22:52:56Z"
|
||||
deployedAt: "2024-03-04T22:52:56Z"
|
||||
id: 19
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
- deployStartedAt: "2024-03-04T22:56:15Z"
|
||||
deployedAt: "2024-03-04T22:56:15Z"
|
||||
id: 20
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
- deployStartedAt: "2024-03-05T07:31:56Z"
|
||||
deployedAt: "2024-03-05T07:31:57Z"
|
||||
id: 21
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
- deployStartedAt: "2024-03-05T07:32:44Z"
|
||||
deployedAt: "2024-03-05T07:32:44Z"
|
||||
id: 22
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
- deployStartedAt: "2024-03-05T07:33:03Z"
|
||||
deployedAt: "2024-03-05T07:33:04Z"
|
||||
id: 23
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
operationState:
|
||||
finishedAt: "2024-03-05T07:33:04Z"
|
||||
message: successfully synced (all tasks run)
|
||||
operation:
|
||||
initiatedBy:
|
||||
username: laurent.lavaud@mirakl.com
|
||||
retry:
|
||||
backoff:
|
||||
duration: 5s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
limit: 10
|
||||
sync:
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
syncOptions:
|
||||
- ServerSideApply=true
|
||||
syncStrategy:
|
||||
hook: {}
|
||||
phase: Succeeded
|
||||
startedAt: "2024-03-05T07:33:03Z"
|
||||
syncResult:
|
||||
resources:
|
||||
- group: ""
|
||||
hookPhase: Running
|
||||
kind: Service
|
||||
message: service/test-lla serverside-applied
|
||||
name: test-lla
|
||||
namespace: test-lla
|
||||
status: Synced
|
||||
syncPhase: Sync
|
||||
version: v1
|
||||
- group: apps
|
||||
hookPhase: Running
|
||||
kind: Deployment
|
||||
message: deployment.apps/test-lla serverside-applied
|
||||
name: test-lla
|
||||
namespace: test-lla
|
||||
status: Synced
|
||||
syncPhase: Sync
|
||||
version: v1
|
||||
revision: ea8759964626a583667a2bfd08f334ec2070040a
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
reconciledAt: "2024-03-05T07:33:04Z"
|
||||
resources:
|
||||
- health:
|
||||
status: Healthy
|
||||
kind: Service
|
||||
name: test-lla
|
||||
namespace: test-lla
|
||||
status: Synced
|
||||
version: v1
|
||||
- group: apps
|
||||
health:
|
||||
status: Healthy
|
||||
kind: Deployment
|
||||
name: test-lla
|
||||
namespace: test-lla
|
||||
status: Synced
|
||||
version: v1
|
||||
sourceType: Plugin
|
||||
summary:
|
||||
images:
|
||||
- nginx:latest
|
||||
sync:
|
||||
comparedTo:
|
||||
destination:
|
||||
name: gke-alpha-01-europe-west1
|
||||
namespace: test-lla
|
||||
source:
|
||||
path: instances/test
|
||||
plugin:
|
||||
env:
|
||||
- name: RELEASE_NAME
|
||||
value: test-lla
|
||||
- name: CHART_REPOSITORY
|
||||
value: oci://europe-west1-docker.pkg.dev/platform-89be/charts
|
||||
- name: CHART_NAME
|
||||
value: velero
|
||||
- name: PREVIEW
|
||||
value: "false"
|
||||
- name: HELM_VALUES
|
||||
value: |
|
||||
global:
|
||||
app:
|
||||
cluster_name: gke-alpha-01-europe-west1
|
||||
service_class: alpha
|
||||
cloud_provider: gcp
|
||||
cluster_stack: gke-v2
|
||||
- name: HELM_ARGS
|
||||
value: ""
|
||||
name: cmp-helm-v2
|
||||
repoURL: https://github.com/mirakl/manifests-velero.git
|
||||
targetRevision: test-lla
|
||||
revision: rev1
|
||||
status: Synced
|
||||
BIN
docs/assets/api-management.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 119 KiB |
BIN
docs/assets/groups-claim.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
docs/assets/groups-scope.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 224 KiB |
|
Before Width: | Height: | Size: 352 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 183 KiB |
@@ -53,7 +53,7 @@ div[data-md-component=announce]>div#announce-msg>a{
|
||||
}
|
||||
|
||||
/* from https://assets.readthedocs.org/static/css/badge_only.css,
|
||||
most styles have to be overridden here */
|
||||
most styles have to be overriden here */
|
||||
.rst-versions{
|
||||
position: relative !important;
|
||||
bottom: 0;
|
||||
|
||||
@@ -37,17 +37,6 @@ sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
|
||||
rm argocd-linux-amd64
|
||||
```
|
||||
|
||||
#### Download latest stable version
|
||||
|
||||
You can download the latest stable release by executing below steps:
|
||||
|
||||
```bash
|
||||
VERSION=$(curl -L -s https://raw.githubusercontent.com/argoproj/argo-cd/stable/VERSION)
|
||||
curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/download/v$VERSION/argocd-linux-amd64
|
||||
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
|
||||
rm argocd-linux-amd64
|
||||
```
|
||||
|
||||
You should now be able to run `argocd` commands.
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ and the CLI functionalities.
|
||||
### Application Controller
|
||||
|
||||
The Application Controller is responsible for reconciling the
|
||||
Application resource in Kubernetes synchronizing the desired
|
||||
Application resource in Kubernetes syncronizing the desired
|
||||
application state (provided in Git) with the live state (in
|
||||
Kubernetes). The Application Controller is also responsible for
|
||||
reconciling the Project resource.
|
||||
|
||||