Compare commits

...

1 Commits

Author SHA1 Message Date
Justin Marquis
06b529c41d sibling of 95d37dc87d 2024-05-21 15:44:25 +00:00
1054 changed files with 32208 additions and 113560 deletions

View File

@@ -18,10 +18,8 @@ hack/
docs/
examples/
.github/
!test/container
!test/e2e/testdata
!test/fixture
!test/remote
!test/container
!hack/installers
!hack/gpg-wrapper.sh
!hack/git-verify-wrapper.sh

View File

@@ -9,6 +9,12 @@ assignees: ''
Target RC1 date: ___. __, ____
Target GA date: ___. __, ____
- [ ] Create new section in the [Release Planning doc](https://docs.google.com/document/d/1trJIomcgXcfvLw0aYnERrFWfPjQOfYMDJOCh1S8nMBc/edit?usp=sharing)
- [ ] Schedule a Release Planning meeting roughly two weeks before the scheduled Release freeze date by adding it to the community calendar (or delegate this task to someone with write access to the community calendar)
- [ ] Include Zoom link in the invite
- [ ] Post in #argo-cd and #argo-contributors one week before the meeting
- [ ] Post again one hour before the meeting
- [ ] At the meeting, remove issues/PRs from the project's column for that release which have not been “claimed” by at least one Approver (add it to the next column if Approver requests that)
- [ ] 1wk before feature freeze post in #argo-contributors that PRs must be merged by DD-MM-YYYY to be included in the release - ask approvers to drop items from milestone they cant merge
- [ ] At least two days before RC1 date, draft RC blog post and submit it for review (or delegate this task)
- [ ] Cut RC1 (or delegate this task to an Approver and coordinate timing)

View File

@@ -17,11 +17,6 @@ updates:
schedule:
interval: "daily"
- package-ecosystem: "npm"
directory: "/ui-test/"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/"
schedule:

View File

@@ -13,12 +13,11 @@ Checklist:
* [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
* [ ] Does this PR require documentation updates?
* [ ] I've updated documentation as required by this PR.
* [ ] Optional. My organization is added to USERS.md.
* [ ] I have signed off all my commits as required by [DCO](https://github.com/argoproj/argoproj/blob/master/community/CONTRIBUTING.md#legal)
* [ ] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
* [ ] My build is green ([troubleshooting builds](https://argo-cd.readthedocs.io/en/latest/developer-guide/ci/)).
* [ ] My new feature complies with the [feature status](https://github.com/argoproj/argoproj/blob/master/community/feature-status.md) guidelines.
* [ ] I have added a brief description of why this PR is necessary and/or what this PR solves.
* [ ] Optional. My organization is added to USERS.md.
* [ ] Optional. For bug fixes, I've indicated what older releases this fix should be cherry-picked into (this may or may not happen depending on risk/complexity).
<!-- Please see [Contribution FAQs](https://argo-cd.readthedocs.io/en/latest/developer-guide/faq/) if you have questions about your pull-request. -->

View File

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

View File

@@ -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,15 +67,12 @@ 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
@@ -114,23 +83,21 @@ jobs:
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,20 +260,17 @@ jobs:
build-ui:
name: Build, test & lint UI code
if: ${{ 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@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') }}
@@ -333,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') }}
@@ -358,12 +315,12 @@ 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
@@ -379,37 +336,34 @@ 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"
@@ -419,15 +373,15 @@ jobs:
ARGOCD_E2E_K3S: "true"
ARGOCD_IN_CI: "true"
ARGOCD_E2E_APISERVER_PORT: "8088"
ARGOCD_APPLICATION_NAMESPACES: "argocd-e2e-external,argocd-e2e-external-2"
ARGOCD_APPLICATION_NAMESPACES: "argocd-e2e-external"
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
@@ -446,7 +400,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 }}
@@ -472,7 +426,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
@@ -502,31 +456,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

View File

@@ -27,19 +27,14 @@ jobs:
# CodeQL runs on ubuntu-latest and windows-latest
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # 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
with:
go-version-file: go.mod
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
# 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 +42,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 +56,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

View File

@@ -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@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@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.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@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.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@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.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@2eb1c1961a95fc15694676618e422e8ba1d63825 #v4.1.1
with:
context: .
platforms: ${{ inputs.platforms }}

View File

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

View File

@@ -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@9153d834b60caba6d51c9b9510b087acf9f33f83 # v6.0.4
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"

View File

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

View File

@@ -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 }}
@@ -87,14 +87,6 @@ jobs:
echo "KUBECTL_VERSION=$(go list -m k8s.io/client-go | head -n 1 | rev | cut -d' ' -f1 | rev)" >> $GITHUB_ENV
echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
large-packages: false
docker-images: false
swap-storage: false
tool-cache: false
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
id: run-goreleaser
@@ -128,7 +120,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 +139,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 +189,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 +204,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 +222,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 }}
@@ -273,13 +265,11 @@ jobs:
set -xue
SOURCE_TAG=${{ github.ref_name }}
VERSION_REF="${SOURCE_TAG#*v}"
COMMIT_HASH=$(git rev-parse HEAD)
if echo "$VERSION_REF" | grep -E -- '^[0-9]+\.[0-9]+\.0-rc1';then
VERSION=$(awk 'BEGIN {FS=OFS="."} {$2++; print}' <<< "${VERSION_REF%-rc1}")
echo "Updating VERSION to: $VERSION"
echo "UPDATE_VERSION=true" >> $GITHUB_ENV
echo "NEW_VERSION=$VERSION" >> $GITHUB_ENV
echo "COMMIT_HASH=$COMMIT_HASH" >> $GITHUB_ENV
else
echo "Not updating VERSION"
echo "UPDATE_VERSION=false" >> $GITHUB_ENV
@@ -288,14 +278,10 @@ jobs:
- name: Update VERSION on master branch
run: |
echo ${{ env.NEW_VERSION }} > VERSION
# Replace the 'project-release: vX.X.X-rcX' line in SECURITY-INSIGHTS.yml
sed -i "s/project-release: v.*$/project-release: v${{ env.NEW_VERSION }}/" SECURITY-INSIGHTS.yml
# Update the 'commit-hash: XXXXXXX' line in SECURITY-INSIGHTS.yml
sed -i "s/commit-hash: .*/commit-hash: ${{ env.NEW_VERSION }}/" SECURITY-INSIGHTS.yml
if: ${{ env.UPDATE_VERSION == 'true' }}
- name: Create PR to update VERSION on master branch
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83 # v6.0.4
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2
with:
commit-message: Bump version in master
title: "chore: Bump version in master"

View File

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

View File

@@ -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
View File

@@ -19,7 +19,6 @@ node_modules/
./test/cmp/*.sock
.envrc.remote
.*.swp
rerunreport.txt
# ignore built binaries
cmd/argocd/argocd

View File

@@ -114,7 +114,7 @@ changelog:
exclude:
- '^test:'
- '^.*?Bump(\([[:word:]]+\))?.+$'
- '^.*?\[Bot\](\([[:word:]]+\))?.+$'
- '^.*?[Bot](\([[:word:]]+\))?.+$'
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json

View File

@@ -2,10 +2,7 @@
** @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
# CI
/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci

View File

@@ -1 +0,0 @@
Please refer to [the Contribution Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/code-contributions/)

View File

@@ -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.3@sha256:02d7116222536a5cf0fcf631f90b507758b669648e0f20186d2dc94a9b419a9b 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:21.7.0@sha256:104b26b5d34f9907f1f1e5e51fd9e557845f1a354f07ee9f28814dd9574a6154 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.3@sha256:02d7116222536a5cf0fcf631f90b507758b669648e0f20186d2dc94a9b419a9b AS argocd-build
WORKDIR /go/src/github.com/argoproj/argo-cd

119
Makefile
View File

@@ -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
@@ -57,7 +49,7 @@ ARGOCD_E2E_DEX_PORT?=5556
ARGOCD_E2E_YARN_HOST?=localhost
ARGOCD_E2E_DISABLE_AUTH?=
ARGOCD_E2E_TEST_TIMEOUT?=90m
ARGOCD_E2E_TEST_TIMEOUT?=45m
ARGOCD_IN_CI?=false
ARGOCD_TEST_E2E?=true
@@ -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 ./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
@@ -391,9 +386,9 @@ test: test-tools-image
.PHONY: test-local
test-local:
if test "$(TEST_MODULE)" = ""; then \
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -coverprofile=coverage.out; \
./hack/test.sh -coverprofile=coverage.out `go list ./... | grep -v 'test/e2e'`; \
else \
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \
./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \
fi
.PHONY: test-race
@@ -405,9 +400,9 @@ test-race: test-tools-image
.PHONY: test-race-local
test-race-local:
if test "$(TEST_MODULE)" = ""; then \
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -race -coverprofile=coverage.out; \
./hack/test.sh -race -coverprofile=coverage.out `go list ./... | grep -v 'test/e2e'`; \
else \
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -race -coverprofile=coverage.out; \
./hack/test.sh -race -coverprofile=coverage.out "$(TEST_MODULE)"; \
fi
# Run the E2E test suite. E2E test servers (see start-e2e target) must be
@@ -421,7 +416,7 @@ test-e2e:
test-e2e-local: cli-local
# NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system
export GO111MODULE=off
DIST_DIR=${DIST_DIR} RERUN_FAILS=5 PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v
ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v ./test/e2e
# Spawns a shell in the test server container for debugging purposes
debug-test-server: test-tools-image
@@ -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)
@@ -443,7 +438,6 @@ start-e2e: test-tools-image
start-e2e-local: mod-vendor-local dep-ui-local cli-local
kubectl create ns argocd-e2e || true
kubectl create ns argocd-e2e-external || true
kubectl create ns argocd-e2e-external-2 || true
kubectl config set-context --current --namespace=argocd-e2e
kustomize build test/manifests/base | kubectl apply -f -
kubectl apply -f https://raw.githubusercontent.com/open-cluster-management/api/a6845f2ebcb186ec26b832f60c988537a58f3859/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml
@@ -464,8 +458,8 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local
ARGOCD_ZJWT_FEATURE_FLAG=always \
ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
BIN_MODE=$(ARGOCD_BIN_MODE) \
ARGOCD_APPLICATION_NAMESPACES=argocd-e2e-external,argocd-e2e-external-2 \
ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES=argocd-e2e-external,argocd-e2e-external-2 \
ARGOCD_APPLICATION_NAMESPACES=argocd-e2e-external \
ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES=argocd-e2e-external \
ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS=http://127.0.0.1:8341,http://127.0.0.1:8342,http://127.0.0.1:8343,http://127.0.0.1:8344 \
ARGOCD_E2E_TEST=true \
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
@@ -481,7 +475,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
@@ -497,7 +491,6 @@ start-local: mod-vendor-local dep-ui-local cli-local
ARGOCD_ZJWT_FEATURE_FLAG=always \
ARGOCD_IN_CI=false \
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
BIN_MODE=$(ARGOCD_BIN_MODE) \
ARGOCD_E2E_TEST=false \
ARGOCD_APPLICATION_NAMESPACES=$(ARGOCD_APPLICATION_NAMESPACES) \
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
@@ -531,7 +524,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:
@@ -539,7 +532,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
@@ -562,8 +556,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 gotestsum
./hack/install.sh helm-linux
# Installs all tools required for running codegen (Linux packages)
.PHONY: install-codegen-tools-local
@@ -591,7 +584,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:

2
OWNERS
View File

@@ -5,7 +5,6 @@ owners:
approvers:
- alexec
- alexmt
- gdsoumya
- jannfis
- jessesuen
- jgwest
@@ -31,3 +30,4 @@ reviewers:
- zachaller
- 34fathombelow
- alexef
- gdsoumya

View File

@@ -1,4 +1,4 @@
controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "HOSTNAME=testappcontroller-1 FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --server-side-diff-enabled=${ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF:-'false'}"
controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml"
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" = 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} docker.io/library/redis:$(grep "image: redis" manifests/base/redis/argocd-redis-deployment.yaml | cut -d':' -f3) --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
@@ -9,5 +9,4 @@ git-server: test/fixture/testrepos/start-git.sh
helm-registry: test/fixture/testrepos/start-helm-registry.sh
dev-mounter: [[ "$ARGOCD_E2E_TEST" != "true" ]] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source}
applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}"
notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug"

View File

@@ -13,7 +13,6 @@
**Social:**
[![Twitter Follow](https://img.shields.io/twitter/follow/argoproj?style=social)](https://twitter.com/argoproj)
[![Slack](https://img.shields.io/badge/slack-argoproj-brightgreen.svg?logo=slack)](https://argoproj.github.io/community/join-slack)
[![LinkedIn](https://img.shields.io/badge/LinkedIn-argoproj-blue.svg?logo=linkedin)](https://www.linkedin.com/company/argoproj/)
# Argo CD - Declarative Continuous Delivery for Kubernetes
@@ -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/)

View File

@@ -1,128 +0,0 @@
header:
schema-version: 1.0.0
expiration-date: '2024-10-31T00:00:00.000Z' # One year from initial release.
last-updated: '2023-10-27'
last-reviewed: '2023-10-27'
commit-hash: b71277c6beb949d0199d647a582bc25822b88838
project-url: https://github.com/argoproj/argo-cd
project-release: v2.9.0-rc3
changelog: https://github.com/argoproj/argo-cd/releases
license: https://github.com/argoproj/argo-cd/blob/master/LICENSE
project-lifecycle:
status: active
roadmap: https://github.com/orgs/argoproj/projects/25
bug-fixes-only: false
core-maintainers:
- https://github.com/argoproj/argoproj/blob/master/MAINTAINERS.md
release-cycle: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/
release-process: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/#release-process
contribution-policy:
accepts-pull-requests: true
accepts-automated-pull-requests: true
automated-tools-list:
- automated-tool: dependabot
action: allowed
path:
- /
- automated-tool: snyk-report
action: allowed
path:
- docs/snyk
comment: |
This tool runs Snyk and generates a report of vulnerabilities in the project's dependencies. The report is
placed in the project's documentation. The workflow is defined here:
https://github.com/argoproj/argo-cd/blob/master/.github/workflows/update-snyk.yaml
contributing-policy: https://argo-cd.readthedocs.io/en/stable/developer-guide/code-contributions/
code-of-conduct: https://github.com/cncf/foundation/blob/master/code-of-conduct.md
documentation:
- https://argo-cd.readthedocs.io/
distribution-points:
- https://github.com/argoproj/argo-cd/releases
- https://quay.io/repository/argoproj/argocd
security-artifacts:
threat-model:
threat-model-created: true
evidence-url:
- https://github.com/argoproj/argoproj/blob/master/docs/argo_threat_model.pdf
- https://github.com/argoproj/argoproj/blob/master/docs/end_user_threat_model.pdf
self-assessment:
self-assessment-created: false
comment: |
An extensive self-assessment was performed for CNCF graduation. Because the self-assessment process was evolving
at the time, no standardized document has been published.
security-testing:
- tool-type: sca
tool-name: Dependabot
tool-version: "2"
tool-url: https://github.com/dependabot
integration:
ad-hoc: false
ci: false
before-release: false
tool-rulesets:
- https://github.com/argoproj/argo-cd/blob/master/.github/dependabot.yml
- tool-type: sca
tool-name: Snyk
tool-version: latest
tool-url: https://snyk.io/
integration:
ad-hoc: true
ci: true
before-release: false
- tool-type: sast
tool-name: CodeQL
tool-version: latest
tool-url: https://codeql.github.com/
integration:
ad-hoc: false
ci: true
before-release: false
comment: |
We use the default configuration with the latest version.
security-assessments:
- auditor-name: Trail of Bits
auditor-url: https://trailofbits.com
auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/argo_security_final_report.pdf
report-year: 2021
- auditor-name: Ada Logics
auditor-url: https://adalogics.com
auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/argo_security_audit_2022.pdf
report-year: 2022
- auditor-name: Ada Logics
auditor-url: https://adalogics.com
auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/audit_fuzzer_adalogics_2022.pdf
report-year: 2022
comment: |
Part of the audit was performed by Ada Logics, focussed on fuzzing.
- auditor-name: Chainguard
auditor-url: https://chainguard.dev
auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/software_supply_chain_slsa_assessment_chainguard_2023.pdf
report-year: 2023
comment: |
Confirmed the project's release process as achieving SLSA (v0.1) level 3.
security-contacts:
- type: email
value: cncf-argo-security@lists.cncf.io
primary: true
vulnerability-reporting:
accepts-vulnerability-reports: true
email-contact: cncf-argo-security@lists.cncf.io
security-policy: https://github.com/argoproj/argo-cd/security/policy
bug-bounty-available: true
bug-bounty-url: https://hackerone.com/ibb/policy_scopes
out-scope:
- vulnerable and outdated components # See https://github.com/argoproj/argo-cd/blob/master/SECURITY.md#a-word-about-security-scanners
- security logging and monitoring failures
dependencies:
third-party-packages: true
dependencies-lists:
- https://github.com/argoproj/argo-cd/blob/master/go.mod
- https://github.com/argoproj/argo-cd/blob/master/Dockerfile
- https://github.com/argoproj/argo-cd/blob/master/ui/package.json
sbom:
- sbom-file: https://github.com/argoproj/argo-cd/releases # Every release's assets include SBOMs.
sbom-format: SPDX
dependencies-lifecycle:
policy-url: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/#dependencies-lifecycle-policy
env-dependencies-policy:
policy-url: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/#dependencies-lifecycle-policy

View File

@@ -18,51 +18,42 @@ 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)
2. [Autodesk](https://www.autodesk.com)
1. [Axual B.V.](https://axual.com)
1. [Back Market](https://www.backmarket.com)
1. [Baloise](https://www.baloise.com)
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/)
@@ -101,9 +92,7 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Fave](https://myfave.com)
1. [Flexport](https://www.flexport.com/)
1. [Flip](https://flip.id)
1. [Fly Security](https://www.flysecurity.com.br/)
1. [Fonoa](https://www.fonoa.com/)
1. [Fortra](https://www.fortra.com)
1. [freee](https://corp.freee.co.jp/en/company/)
1. [Freshop, Inc](https://www.freshop.com/)
1. [Future PLC](https://www.futureplc.com/)
@@ -120,8 +109,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)
@@ -134,12 +123,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/)
@@ -160,7 +146,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Kinguin](https://www.kinguin.net/)
1. [KintoHub](https://www.kintohub.com/)
1. [KompiTech GmbH](https://www.kompitech.com/)
1. [Kong Inc.](https://konghq.com/)
1. [KPMG](https://kpmg.com/uk)
1. [KubeSphere](https://github.com/kubesphere)
1. [Kurly](https://www.kurly.com/)
@@ -184,8 +169,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Meican](https://meican.com/)
1. [Meilleurs Agents](https://www.meilleursagents.com/)
1. [Mercedes-Benz Tech Innovation](https://www.mercedes-benz-techinnovation.com/)
1. [Mercedes-Benz.io](https://www.mercedes-benz.io/)
1. [Metacore Games](https://metacoregames.com/)
1. [Metanet](http://www.metanet.co.kr/en/)
1. [MindSpore](https://mindspore.cn)
1. [Mirantis](https://mirantis.com/)
@@ -198,7 +181,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)
@@ -206,11 +188,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)
@@ -227,16 +207,12 @@ 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. [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)
@@ -252,29 +228,22 @@ Currently, the following organizations are **officially** using Argo CD:
1. [QuintoAndar](https://quintoandar.com.br)
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/)
1. [Saloodo! GmbH](https://www.saloodo.com)
1. [Sap Labs](http://sap.com)
1. [Sauce Labs](https://saucelabs.com/)
1. [Schwarz IT](https://jobs.schwarz/it-mission)
1. [SCRM Lidl International Hub](https://scrm.lidl)
1. [SEEK](https://seek.com.au)
1. [Semgrep](https://semgrep.com)
1. [Shield](https://shield.com)
1. [SI Analytics](https://si-analytics.ai)
1. [Skit](https://skit.ai/)
1. [Skyscanner](https://www.skyscanner.net/)
@@ -289,8 +258,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Spendesk](https://spendesk.com/)
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/)
@@ -304,7 +271,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/)
@@ -328,7 +294,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/)

View File

@@ -1 +1 @@
2.11.0
2.9.15

View File

@@ -17,11 +17,9 @@ package controllers
import (
"context"
"fmt"
"strings"
"reflect"
"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"
@@ -41,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"
@@ -110,40 +109,24 @@ 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
}
return ctrl.Result{}, nil
}
// 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(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
@@ -171,16 +154,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.
@@ -189,19 +162,24 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
if r.EnableProgressiveSyncs {
if applicationSetInfo.Spec.Strategy == nil && len(applicationSetInfo.Status.ApplicationStatus) > 0 {
// If appset used progressive sync but stopped, clean up the progressive sync application statuses
logCtx.Infof("Removing %v unnecessary AppStatus entries from ApplicationSet %v", len(applicationSetInfo.Status.ApplicationStatus), applicationSetInfo.Name)
log.Infof("Removing %v unnecessary AppStatus entries from ApplicationSet %v", len(applicationSetInfo.Status.ApplicationStatus), applicationSetInfo.Name)
err := r.setAppSetApplicationStatus(ctx, logCtx, &applicationSetInfo, []argov1alpha1.ApplicationSetApplicationStatus{})
err := r.setAppSetApplicationStatus(ctx, &applicationSetInfo, []argov1alpha1.ApplicationSetApplicationStatus{})
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to clear previous AppSet application statuses for %v: %w", applicationSetInfo.Name, err)
}
} 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, applicationSetInfo, applications, desiredApplications, appMap)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to perform progressive sync reconciliation for application set: %w", err)
}
@@ -239,7 +217,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
if r.EnableProgressiveSyncs {
// trigger appropriate application syncs if RollingSync strategy is enabled
if progressiveSyncsStrategyEnabled(&applicationSetInfo, "RollingSync") {
validApps, err = r.syncValidApplications(logCtx, &applicationSetInfo, appSyncMap, appMap, validApps)
validApps, err = r.syncValidApplications(ctx, &applicationSetInfo, appSyncMap, appMap, validApps)
if err != nil {
_ = r.setApplicationSetStatusCondition(ctx,
@@ -257,7 +235,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}
if utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowUpdate() {
err = r.createOrUpdateInCluster(ctx, logCtx, applicationSetInfo, validApps)
err = r.createOrUpdateInCluster(ctx, applicationSetInfo, validApps)
if err != nil {
_ = r.setApplicationSetStatusCondition(ctx,
&applicationSetInfo,
@@ -271,7 +249,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
return ctrl.Result{}, err
}
} else {
err = r.createInCluster(ctx, logCtx, applicationSetInfo, validApps)
err = r.createInCluster(ctx, applicationSetInfo, validApps)
if err != nil {
_ = r.setApplicationSetStatusCondition(ctx,
&applicationSetInfo,
@@ -287,7 +265,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}
if utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete() {
err = r.deleteInCluster(ctx, logCtx, applicationSetInfo, desiredApplications)
err = r.deleteInCluster(ctx, applicationSetInfo, desiredApplications)
if err != nil {
_ = r.setApplicationSetStatusCondition(ctx,
&applicationSetInfo,
@@ -322,7 +300,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{
@@ -512,7 +490,7 @@ func getTempApplication(applicationSetTemplate argov1alpha1.ApplicationSetTempla
return &tmplApplication
}
func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, applicationSetInfo argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) {
func (r *ApplicationSetReconciler) generateApplications(applicationSetInfo argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) {
var res []argov1alpha1.Application
var firstError error
@@ -521,7 +499,7 @@ func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, appli
for _, requestedGenerator := range applicationSetInfo.Spec.Generators {
t, err := generators.Transform(requestedGenerator, r.Generators, applicationSetInfo.Spec.Template, &applicationSetInfo, map[string]interface{}{})
if err != nil {
logCtx.WithError(err).WithField("generator", requestedGenerator).
log.WithError(err).WithField("generator", requestedGenerator).
Error("error generating application from params")
if firstError == nil {
firstError = err
@@ -535,9 +513,8 @@ func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, appli
for _, p := range a.Params {
app, err := r.Renderer.RenderTemplateParams(tmplApplication, applicationSetInfo.Spec.SyncPolicy, p, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
if err != nil {
logCtx.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator).
log.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator).
Error("error generating application from params")
if firstError == nil {
@@ -546,45 +523,17 @@ func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, appli
}
continue
}
if applicationSetInfo.Spec.TemplatePatch != nil {
patchedApplication, err := r.applyTemplatePatch(app, applicationSetInfo, p)
if err != nil {
log.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator).
Error("error generating application from params")
if firstError == nil {
firstError = err
applicationSetReason = argov1alpha1.ApplicationSetReasonRenderTemplateParamsError
}
continue
}
app = patchedApplication
}
res = append(res, *app)
}
}
logCtx.WithField("generator", requestedGenerator).Infof("generated %d applications", len(res))
logCtx.WithField("generator", requestedGenerator).Debugf("apps from generator: %+v", res)
log.WithField("generator", requestedGenerator).Infof("generated %d applications", len(res))
log.WithField("generator", requestedGenerator).Debugf("apps from generator: %+v", res)
}
return res, applicationSetReason, firstError
}
func (r *ApplicationSetReconciler) applyTemplatePatch(app *argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet, params map[string]interface{}) (*argov1alpha1.Application, error) {
replacedTemplate, err := r.Renderer.Replace(*applicationSetInfo.Spec.TemplatePatch, params, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
if err != nil {
return nil, fmt.Errorf("error replacing values in templatePatch: %w", err)
}
return applyTemplatePatch(app, replacedTemplate)
}
func ignoreNotAllowedNamespaces(namespaces []string) predicate.Predicate {
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
@@ -593,24 +542,22 @@ func ignoreNotAllowedNamespaces(namespaces []string) predicate.Predicate {
}
}
func appControllerIndexer(rawObj client.Object) []string {
// grab the job object, extract the owner...
app := rawObj.(*argov1alpha1.Application)
owner := metav1.GetControllerOf(app)
if owner == nil {
return nil
}
// ...make sure it's a application set...
if owner.APIVersion != argov1alpha1.SchemeGroupVersion.String() || owner.Kind != "ApplicationSet" {
return nil
}
// ...and if so, return it
return []string{owner.Name}
}
func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProgressiveSyncs bool, maxConcurrentReconciliations int) error {
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &argov1alpha1.Application{}, ".metadata.controller", appControllerIndexer); err != nil {
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &argov1alpha1.Application{}, ".metadata.controller", func(rawObj client.Object) []string {
// grab the job object, extract the owner...
app := rawObj.(*argov1alpha1.Application)
owner := metav1.GetControllerOf(app)
if owner == nil {
return nil
}
// ...make sure it's a application set...
if owner.APIVersion != argov1alpha1.SchemeGroupVersion.String() || owner.Kind != "ApplicationSet" {
return nil
}
// ...and if so, return it
return []string{owner.Name}
}); err != nil {
return fmt.Errorf("error setting up with manager: %w", err)
}
@@ -622,7 +569,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"),
@@ -654,16 +601,14 @@ func (r *ApplicationSetReconciler) updateCache(ctx context.Context, obj client.O
// - For new applications, it will call create
// - For existing application, it will call update
// The function also adds owner reference to all applications, and uses it to delete them.
func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
var firstError error
// Creates or updates the application in appList
for _, generatedApp := range desiredApplications {
// The app's namespace must be the same as the AppSet's namespace to preserve the appsets-in-any-namespace
// security boundary.
generatedApp.Namespace = applicationSet.Namespace
appLog := logCtx.WithFields(log.Fields{"app": generatedApp.QualifiedName()})
appLog := log.WithFields(log.Fields{"app": generatedApp.Name, "appSet": applicationSet.Name})
generatedApp.Namespace = applicationSet.Namespace
// Normalize to avoid fighting with the application controller.
generatedApp.Spec = *argoutil.NormalizeApplicationSpec(&generatedApp.Spec)
@@ -727,17 +672,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
@@ -770,7 +704,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
// createInCluster will filter from the desiredApplications only the application that needs to be created
// Then it will call createOrUpdateInCluster to do the actual create
func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
var createApps []argov1alpha1.Application
current, err := r.getCurrentApplications(ctx, applicationSet)
@@ -793,12 +727,13 @@ func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, logCtx *
}
}
return r.createOrUpdateInCluster(ctx, logCtx, applicationSet, createApps)
return r.createOrUpdateInCluster(ctx, applicationSet, createApps)
}
func (r *ApplicationSetReconciler) getCurrentApplications(ctx context.Context, applicationSet argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) {
func (r *ApplicationSetReconciler) getCurrentApplications(_ context.Context, applicationSet argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) {
// TODO: Should this use the context param?
var current argov1alpha1.ApplicationList
err := r.Client.List(ctx, &current, client.MatchingFields{".metadata.controller": applicationSet.Name}, client.InNamespace(applicationSet.Namespace))
err := r.Client.List(context.Background(), &current, client.MatchingFields{".metadata.controller": applicationSet.Name})
if err != nil {
return nil, fmt.Errorf("error retrieving applications: %w", err)
@@ -809,7 +744,7 @@ func (r *ApplicationSetReconciler) getCurrentApplications(ctx context.Context, a
// deleteInCluster will delete Applications that are currently on the cluster, but not in appList.
// The function must be called after all generators had been called and generated applications
func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
// settingsMgr := settings.NewSettingsManager(context.TODO(), r.KubeClientset, applicationSet.Namespace)
// argoDB := db.NewDB(applicationSet.Namespace, settingsMgr, r.KubeClientset)
// clusterList, err := argoDB.ListClusters(ctx)
@@ -833,15 +768,15 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *
// Delete apps that are not in m[string]bool
var firstError error
for _, app := range current {
logCtx = logCtx.WithField("app", app.QualifiedName())
appLog := log.WithFields(log.Fields{"app": app.Name, "appSet": applicationSet.Name})
_, exists := m[app.Name]
if !exists {
// Removes the Argo CD resources finalizer if the application contains an invalid target (eg missing cluster)
err := r.removeFinalizerOnInvalidDestination(ctx, applicationSet, &app, clusterList, logCtx)
err := r.removeFinalizerOnInvalidDestination(ctx, applicationSet, &app, clusterList, appLog)
if err != nil {
logCtx.WithError(err).Error("failed to update Application")
appLog.WithError(err).Error("failed to update Application")
if firstError != nil {
firstError = err
}
@@ -850,14 +785,14 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *
err = r.Client.Delete(ctx, &app)
if err != nil {
logCtx.WithError(err).Error("failed to delete Application")
appLog.WithError(err).Error("failed to delete Application")
if firstError != nil {
firstError = err
}
continue
}
r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, "Deleted", "Deleted Application %q", app.Name)
logCtx.Log(log.InfoLevel, "Deleted application")
appLog.Log(log.InfoLevel, "Deleted application")
}
}
return firstError
@@ -939,38 +874,21 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte
return nil
}
func (r *ApplicationSetReconciler) removeOwnerReferencesOnDeleteAppSet(ctx context.Context, applicationSet argov1alpha1.ApplicationSet) error {
applications, err := r.getCurrentApplications(ctx, applicationSet)
if err != nil {
return err
}
func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context, appset argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, desiredApplications []argov1alpha1.Application, appMap map[string]argov1alpha1.Application) (map[string]bool, error) {
for _, app := range applications {
app.SetOwnerReferences([]metav1.OwnerReference{})
err := r.Client.Update(ctx, &app)
if err != nil {
return err
}
}
return nil
}
func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context, logCtx *log.Entry, appset argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, desiredApplications []argov1alpha1.Application, appMap map[string]argov1alpha1.Application) (map[string]bool, error) {
appDependencyList, appStepMap, err := r.buildAppDependencyList(logCtx, appset, desiredApplications)
appDependencyList, appStepMap, err := r.buildAppDependencyList(ctx, appset, desiredApplications)
if err != nil {
return nil, fmt.Errorf("failed to build app dependency list: %w", err)
}
_, err = r.updateApplicationSetApplicationStatus(ctx, logCtx, &appset, applications, appStepMap)
_, err = r.updateApplicationSetApplicationStatus(ctx, &appset, applications, appStepMap)
if err != nil {
return nil, fmt.Errorf("failed to update applicationset app status: %w", err)
}
logCtx.Infof("ApplicationSet %v step list:", appset.Name)
log.Infof("ApplicationSet %v step list:", appset.Name)
for i, step := range appDependencyList {
logCtx.Infof("step %v: %+v", i+1, step)
log.Infof("step %v: %+v", i+1, step)
}
appSyncMap, err := r.buildAppSyncMap(ctx, appset, appDependencyList, appMap)
@@ -978,9 +896,9 @@ func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context,
return nil, fmt.Errorf("failed to build app sync map: %w", err)
}
logCtx.Infof("Application allowed to sync before maxUpdate?: %+v", appSyncMap)
log.Infof("Application allowed to sync before maxUpdate?: %+v", appSyncMap)
_, err = r.updateApplicationSetApplicationStatusProgress(ctx, logCtx, &appset, appSyncMap, appStepMap, appMap)
_, err = r.updateApplicationSetApplicationStatusProgress(ctx, &appset, appSyncMap, appStepMap, appMap)
if err != nil {
return nil, fmt.Errorf("failed to update applicationset application status progress: %w", err)
}
@@ -994,7 +912,7 @@ func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context,
}
// this list tracks which Applications belong to each RollingUpdate step
func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([][]string, map[string]int, error) {
func (r *ApplicationSetReconciler) buildAppDependencyList(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([][]string, map[string]int, error) {
if applicationSet.Spec.Strategy == nil || applicationSet.Spec.Strategy.Type == "" || applicationSet.Spec.Strategy.Type == "AllAtOnce" {
return [][]string{}, map[string]int{}, nil
@@ -1021,9 +939,9 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, app
for _, matchExpression := range step.MatchExpressions {
if val, ok := app.Labels[matchExpression.Key]; ok {
valueMatched := labelMatchedExpression(logCtx, val, matchExpression)
valueMatched := labelMatchedExpression(val, matchExpression)
if !valueMatched { // none of the matchExpression values was a match with the Application's labels
if !valueMatched { // none of the matchExpression values was a match with the Application'ss labels
selected = false
break
}
@@ -1036,7 +954,7 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, app
if selected {
appDependencyList[i] = append(appDependencyList[i], app.Name)
if val, ok := appStepMap[app.Name]; ok {
logCtx.Warnf("AppSet '%v' has a invalid matchExpression that selects Application '%v' label twice, in steps %v and %v", applicationSet.Name, app.Name, val+1, i+1)
log.Warnf("AppSet '%v' has a invalid matchExpression that selects Application '%v' label twice, in steps %v and %v", applicationSet.Name, app.Name, val+1, i+1)
} else {
appStepMap[app.Name] = i
}
@@ -1047,9 +965,9 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, app
return appDependencyList, appStepMap, nil
}
func labelMatchedExpression(logCtx *log.Entry, val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool {
func labelMatchedExpression(val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool {
if matchExpression.Operator != "In" && matchExpression.Operator != "NotIn" {
logCtx.Errorf("skipping AppSet rollingUpdate step Application selection, invalid matchExpression operator provided: %q ", matchExpression.Operator)
log.Errorf("skipping AppSet rollingUpdate step Application selection, invalid matchExpression operator provided: %q ", matchExpression.Operator)
return false
}
@@ -1153,7 +1071,7 @@ func statusStrings(app argov1alpha1.Application) (string, string, string) {
}
// check the status of each Application's status and promote Applications to the next status if needed
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, appStepMap map[string]int) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, appStepMap map[string]int) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
now := metav1.Now()
appStatuses := make([]argov1alpha1.ApplicationSetApplicationStatus, 0, len(applications))
@@ -1186,7 +1104,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
}
if appOutdated && currentAppStatus.Status != "Waiting" && currentAppStatus.Status != "Pending" {
logCtx.Infof("Application %v is outdated, updating its ApplicationSet status to Waiting", app.Name)
log.Infof("Application %v is outdated, updating its ApplicationSet status to Waiting", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = "Waiting"
currentAppStatus.Message = "Application has pending changes, setting status to Waiting."
@@ -1198,15 +1116,15 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
// this covers race conditions where syncs initiated by RollingSync miraculously have a sync time before the transition to Pending state occurred (could be a few seconds)
if operationPhaseString == "Succeeded" && app.Status.OperationState.StartedAt.Add(time.Duration(10)*time.Second).After(currentAppStatus.LastTransitionTime.Time) {
if !app.Status.OperationState.StartedAt.After(currentAppStatus.LastTransitionTime.Time) {
logCtx.Warnf("Application %v was synced less than 10s prior to entering Pending status, we'll assume the AppSet controller triggered this sync and update its status to Progressing", app.Name)
log.Warnf("Application %v was synced less than 10s prior to entering Pending status, we'll assume the AppSet controller triggered this sync and update its status to Progressing", app.Name)
}
logCtx.Infof("Application %v has completed a sync successfully, updating its ApplicationSet status to Progressing", app.Name)
log.Infof("Application %v has completed a sync successfully, updating its ApplicationSet status to Progressing", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = "Progressing"
currentAppStatus.Message = "Application resource completed a sync successfully, updating status from Pending to Progressing."
currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1)
} else if operationPhaseString == "Running" || healthStatusString == "Progressing" {
logCtx.Infof("Application %v has entered Progressing status, updating its ApplicationSet status to Progressing", app.Name)
log.Infof("Application %v has entered Progressing status, updating its ApplicationSet status to Progressing", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = "Progressing"
currentAppStatus.Message = "Application resource became Progressing, updating status from Pending to Progressing."
@@ -1215,7 +1133,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
}
if currentAppStatus.Status == "Waiting" && isApplicationHealthy(app) {
logCtx.Infof("Application %v is already synced and healthy, updating its ApplicationSet status to Healthy", app.Name)
log.Infof("Application %v is already synced and healthy, updating its ApplicationSet status to Healthy", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = healthStatusString
currentAppStatus.Message = "Application resource is already Healthy, updating status from Waiting to Healthy."
@@ -1223,7 +1141,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
}
if currentAppStatus.Status == "Progressing" && isApplicationHealthy(app) {
logCtx.Infof("Application %v has completed Progressing status, updating its ApplicationSet status to Healthy", app.Name)
log.Infof("Application %v has completed Progressing status, updating its ApplicationSet status to Healthy", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = healthStatusString
currentAppStatus.Message = "Application resource became Healthy, updating status from Progressing to Healthy."
@@ -1233,7 +1151,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
appStatuses = append(appStatuses, currentAppStatus)
}
err := r.setAppSetApplicationStatus(ctx, logCtx, applicationSet, appStatuses)
err := r.setAppSetApplicationStatus(ctx, applicationSet, appStatuses)
if err != nil {
return nil, fmt.Errorf("failed to set AppSet application statuses: %w", err)
}
@@ -1242,7 +1160,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
}
// check Applications that are in Waiting status and promote them to Pending if needed
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appStepMap map[string]int, appMap map[string]argov1alpha1.Application) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appStepMap map[string]int, appMap map[string]argov1alpha1.Application) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
now := metav1.Now()
appStatuses := make([]argov1alpha1.ApplicationSetApplicationStatus, 0, len(applicationSet.Status.ApplicationStatus))
@@ -1284,7 +1202,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
if maxUpdate != nil {
maxUpdateVal, err := intstr.GetScaledValueFromIntOrPercent(maxUpdate, totalCountMap[appStepMap[appStatus.Application]], false)
if err != nil {
logCtx.Warnf("AppSet '%v' has a invalid maxUpdate value '%+v', ignoring maxUpdate logic for this step: %v", applicationSet.Name, maxUpdate, err)
log.Warnf("AppSet '%v' has a invalid maxUpdate value '%+v', ignoring maxUpdate logic for this step: %v", applicationSet.Name, maxUpdate, err)
}
// ensure that percentage values greater than 0% always result in at least 1 Application being selected
@@ -1294,13 +1212,13 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
if updateCountMap[appStepMap[appStatus.Application]] >= maxUpdateVal {
maxUpdateAllowed = false
logCtx.Infof("Application %v is not allowed to update yet, %v/%v Applications already updating in step %v in AppSet %v", appStatus.Application, updateCountMap[appStepMap[appStatus.Application]], maxUpdateVal, appStepMap[appStatus.Application]+1, applicationSet.Name)
log.Infof("Application %v is not allowed to update yet, %v/%v Applications already updating in step %v in AppSet %v", appStatus.Application, updateCountMap[appStepMap[appStatus.Application]], maxUpdateVal, appStepMap[appStatus.Application]+1, applicationSet.Name)
}
}
if appStatus.Status == "Waiting" && appSyncMap[appStatus.Application] && maxUpdateAllowed {
logCtx.Infof("Application %v moved to Pending status, watching for the Application to start Progressing", appStatus.Application)
log.Infof("Application %v moved to Pending status, watching for the Application to start Progressing", appStatus.Application)
appStatus.LastTransitionTime = &now
appStatus.Status = "Pending"
appStatus.Message = "Application moved to Pending status, watching for the Application resource to start Progressing."
@@ -1313,7 +1231,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
}
}
err := r.setAppSetApplicationStatus(ctx, logCtx, applicationSet, appStatuses)
err := r.setAppSetApplicationStatus(ctx, applicationSet, appStatuses)
if err != nil {
return nil, fmt.Errorf("failed to set AppSet app status: %w", err)
}
@@ -1373,89 +1291,9 @@ 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 ApplicatonSet's status field
// with any new/changed Application statuses.
func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, applicationStatuses []argov1alpha1.ApplicationSetApplicationStatus) error {
func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, applicationStatuses []argov1alpha1.ApplicationSetApplicationStatus) error {
needToUpdateStatus := false
if len(applicationStatuses) != len(applicationSet.Status.ApplicationStatus) {
@@ -1489,7 +1327,7 @@ func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Contex
err := r.Client.Status().Update(ctx, applicationSet)
if err != nil {
logCtx.Errorf("unable to set application set status: %v", err)
log.Errorf("unable to set application set status: %v", err)
return fmt.Errorf("unable to set application set status: %v", err)
}
@@ -1504,7 +1342,7 @@ func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Contex
return nil
}
func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appMap map[string]argov1alpha1.Application, validApps []argov1alpha1.Application) ([]argov1alpha1.Application, error) {
func (r *ApplicationSetReconciler) syncValidApplications(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appMap map[string]argov1alpha1.Application, validApps []argov1alpha1.Application) ([]argov1alpha1.Application, error) {
rolloutApps := []argov1alpha1.Application{}
for i := range validApps {
pruneEnabled := false
@@ -1524,7 +1362,7 @@ func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, appl
// check appSyncMap to determine which Applications are ready to be updated and which should be skipped
if appSyncMap[validApps[i].Name] && appMap[validApps[i].Name].Status.Sync.Status == "OutOfSync" && appSetStatusPending {
logCtx.Infof("triggering sync for application: %v, prune enabled: %v", validApps[i].Name, pruneEnabled)
log.Infof("triggering sync for application: %v, prune enabled: %v", validApps[i].Name, pruneEnabled)
validApps[i], _ = syncApplication(validApps[i], pruneEnabled)
}
rolloutApps = append(rolloutApps, validApps[i])
@@ -1568,51 +1406,29 @@ func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {
CreateFunc: func(e event.CreateEvent) bool {
// if we are the owner and there is a create event, we most likely created it and do not need to
// re-reconcile
if log.IsLevelEnabled(log.DebugLevel) {
var appName string
app, isApp := e.Object.(*argov1alpha1.Application)
if isApp {
appName = app.QualifiedName()
}
log.WithField("app", appName).Debugln("received create event from owning an application")
}
log.Debugln("received create event from owning an application")
return false
},
DeleteFunc: func(e event.DeleteEvent) bool {
if log.IsLevelEnabled(log.DebugLevel) {
var appName string
app, isApp := e.Object.(*argov1alpha1.Application)
if isApp {
appName = app.QualifiedName()
}
log.WithField("app", appName).Debugln("received delete event from owning an application")
}
log.Debugln("received delete event from owning an application")
return true
},
UpdateFunc: func(e event.UpdateEvent) bool {
log.Debugln("received update event from owning an application")
appOld, isApp := e.ObjectOld.(*argov1alpha1.Application)
if !isApp {
return false
}
logCtx := log.WithField("app", appOld.QualifiedName())
logCtx.Debugln("received update event from owning an application")
appNew, isApp := e.ObjectNew.(*argov1alpha1.Application)
if !isApp {
return false
}
requeue := shouldRequeueApplicationSet(appOld, appNew, enableProgressiveSyncs)
logCtx.WithField("requeue", requeue).Debugf("requeue: %t caused by application %s\n", requeue, appNew.Name)
log.Debugf("requeue: %t caused by application %s\n", requeue, appNew.Name)
return requeue
},
GenericFunc: func(e event.GenericEvent) bool {
if log.IsLevelEnabled(log.DebugLevel) {
var appName string
app, isApp := e.Object.(*argov1alpha1.Application)
if isApp {
appName = app.QualifiedName()
}
log.WithField("app", appName).Debugln("received generic event from owning an application")
}
log.Debugln("received generic event from owning an application")
return true
},
}
@@ -1630,14 +1446,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 comparsion
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
}

View File

@@ -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
}
@@ -87,12 +86,6 @@ func (g *generatorMock) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetG
return args.Get(0).([]map[string]interface{}), args.Error(1)
}
func (g *generatorMock) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) {
args := g.Called(tmpl, replaceMap, useGoTemplate, goTemplateOptions)
return args.Get(0).(string), args.Error(1)
}
type rendererMock struct {
mock.Mock
}
@@ -114,12 +107,6 @@ func (r *rendererMock) RenderTemplateParams(tmpl *v1alpha1.Application, syncPoli
}
func (r *rendererMock) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) {
args := r.Called(tmpl, replaceMap, useGoTemplate, goTemplateOptions)
return args.Get(0).(string), args.Error(1)
}
func TestExtractApplications(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
@@ -231,7 +218,7 @@ func TestExtractApplications(t *testing.T) {
Cache: &fakeCache{},
}
got, reason, err := r.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
got, reason, err := r.generateApplications(v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "namespace",
@@ -344,7 +331,7 @@ func TestMergeTemplateApplications(t *testing.T) {
KubeClientset: kubefake.NewSimpleClientset(),
}
got, _, _ := r.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
got, _, _ := r.generateApplications(v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "namespace",
@@ -1282,71 +1269,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",
},
},
},
},
} {
@@ -1360,7 +1282,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
initObjs = append(initObjs, &a)
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
r := ApplicationSetReconciler{
Client: client,
@@ -1369,8 +1291,8 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
Cache: &fakeCache{},
}
err = r.createOrUpdateInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps)
assert.NoError(t, err)
err = r.createOrUpdateInCluster(context.TODO(), c.appSet, c.desiredApps)
assert.Nil(t, err)
for _, obj := range c.expected {
got := &v1alpha1.Application{}
@@ -1453,7 +1375,7 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
initObjs := []crtclient.Object{&app, &appSet}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-secret",
@@ -1615,7 +1537,7 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
initObjs := []crtclient.Object{&app, &appSet}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-secret",
@@ -1671,81 +1593,6 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
}
}
func TestRemoveOwnerReferencesOnDeleteAppSet(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
assert.Nil(t, err)
err = v1alpha1.AddToScheme(scheme)
assert.Nil(t, err)
for _, c := range []struct {
// name is human-readable test name
name string
}{
{
name: "ownerReferences cleared",
},
} {
t.Run(c.name, func(t *testing.T) {
appSet := v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "namespace",
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSetSpec{
Template: v1alpha1.ApplicationSetTemplate{
Spec: v1alpha1.ApplicationSpec{
Project: "project",
},
},
},
}
app := v1alpha1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "app1",
Namespace: "namespace",
},
Spec: v1alpha1.ApplicationSpec{
Project: "project",
Source: &v1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"},
Destination: v1alpha1.ApplicationDestination{
Namespace: "namespace",
Server: "https://kubernetes.default.svc",
},
},
}
err := controllerutil.SetControllerReference(&appSet, &app, scheme)
assert.NoError(t, err, "Unexpected error")
initObjs := []crtclient.Object{&app, &appSet}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(10),
KubeClientset: nil,
Cache: &fakeCache{},
}
err = r.removeOwnerReferencesOnDeleteAppSet(context.Background(), appSet)
assert.NoError(t, err, "Unexpected error")
retrievedApp := v1alpha1.Application{}
err = client.Get(context.Background(), crtclient.ObjectKeyFromObject(&app), &retrievedApp)
assert.NoError(t, err, "Unexpected error")
ownerReferencesRemoved := len(retrievedApp.OwnerReferences) == 0
assert.True(t, ownerReferencesRemoved)
})
}
}
func TestCreateApplications(t *testing.T) {
scheme := runtime.NewScheme()
@@ -1922,7 +1769,7 @@ func TestCreateApplications(t *testing.T) {
initObjs = append(initObjs, &a)
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
r := ApplicationSetReconciler{
Client: client,
@@ -1931,7 +1778,7 @@ func TestCreateApplications(t *testing.T) {
Cache: &fakeCache{},
}
err = r.createInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.apps)
err = r.createInCluster(context.TODO(), c.appSet, c.apps)
assert.Nil(t, err)
for _, obj := range c.expected {
@@ -2066,7 +1913,7 @@ func TestDeleteInCluster(t *testing.T) {
initObjs = append(initObjs, &temp)
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
r := ApplicationSetReconciler{
Client: client,
@@ -2075,7 +1922,7 @@ func TestDeleteInCluster(t *testing.T) {
KubeClientset: kubefake.NewSimpleClientset(),
}
err = r.deleteInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps)
err = r.deleteInCluster(context.TODO(), c.appSet, c.desiredApps)
assert.Nil(t, err)
// For each of the expected objects, verify they exist on the cluster
@@ -2440,15 +2287,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()
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)
argoDBMock.On("GetCluster", mock.Anything, "https://bad-cluster").Return(&badCluster, nil)
argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{
goodCluster,
}}, nil)
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
@@ -2489,91 +2328,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)
@@ -2609,7 +2363,7 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
argoDBMock := dbmocks.ArgoDB{}
argoObjs := []runtime.Object{}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
r := ApplicationSetReconciler{
Client: client,
@@ -2679,7 +2433,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).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 +2603,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).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{
@@ -2978,24 +2732,17 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
{
name: "Generate an application from a go template application set manifest using a pull request generator",
params: []map[string]interface{}{{
"number": "1",
"branch": "branch1",
"branch_slug": "branchSlug1",
"head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958",
"head_short_sha": "089d92cb",
"branch_slugify_default": "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
"branch_slugify_smarttruncate_disabled": "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature",
"branch_slugify_smarttruncate_enabled": "feat/testwithsmarttruncateenabledramdomlonglistofcharacters",
"labels": []string{"label1"}},
},
"number": "1",
"branch": "branch1",
"branch_slug": "branchSlug1",
"head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958",
"head_short_sha": "089d92cb",
"labels": []string{"label1"}}},
template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
Name: "AppSet-{{.branch}}-{{.number}}",
Labels: map[string]string{
"app1": "{{index .labels 0}}",
"branch-test1": "AppSet-{{.branch_slugify_default | slugify }}",
"branch-test2": "AppSet-{{.branch_slugify_smarttruncate_disabled | slugify 49 false }}",
"branch-test3": "AppSet-{{.branch_slugify_smarttruncate_enabled | slugify 50 true }}",
"app1": "{{index .labels 0}}",
},
},
Spec: v1alpha1.ApplicationSpec{
@@ -3014,10 +2761,7 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "AppSet-branch1-1",
Labels: map[string]string{
"app1": "label1",
"branch-test1": "AppSet-feat-a-really-long-pull-request-name-to-test-argo",
"branch-test2": "AppSet-feat-areallylongpullrequestnametotestargoslugific",
"branch-test3": "AppSet-feat",
"app1": "label1",
},
},
Spec: v1alpha1.ApplicationSpec{
@@ -3060,7 +2804,7 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
KubeClientset: kubefake.NewSimpleClientset(),
}
gotApp, _, _ := appSetReconciler.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
gotApp, _, _ := appSetReconciler.generateApplications(v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Generators: []v1alpha1.ApplicationSetGenerator{{
@@ -3170,7 +2914,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).Build()
r := ApplicationSetReconciler{
Client: client,
@@ -3333,7 +3077,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,
@@ -3349,7 +3093,7 @@ func TestSetApplicationSetApplicationStatus(t *testing.T) {
KubeClientset: kubeclientset,
}
err = r.setAppSetApplicationStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appStatuses)
err = r.setAppSetApplicationStatus(context.TODO(), &cc.appSet, cc.appStatuses)
assert.Nil(t, err)
assert.Equal(t, cc.expectedAppStatuses, cc.appSet.Status.ApplicationStatus)
@@ -4112,7 +3856,7 @@ func TestBuildAppDependencyList(t *testing.T) {
KubeClientset: kubeclientset,
}
appDependencyList, appStepMap, err := r.buildAppDependencyList(log.NewEntry(log.StandardLogger()), cc.appSet, cc.apps)
appDependencyList, appStepMap, err := r.buildAppDependencyList(context.TODO(), cc.appSet, cc.apps)
assert.Equal(t, err, nil, "expected no errors, but errors occured")
assert.Equal(t, cc.expectedList, appDependencyList, "expected appDependencyList did not match actual")
assert.Equal(t, cc.expectedStepMap, appStepMap, "expected appStepMap did not match actual")
@@ -5353,7 +5097,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,
@@ -5366,7 +5110,7 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
KubeClientset: kubeclientset,
}
appStatuses, err := r.updateApplicationSetApplicationStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps, cc.appStepMap)
appStatuses, err := r.updateApplicationSetApplicationStatus(context.TODO(), &cc.appSet, cc.apps, cc.appStepMap)
// opt out of testing the LastTransitionTime is accurate
for i := range appStatuses {
@@ -6107,7 +5851,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,
@@ -6120,7 +5864,7 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) {
KubeClientset: kubeclientset,
}
appStatuses, err := r.updateApplicationSetApplicationStatusProgress(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appSyncMap, cc.appStepMap, cc.appMap)
appStatuses, err := r.updateApplicationSetApplicationStatusProgress(context.TODO(), &cc.appSet, cc.appSyncMap, cc.appStepMap, cc.appMap)
// opt out of testing the LastTransitionTime is accurate
for i := range appStatuses {
@@ -6133,219 +5877,6 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) {
}
}
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 occured")
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)
@@ -6451,70 +5982,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"}}},

View File

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

View File

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

View File

@@ -60,9 +60,9 @@ func TestRequeueAfter(t *testing.T) {
"List": generators.NewListGenerator(),
"Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"),
"Git": generators.NewGitGenerator(mockServer),
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, "", []string{""}, true),
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, "", []string{""}),
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
"PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}, true),
"PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}),
}
nestedGenerators := map[string]generators.Generator{

View File

@@ -1,46 +0,0 @@
package controllers
import (
"encoding/json"
"fmt"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"github.com/argoproj/argo-cd/v2/applicationset/utils"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)
func applyTemplatePatch(app *appv1.Application, templatePatch string) (*appv1.Application, error) {
appString, err := json.Marshal(app)
if err != nil {
return nil, fmt.Errorf("error while marhsalling Application %w", err)
}
convertedTemplatePatch, err := utils.ConvertYAMLToJSON(templatePatch)
if err != nil {
return nil, fmt.Errorf("error while converting template to json %q: %w", convertedTemplatePatch, err)
}
if err := json.Unmarshal([]byte(convertedTemplatePatch), &appv1.Application{}); err != nil {
return nil, fmt.Errorf("invalid templatePatch %q: %w", convertedTemplatePatch, err)
}
data, err := strategicpatch.StrategicMergePatch(appString, []byte(convertedTemplatePatch), appv1.Application{})
if err != nil {
return nil, fmt.Errorf("error while applying templatePatch template to json %q: %w", convertedTemplatePatch, err)
}
finalApp := appv1.Application{}
err = json.Unmarshal(data, &finalApp)
if err != nil {
return nil, fmt.Errorf("error while unmarhsalling patched application: %w", err)
}
// Prevent changes to the `project` field. This helps prevent malicious template patches
finalApp.Spec.Project = app.Spec.Project
return &finalApp, nil
}

View File

@@ -1,249 +0,0 @@
package controllers
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)
func Test_ApplyTemplatePatch(t *testing.T) {
testCases := []struct {
name string
appTemplate *appv1.Application
templatePatch string
expectedApp *appv1.Application
}{
{
name: "patch with JSON",
appTemplate: &appv1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: "namespace",
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
},
Spec: appv1.ApplicationSpec{
Project: "default",
Source: &appv1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: appv1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
},
templatePatch: `{
"metadata": {
"annotations": {
"annotation-some-key": "annotation-some-value"
}
},
"spec": {
"source": {
"helm": {
"valueFiles": [
"values.test.yaml",
"values.big.yaml"
]
}
},
"syncPolicy": {
"automated": {
"prune": true
}
}
}
}`,
expectedApp: &appv1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: "namespace",
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
Annotations: map[string]string{
"annotation-some-key": "annotation-some-value",
},
},
Spec: appv1.ApplicationSpec{
Project: "default",
Source: &appv1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
Helm: &appv1.ApplicationSourceHelm{
ValueFiles: []string{
"values.test.yaml",
"values.big.yaml",
},
},
},
Destination: appv1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
SyncPolicy: &appv1.SyncPolicy{
Automated: &appv1.SyncPolicyAutomated{
Prune: true,
},
},
},
},
},
{
name: "patch with YAML",
appTemplate: &appv1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: "namespace",
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
},
Spec: appv1.ApplicationSpec{
Project: "default",
Source: &appv1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: appv1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
},
templatePatch: `
metadata:
annotations:
annotation-some-key: annotation-some-value
spec:
source:
helm:
valueFiles:
- values.test.yaml
- values.big.yaml
syncPolicy:
automated:
prune: true`,
expectedApp: &appv1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: "namespace",
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
Annotations: map[string]string{
"annotation-some-key": "annotation-some-value",
},
},
Spec: appv1.ApplicationSpec{
Project: "default",
Source: &appv1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
Helm: &appv1.ApplicationSourceHelm{
ValueFiles: []string{
"values.test.yaml",
"values.big.yaml",
},
},
},
Destination: appv1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
SyncPolicy: &appv1.SyncPolicy{
Automated: &appv1.SyncPolicyAutomated{
Prune: true,
},
},
},
},
},
{
name: "project field isn't overwritten",
appTemplate: &appv1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: "namespace",
},
Spec: appv1.ApplicationSpec{
Project: "default",
Source: &appv1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: appv1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
},
templatePatch: `
spec:
project: my-project`,
expectedApp: &appv1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: "namespace",
},
Spec: appv1.ApplicationSpec{
Project: "default",
Source: &appv1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: appv1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
},
},
}
for _, tc := range testCases {
tcc := tc
t.Run(tcc.name, func(t *testing.T) {
result, err := applyTemplatePatch(tcc.appTemplate, tcc.templatePatch)
require.NoError(t, err)
assert.Equal(t, *tcc.expectedApp, *result)
})
}
}
func TestError(t *testing.T) {
app := &appv1.Application{}
result, err := applyTemplatePatch(app, "hello world")
require.Error(t, err)
require.Nil(t, result)
}

View File

@@ -150,9 +150,6 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
return nil, fmt.Errorf("unable to parse file: %v", err)
}
objectsFound = append(objectsFound, singleObj)
} else if len(objectsFound) == 0 {
// If file is valid but empty, add a default empty item
objectsFound = append(objectsFound, map[string]interface{}{})
}
res := []map[string]interface{}{}

View File

@@ -4,173 +4,119 @@ import (
"fmt"
"testing"
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)
func Test_generateParamsFromGitFile(t *testing.T) {
defaultContent := []byte(`
values := map[string]string{}
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`)
type args struct {
filePath string
fileContent []byte
values map[string]string
useGoTemplate bool
goTemplateOptions []string
pathParamPrefix string
`), values, false, nil, "")
if err != nil {
t.Fatal(err)
}
tests := []struct {
name string
args args
want []map[string]interface{}
wantErr bool
}{
assert.Equal(t, []map[string]interface{}{
{
name: "empty file returns path parameters",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: []byte(""),
values: map[string]string{},
useGoTemplate: false,
},
want: []map[string]interface{}{
{
"path": "path/dir",
"path.basename": "dir",
"path.filename": "file_name.yaml",
"path.basenameNormalized": "dir",
"path.filenameNormalized": "file-name.yaml",
"path[0]": "path",
"path[1]": "dir",
},
},
},
{
name: "invalid json/yaml file returns error",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: []byte("this is not json or yaml"),
values: map[string]string{},
useGoTemplate: false,
},
wantErr: true,
},
{
name: "file parameters are added to params",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: defaultContent,
values: map[string]string{},
useGoTemplate: false,
},
want: []map[string]interface{}{
{
"foo.bar": "baz",
"path": "path/dir",
"path.basename": "dir",
"path.filename": "file_name.yaml",
"path.basenameNormalized": "dir",
"path.filenameNormalized": "file-name.yaml",
"path[0]": "path",
"path[1]": "dir",
},
},
},
{
name: "path parameter are prefixed",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: defaultContent,
values: map[string]string{},
useGoTemplate: false,
pathParamPrefix: "myRepo",
},
want: []map[string]interface{}{
{
"foo.bar": "baz",
"myRepo.path": "path/dir",
"myRepo.path.basename": "dir",
"myRepo.path.filename": "file_name.yaml",
"myRepo.path.basenameNormalized": "dir",
"myRepo.path.filenameNormalized": "file-name.yaml",
"myRepo.path[0]": "path",
"myRepo.path[1]": "dir",
},
},
},
{
name: "file parameters are added to params with go template",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: defaultContent,
values: map[string]string{},
useGoTemplate: true,
},
want: []map[string]interface{}{
{
"foo": map[string]interface{}{
"bar": "baz",
},
"path": map[string]interface{}{
"path": "path/dir",
"basename": "dir",
"filename": "file_name.yaml",
"basenameNormalized": "dir",
"filenameNormalized": "file-name.yaml",
"segments": []string{
"path",
"dir",
},
},
},
},
},
{
name: "path parameter are prefixed with go template",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: defaultContent,
values: map[string]string{},
useGoTemplate: true,
pathParamPrefix: "myRepo",
},
want: []map[string]interface{}{
{
"foo": map[string]interface{}{
"bar": "baz",
},
"myRepo": map[string]interface{}{
"path": map[string]interface{}{
"path": "path/dir",
"basename": "dir",
"filename": "file_name.yaml",
"basenameNormalized": "dir",
"filenameNormalized": "file-name.yaml",
"segments": []string{
"path",
"dir",
},
},
},
},
},
"foo.bar": "baz",
"path": "path/dir",
"path.basename": "dir",
"path.filename": "file_name.yaml",
"path.basenameNormalized": "dir",
"path.filenameNormalized": "file-name.yaml",
"path[0]": "path",
"path[1]": "dir",
},
}, params)
}
func Test_generatePrefixedParamsFromGitFile(t *testing.T) {
values := map[string]string{}
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`), values, false, nil, "myRepo")
if err != nil {
t.Fatal(err)
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
params, err := (*GitGenerator)(nil).generateParamsFromGitFile(tt.args.filePath, tt.args.fileContent, tt.args.values, tt.args.useGoTemplate, tt.args.goTemplateOptions, tt.args.pathParamPrefix)
if (err != nil) != tt.wantErr {
t.Errorf("GitGenerator.generateParamsFromGitFile() error = %v, wantErr %v", err, tt.wantErr)
return
}
assert.Equal(t, tt.want, params)
})
assert.Equal(t, []map[string]interface{}{
{
"foo.bar": "baz",
"myRepo.path": "path/dir",
"myRepo.path.basename": "dir",
"myRepo.path.filename": "file_name.yaml",
"myRepo.path.basenameNormalized": "dir",
"myRepo.path.filenameNormalized": "file-name.yaml",
"myRepo.path[0]": "path",
"myRepo.path[1]": "dir",
},
}, params)
}
func Test_generateParamsFromGitFileGoTemplate(t *testing.T) {
values := map[string]string{}
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`), values, true, nil, "")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, []map[string]interface{}{
{
"foo": map[string]interface{}{
"bar": "baz",
},
"path": map[string]interface{}{
"path": "path/dir",
"basename": "dir",
"filename": "file_name.yaml",
"basenameNormalized": "dir",
"filenameNormalized": "file-name.yaml",
"segments": []string{
"path",
"dir",
},
},
},
}, params)
}
func Test_generatePrefixedParamsFromGitFileGoTemplate(t *testing.T) {
values := map[string]string{}
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`), values, true, nil, "myRepo")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, []map[string]interface{}{
{
"foo": map[string]interface{}{
"bar": "baz",
},
"myRepo": map[string]interface{}{
"path": map[string]interface{}{
"path": "path/dir",
"basename": "dir",
"filename": "file_name.yaml",
"basenameNormalized": "dir",
"filenameNormalized": "file-name.yaml",
"segments": []string{
"path",
"dir",
},
},
},
},
}, params)
}
func TestGitGenerateParamsFromDirectories(t *testing.T) {

View File

@@ -27,16 +27,14 @@ type PullRequestGenerator struct {
auth SCMAuthProviders
scmRootCAPath string
allowedSCMProviders []string
enableSCMProviders bool
}
func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string, enableSCMProviders bool) Generator {
func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string) Generator {
g := &PullRequestGenerator{
client: client,
auth: auth,
scmRootCAPath: scmRootCAPath,
allowedSCMProviders: allowedScmProviders,
enableSCMProviders: enableSCMProviders,
}
g.selectServiceProviderFunc = g.selectServiceProvider
return g
@@ -68,7 +66,7 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
ctx := context.Background()
svc, err := g.selectServiceProviderFunc(ctx, appSetGenerator.PullRequest, applicationSetInfo)
if err != nil {
return nil, fmt.Errorf("failed to select pull request service provider: %w", err)
return nil, fmt.Errorf("failed to select pull request service provider: %v", err)
}
pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters)
@@ -123,18 +121,17 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
// selectServiceProvider selects the provider to get pull requests from the configuration
func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, generatorConfig *argoprojiov1alpha1.PullRequestGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
if !g.enableSCMProviders {
return nil, ErrSCMProvidersDisabled
}
if err := ScmProviderAllowed(applicationSetInfo, generatorConfig, g.allowedSCMProviders); err != nil {
return nil, fmt.Errorf("scm provider not allowed: %w", err)
}
if generatorConfig.Github != nil {
if !ScmProviderAllowed(applicationSetInfo, generatorConfig.Github.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", generatorConfig.Github.API)
}
return g.github(ctx, generatorConfig.Github, applicationSetInfo)
}
if generatorConfig.GitLab != nil {
providerConfig := generatorConfig.GitLab
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
}
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Secret token: %v", err)
@@ -143,6 +140,9 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
}
if generatorConfig.Gitea != nil {
providerConfig := generatorConfig.Gitea
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", generatorConfig.Gitea.API)
}
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Secret token: %v", err)
@@ -151,6 +151,9 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
}
if generatorConfig.BitbucketServer != nil {
providerConfig := generatorConfig.BitbucketServer
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
}
if providerConfig.BasicAuth != nil {
password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace)
if err != nil {

View File

@@ -278,7 +278,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
cases := []struct {
name string
providerConfig *argoprojiov1alpha1.PullRequestGenerator
expectedError error
expectedError string
}{
{
name: "Error Github",
@@ -287,7 +287,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Gitlab",
@@ -296,7 +296,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Gitea",
@@ -305,7 +305,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Bitbucket",
@@ -314,7 +314,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
}
@@ -330,7 +330,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
"gitea.myorg.com",
"bitbucket.myorg.com",
"azuredevops.myorg.com",
}, true)
})
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
@@ -346,29 +346,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
_, err := pullRequestGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
assert.Error(t, err, "Must return an error")
assert.ErrorAs(t, err, testCaseCopy.expectedError)
assert.Equal(t, testCaseCopy.expectedError, err.Error())
})
}
}
func TestSCMProviderDisabled_PRGenerator(t *testing.T) {
generator := NewPullRequestGenerator(nil, SCMAuthProviders{}, "", []string{}, false)
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
},
Spec: argoprojiov1alpha1.ApplicationSetSpec{
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
}},
},
}
_, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
assert.ErrorIs(t, err, ErrSCMProvidersDisabled)
}

View File

@@ -2,7 +2,6 @@ package generators
import (
"context"
"errors"
"fmt"
"strings"
"time"
@@ -32,26 +31,24 @@ type SCMProviderGenerator struct {
SCMAuthProviders
scmRootCAPath string
allowedSCMProviders []string
enableSCMProviders bool
}
type SCMAuthProviders struct {
GitHubApps github_app_auth.Credentials
}
func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool) Generator {
func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string) Generator {
return &SCMProviderGenerator{
client: client,
SCMAuthProviders: providers,
scmRootCAPath: scmRootCAPath,
allowedSCMProviders: allowedSCMProviders,
enableSCMProviders: enableSCMProviders,
}
}
// Testing generator
func NewTestSCMProviderGenerator(overrideProvider scm_provider.SCMProviderService) Generator {
return &SCMProviderGenerator{overrideProvider: overrideProvider, enableSCMProviders: true}
return &SCMProviderGenerator{overrideProvider: overrideProvider}
}
func (g *SCMProviderGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
@@ -68,34 +65,14 @@ func (g *SCMProviderGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.A
return &appSetGenerator.SCMProvider.Template
}
var ErrSCMProvidersDisabled = errors.New("scm providers are disabled")
type ErrDisallowedSCMProvider struct {
Provider string
Allowed []string
}
func NewErrDisallowedSCMProvider(provider string, allowed []string) ErrDisallowedSCMProvider {
return ErrDisallowedSCMProvider{
Provider: provider,
Allowed: allowed,
}
}
func (e ErrDisallowedSCMProvider) Error() string {
return fmt.Sprintf("scm provider %q not allowed, must use one of the following: %s", e.Provider, strings.Join(e.Allowed, ", "))
}
func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, generator SCMGeneratorWithCustomApiUrl, allowedScmProviders []string) error {
url := generator.CustomApiUrl()
func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, url string, allowedScmProviders []string) bool {
if url == "" || len(allowedScmProviders) == 0 {
return nil
return true
}
for _, allowedScmProvider := range allowedScmProviders {
if url == allowedScmProvider {
return nil
return true
}
}
@@ -103,9 +80,9 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, g
common.SecurityField: common.SecurityMedium,
"applicationset": applicationSetInfo.Name,
"appSetNamespace": applicationSetInfo.Namespace,
}).Debugf("attempted to use disallowed SCM %q, must use one of the following: %s", url, strings.Join(allowedScmProviders, ", "))
}).Debugf("attempted to use disallowed SCM %q", url)
return NewErrDisallowedSCMProvider(url, allowedScmProviders)
return false
}
func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
@@ -117,28 +94,26 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
return nil, EmptyAppSetGeneratorError
}
if !g.enableSCMProviders {
return nil, ErrSCMProvidersDisabled
}
ctx := context.Background()
// Create the SCM provider helper.
providerConfig := appSetGenerator.SCMProvider
if err := ScmProviderAllowed(applicationSetInfo, providerConfig, g.allowedSCMProviders); err != nil {
return nil, fmt.Errorf("scm provider not allowed: %w", err)
}
ctx := context.Background()
var provider scm_provider.SCMProviderService
if g.overrideProvider != nil {
provider = g.overrideProvider
} else if providerConfig.Github != nil {
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Github.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Github.API)
}
var err error
provider, err = g.githubProvider(ctx, providerConfig.Github, applicationSetInfo)
if err != nil {
return nil, fmt.Errorf("scm provider: %w", err)
}
} else if providerConfig.Gitlab != nil {
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Gitlab.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Gitlab.API)
}
token, err := g.getSecretRef(ctx, providerConfig.Gitlab.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Gitlab token: %v", err)
@@ -148,6 +123,9 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
return nil, fmt.Errorf("error initializing Gitlab service: %v", err)
}
} else if providerConfig.Gitea != nil {
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Gitea.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Gitea.API)
}
token, err := g.getSecretRef(ctx, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Gitea token: %v", err)
@@ -158,6 +136,9 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
}
} else if providerConfig.BitbucketServer != nil {
providerConfig := providerConfig.BitbucketServer
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
}
var scmError error
if providerConfig.BasicAuth != nil {
password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace)
@@ -172,6 +153,9 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
return nil, fmt.Errorf("error initializing Bitbucket Server service: %v", scmError)
}
} else if providerConfig.AzureDevOps != nil {
if !ScmProviderAllowed(applicationSetInfo, providerConfig.AzureDevOps.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.AzureDevOps.API)
}
token, err := g.getSecretRef(ctx, providerConfig.AzureDevOps.AccessTokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Azure Devops access token: %v", err)

View File

@@ -174,7 +174,7 @@ func TestSCMProviderGenerateParams(t *testing.T) {
mockProvider := &scm_provider.MockProvider{
Repos: testCaseCopy.repos,
}
scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider, enableSCMProviders: true}
scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider}
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
@@ -205,7 +205,7 @@ func TestAllowedSCMProvider(t *testing.T) {
cases := []struct {
name string
providerConfig *argoprojiov1alpha1.SCMProviderGenerator
expectedError error
expectedError string
}{
{
name: "Error Github",
@@ -214,7 +214,7 @@ func TestAllowedSCMProvider(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Gitlab",
@@ -223,7 +223,7 @@ func TestAllowedSCMProvider(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Gitea",
@@ -232,7 +232,7 @@ func TestAllowedSCMProvider(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Bitbucket",
@@ -241,7 +241,7 @@ func TestAllowedSCMProvider(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error AzureDevops",
@@ -250,7 +250,7 @@ func TestAllowedSCMProvider(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
}
@@ -260,16 +260,13 @@ func TestAllowedSCMProvider(t *testing.T) {
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
scmGenerator := &SCMProviderGenerator{
allowedSCMProviders: []string{
"github.myorg.com",
"gitlab.myorg.com",
"gitea.myorg.com",
"bitbucket.myorg.com",
"azuredevops.myorg.com",
},
enableSCMProviders: true,
}
scmGenerator := &SCMProviderGenerator{allowedSCMProviders: []string{
"github.myorg.com",
"gitlab.myorg.com",
"gitea.myorg.com",
"bitbucket.myorg.com",
"azuredevops.myorg.com",
}}
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
@@ -285,29 +282,7 @@ func TestAllowedSCMProvider(t *testing.T) {
_, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
assert.Error(t, err, "Must return an error")
assert.ErrorAs(t, err, testCaseCopy.expectedError)
assert.Equal(t, testCaseCopy.expectedError, err.Error())
})
}
}
func TestSCMProviderDisabled_SCMGenerator(t *testing.T) {
generator := &SCMProviderGenerator{enableSCMProviders: false}
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
},
Spec: argoprojiov1alpha1.ApplicationSetSpec{
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
SCMProvider: &argoprojiov1alpha1.SCMProviderGenerator{
Github: &argoprojiov1alpha1.SCMProviderGeneratorGithub{
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
}},
},
}
_, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
assert.ErrorIs(t, err, ErrSCMProvidersDisabled)
}

View File

@@ -1,5 +0,0 @@
package generators
type SCMGeneratorWithCustomApiUrl interface {
CustomApiUrl() string
}

View File

@@ -206,9 +206,9 @@ func TestBuildURL(t *testing.T) {
},
{
name: "Provided custom URL and organization",
url: "https://azuredevops.example.com/",
url: "https://azuredevops.mycompany.com/",
organization: "myorganization",
expected: "https://azuredevops.example.com/myorganization",
expected: "https://azuredevops.mycompany.com/myorganization",
},
}

View File

@@ -8,7 +8,7 @@ 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"
@@ -16,7 +16,7 @@ import (
)
func s(input string) *string {
return ptr.To(input)
return pointer.String(input)
}
func TestAzureDevopsRepoHasPath(t *testing.T) {

View File

@@ -100,20 +100,12 @@ func (g *GitlabProvider) ListRepos(ctx context.Context, cloneProtocol string) ([
return nil, fmt.Errorf("unknown clone protocol for Gitlab %v", cloneProtocol)
}
var repoLabels []string
if len(gitlabRepo.Topics) == 0 {
// fallback to for gitlab prior to 14.5
repoLabels = gitlabRepo.TagList
} else {
repoLabels = gitlabRepo.Topics
}
repos = append(repos, &Repository{
Organization: gitlabRepo.Namespace.FullPath,
Repository: gitlabRepo.Path,
URL: url,
Branch: gitlabRepo.DefaultBranch,
Labels: repoLabels,
Labels: gitlabRepo.TagList,
RepositoryId: gitlabRepo.ID,
})
}

View File

@@ -1063,16 +1063,6 @@ func TestGitlabListRepos(t *testing.T) {
proto: "ssh",
url: "git@gitlab.com:test-argocd-proton/argocd.git",
},
{
name: "labelmatch",
proto: "ssh",
url: "git@gitlab.com:test-argocd-proton/argocd.git",
filters: []v1alpha1.SCMProviderGeneratorFilter{
{
LabelMatch: strp("test-topic"),
},
},
},
{
name: "https protocol",
proto: "https",

View File

@@ -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.Int64Ptr(int64(val))
}
}
cluster := appv1.Cluster{

View File

@@ -16,7 +16,6 @@ import (
"unsafe"
"github.com/Masterminds/sprig/v3"
"github.com/gosimple/slug"
"github.com/valyala/fasttemplate"
"sigs.k8s.io/yaml"
@@ -33,7 +32,6 @@ func init() {
delete(sprigFuncMap, "expandenv")
delete(sprigFuncMap, "getHostByName")
sprigFuncMap["normalize"] = SanitizeName
sprigFuncMap["slugify"] = SlugifyName
sprigFuncMap["toYaml"] = toYAML
sprigFuncMap["fromYaml"] = fromYAML
sprigFuncMap["fromYamlArray"] = fromYAMLArray
@@ -41,7 +39,6 @@ func init() {
type Renderer interface {
RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *argoappsv1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (*argoappsv1.Application, error)
Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error)
}
type Render struct {
@@ -437,54 +434,6 @@ func NormalizeBitbucketBasePath(basePath string) string {
return basePath
}
// SlugifyName generates a URL-friendly slug from the provided name and additional options.
// The slug is generated in accordance with the following rules:
// 1. The generated slug will be URL-safe and suitable for use in URLs.
// 2. The maximum length of the slug can be specified using the `maxSize` argument.
// 3. Smart truncation can be enabled or disabled using the `EnableSmartTruncate` argument.
// 4. The input name can be any string value that needs to be converted into a slug.
//
// Args:
// - args: A variadic number of arguments where:
// - The first argument (if provided) is an integer specifying the maximum length of the slug.
// - The second argument (if provided) is a boolean indicating whether smart truncation is enabled.
// - The last argument (if provided) is the input name that needs to be slugified.
// If no name is provided, an empty string will be used.
//
// Returns:
// - string: The generated URL-friendly slug based on the input name and options.
func SlugifyName(args ...interface{}) string {
// Default values for arguments
maxSize := 50
EnableSmartTruncate := true
name := ""
// Process the arguments
for idx, arg := range args {
switch idx {
case len(args) - 1:
name = arg.(string)
case 0:
maxSize = arg.(int)
case 1:
EnableSmartTruncate = arg.(bool)
default:
log.Errorf("Bad 'slugify' arguments.")
}
}
sanitizedName := SanitizeName(name)
// Configure slug generation options
slug.EnableSmartTruncate = EnableSmartTruncate
slug.MaxLength = maxSize
// Generate the slug from the input name
urlSlug := slug.Make(sanitizedName)
return urlSlug
}
func getTlsConfigWithCACert(scmRootCAPath string) *tls.Config {
tlsConfig := &tls.Config{}

View File

@@ -1243,43 +1243,6 @@ func TestNormalizeBitbucketBasePath(t *testing.T) {
}
}
func TestSlugify(t *testing.T) {
for _, c := range []struct {
branch string
smartTruncate bool
length int
expectedBasePath string
}{
{
branch: "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
smartTruncate: false,
length: 50,
expectedBasePath: "feat-a-really-long-pull-request-name-to-test-argo",
},
{
branch: "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
smartTruncate: true,
length: 53,
expectedBasePath: "feat-a-really-long-pull-request-name-to-test-argo",
},
{
branch: "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature",
smartTruncate: true,
length: 50,
expectedBasePath: "feat",
},
{
branch: "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature",
smartTruncate: false,
length: 50,
expectedBasePath: "feat-areallylongpullrequestnametotestargoslugifica",
},
} {
result := SlugifyName(c.length, c.smartTruncate, c.branch)
assert.Equal(t, c.expectedBasePath, result, c.branch)
}
}
func TestGetTLSConfig(t *testing.T) {
// certParsed, err := tls.X509KeyPair(test.Cert, test.PrivateKey)
// require.NoError(t, err)

View File

@@ -1,473 +0,0 @@
{
"action": "labeled",
"number": 2,
"label": {
"id": 6129306173,
"node_id": "LA_kwDOIqudU88AAAABbVXKPQ",
"url": "https://api.github.com/repos/SG60/backstage/labels/deploy-preview",
"name": "deploy-preview",
"color": "bfd4f2",
"default": false,
"description": ""
},
"pull_request": {
"url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2",
"id": 279147437,
"node_id": "MDExOlB1bGxSZXF1ZXN0Mjc5MTQ3NDM3",
"html_url": "https://github.com/Codertocat/Hello-World/pull/2",
"diff_url": "https://github.com/Codertocat/Hello-World/pull/2.diff",
"patch_url": "https://github.com/Codertocat/Hello-World/pull/2.patch",
"issue_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/2",
"number": 2,
"state": "open",
"locked": false,
"title": "Update the README with new information.",
"user": {
"login": "Codertocat",
"id": 21031067,
"node_id": "MDQ6VXNlcjIxMDMxMDY3",
"avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Codertocat",
"html_url": "https://github.com/Codertocat",
"followers_url": "https://api.github.com/users/Codertocat/followers",
"following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
"gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
"organizations_url": "https://api.github.com/users/Codertocat/orgs",
"repos_url": "https://api.github.com/users/Codertocat/repos",
"events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/Codertocat/received_events",
"type": "User",
"site_admin": false
},
"body": "This is a pretty simple change that we need to pull into master.",
"created_at": "2019-05-15T15:20:33Z",
"updated_at": "2019-05-15T15:20:33Z",
"closed_at": null,
"merged_at": null,
"merge_commit_sha": null,
"assignee": null,
"assignees": [],
"requested_reviewers": [],
"requested_teams": [],
"labels": [
{
"id": 6129306173,
"node_id": "LA_kwDOIqudU88AAAABbVXKPQ",
"url": "https://api.github.com/repos/Codertocat/Hello-World/labels/deploy-preview",
"name": "deploy-preview",
"color": "bfd4f2",
"default": false,
"description": ""
}
],
"milestone": null,
"commits_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/commits",
"review_comments_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/comments",
"review_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/comments{/number}",
"comments_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/2/comments",
"statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/ec26c3e57ca3a959ca5aad62de7213c562f8c821",
"head": {
"label": "Codertocat:changes",
"ref": "changes",
"sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821",
"user": {
"login": "Codertocat",
"id": 21031067,
"node_id": "MDQ6VXNlcjIxMDMxMDY3",
"avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Codertocat",
"html_url": "https://github.com/Codertocat",
"followers_url": "https://api.github.com/users/Codertocat/followers",
"following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
"gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
"organizations_url": "https://api.github.com/users/Codertocat/orgs",
"repos_url": "https://api.github.com/users/Codertocat/repos",
"events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/Codertocat/received_events",
"type": "User",
"site_admin": false
},
"repo": {
"id": 186853002,
"node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=",
"name": "Hello-World",
"full_name": "Codertocat/Hello-World",
"private": false,
"owner": {
"login": "Codertocat",
"id": 21031067,
"node_id": "MDQ6VXNlcjIxMDMxMDY3",
"avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Codertocat",
"html_url": "https://github.com/Codertocat",
"followers_url": "https://api.github.com/users/Codertocat/followers",
"following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
"gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
"organizations_url": "https://api.github.com/users/Codertocat/orgs",
"repos_url": "https://api.github.com/users/Codertocat/repos",
"events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/Codertocat/received_events",
"type": "User",
"site_admin": false
},
"html_url": "https://github.com/Codertocat/Hello-World",
"description": null,
"fork": false,
"url": "https://api.github.com/repos/Codertocat/Hello-World",
"forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks",
"keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams",
"hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks",
"issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}",
"events_url": "https://api.github.com/repos/Codertocat/Hello-World/events",
"assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}",
"branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}",
"tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags",
"blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}",
"languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages",
"stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers",
"contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors",
"subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers",
"subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription",
"commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}",
"compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges",
"archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads",
"issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}",
"pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}",
"milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}",
"notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}",
"releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}",
"deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments",
"created_at": "2019-05-15T15:19:25Z",
"updated_at": "2019-05-15T15:19:27Z",
"pushed_at": "2019-05-15T15:20:32Z",
"git_url": "git://github.com/Codertocat/Hello-World.git",
"ssh_url": "git@github.com:Codertocat/Hello-World.git",
"clone_url": "https://github.com/Codertocat/Hello-World.git",
"svn_url": "https://github.com/Codertocat/Hello-World",
"homepage": null,
"size": 0,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": true,
"forks_count": 0,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 2,
"license": null,
"forks": 0,
"open_issues": 2,
"watchers": 0,
"default_branch": "master",
"allow_squash_merge": true,
"allow_merge_commit": true,
"allow_rebase_merge": true,
"delete_branch_on_merge": false
}
},
"base": {
"label": "Codertocat:master",
"ref": "master",
"sha": "f95f852bd8fca8fcc58a9a2d6c842781e32a215e",
"user": {
"login": "Codertocat",
"id": 21031067,
"node_id": "MDQ6VXNlcjIxMDMxMDY3",
"avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Codertocat",
"html_url": "https://github.com/Codertocat",
"followers_url": "https://api.github.com/users/Codertocat/followers",
"following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
"gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
"organizations_url": "https://api.github.com/users/Codertocat/orgs",
"repos_url": "https://api.github.com/users/Codertocat/repos",
"events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/Codertocat/received_events",
"type": "User",
"site_admin": false
},
"repo": {
"id": 186853002,
"node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=",
"name": "Hello-World",
"full_name": "Codertocat/Hello-World",
"private": false,
"owner": {
"login": "Codertocat",
"id": 21031067,
"node_id": "MDQ6VXNlcjIxMDMxMDY3",
"avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Codertocat",
"html_url": "https://github.com/Codertocat",
"followers_url": "https://api.github.com/users/Codertocat/followers",
"following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
"gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
"organizations_url": "https://api.github.com/users/Codertocat/orgs",
"repos_url": "https://api.github.com/users/Codertocat/repos",
"events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/Codertocat/received_events",
"type": "User",
"site_admin": false
},
"html_url": "https://github.com/Codertocat/Hello-World",
"description": null,
"fork": false,
"url": "https://api.github.com/repos/Codertocat/Hello-World",
"forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks",
"keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams",
"hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks",
"issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}",
"events_url": "https://api.github.com/repos/Codertocat/Hello-World/events",
"assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}",
"branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}",
"tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags",
"blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}",
"languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages",
"stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers",
"contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors",
"subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers",
"subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription",
"commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}",
"compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges",
"archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads",
"issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}",
"pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}",
"milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}",
"notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}",
"releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}",
"deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments",
"created_at": "2019-05-15T15:19:25Z",
"updated_at": "2019-05-15T15:19:27Z",
"pushed_at": "2019-05-15T15:20:32Z",
"git_url": "git://github.com/Codertocat/Hello-World.git",
"ssh_url": "git@github.com:Codertocat/Hello-World.git",
"clone_url": "https://github.com/Codertocat/Hello-World.git",
"svn_url": "https://github.com/Codertocat/Hello-World",
"homepage": null,
"size": 0,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": true,
"forks_count": 0,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 2,
"license": null,
"forks": 0,
"open_issues": 2,
"watchers": 0,
"default_branch": "master",
"allow_squash_merge": true,
"allow_merge_commit": true,
"allow_rebase_merge": true,
"delete_branch_on_merge": false
}
},
"_links": {
"self": {
"href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2"
},
"html": {
"href": "https://github.com/Codertocat/Hello-World/pull/2"
},
"issue": {
"href": "https://api.github.com/repos/Codertocat/Hello-World/issues/2"
},
"comments": {
"href": "https://api.github.com/repos/Codertocat/Hello-World/issues/2/comments"
},
"review_comments": {
"href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/comments"
},
"review_comment": {
"href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/comments{/number}"
},
"commits": {
"href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/commits"
},
"statuses": {
"href": "https://api.github.com/repos/Codertocat/Hello-World/statuses/ec26c3e57ca3a959ca5aad62de7213c562f8c821"
}
},
"author_association": "OWNER",
"draft": false,
"merged": false,
"mergeable": null,
"rebaseable": null,
"mergeable_state": "unknown",
"merged_by": null,
"comments": 0,
"review_comments": 0,
"maintainer_can_modify": false,
"commits": 1,
"additions": 1,
"deletions": 1,
"changed_files": 1
},
"repository": {
"id": 186853002,
"node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=",
"name": "Hello-World",
"full_name": "Codertocat/Hello-World",
"private": false,
"owner": {
"login": "Codertocat",
"id": 21031067,
"node_id": "MDQ6VXNlcjIxMDMxMDY3",
"avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Codertocat",
"html_url": "https://github.com/Codertocat",
"followers_url": "https://api.github.com/users/Codertocat/followers",
"following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
"gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
"organizations_url": "https://api.github.com/users/Codertocat/orgs",
"repos_url": "https://api.github.com/users/Codertocat/repos",
"events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/Codertocat/received_events",
"type": "User",
"site_admin": false
},
"html_url": "https://github.com/Codertocat/Hello-World",
"description": null,
"fork": false,
"url": "https://api.github.com/repos/Codertocat/Hello-World",
"forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks",
"keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams",
"hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks",
"issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}",
"events_url": "https://api.github.com/repos/Codertocat/Hello-World/events",
"assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}",
"branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}",
"tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags",
"blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}",
"languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages",
"stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers",
"contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors",
"subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers",
"subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription",
"commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}",
"compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges",
"archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads",
"issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}",
"pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}",
"milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}",
"notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}",
"releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}",
"deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments",
"created_at": "2019-05-15T15:19:25Z",
"updated_at": "2019-05-15T15:19:27Z",
"pushed_at": "2019-05-15T15:20:32Z",
"git_url": "git://github.com/Codertocat/Hello-World.git",
"ssh_url": "git@github.com:Codertocat/Hello-World.git",
"clone_url": "https://github.com/Codertocat/Hello-World.git",
"svn_url": "https://github.com/Codertocat/Hello-World",
"homepage": null,
"size": 0,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": true,
"forks_count": 0,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 2,
"license": null,
"forks": 0,
"open_issues": 2,
"watchers": 0,
"default_branch": "master"
},
"sender": {
"login": "Codertocat",
"id": 21031067,
"node_id": "MDQ6VXNlcjIxMDMxMDY3",
"avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Codertocat",
"html_url": "https://github.com/Codertocat",
"followers_url": "https://api.github.com/users/Codertocat/followers",
"following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
"gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
"organizations_url": "https://api.github.com/users/Codertocat/orgs",
"repos_url": "https://api.github.com/users/Codertocat/repos",
"events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/Codertocat/received_events",
"type": "User",
"site_admin": false
}
}

View File

@@ -412,12 +412,10 @@ func shouldRefreshPRGenerator(gen *v1alpha1.PullRequestGenerator, info *prGenera
}
if gen.Github != nil && info.Github != nil {
// repository owner and name are case-insensitive
// See https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests
if !strings.EqualFold(gen.Github.Owner, info.Github.Owner) {
if gen.Github.Owner != info.Github.Owner {
return false
}
if !strings.EqualFold(gen.Github.Repo, info.Github.Repo) {
if gen.Github.Repo != info.Github.Repo {
return false
}
api := gen.Github.API

View File

@@ -111,7 +111,7 @@ func TestWebhookHandler(t *testing.T) {
expectedRefresh: false,
},
{
desc: "WebHook from a GitHub repository via pull_request opened event",
desc: "WebHook from a GitHub repository via pull_reqeuest opened event",
headerKey: "X-GitHub-Event",
headerValue: "pull_request",
payloadFile: "github-pull-request-opened-event.json",
@@ -120,7 +120,7 @@ func TestWebhookHandler(t *testing.T) {
expectedRefresh: true,
},
{
desc: "WebHook from a GitHub repository via pull_request assigned event",
desc: "WebHook from a GitHub repository via pull_reqeuest assigned event",
headerKey: "X-GitHub-Event",
headerValue: "pull_request",
payloadFile: "github-pull-request-assigned-event.json",
@@ -128,15 +128,6 @@ func TestWebhookHandler(t *testing.T) {
expectedStatusCode: http.StatusOK,
expectedRefresh: false,
},
{
desc: "WebHook from a GitHub repository via pull_request labeled event",
headerKey: "X-GitHub-Event",
headerValue: "pull_request",
payloadFile: "github-pull-request-labeled-event.json",
effectedAppSets: []string{"pull-request-github", "matrix-pull-request-github", "matrix-scm-pull-request-github", "merge-pull-request-github", "plugin", "matrix-pull-request-github-plugin"},
expectedStatusCode: http.StatusOK,
expectedRefresh: true,
},
{
desc: "WebHook from a GitLab repository via open merge request event",
headerKey: "X-Gitlab-Event",
@@ -189,7 +180,7 @@ func TestWebhookHandler(t *testing.T) {
fakeAppWithGitGenerator("git-github", namespace, "https://github.com/org/repo"),
fakeAppWithGitGenerator("git-gitlab", namespace, "https://gitlab/group/name"),
fakeAppWithGitGenerator("git-azure-devops", namespace, "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git"),
fakeAppWithGithubPullRequestGenerator("pull-request-github", namespace, "CodErTOcat", "Hello-World"),
fakeAppWithGithubPullRequestGenerator("pull-request-github", namespace, "Codertocat", "Hello-World"),
fakeAppWithGitlabPullRequestGenerator("pull-request-gitlab", namespace, "100500"),
fakeAppWithAzureDevOpsPullRequestGenerator("pull-request-azure-devops", namespace, "DefaultCollection", "Fabrikam"),
fakeAppWithPluginGenerator("plugin", namespace),
@@ -198,7 +189,7 @@ func TestWebhookHandler(t *testing.T) {
fakeAppWithMatrixAndScmWithGitGenerator("matrix-scm-git-github", namespace, "org"),
fakeAppWithMatrixAndScmWithPullRequestGenerator("matrix-scm-pull-request-github", namespace, "Codertocat"),
fakeAppWithMatrixAndNestedGitGenerator("matrix-nested-git-github", namespace, "https://github.com/org/repo"),
fakeAppWithMatrixAndPullRequestGeneratorWithPluginGenerator("matrix-pull-request-github-plugin", namespace, "coDErtoCat", "HeLLO-WorLD", "plugin-cm"),
fakeAppWithMatrixAndPullRequestGeneratorWithPluginGenerator("matrix-pull-request-github-plugin", namespace, "Codertocat", "Hello-World", "plugin-cm"),
fakeAppWithMergeAndGitGenerator("merge-git-github", namespace, "https://github.com/org/repo"),
fakeAppWithMergeAndPullRequestGenerator("merge-pull-request-github", namespace, "Codertocat", "Hello-World"),
fakeAppWithMergeAndNestedGitGenerator("merge-nested-git-github", namespace, "https://github.com/org/repo"),

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -234,7 +234,7 @@
},
{
"type": "string",
"description": "forces application reconciliation if set to 'hard'.",
"description": "forces application reconciliation if set to true.",
"name": "refresh",
"in": "query"
},
@@ -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
@@ -573,7 +573,7 @@
},
{
"type": "string",
"description": "forces application reconciliation if set to 'hard'.",
"description": "forces application reconciliation if set to true.",
"name": "refresh",
"in": "query"
},
@@ -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
@@ -3872,7 +3816,7 @@
},
{
"type": "string",
"description": "forces application reconciliation if set to 'hard'.",
"description": "forces application reconciliation if set to true.",
"name": "refresh",
"in": "query"
},
@@ -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"
},
@@ -4531,9 +4462,6 @@
"clientID": {
"type": "string"
},
"enablePKCEAuthentication": {
"type": "boolean"
},
"idTokenClaims": {
"type": "object",
"additionalProperties": {
@@ -5403,8 +5331,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 +5382,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,11 +5491,15 @@
"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"
}
},
"clusterName": {
"description": "Deprecated: ClusterName is a legacy field that was always cleared by\nthe system and never used; it will be removed completely in 1.25.\n\nThe name in the go struct is changed to help clients detect\naccidental use.\n\n+optional",
"type": "string"
},
"creationTimestamp": {
"$ref": "#/definitions/v1Time"
},
@@ -5601,7 +5529,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 +5543,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 +5565,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 +5626,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 +5665,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"
@@ -6220,9 +6144,6 @@
},
"template": {
"$ref": "#/definitions/v1alpha1ApplicationSetTemplate"
},
"templatePatch": {
"type": "string"
}
}
},
@@ -6242,13 +6163,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"
}
}
}
},
@@ -6320,19 +6234,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",
@@ -6495,13 +6396,6 @@
"type": "string"
}
},
"components": {
"type": "array",
"title": "Components specifies a list of kustomize components to add to the kustomization before building",
"items": {
"type": "string"
}
},
"forceCommonAnnotations": {
"type": "boolean",
"title": "ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize apps"
@@ -6517,10 +6411,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"
@@ -8601,9 +8491,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"

View File

@@ -19,18 +19,20 @@ import (
"github.com/argoproj/argo-cd/v2/controller/sharding"
"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/ratelimiter"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/env"
"github.com/argoproj/argo-cd/v2/util/errors"
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/v2/util/tls"
"github.com/argoproj/argo-cd/v2/util/trace"
kubeerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
@@ -44,12 +46,9 @@ const (
func NewCommand() *cobra.Command {
var (
workqueueRateLimit ratelimiter.AppControllerRateLimiterConfig
clientConfig clientcmd.ClientConfig
appResyncPeriod int64
appHardResyncPeriod int64
appResyncJitter int64
repoErrorGracePeriod int64
repoServerAddress string
repoServerTimeoutSeconds int
selfHealTimeoutSeconds int
@@ -65,14 +64,11 @@ func NewCommand() *cobra.Command {
repoServerPlaintext bool
repoServerStrictTLS bool
otlpAddress string
otlpInsecure bool
otlpHeaders map[string]string
otlpAttrs []string
applicationNamespaces []string
persistResourceHealth bool
shardingAlgorithm string
enableDynamicClusterDistribution bool
serverSideDiff bool
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
)
var command = cobra.Command{
@@ -146,7 +142,7 @@ func NewCommand() *cobra.Command {
appController.InvalidateProjectsCache()
}))
kubectl := kubeutil.NewKubectl()
clusterSharding, err := sharding.GetClusterSharding(kubeClient, settingsMgr, shardingAlgorithm, enableDynamicClusterDistribution)
clusterFilter := getClusterFilter(kubeClient, settingsMgr, shardingAlgorithm, enableDynamicClusterDistribution)
errors.CheckError(err)
appController, err = controller.NewApplicationController(
namespace,
@@ -158,19 +154,14 @@ func NewCommand() *cobra.Command {
kubectl,
resyncDuration,
hardResyncDuration,
time.Duration(appResyncJitter)*time.Second,
time.Duration(selfHealTimeoutSeconds)*time.Second,
time.Duration(repoErrorGracePeriod)*time.Second,
metricsPort,
metricsCacheExpiration,
metricsAplicationLabels,
kubectlParallelismLimit,
persistResourceHealth,
clusterSharding,
clusterFilter,
applicationNamespaces,
&workqueueRateLimit,
serverSideDiff,
enableDynamicClusterDistribution,
ignoreNormalizerOpts,
)
errors.CheckError(err)
@@ -181,7 +172,7 @@ func NewCommand() *cobra.Command {
stats.RegisterHeapDumper("memprofile")
if otlpAddress != "" {
closeTracer, err := trace.InitTracer(ctx, "argocd-controller", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs)
closeTracer, err := trace.InitTracer(ctx, "argocd-controller", otlpAddress, otlpAttrs)
if err != nil {
log.Fatalf("failed to initialize tracing: %v", err)
}
@@ -198,8 +189,6 @@ func NewCommand() *cobra.Command {
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().Int64Var(&appResyncPeriod, "app-resync", int64(env.ParseDurationFromEnv("ARGOCD_RECONCILIATION_TIMEOUT", defaultAppResyncPeriod*time.Second, 0, math.MaxInt64).Seconds()), "Time period in seconds for application resync.")
command.Flags().Int64Var(&appHardResyncPeriod, "app-hard-resync", int64(env.ParseDurationFromEnv("ARGOCD_HARD_RECONCILIATION_TIMEOUT", defaultAppHardResyncPeriod*time.Second, 0, math.MaxInt64).Seconds()), "Time period in seconds for application hard resync.")
command.Flags().Int64Var(&appResyncJitter, "app-resync-jitter", int64(env.ParseDurationFromEnv("ARGOCD_RECONCILIATION_JITTER", 0*time.Second, 0, math.MaxInt64).Seconds()), "Maximum time period in seconds to add as a delay jitter for application resync.")
command.Flags().Int64Var(&repoErrorGracePeriod, "repo-error-grace-period-seconds", int64(env.ParseDurationFromEnv("ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS", defaultAppResyncPeriod*time.Second, 0, math.MaxInt64).Seconds()), "Grace period in seconds for ignoring consecutive errors while communicating with repo server.")
command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address.")
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.")
command.Flags().IntVar(&statusProcessors, "status-processors", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS", 20, 0, math.MaxInt32), "Number of application status processors")
@@ -215,28 +204,69 @@ func NewCommand() *cobra.Command {
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS", false), "Whether to use strict validation of the TLS cert presented by the repo server")
command.Flags().StringSliceVar(&metricsAplicationLabels, "metrics-application-labels", []string{}, "List of Application labels that will be added to the argocd_application_labels metric")
command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to")
command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode")
command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)")
command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)")
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that applications are allowed to be reconciled from")
command.Flags().BoolVar(&persistResourceHealth, "persist-resource-health", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH", true), "Enables storing the managed resources health in the Application CRD")
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", env.StringFromEnv(common.EnvControllerShardingAlgorithm, common.DefaultShardingAlgorithm), "Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] ")
// 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")
// individual item rate limit config
// when WORKQUEUE_FAILURE_COOLDOWN is 0 per item rate limiting is disabled(default)
command.Flags().DurationVar(&workqueueRateLimit.FailureCoolDown, "wq-cooldown-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_FAILURE_COOLDOWN_NS", 0, 0, (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Cooldown duration in ns, default 0(per item rate limiter disabled)")
command.Flags().DurationVar(&workqueueRateLimit.BaseDelay, "wq-basedelay-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_BASE_DELAY_NS", time.Millisecond.Nanoseconds(), time.Nanosecond.Nanoseconds(), (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Base Delay duration in nanoseconds, default 1000000 (1ms)")
command.Flags().DurationVar(&workqueueRateLimit.MaxDelay, "wq-maxdelay-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_MAX_DELAY_NS", time.Second.Nanoseconds(), 1*time.Millisecond.Nanoseconds(), (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Max Delay duration in nanoseconds, default 1000000000 (1s)")
command.Flags().Float64Var(&workqueueRateLimit.BackoffFactor, "wq-backoff-factor", env.ParseFloat64FromEnv("WORKQUEUE_BACKOFF_FACTOR", 1.5, 0, math.MaxFloat64), "Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5")
command.Flags().BoolVar(&enableDynamicClusterDistribution, "dynamic-cluster-distribution-enabled", env.ParseBoolFromEnv(common.EnvEnableDynamicClusterDistribution, false), "Enables dynamic cluster distribution.")
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
}
func getClusterFilter(kubeClient *kubernetes.Clientset, settingsMgr *settings.SettingsManager, shardingAlgorithm string, enableDynamicClusterDistribution bool) sharding.ClusterFilterFunction {
var replicas int
shard := env.ParseNumFromEnv(common.EnvControllerShard, -1, -math.MaxInt32, math.MaxInt32)
applicationControllerName := env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName)
appControllerDeployment, err := kubeClient.AppsV1().Deployments(settingsMgr.GetNamespace()).Get(context.Background(), applicationControllerName, metav1.GetOptions{})
// if the application controller deployment was not found, the Get() call returns an empty Deployment object. So, set the variable to nil explicitly
if err != nil && kubeerrors.IsNotFound(err) {
appControllerDeployment = nil
}
if enableDynamicClusterDistribution && appControllerDeployment != nil && appControllerDeployment.Spec.Replicas != nil {
replicas = int(*appControllerDeployment.Spec.Replicas)
} else {
replicas = env.ParseNumFromEnv(common.EnvControllerReplicas, 0, 0, math.MaxInt32)
}
var clusterFilter func(cluster *v1alpha1.Cluster) bool
if replicas > 1 {
// check for shard mapping using configmap if application-controller is a deployment
// else use existing logic to infer shard from pod name if application-controller is a statefulset
if enableDynamicClusterDistribution && appControllerDeployment != nil {
var err error
// retry 3 times if we find a conflict while updating shard mapping configMap.
// If we still see conflicts after the retries, wait for next iteration of heartbeat process.
for i := 0; i <= common.AppControllerHeartbeatUpdateRetryCount; i++ {
shard, err = sharding.GetOrUpdateShardFromConfigMap(kubeClient, settingsMgr, replicas, shard)
if !kubeerrors.IsConflict(err) {
err = fmt.Errorf("unable to get shard due to error updating the sharding config map: %s", err)
break
}
log.Warnf("conflict when getting shard from shard mapping configMap. Retrying (%d/3)", i)
}
errors.CheckError(err)
} else {
if shard < 0 {
var err error
shard, err = sharding.InferShard()
errors.CheckError(err)
}
}
log.Infof("Processing clusters from shard %d", shard)
db := db.NewDB(settingsMgr.GetNamespace(), settingsMgr, kubeClient)
log.Infof("Using filter function: %s", shardingAlgorithm)
distributionFunction := sharding.GetDistributionFunction(db, shardingAlgorithm)
clusterFilter = sharding.GetClusterFilter(db, distributionFunction, shard)
} else {
log.Info("Processing all cluster shards")
}
return clusterFilter
}

View File

@@ -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"
@@ -68,7 +65,6 @@ func NewCommand() *cobra.Command {
allowedScmProviders []string
globalPreservedAnnotations []string
globalPreservedLabels []string
enableScmProviders bool
)
scheme := runtime.NewScheme()
_ = clientgoscheme.AddToScheme(scheme)
@@ -111,33 +107,20 @@ func NewCommand() *cobra.Command {
// If the applicationset-namespaces contains only one namespace it corresponds to the current namespace
if len(applicationSetNamespaces) == 1 {
watchedNamespace = (applicationSetNamespaces)[0]
} else if enableScmProviders && len(allowedScmProviders) == 0 {
log.Error("When enabling applicationset in any namespace using applicationset-namespaces, you must either set --enable-scm-providers=false or specify --allowed-scm-providers")
} else if len(allowedScmProviders) == 0 {
log.Error("When enabling applicationset in any namespace using applicationset-namespaces, allowed-scm-providers is required")
os.Exit(1)
}
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 {
@@ -179,9 +162,9 @@ func NewCommand() *cobra.Command {
"List": generators.NewListGenerator(),
"Clusters": generators.NewClusterGenerator(mgr.GetClient(), ctx, k8sClient, namespace),
"Git": generators.NewGitGenerator(argoCDService),
"SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders),
"SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders),
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
"PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders),
"PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders),
"Plugin": generators.NewPluginGenerator(mgr.GetClient(), ctx, k8sClient, namespace),
}
@@ -264,8 +247,7 @@ func NewCommand() *cobra.Command {
command.Flags().BoolVar(&debugLog, "debug", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG", false), "Print debug logs. Takes precedence over loglevel")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
command.Flags().StringSliceVar(&allowedScmProviders, "allowed-scm-providers", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS", []string{}, ","), "The list of allowed custom SCM provider API URLs. This restriction does not apply to SCM or PR generators which do not accept a custom API URL. (Default: Empty = all)")
command.Flags().BoolVar(&enableScmProviders, "enable-scm-providers", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS", true), "Enable retrieving information from SCM providers, used by the SCM and PR generators (Default: true)")
command.Flags().StringSliceVar(&allowedScmProviders, "allowed-scm-providers", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS", []string{}, ","), "The list of allowed scm providers. (Default: Empty = all)")
command.Flags().BoolVar(&dryRun, "dry-run", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DRY_RUN", false), "Enable dry run mode")
command.Flags().BoolVar(&enableProgressiveSyncs, "enable-progressive-syncs", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS", false), "Enable use of the experimental progressive syncs feature.")
command.Flags().BoolVar(&enableNewGitFileGlobbing, "enable-new-git-file-globbing", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING", false), "Enable new globbing in Git files generator.")

View File

@@ -26,8 +26,6 @@ func NewCommand() *cobra.Command {
var (
configFilePath string
otlpAddress string
otlpInsecure bool
otlpHeaders map[string]string
otlpAttrs []string
)
var command = cobra.Command{
@@ -58,7 +56,7 @@ func NewCommand() *cobra.Command {
if otlpAddress != "" {
var closer func()
var err error
closer, err = traceutil.InitTracer(ctx, "argocd-cmp-server", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs)
closer, err = traceutil.InitTracer(ctx, "argocd-cmp-server", otlpAddress, otlpAttrs)
if err != nil {
log.Fatalf("failed to initialize tracing: %v", err)
}
@@ -85,8 +83,6 @@ func NewCommand() *cobra.Command {
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().StringVar(&configFilePath, "config-dir-path", common.DefaultPluginConfigFilePath, "Config management plugin configuration file location, Default is '/home/argocd/cmp-server/config/'")
command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_CMP_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to")
command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_CMP_SERVER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode")
command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_CMP_SERVER_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)")
command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_CMP_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)")
return &command
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -43,20 +43,19 @@ func addK8SFlagsToCmd(cmd *cobra.Command) clientcmd.ClientConfig {
func NewCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
processorsCount int
namespace string
appLabelSelector string
logLevel string
logFormat string
metricsPort int
argocdRepoServer string
argocdRepoServerPlaintext bool
argocdRepoServerStrictTLS bool
configMapName string
secretName string
applicationNamespaces []string
selfServiceNotificationEnabled bool
clientConfig clientcmd.ClientConfig
processorsCount int
namespace string
appLabelSelector string
logLevel string
logFormat string
metricsPort int
argocdRepoServer string
argocdRepoServerPlaintext bool
argocdRepoServerStrictTLS bool
configMapName string
secretName string
applicationNamespaces []string
)
var command = cobra.Command{
Use: "controller",
@@ -140,7 +139,7 @@ func NewCommand() *cobra.Command {
log.Infof("serving metrics on port %d", metricsPort)
log.Infof("loading configuration %d", metricsPort)
ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, applicationNamespaces, appLabelSelector, registry, secretName, configMapName, selfServiceNotificationEnabled)
ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, applicationNamespaces, appLabelSelector, registry, secretName, configMapName)
err = ctrl.Init(ctx)
if err != nil {
return fmt.Errorf("failed to initialize controller: %w", err)
@@ -164,6 +163,5 @@ func NewCommand() *cobra.Command {
command.Flags().StringVar(&configMapName, "config-map-name", "argocd-notifications-cm", "Set notifications ConfigMap name")
command.Flags().StringVar(&secretName, "secret-name", "argocd-notifications-secret", "Set notifications Secret name")
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that this controller should send notifications for")
command.Flags().BoolVar(&selfServiceNotificationEnabled, "self-service-notification-enabled", env.ParseBoolFromEnv("ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED", false), "Allows the Argo CD notification controller to pull notification config from the namespace that the resource is in. This is useful for self-service notification.")
return &command
}

View File

@@ -54,8 +54,6 @@ func NewCommand() *cobra.Command {
metricsPort int
metricsHost string
otlpAddress string
otlpInsecure bool
otlpHeaders map[string]string
otlpAttrs []string
cacheSrc func() (*reposervercache.Cache, error)
tlsConfigCustomizer tls.ConfigCustomizer
@@ -136,7 +134,7 @@ func NewCommand() *cobra.Command {
if otlpAddress != "" {
var closer func()
var err error
closer, err = traceutil.InitTracer(ctx, "argocd-repo-server", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs)
closer, err = traceutil.InitTracer(ctx, "argocd-repo-server", otlpAddress, otlpAttrs)
if err != nil {
log.Fatalf("failed to initialize tracing: %v", err)
}
@@ -203,8 +201,6 @@ func NewCommand() *cobra.Command {
command.Flags().StringVar(&metricsHost, "metrics-address", env.StringFromEnv("ARGOCD_REPO_SERVER_METRICS_LISTEN_ADDRESS", common.DefaultAddressRepoServerMetrics), "Listen on given address for metrics")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortRepoServerMetrics, "Start metrics server on given port")
command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_REPO_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to")
command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode")
command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_REPO_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)")
command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_REPO_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)")
command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_TLS", false), "Disable TLS on the gRPC endpoint")
command.Flags().StringVar(&maxCombinedDirectoryManifestsSize, "max-combined-directory-manifests-size", env.StringFromEnv("ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE", "10M"), "Max combined size of manifest files in a directory-type Application")
@@ -216,10 +212,8 @@ func NewCommand() *cobra.Command {
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")
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
}

View File

@@ -19,16 +19,13 @@ 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"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/kube"
"github.com/argoproj/argo-cd/v2/util/templates"
"github.com/argoproj/argo-cd/v2/util/tls"
traceutil "github.com/argoproj/argo-cd/v2/util/trace"
)
@@ -53,8 +50,6 @@ func NewCommand() *cobra.Command {
metricsHost string
metricsPort int
otlpAddress string
otlpInsecure bool
otlpHeaders map[string]string
otlpAttrs []string
glogLevel int
clientConfig clientcmd.ClientConfig
@@ -68,7 +63,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 +104,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 +188,6 @@ func NewCommand() *cobra.Command {
EnableGZip: enableGZip,
TLSConfigCustomizer: tlsConfigCustomizer,
Cache: cache,
RepoServerCache: repoServerCache,
XFrameOptions: frameOptions,
ContentSecurityPolicy: contentSecurityPolicy,
RedisClient: redisClient,
@@ -216,7 +207,7 @@ func NewCommand() *cobra.Command {
var closer func()
ctx, cancel := context.WithCancel(ctx)
if otlpAddress != "" {
closer, err = traceutil.InitTracer(ctx, "argocd-server", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs)
closer, err = traceutil.InitTracer(ctx, "argocd-server", otlpAddress, otlpAttrs)
if err != nil {
log.Fatalf("failed to initialize tracing: %v", err)
}
@@ -228,13 +219,6 @@ func NewCommand() *cobra.Command {
}
}
},
Example: templates.Examples(`
# Start the Argo CD API server with default settings
$ argocd-server
# Start the Argo CD API server on a custom port and enable tracing
$ argocd-server --port 8888 --otlp-address localhost:4317
`),
}
clientConfig = cli.AddKubectlFlagsToCmd(command)
@@ -256,8 +240,6 @@ func NewCommand() *cobra.Command {
command.Flags().StringVar(&metricsHost, env.StringFromEnv("ARGOCD_SERVER_METRICS_LISTEN_ADDRESS", "metrics-address"), common.DefaultAddressAPIServerMetrics, "Listen for metrics on given address")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port")
command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to")
command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_SERVER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode")
command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_SERVER_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)")
command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)")
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.")
command.Flags().StringVar(&frameOptions, "x-frame-options", env.StringFromEnv("ARGOCD_SERVER_X_FRAME_OPTIONS", "sameorigin"), "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".")
@@ -269,11 +251,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
}

View File

@@ -26,26 +26,12 @@ import (
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/localconfig"
sessionutil "github.com/argoproj/argo-cd/v2/util/session"
"github.com/argoproj/argo-cd/v2/util/templates"
)
func NewAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "account",
Short: "Manage account settings",
Example: templates.Examples(`
# List accounts
argocd account list
# Update the current user's password
argocd account update-password
# Can I sync any app?
argocd account can-i sync applications '*'
# Get User information
argocd account get-user-info
`),
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
@@ -157,13 +143,6 @@ func NewAccountGetUserInfoCommand(clientOpts *argocdclient.ClientOptions) *cobra
var command = &cobra.Command{
Use: "get-user-info",
Short: "Get user info",
Example: templates.Examples(`
# Get User information for the currently logged-in user (see 'argocd login')
argocd account get-user-info
# Get User information in yaml format
argocd account get-user-info -o yaml
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

@@ -48,12 +48,6 @@ 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
$ argocd admin dashboard
# Reset the initial admin password
$ argocd admin initial-password reset
`,
}
command.AddCommand(NewClusterCommand(clientOpts, pathOpts))
@@ -66,6 +60,7 @@ $ argocd admin initial-password reset
command.AddCommand(NewDashboardCommand(clientOpts))
command.AddCommand(NewNotificationsCommand())
command.AddCommand(NewInitialPasswordCommand())
command.AddCommand(NewRedisInitialPasswordCommand())
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")

View File

@@ -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"
@@ -47,16 +46,6 @@ func NewAppCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "app",
Short: "Manage applications configuration",
Example: `
# Compare results of two reconciliations and print diff
argocd admin app diff-reconcile-results APPNAME [flags]
# Generate declarative config for an application
argocd admin app generate-spec APPNAME
# Reconcile all applications and store reconciliation summary in the specified file
argocd admin app get-reconcile-results APPNAME
`,
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
@@ -245,7 +234,6 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
repoServerAddress string
outputFormat string
refresh bool
serverSideDiff bool
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
)
@@ -272,27 +260,19 @@ 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})
result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache, serverSideDiff, ignoreNormalizerOpts)
appClientset := appclientset.NewForConfigOrDie(cfg)
kubeClientset := kubernetes.NewForConfigOrDie(cfg)
result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache, ignoreNormalizerOpts)
errors.CheckError(err)
} else {
appClientset := appclientset.NewForConfigOrDie(cfg)
@@ -307,8 +287,8 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
command.Flags().StringVar(&selector, "l", "", "Label selector")
command.Flags().StringVar(&outputFormat, "o", "yaml", "Output format (yaml|json)")
command.Flags().BoolVar(&refresh, "refresh", false, "If set to true then recalculates apps reconciliation")
command.Flags().BoolVar(&serverSideDiff, "server-side-diff", false, "If set to \"true\" will use server-side diff while comparing resources. Default (\"false\")")
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
return command
}
@@ -357,7 +337,6 @@ func reconcileApplications(
repoServerClient reposerverclient.Clientset,
selector string,
createLiveStateCache func(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache,
serverSideDiff bool,
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts,
) ([]appReconcileResult, error) {
settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, namespace)
@@ -399,7 +378,7 @@ func reconcileApplications(
)
appStateManager := controller.NewAppStateManager(
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false, 0, serverSideDiff, ignoreNormalizerOpts)
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false, ignoreNormalizerOpts)
appsList, err := appClientset.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{LabelSelector: selector})
if err != nil {
@@ -434,10 +413,7 @@ func reconcileApplications(
sources = append(sources, app.Spec.GetSource())
revisions = append(revisions, app.Spec.GetSource().TargetRevision)
res, err := appStateManager.CompareAppState(&app, proj, revisions, sources, false, false, nil, false)
if err != nil {
return nil, err
}
res := appStateManager.CompareAppState(&app, proj, revisions, sources, false, false, nil, false)
items = append(items, appReconcileResult{
Name: app.Name,
Conditions: app.Status.Conditions,
@@ -449,5 +425,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())
}

View File

@@ -114,7 +114,6 @@ func TestGetReconcileResults_Refresh(t *testing.T) {
func(argoDB db.ArgoDB, appInformer cache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) statecache.LiveStateCache {
return &liveStateCache
},
false,
normalizers.IgnoreNormalizerOpts{},
)

View File

@@ -19,13 +19,13 @@ 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"
@@ -44,15 +44,6 @@ func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientc
var command = &cobra.Command{
Use: "cluster",
Short: "Manage clusters configuration",
Example: `
#Generate declarative config for a cluster
argocd admin cluster generate-spec my-cluster -o yaml
#Generate a kubeconfig for a cluster named "my-cluster" and display it in the console
argocd admin cluster kubeconfig my-cluster
#Print information namespaces which Argo CD manages in each cluster
argocd admin cluster namespaces my-cluster `,
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
@@ -71,14 +62,14 @@ 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
Namespaces []string
}
func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, shardingAlgorithm string, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int, redisName string, redisHaProxyName string, redisCompressionStr string) ([]ClusterWithInfo, error) {
func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int, redisName string, redisHaProxyName string) ([]ClusterWithInfo, error) {
settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace)
argoDB := db.NewDB(namespace, settingsMgr, kubeClient)
@@ -86,14 +77,6 @@ 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)
clusterShards := clusterShardingCache.GetDistribution()
var cache *appstatecache.Cache
if portForwardRedis {
overrides := clientcmd.ConfigOverrides{}
@@ -105,11 +88,7 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
return nil, err
}
client := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", port)})
compressionType, err := cacheutil.CompressionTypeFromString(redisCompressionStr)
if err != nil {
return nil, err
}
cache = appstatecache.NewCache(cacheutil.NewCache(cacheutil.NewRedisCache(client, time.Hour, compressionType)), time.Hour)
cache = appstatecache.NewCache(cacheutil.NewCache(cacheutil.NewRedisCache(client, time.Hour, cacheutil.RedisCompressionNone)), time.Hour)
} else {
cache, err = cacheSrc()
if err != nil {
@@ -117,6 +96,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)
@@ -126,7 +109,6 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
apps[i] = app
}
clusters := make([]ClusterWithInfo, len(clustersList.Items))
batchSize := 10
batchesCount := int(math.Ceil(float64(len(clusters)) / float64(batchSize)))
for batchNum := 0; batchNum < batchesCount; batchNum++ {
@@ -140,10 +122,12 @@ 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(argoDB, common.DefaultShardingAlgorithm)
distributionFunction(&cluster)
cluster.Shard = pointer.Int64Ptr(int64(clusterShard))
log.Infof("Cluster with uid: %s will be processed by shard %d", cluster.ID, clusterShard)
}
if shard != -1 && clusterShard != shard {
return nil
}
@@ -177,17 +161,15 @@ func getControllerReplicas(ctx context.Context, kubeClient *kubernetes.Clientset
func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
shard int
replicas int
shardingAlgorithm string
clientConfig clientcmd.ClientConfig
cacheSrc func() (*appstatecache.Cache, error)
portForwardRedis bool
redisCompressionStr string
shard int
replicas int
clientConfig clientcmd.ClientConfig
cacheSrc func() (*appstatecache.Cache, error)
portForwardRedis bool
)
var command = cobra.Command{
Use: "shards",
Short: "Print information about each controller shard and the estimated portion of Kubernetes resources it is responsible for.",
Short: "Print information about each controller shard and portion of Kubernetes resources it is responsible for.",
Run: func(cmd *cobra.Command, args []string) {
ctx := cmd.Context()
@@ -207,7 +189,8 @@ func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
if replicas == 0 {
return
}
clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, shardingAlgorithm, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName, redisCompressionStr)
clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName)
errors.CheckError(err)
if len(clusters) == 0 {
return
@@ -219,16 +202,8 @@ 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] ")
command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?")
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command)
// parse all added flags so far to get the redis-compression flag that was added by AddCacheFlagsToCmd() above
// we can ignore unchecked error here as the command will be parsed again and checked when command.Execute() is run later
// nolint:errcheck
command.ParseFlags(os.Args[1:])
redisCompressionStr, _ = command.Flags().GetString(cacheutil.CLIFlagRedisCompress)
return &command
}
@@ -464,26 +439,15 @@ func NewClusterDisableNamespacedMode() *cobra.Command {
func NewClusterStatsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
shard int
replicas int
shardingAlgorithm string
clientConfig clientcmd.ClientConfig
cacheSrc func() (*appstatecache.Cache, error)
portForwardRedis bool
redisCompressionStr string
shard int
replicas int
clientConfig clientcmd.ClientConfig
cacheSrc func() (*appstatecache.Cache, error)
portForwardRedis bool
)
var command = cobra.Command{
Use: "stats",
Short: "Prints information cluster statistics and inferred shard number",
Example: `
#Display stats and shards for clusters
argocd admin cluster stats
#Display Cluster Statistics for a Specific Shard
argocd admin cluster stats --shard=1
#In a multi-cluster environment to print stats for a specific cluster say(target-cluster)
argocd admin cluster stats target-cluster`,
Run: func(cmd *cobra.Command, args []string) {
ctx := cmd.Context()
@@ -500,7 +464,7 @@ argocd admin cluster stats target-cluster`,
replicas, err = getControllerReplicas(ctx, kubeClient, namespace, clientOpts.AppControllerName)
errors.CheckError(err)
}
clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, shardingAlgorithm, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName, redisCompressionStr)
clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName)
errors.CheckError(err)
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
@@ -514,15 +478,8 @@ 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] ")
command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?")
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command)
// parse all added flags so far to get the redis-compression flag that was added by AddCacheFlagsToCmd() above
// we can ignore unchecked error here as the command will be parsed again and checked when command.Execute() is run later
// nolint:errcheck
command.ParseFlags(os.Args[1:])
redisCompressionStr, _ = command.Flags().GetString(cacheutil.CLIFlagRedisCompress)
return &command
}
@@ -535,18 +492,6 @@ func NewClusterConfig() *cobra.Command {
Use: "kubeconfig CLUSTER_URL OUTPUT_PATH",
Short: "Generates kubeconfig for the specified cluster",
DisableAutoGenTag: true,
Example: `
#Generate a kubeconfig for a cluster named "my-cluster" on console
argocd admin cluster kubeconfig my-cluster
#Listing available kubeconfigs for clusters managed by argocd
argocd admin cluster kubeconfig
#Removing a specific kubeconfig file
argocd admin cluster kubeconfig my-cluster --delete
#Generate a Kubeconfig for a Cluster with TLS Verification Disabled
argocd admin cluster kubeconfig https://cluster-api-url:6443 /path/to/output/kubeconfig.yaml --insecure-skip-tls-verify`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -617,16 +562,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 +594,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.

View File

@@ -36,15 +36,6 @@ func NewDashboardCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
println(fmt.Sprintf("Argo CD UI is available at http://%s:%d", address, port))
<-ctx.Done()
},
Example: `# Start the Argo CD Web UI locally on the default port and address
$ argocd admin dashboard
# Start the Argo CD Web UI locally on a custom port and address
$ argocd admin dashboard --port 8080 --address 127.0.0.1
# Start the Argo CD Web UI with GZip compression
$ argocd admin dashboard --redis-compress gzip
`,
}
clientConfig = cli.AddKubectlFlagsToSet(cmd.Flags())
cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port")

View File

@@ -36,7 +36,7 @@ func NewNotificationsCommand() *cobra.Command {
"notifications",
"argocd admin notifications",
applications,
settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false), func(clientConfig clientcmd.ClientConfig) {
settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm"), func(clientConfig clientcmd.ClientConfig) {
k8sCfg, err := clientConfig.ClientConfig()
if err != nil {
log.Fatalf("Failed to parse k8s config: %v", err)

View File

@@ -14,7 +14,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/templates"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/spf13/cobra"
@@ -48,17 +47,6 @@ func NewGenProjectSpecCommand() *cobra.Command {
var command = &cobra.Command{
Use: "generate-spec PROJECT",
Short: "Generate declarative config for a project",
Example: templates.Examples(`
# Generate a YAML configuration for a project named "myproject"
argocd admin projects generate-spec myproject
# Generate a JSON configuration for a project named "anotherproject" and specify an output file
argocd admin projects generate-spec anotherproject --output json --file config.json
# Generate a YAML configuration for a project named "someproject" and write it back to the input file
argocd admin projects generate-spec someproject --inline
`),
Run: func(c *cobra.Command, args []string) {
proj, err := cmdutil.ConstructAppProj(fileURL, args, opts, c)
errors.CheckError(err)

View File

@@ -41,8 +41,6 @@ func NewProjectAllowListGenCommand() *cobra.Command {
var command = &cobra.Command{
Use: "generate-allow-list CLUSTERROLE_PATH PROJ_NAME",
Short: "Generates project allow list from the specified clusterRole file",
Example: `# Generates project allow list from the specified clusterRole file
argocd admin proj generate-allow-list /path/to/clusterrole.yaml my-project`,
Run: func(c *cobra.Command, args []string) {
if len(args) != 2 {
c.HelpFunc()(c, args)

View File

@@ -0,0 +1,98 @@
package admin
import (
"context"
"crypto/rand"
"fmt"
"math/big"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/cli"
apierr "k8s.io/apimachinery/pkg/api/errors"
"github.com/argoproj/argo-cd/v2/util/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
)
const defaulRedisInitialPasswordSecretName = "argocd-redis"
const defaultResisInitialPasswordKey = "auth"
func generateRandomPassword() (string, error) {
const initialPasswordLength = 16
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
randBytes := make([]byte, initialPasswordLength)
for i := 0; i < initialPasswordLength; i++ {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
if err != nil {
return "", err
}
randBytes[i] = letters[num.Int64()]
}
initialPassword := string(randBytes)
return initialPassword, nil
}
// NewRedisInitialPasswordCommand defines a new command to ensure Argo CD Redis password secret exists.
func NewRedisInitialPasswordCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
)
var command = cobra.Command{
Use: "redis-initial-password",
Short: "Ensure the Redis password exists, creating a new one if necessary.",
Run: func(c *cobra.Command, args []string) {
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
redisInitialPasswordSecretName := defaulRedisInitialPasswordSecretName
redisInitialPasswordKey := defaultResisInitialPasswordKey
fmt.Printf("Checking for initial Redis password in secret %s/%s at key %s. \n", namespace, redisInitialPasswordSecretName, redisInitialPasswordKey)
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
errors.CheckError(v1alpha1.SetK8SConfigDefaults(config))
kubeClientset := kubernetes.NewForConfigOrDie(config)
randomPassword, err := generateRandomPassword()
errors.CheckError(err)
data := map[string][]byte{
redisInitialPasswordKey: []byte(randomPassword),
}
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: redisInitialPasswordSecretName,
Namespace: namespace,
},
Data: data,
Type: corev1.SecretTypeOpaque,
}
_, err = kubeClientset.CoreV1().Secrets(namespace).Create(context.Background(), secret, metav1.CreateOptions{})
if err != nil && !apierr.IsAlreadyExists(err) {
errors.CheckError(err)
}
fmt.Println("Argo CD Redis secret state confirmed: secret name argocd-redis.")
secret, err = kubeClientset.CoreV1().Secrets(namespace).Get(context.Background(), redisInitialPasswordSecretName, v1.GetOptions{})
errors.CheckError(err)
if _, ok := secret.Data[redisInitialPasswordKey]; ok {
fmt.Println("Password secret is configured properly.")
} else {
err := fmt.Errorf("key %s doesn't exist in secret %s. \n", redisInitialPasswordKey, redisInitialPasswordSecretName)
errors.CheckError(err)
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
return &command
}

View File

@@ -373,7 +373,11 @@ func executeResourceOverrideCommand(ctx context.Context, cmdCtx commandContext,
if gvk.Group != "" {
key = fmt.Sprintf("%s/%s", gvk.Group, gvk.Kind)
}
override := overrides[key]
override, hasOverride := overrides[key]
if !hasOverride {
_, _ = fmt.Printf("No overrides configured for '%s/%s'\n", gvk.Group, gvk.Kind)
return
}
callback(res, override, overrides)
}
@@ -519,16 +523,16 @@ argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path .
executeResourceOverrideCommand(ctx, cmdCtx, args, func(res unstructured.Unstructured, override v1alpha1.ResourceOverride, overrides map[string]v1alpha1.ResourceOverride) {
gvk := res.GroupVersionKind()
resHealth, err := healthutil.GetResourceHealth(&res, lua.ResourceHealthOverrides(overrides))
if err != nil {
errors.CheckError(err)
} else if resHealth == nil {
fmt.Printf("Health script is not configured for '%s/%s'\n", gvk.Group, gvk.Kind)
} else {
_, _ = fmt.Printf("STATUS: %s\n", resHealth.Status)
_, _ = fmt.Printf("MESSAGE: %s\n", resHealth.Message)
if override.HealthLua == "" {
_, _ = fmt.Printf("Health script is not configured for '%s/%s'\n", gvk.Group, gvk.Kind)
return
}
resHealth, err := healthutil.GetResourceHealth(&res, lua.ResourceHealthOverrides(overrides))
errors.CheckError(err)
_, _ = fmt.Printf("STATUS: %s\n", resHealth.Status)
_, _ = fmt.Printf("MESSAGE: %s\n", resHealth.Message)
})
},
}

View File

@@ -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'
@@ -221,6 +189,7 @@ argocd admin settings rbac can someuser create application 'default/app' --defau
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(command)
command.Flags().StringVar(&policyFile, "policy-file", "", "path to the policy file to use")
command.Flags().StringVar(&defaultRole, "default-role", "", "name of the default role to use")
@@ -233,55 +202,24 @@ argocd admin settings rbac can someuser create application 'default/app' --defau
// NewRBACValidateCommand returns a new rbac validate command
func NewRBACValidateCommand() *cobra.Command {
var (
policyFile string
namespace string
clientConfig clientcmd.ClientConfig
policyFile string
)
var command = &cobra.Command{
Use: "validate [--policy-file POLICYFILE] [--namespace NAMESPACE]",
Use: "validate --policy-file=POLICYFILE",
Short: "Validate RBAC policy",
Long: `
Validates an RBAC policy for being syntactically correct. The policy must be
a local file or a K8s ConfigMap in the provided namespace, and in either CSV or K8s ConfigMap format.
`,
Example: `
# Check whether a given policy file is valid using a local policy.csv file.
argocd admin settings rbac validate --policy-file policy.csv
# Policy file can also be K8s config map with data keys like argocd-rbac-cm,
# i.e. 'policy.csv' and (optionally) 'policy.default'
argocd admin settings rbac validate --policy-file argocd-rbac-cm.yaml
# If --policy-file is not given, and instead --namespace is giventhe ConfigMap 'argocd-rbac-cm'
# from K8s is used.
argocd admin settings rbac validate --namespace argocd
# Either --policy-file or --namespace must be given.
a local file, and in either CSV or K8s ConfigMap format.
`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
if len(args) > 0 {
if policyFile == "" {
c.HelpFunc()(c, args)
log.Fatalf("too many arguments")
log.Fatalf("Please specify policy to validate using --policy-file")
}
if (namespace == "" && policyFile == "") || (namespace != "" && policyFile != "") {
c.HelpFunc()(c, args)
log.Fatalf("please provide exactly one of --policy-file or --namespace")
}
restConfig, err := clientConfig.ClientConfig()
if err != nil {
log.Fatalf("could not get config to create k8s client: %v", err)
}
realClientset, err := kubernetes.NewForConfig(restConfig)
if err != nil {
log.Fatalf("could not create k8s client: %v", err)
}
userPolicy, _, _ := getPolicy(ctx, policyFile, realClientset, namespace)
userPolicy, _, _ := getPolicy(ctx, policyFile, nil, "")
if userPolicy != "" {
if err := rbac.ValidatePolicy(userPolicy); err == nil {
fmt.Printf("Policy is valid.\n")
@@ -290,15 +228,11 @@ argocd admin settings rbac validate --namespace argocd
fmt.Printf("Policy is invalid: %v\n", err)
os.Exit(1)
}
} else {
log.Fatalf("Policy is empty or could not be loaded.")
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(command)
command.Flags().StringVar(&policyFile, "policy-file", "", "path to the policy file to use")
command.Flags().StringVar(&namespace, "namespace", "", "namespace to get argo rbac configmap from")
command.Flags().StringVar(&policyFile, "policy-file", "", "path to the policy file to use")
return command
}
@@ -408,9 +342,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 +372,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
}

View File

@@ -5,112 +5,44 @@ 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"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"github.com/argoproj/argo-cd/v2/util/assets"
)
type FakeClientConfig struct {
clientConfig clientcmd.ClientConfig
}
func NewFakeClientConfig(clientConfig clientcmd.ClientConfig) *FakeClientConfig {
return &FakeClientConfig{clientConfig: clientConfig}
}
func (f *FakeClientConfig) RawConfig() (clientcmdapi.Config, error) {
config, err := f.clientConfig.RawConfig()
return config, err
}
func (f *FakeClientConfig) ClientConfig() (*restclient.Config, error) {
return f.clientConfig.ClientConfig()
}
func (f *FakeClientConfig) Namespace() (string, bool, error) {
return f.clientConfig.Namespace()
}
func (f *FakeClientConfig) ConfigAccess() clientcmd.ConfigAccess {
return nil
}
func Test_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) {
@@ -268,19 +200,3 @@ p, role:, certificates, get, .*, allow`
require.True(t, ok)
})
}
func TestNewRBACCanCommand(t *testing.T) {
command := NewRBACCanCommand()
require.NotNil(t, command)
assert.Equal(t, "can", command.Name())
assert.Equal(t, "Check RBAC permissions for a role or subject", command.Short)
}
func TestNewRBACValidateCommand(t *testing.T) {
command := NewRBACValidateCommand()
require.NotNil(t, command)
assert.Equal(t, "validate", command.Name())
assert.Equal(t, "Validate RBAC policy", command.Short)
}

View File

@@ -226,18 +226,6 @@ spec:
replicas: 0`
)
const (
testCustomResourceYAML = `apiVersion: v1
apiVersion: example.com/v1alpha1
kind: ExampleResource
metadata:
name: example-resource
labels:
app: example
spec:
replicas: 0`
)
const (
testCronJobYAML = `apiVersion: batch/v1
kind: CronJob
@@ -297,7 +285,7 @@ func TestResourceOverrideIgnoreDifferences(t *testing.T) {
assert.NoError(t, err)
})
assert.NoError(t, err)
assert.Contains(t, out, "Ignore differences are not configured for 'apps/Deployment'\n")
assert.Contains(t, out, "No overrides configured")
})
t.Run("DataIgnored", func(t *testing.T) {
@@ -317,7 +305,7 @@ func TestResourceOverrideIgnoreDifferences(t *testing.T) {
}
func TestResourceOverrideHealth(t *testing.T) {
f, closer, err := tempFile(testCustomResourceYAML)
f, closer, err := tempFile(testDeploymentYAML)
if !assert.NoError(t, err) {
return
}
@@ -325,34 +313,19 @@ func TestResourceOverrideHealth(t *testing.T) {
t.Run("NoHealthAssessment", func(t *testing.T) {
cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{
"resource.customizations": `example.com/ExampleResource: {}`}))
"resource.customizations": `apps/Deployment: {}`}))
out, err := captureStdout(func() {
cmd.SetArgs([]string{"health", f})
err := cmd.Execute()
assert.NoError(t, err)
})
assert.NoError(t, err)
assert.Contains(t, out, "Health script is not configured for 'example.com/ExampleResource'\n")
assert.Contains(t, out, "Health script is not configured")
})
t.Run("HealthAssessmentConfigured", func(t *testing.T) {
cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{
"resource.customizations": `example.com/ExampleResource:
health.lua: |
return { status = "Progressing" }
`}))
out, err := captureStdout(func() {
cmd.SetArgs([]string{"health", f})
err := cmd.Execute()
assert.NoError(t, err)
})
assert.NoError(t, err)
assert.Contains(t, out, "Progressing")
})
t.Run("HealthAssessmentConfiguredWildcard", func(t *testing.T) {
cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{
"resource.customizations": `example.com/*:
"resource.customizations": `apps/Deployment:
health.lua: |
return { status = "Progressing" }
`}))
@@ -439,7 +412,7 @@ resume false
action.lua: |
job1 = {}
job1.apiVersion = "batch/v1"
job1.kind = "Job"
job1.kind = "Job"
job1.metadata = {}
job1.metadata.name = "hello-1"
job1.metadata.namespace = "obj.metadata.namespace"

File diff suppressed because it is too large Load Diff

View File

@@ -8,13 +8,11 @@ import (
"strconv"
"text/tabwriter"
"github.com/argoproj/argo-cd/v2/util/templates"
"github.com/argoproj/argo-cd/v2/cmd/util"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/utils/ptr"
"k8s.io/utils/pointer"
"sigs.k8s.io/yaml"
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
@@ -35,22 +33,11 @@ type DisplayedAction struct {
Disabled bool
}
var (
appActionExample = templates.Examples(`
# List all the available actions for an application
argocd app actions list APPNAME
# Run an available action for an application
argocd app actions run APPNAME ACTION --kind KIND [--resource-name RESOURCE] [--namespace NAMESPACE] [--group GROUP]
`)
)
// NewApplicationResourceActionsCommand returns a new instance of an `argocd app actions` command
func NewApplicationResourceActionsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "actions",
Short: "Manage Resource actions",
Example: appActionExample,
Use: "actions",
Short: "Manage Resource actions",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
@@ -71,10 +58,6 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt
var command = &cobra.Command{
Use: "list APPNAME",
Short: "Lists available actions on a resource",
Example: templates.Examples(`
# List all the available actions for an application
argocd app actions list APPNAME
`),
}
command.Run = func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -97,11 +80,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 {
@@ -153,10 +136,6 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti
var command = &cobra.Command{
Use: "run APPNAME ACTION",
Short: "Runs an available action on resource(s)",
Example: templates.Examples(`
# Run an available action for an application
argocd app actions run APPNAME ACTION --kind KIND [--resource-name RESOURCE] [--namespace NAMESPACE] [--group GROUP]
`),
}
command.Flags().StringVar(&resourceName, "resource-name", "", "Name of resource")
@@ -196,12 +175,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)
}

View File

@@ -3,7 +3,6 @@ package commands
import (
"fmt"
"os"
"text/tabwriter"
"github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
@@ -11,7 +10,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"
@@ -19,6 +18,8 @@ import (
"github.com/argoproj/argo-cd/v2/util/argo"
"github.com/argoproj/argo-cd/v2/util/errors"
argoio "github.com/argoproj/argo-cd/v2/util/io"
"text/tabwriter"
)
func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
@@ -29,7 +30,6 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions)
var kind string
var group string
var all bool
var project string
command := &cobra.Command{
Use: "patch-resource APPNAME",
Short: "Patch resource in an application",
@@ -46,7 +46,6 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions)
command.Flags().StringVar(&group, "group", "", "Group")
command.Flags().StringVar(&namespace, "namespace", "", "Namespace")
command.Flags().BoolVar(&all, "all", false, "Indicates whether to patch multiple matching of resources")
command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`)
command.Run = func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -71,14 +70,13 @@ 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),
})
errors.CheckError(err)
log.Infof("Resource '%s' patched", obj.GetName())
@@ -96,7 +94,6 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions)
var force bool
var orphan bool
var all bool
var project string
command := &cobra.Command{
Use: "delete-resource APPNAME",
Short: "Delete resource in an application",
@@ -108,10 +105,9 @@ 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) {
ctx := c.Context()
@@ -136,14 +132,13 @@ 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),
})
errors.CheckError(err)
log.Infof("Resource '%s' deleted", obj.GetName())
@@ -255,7 +250,6 @@ func printResources(listAll bool, orphaned bool, appResourceTree *v1alpha1.Appli
func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var orphaned bool
var output string
var project string
var command = &cobra.Command{
Use: "resources APPNAME",
Short: "List resource of application",
@@ -272,7 +266,6 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions)
appResourceTree, err := appIf.ResourceTree(ctx, &applicationpkg.ResourcesQuery{
ApplicationName: &appName,
AppNamespace: &appNs,
Project: &project,
})
errors.CheckError(err)
printResources(listAll, orphaned, appResourceTree, output)
@@ -280,6 +273,5 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions)
}
command.Flags().BoolVar(&orphaned, "orphaned", false, "Lists only orphaned resources")
command.Flags().StringVar(&output, "output", "", "Provides the tree view of the resources")
command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`)
return command
}

View File

@@ -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) {
@@ -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{
@@ -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
}

View File

@@ -67,10 +67,6 @@ func NewApplicationSetGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.
var command = &cobra.Command{
Use: "get APPSETNAME",
Short: "Get ApplicationSet details",
Example: templates.Examples(`
# Get ApplicationSets
argocd appset get APPSETNAME
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -350,11 +346,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 (

View File

@@ -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
`,
},

View File

@@ -15,9 +15,7 @@ func NewBcryptCmd() *cobra.Command {
)
var bcryptCmd = &cobra.Command{
Use: "bcrypt",
Short: "Generate bcrypt hash for any password",
Example: `# Generate bcrypt hash for any password
argocd account bcrypt --password YOUR_PASSWORD`,
Short: "Generate bcrypt hash for the admin password",
Run: func(cmd *cobra.Command, args []string) {
bytePassword := []byte(password)
// Hashing the password

View File

@@ -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{
@@ -486,23 +485,6 @@ func NewClusterListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
}
},
Example: `
# List Clusters in Default "Wide" Format
argocd cluster list
# List Cluster via specifing the server
argocd cluster list --server <ARGOCD_SERVER_ADDRESS>
# List Clusters in JSON Format
argocd cluster list -o json --server <ARGOCD_SERVER_ADDRESS>
# List Clusters in YAML Format
argocd cluster list -o yaml --server <ARGOCD_SERVER_ADDRESS>
# List Clusters that have been added to your Argo CD
argocd cluster list -o server <ARGOCD_SERVER_ADDRESS>
`,
}
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|server")
return command

View File

@@ -211,13 +211,6 @@ compdef _argocd argocd
Optionally, also add the following, in case you are getting errors involving compdef & compinit such as command not found: compdef:
autoload -Uz compinit
compinit
`,
Example: `# For bash
$ source <(argocd completion bash)
# For zsh
$ argocd completion zsh > _argocd
$ source _argocd
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {

View File

@@ -22,14 +22,6 @@ func NewContextCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
Use: "context [CONTEXT]",
Aliases: []string{"ctx"},
Short: "Switch between contexts",
Example: `# List Argo CD Contexts
argocd context
# Switch Argo CD context
argocd context cd.argoproj.io
# Delete Argo CD context
argocd context cd.argoproj.io --delete`,
Run: func(c *cobra.Command, args []string) {
localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath)

View File

@@ -14,7 +14,6 @@ import (
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/errors"
argoio "github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/templates"
)
// NewGPGCommand returns a new instance of an `argocd repo` command
@@ -43,17 +42,6 @@ func NewGPGListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "list",
Short: "List configured GPG public keys",
Example: templates.Examples(`
# List all configured GPG public keys in wide format (default).
argocd gpg list
# List all configured GPG public keys in JSON format.
argocd gpg list -o json
# List all configured GPG public keys in YAML format.
argocd gpg list -o yaml
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -84,17 +72,6 @@ func NewGPGGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "get KEYID",
Short: "Get the GPG public key with ID <KEYID> from the server",
Example: templates.Examples(`
# Get a GPG public key with the specified KEYID in wide format (default).
argocd gpg get KEYID
# Get a GPG public key with the specified KEYID in JSON format.
argocd gpg get KEYID -o json
# Get a GPG public key with the specified KEYID in YAML format.
argocd gpg get KEYID -o yaml
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -132,11 +109,6 @@ func NewGPGAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "add",
Short: "Adds a GPG public key to the server's keyring",
Example: templates.Examples(`
# Add a GPG public key to the server's keyring from a file.
argocd gpg add --from /path/to/keyfile
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

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

View File

@@ -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
@@ -107,7 +106,6 @@ argocd login cd.argoproj.io --core`,
PortForwardNamespace: globalClientOpts.PortForwardNamespace,
Headers: globalClientOpts.Headers,
KubeOverrides: globalClientOpts.KubeOverrides,
ServerName: globalClientOpts.ServerName,
}
if ctxName == "" {
@@ -307,7 +305,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 +315,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...)

View File

@@ -18,10 +18,6 @@ func NewLogoutCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comma
Use: "logout CONTEXT",
Short: "Log out from Argo CD",
Long: "Log out from Argo CD",
Example: `# To log out of argocd
$ argocd logout
# This can be helpful for security reasons or when you want to switch between different Argo CD contexts or accounts.
`,
Run: func(c *cobra.Command, args []string) {
if len(args) == 0 {
c.HelpFunc()(c, args)

View File

@@ -26,7 +26,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/gpg"
argoio "github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/templates"
)
type policyOpts struct {
@@ -40,19 +39,6 @@ func NewProjectCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "proj",
Short: "Manage projects",
Example: templates.Examples(`
# List all available projects
argocd proj list
# Create a new project with name PROJECT
argocd proj create PROJECT
# Delete the project with name PROJECT
argocd proj delete PROJECT
# Edit the information on project with name PROJECT
argocd proj edit PROJECT
`),
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
@@ -78,8 +64,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
}
@@ -104,13 +88,6 @@ func NewProjectCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
var command = &cobra.Command{
Use: "create PROJECT",
Short: "Create a project",
Example: templates.Examples(`
# Create a new project with name PROJECT
argocd proj create PROJECT
# Create a new project with name PROJECT from a file or URL to a Kubernetes manifest
argocd proj create PROJECT -f FILE|URL
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -141,13 +118,6 @@ func NewProjectSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
var command = &cobra.Command{
Use: "set PROJECT",
Short: "Set project parameters",
Example: templates.Examples(`
# Set project parameters with some allowed cluster resources [RES1,RES2,...] for project with name PROJECT
argocd proj set PROJECT --allow-cluster-resource [RES1,RES2,...]
# Set project parameters with some denied namespaced resources [RES1,RES2,...] for project with name PROJECT
argocd proj set PROJECT ---deny-namespaced-resource [RES1,RES2,...]
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -181,10 +151,6 @@ func NewProjectAddSignatureKeyCommand(clientOpts *argocdclient.ClientOptions) *c
var command = &cobra.Command{
Use: "add-signature-key PROJECT KEY-ID",
Short: "Add GnuPG signature key to project",
Example: templates.Examples(`
# Add GnuPG signature key KEY-ID to project PROJECT
argocd proj add-signature-key PROJECT KEY-ID
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -223,10 +189,6 @@ func NewProjectRemoveSignatureKeyCommand(clientOpts *argocdclient.ClientOptions)
var command = &cobra.Command{
Use: "remove-signature-key PROJECT KEY-ID",
Short: "Remove GnuPG signature key from project",
Example: templates.Examples(`
# Remove GnuPG signature key KEY-ID from project PROJECT
argocd proj remove-signature-key PROJECT KEY-ID
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -277,13 +239,6 @@ func NewProjectAddDestinationCommand(clientOpts *argocdclient.ClientOptions) *co
var command = &cobra.Command{
Use: "add-destination PROJECT SERVER/NAME NAMESPACE",
Short: "Add project destination",
Example: templates.Examples(`
# Add project destination using a server URL (SERVER) in the specified namespace (NAMESPACE) on the project with name PROJECT
argocd proj add-destination PROJECT SERVER NAMESPACE
# Add project destination using a server name (NAME) in the specified namespace (NAMESPACE) on the project with name PROJECT
argocd proj add-destination PROJECT NAME NAMESPACE --name
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -321,10 +276,6 @@ func NewProjectRemoveDestinationCommand(clientOpts *argocdclient.ClientOptions)
var command = &cobra.Command{
Use: "remove-destination PROJECT SERVER NAMESPACE",
Short: "Remove project destination",
Example: templates.Examples(`
# Remove the destination (SERVER) from the specified namespace (NAMESPACE) on the project with name PROJECT
argocd proj remove-destination PROJECT SERVER NAMESPACE
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -369,13 +320,6 @@ func NewProjectAddOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOptions)
var command = &cobra.Command{
Use: "add-orphaned-ignore PROJECT GROUP KIND",
Short: "Add a resource to orphaned ignore list",
Example: templates.Examples(`
# Add a resource of the specified GROUP and KIND to orphaned ignore list on the project with name PROJECT
argocd proj add-orphaned-ignore PROJECT GROUP KIND
# Add resources of the specified GROUP and KIND using a NAME pattern to orphaned ignore list on the project with name PROJECT
argocd proj add-orphaned-ignore PROJECT GROUP KIND --name NAME
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -419,15 +363,8 @@ func NewProjectRemoveOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOption
name string
)
var command = &cobra.Command{
Use: "remove-orphaned-ignore PROJECT GROUP KIND",
Use: "remove-orphaned-ignore PROJECT GROUP KIND NAME",
Short: "Remove a resource from orphaned ignore list",
Example: templates.Examples(`
# Remove a resource of the specified GROUP and KIND from orphaned ignore list on the project with name PROJECT
argocd proj remove-orphaned-ignore PROJECT GROUP KIND
# Remove resources of the specified GROUP and KIND using a NAME pattern from orphaned ignore list on the project with name PROJECT
argocd proj remove-orphaned-ignore PROJECT GROUP KIND --name NAME
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -474,10 +411,6 @@ func NewProjectAddSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
var command = &cobra.Command{
Use: "add-source PROJECT URL",
Short: "Add project source repository",
Example: templates.Examples(`
# Add a source repository (URL) to the project with name PROJECT
argocd proj add-source PROJECT URL
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -511,88 +444,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 {
@@ -622,7 +473,7 @@ func modifyResourcesList(list *[]metav1.GroupKind, add bool, listDesc string, gr
}
}
func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdclient.ClientOptions, allow bool, namespacedList bool) *cobra.Command {
func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.ClientOptions, allow bool, namespacedList bool) *cobra.Command {
var (
listType string
defaultList string
@@ -633,9 +484,8 @@ func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdc
defaultList = "allow"
}
var command = &cobra.Command{
Use: cmdUse,
Short: cmdDesc,
Example: templates.Examples(examples),
Use: cmdUse,
Short: cmdDesc,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -684,44 +534,28 @@ func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdc
func NewProjectAllowNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "allow-namespace-resource PROJECT GROUP KIND"
desc := "Removes a namespaced API resource from the deny list or add a namespaced API resource to the allow list"
examples := `
# Removes a namespaced API resource with specified GROUP and KIND from the deny list or add a namespaced API resource to the allow list for project PROJECT
argocd proj allow-namespace-resource PROJECT GROUP KIND
`
return modifyResourceListCmd(use, desc, examples, clientOpts, true, true)
return modifyResourceListCmd(use, desc, clientOpts, true, true)
}
// NewProjectDenyNamespaceResourceCommand returns a new instance of an `argocd proj deny-namespace-resource` command
func NewProjectDenyNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "deny-namespace-resource PROJECT GROUP KIND"
desc := "Adds a namespaced API resource to the deny list or removes a namespaced API resource from the allow list"
examples := `
# Adds a namespaced API resource with specified GROUP and KIND from the deny list or removes a namespaced API resource from the allow list for project PROJECT
argocd proj deny-namespace-resource PROJECT GROUP KIND
`
return modifyResourceListCmd(use, desc, examples, clientOpts, false, true)
return modifyResourceListCmd(use, desc, clientOpts, false, true)
}
// NewProjectDenyClusterResourceCommand returns a new instance of an `deny-cluster-resource` command
func NewProjectDenyClusterResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "deny-cluster-resource PROJECT GROUP KIND"
desc := "Removes a cluster-scoped API resource from the allow list and adds it to deny list"
examples := `
# Removes a cluster-scoped API resource with specified GROUP and KIND from the allow list and adds it to deny list for project PROJECT
argocd proj deny-cluster-resource PROJECT GROUP KIND
`
return modifyResourceListCmd(use, desc, examples, clientOpts, false, false)
return modifyResourceListCmd(use, desc, clientOpts, false, false)
}
// NewProjectAllowClusterResourceCommand returns a new instance of an `argocd proj allow-cluster-resource` command
func NewProjectAllowClusterResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "allow-cluster-resource PROJECT GROUP KIND"
desc := "Adds a cluster-scoped API resource to the allow list and removes it from deny list"
examples := `
# Adds a cluster-scoped API resource with specified GROUP and KIND to the allow list and removes it from deny list for project PROJECT
argocd proj allow-cluster-resource PROJECT GROUP KIND
`
return modifyResourceListCmd(use, desc, examples, clientOpts, true, false)
return modifyResourceListCmd(use, desc, clientOpts, true, false)
}
// NewProjectRemoveSourceCommand returns a new instance of an `argocd proj remove-src` command
@@ -729,10 +563,6 @@ func NewProjectRemoveSourceCommand(clientOpts *argocdclient.ClientOptions) *cobr
var command = &cobra.Command{
Use: "remove-source PROJECT URL",
Short: "Remove project source repository",
Example: templates.Examples(`
# Remove URL source repository to project PROJECT
argocd proj remove-source PROJECT URL
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -773,10 +603,6 @@ func NewProjectDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
var command = &cobra.Command{
Use: "delete PROJECT",
Short: "Delete project",
Example: templates.Examples(`
# Delete the project with name PROJECT
argocd proj delete PROJECT
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -820,13 +646,6 @@ func NewProjectListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
var command = &cobra.Command{
Use: "list",
Short: "List projects",
Example: templates.Examples(`
# List all available projects
argocd proj list
# List all available projects in yaml format
argocd proj list -o yaml
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -992,14 +811,6 @@ func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
var command = &cobra.Command{
Use: "get PROJECT",
Short: "Get project details",
Example: templates.Examples(`
# Get details from project PROJECT
argocd proj get PROJECT
# Get details from project PROJECT in yaml format
argocd proj get PROJECT -o yaml
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -1037,10 +848,6 @@ func NewProjectEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
var command = &cobra.Command{
Use: "edit PROJECT",
Short: "Edit project",
Example: templates.Examples(`
# Edit the information on project with name PROJECT
argocd proj edit PROJECT
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

@@ -18,7 +18,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/jwt"
"github.com/argoproj/argo-cd/v2/util/templates"
)
const (
@@ -57,30 +56,6 @@ func NewProjectRoleAddPolicyCommand(clientOpts *argocdclient.ClientOptions) *cob
var command = &cobra.Command{
Use: "add-policy PROJECT ROLE-NAME",
Short: "Add a policy to a project role",
Example: `# Before adding new policy
$ argocd proj role get test-project test-role
Role Name: test-role
Description:
Policies:
p, proj:test-project:test-role, projects, get, test-project, allow
JWT Tokens:
ID ISSUED-AT EXPIRES-AT
1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) <none>
# Add a new policy to allow update to the project
$ argocd proj role add-policy test-project test-role -a update -p allow -o project
# Policy should be updated
$ argocd proj role get test-project test-role
Role Name: test-role
Description:
Policies:
p, proj:test-project:test-role, projects, get, test-project, allow
p, proj:test-project:test-role, applications, update, test-project/project, allow
JWT Tokens:
ID ISSUED-AT EXPIRES-AT
1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) <none>
`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -118,30 +93,6 @@ func NewProjectRoleRemovePolicyCommand(clientOpts *argocdclient.ClientOptions) *
var command = &cobra.Command{
Use: "remove-policy PROJECT ROLE-NAME",
Short: "Remove a policy from a role within a project",
Example: `List the policy of the test-role before removing a policy
$ argocd proj role get test-project test-role
Role Name: test-role
Description:
Policies:
p, proj:test-project:test-role, projects, get, test-project, allow
p, proj:test-project:test-role, applications, update, test-project/project, allow
JWT Tokens:
ID ISSUED-AT EXPIRES-AT
1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) <none>
# Remove the policy to allow update to objects
$ argocd proj role remove-policy test-project test-role -a update -p allow -o project
# The role should be removed now.
$ argocd proj role get test-project test-role
Role Name: test-role
Description:
Policies:
p, proj:test-project:test-role, projects, get, test-project, allow
JWT Tokens:
ID ISSUED-AT EXPIRES-AT
1696759698 2023-10-08T11:08:18+01:00 (4 hours ago) <none>
`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -189,11 +140,6 @@ func NewProjectRoleCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
var command = &cobra.Command{
Use: "create PROJECT ROLE-NAME",
Short: "Create a project role",
Example: templates.Examples(`
# Create a project role in the "my-project" project with the name "my-role".
argocd proj role create my-project my-role --description "My project role description"
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -228,9 +174,8 @@ func NewProjectRoleCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
// NewProjectRoleDeleteCommand returns a new instance of an `argocd proj role delete` command
func NewProjectRoleDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "delete PROJECT ROLE-NAME",
Short: "Delete a project role",
Example: `$ argocd proj role delete test-project test-role`,
Use: "delete PROJECT ROLE-NAME",
Short: "Delete a project role",
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -278,15 +223,8 @@ func NewProjectRoleCreateTokenCommand(clientOpts *argocdclient.ClientOptions) *c
tokenID string
)
var command = &cobra.Command{
Use: "create-token PROJECT ROLE-NAME",
Short: "Create a project token",
Example: `$ argocd proj role create-token test-project test-role
Create token succeeded for proj:test-project:test-role.
ID: f316c466-40bd-4cfd-8a8c-1392e92255d4
Issued At: 2023-10-08T15:21:40+01:00
Expires At: Never
Token: xxx
`,
Use: "create-token PROJECT ROLE-NAME",
Short: "Create a project token",
Aliases: []string{"token-create"},
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -350,13 +288,8 @@ func NewProjectRoleListTokensCommand(clientOpts *argocdclient.ClientOptions) *co
useUnixTime bool
)
var command = &cobra.Command{
Use: "list-tokens PROJECT ROLE-NAME",
Short: "List tokens for a given role.",
Example: `$ argocd proj role list-tokens test-project test-role
ID ISSUED AT EXPIRES AT
f316c466-40bd-4cfd-8a8c-1392e92255d4 2023-10-08T15:21:40+01:00 Never
fa9d3517-c52d-434c-9bff-215b38508842 2023-10-08T11:08:18+01:00 Never
`,
Use: "list-tokens PROJECT ROLE-NAME",
Short: "List tokens for a given role.",
Aliases: []string{"list-token", "token-list"},
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -406,35 +339,8 @@ fa9d3517-c52d-434c-9bff-215b38508842 2023-10-08T11:08:18+01:00 Never
// NewProjectRoleDeleteTokenCommand returns a new instance of an `argocd proj role delete-token` command
func NewProjectRoleDeleteTokenCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "delete-token PROJECT ROLE-NAME ISSUED-AT",
Short: "Delete a project token",
Example: `#Create project test-project
$ argocd proj create test-project
# Create a role associated with test-project
$ argocd proj role create test-project test-role
Role 'test-role' created
# Create test-role associated with test-project
$ argocd proj role create-token test-project test-role
Create token succeeded for proj:test-project:test-role.
ID: c312450e-12e1-4e0d-9f65-fac9cb027b32
Issued At: 2023-10-08T13:58:57+01:00
Expires At: Never
Token: xxx
# Get test-role id to input into the delete-token command below
$ argocd proj role get test-project test-role
Role Name: test-role
Description:
Policies:
p, proj:test-project:test-role, projects, get, test-project, allow
JWT Tokens:
ID ISSUED-AT EXPIRES-AT
1696769937 2023-10-08T13:58:57+01:00 (6 minutes ago) <none>
$ argocd proj role delete-token test-project test-role 1696769937
`,
Use: "delete-token PROJECT ROLE-NAME ISSUED-AT",
Short: "Delete a project token",
Aliases: []string{"token-delete", "remove-token"},
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -483,15 +389,6 @@ func NewProjectRoleListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
var command = &cobra.Command{
Use: "list PROJECT",
Short: "List all the roles in a project",
Example: templates.Examples(`
# This command will list all the roles in argocd-project in a default table format.
argocd proj role list PROJECT
# List the roles in the project in formats like json, yaml, wide, or name.
argocd proj role list PROJECT --output json
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -527,16 +424,6 @@ func NewProjectRoleGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
var command = &cobra.Command{
Use: "get PROJECT ROLE-NAME",
Short: "Get the details of a specific role",
Example: `$ argocd proj role get test-project test-role
Role Name: test-role
Description:
Policies:
p, proj:test-project:test-role, projects, get, test-project, allow
JWT Tokens:
ID ISSUED-AT EXPIRES-AT
1696774900 2023-10-08T15:21:40+01:00 (4 minutes ago) <none>
1696759698 2023-10-08T11:08:18+01:00 (4 hours ago) <none>
`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

@@ -22,18 +22,6 @@ func NewProjectWindowsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
roleCommand := &cobra.Command{
Use: "windows",
Short: "Manage a project's sync windows",
Example: `
#Add a sync window to a project
argocd proj windows add my-project \
--schedule "0 0 * * 1-5" \
--duration 3600 \
--prune
#Delete a sync window from a project
argocd proj windows delete <project-name> <window-id>
#List project sync windows
argocd proj windows list <project-name>`,
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
@@ -54,12 +42,6 @@ func NewProjectWindowsDisableManualSyncCommand(clientOpts *argocdclient.ClientOp
Use: "disable-manual-sync PROJECT ID",
Short: "Disable manual sync for a sync window",
Long: "Disable manual sync for a sync window. Requires ID which can be found by running \"argocd proj windows list PROJECT\"",
Example: `
#Disable manual sync for a sync window for the Project
argocd proj windows disable-manual-sync PROJECT ID
#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()
@@ -97,15 +79,6 @@ func NewProjectWindowsEnableManualSyncCommand(clientOpts *argocdclient.ClientOpt
Use: "enable-manual-sync PROJECT ID",
Short: "Enable manual sync for a sync window",
Long: "Enable manual sync for a sync window. Requires ID which can be found by running \"argocd proj windows list PROJECT\"",
Example: `
#Enabling manual sync for a general case
argocd proj windows enable-manual-sync PROJECT ID
#Enabling manual sync for a windows set on the default project with Id 2
argocd proj windows enable-manual-sync default 2
#Enabling manual sync with a custom message
argocd proj windows enable-manual-sync my-app-project --message "Manual sync initiated by admin`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -152,24 +125,6 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) *
var command = &cobra.Command{
Use: "add PROJECT",
Short: "Add a sync window to a project",
Example: `
#Add a 1 hour allow sync window
argocd proj windows add PROJECT \
--kind allow \
--schedule "0 22 * * *" \
--duration 1h \
--applications "*"
#Add a deny sync window with the ability to manually sync.
argocd proj windows add PROJECT \
--kind deny \
--schedule "30 10 * * *" \
--duration 30m \
--applications "prod-\\*,website" \
--namespaces "default,\\*-prod" \
--clusters "prod,staging" \
--manual-sync
`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -203,17 +158,11 @@ argocd proj windows add PROJECT \
return command
}
// NewProjectWindowsDeleteCommand returns a new instance of an `argocd proj windows delete` command
// NewProjectWindowsAddWindowCommand returns a new instance of an `argocd proj windows delete` command
func NewProjectWindowsDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "delete PROJECT ID",
Short: "Delete a sync window from a project. Requires ID which can be found by running \"argocd proj windows list PROJECT\"",
Example: `
#Delete a sync window from a project (default) with ID 0
argocd proj windows delete default 0
#Delete a sync window from a project (new-project) with ID 1
argocd proj windows delete new-project 1`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -256,10 +205,6 @@ func NewProjectWindowsUpdateCommand(clientOpts *argocdclient.ClientOptions) *cob
Use: "update PROJECT ID",
Short: "Update a project sync window",
Long: "Update a project sync window. Requires ID which can be found by running \"argocd proj windows list PROJECT\"",
Example: `# Change a sync window's schedule
argocd proj windows update PROJECT ID \
--schedule "0 20 * * *"
`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -308,15 +253,6 @@ func NewProjectWindowsListCommand(clientOpts *argocdclient.ClientOptions) *cobra
var command = &cobra.Command{
Use: "list PROJECT",
Short: "List project sync windows",
Example: `
#List project windows
argocd proj windows list PROJECT
#List project windows in yaml format
argocd proj windows list PROJECT -o yaml
#List project windows info for a project name (test-project)
argocd proj windows list test-project`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -349,8 +285,8 @@ argocd proj windows list test-project`,
func printSyncWindows(proj *v1alpha1.AppProject) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
var fmtStr string
headers := []interface{}{"ID", "STATUS", "KIND", "SCHEDULE", "DURATION", "APPLICATIONS", "NAMESPACES", "CLUSTERS", "MANUALSYNC", "TIMEZONE"}
fmtStr = strings.Repeat("%s\t", len(headers)) + "\n"
headers := []interface{}{"ID", "STATUS", "KIND", "SCHEDULE", "DURATION", "APPLICATIONS", "NAMESPACES", "CLUSTERS", "MANUALSYNC"}
fmtStr = "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
fmt.Fprintf(w, fmtStr, headers...)
if proj.Spec.SyncWindows.HasWindows() {
for i, window := range proj.Spec.SyncWindows {
@@ -364,7 +300,6 @@ func printSyncWindows(proj *v1alpha1.AppProject) {
formatListOutput(window.Namespaces),
formatListOutput(window.Clusters),
formatManualOutput(window.ManualSync),
window.TimeZone,
}
fmt.Fprintf(w, fmtStr, vals...)
}

View File

@@ -84,18 +84,6 @@ func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comm
errors.CheckError(err)
fmt.Printf("Context '%s' updated\n", localCfg.CurrentContext)
},
Example: `
# Reinitiates the login with previous contexts
argocd relogin
# Reinitiates the login with password
argocd relogin --password YOUR_PASSWORD
# Configure direct access using Kubernetes API server
argocd login cd.argoproj.io --core
# If user logged in with - "argocd login cd.argoproj.io" with sso login
# The command - "argocd relogin" will Reinitiates SSO login and updates the server context`,
}
command.Flags().StringVar(&password, "password", "", "The password of an account to authenticate")
command.Flags().IntVar(&ssoPort, "sso-port", DefaultSSOLocalPort, "Port to run local OAuth2 login application")

View File

@@ -29,19 +29,6 @@ func NewRepoCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
c.HelpFunc()(c, args)
os.Exit(1)
},
Example: `
# Add git repository connection parameters
argocd repo add git@git.example.com:repos/repo
# Get a Configured Repository by URL
argocd repo get https://github.com/yourusername/your-repo.git
# List Configured Repositories
argocd repo list
# Remove Repository Credentials
argocd repo rm https://github.com/yourusername/your-repo.git
`,
}
command.AddCommand(NewRepoAddCommand(clientOpts))
@@ -64,12 +51,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

View File

@@ -17,7 +17,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/templates"
)
// NewRepoCredsCommand returns a new instance of an `argocd repocreds` command
@@ -25,16 +24,6 @@ func NewRepoCredsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
var command = &cobra.Command{
Use: "repocreds",
Short: "Manage repository connection parameters",
Example: templates.Examples(`
# Add credentials with user/pass authentication to use for all repositories under the specified URL
argocd repocreds add URL --username USERNAME --password PASSWORD
# List all the configured repository credentials
argocd repocreds list
# Remove credentials for the repositories with speficied URL
argocd repocreds rm URL
`),
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
@@ -195,10 +184,6 @@ func NewRepoCredsRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
var command = &cobra.Command{
Use: "rm CREDSURL",
Short: "Remove repository credentials",
Example: templates.Examples(`
# Remove credentials for the repositories with URL https://git.example.com/repos
argocd repocreds rm https://git.example.com/repos/
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -246,19 +231,6 @@ func NewRepoCredsListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
var command = &cobra.Command{
Use: "list",
Short: "List configured repository credentials",
Example: templates.Examples(`
# List all repo urls
argocd repocreds list
# List all repo urls in json format
argocd repocreds list -o json
# List all repo urls in yaml format
argocd repocreds list -o yaml
# List all repo urls in url format
argocd repocreds list -o url
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

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

View File

@@ -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.Int64Ptr(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)

View File

@@ -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{}

View File

@@ -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")

View File

@@ -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.BoolPtr(opts.orphanedResourcesWarn)
}
return &settings
}

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