mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-05 07:58:46 +01:00
Compare commits
195 Commits
release-2.
...
vendor-res
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2abaa0f7dd | ||
|
|
9a5bc59816 | ||
|
|
4f16c15854 | ||
|
|
d059048921 | ||
|
|
67ae39e99d | ||
|
|
981a1f1c9f | ||
|
|
95b38f1db4 | ||
|
|
362abff610 | ||
|
|
012ffd584a | ||
|
|
50ed4bcc02 | ||
|
|
a14aa81169 | ||
|
|
18435c998e | ||
|
|
b8685bb243 | ||
|
|
c30b7775ce | ||
|
|
f6f183d1d9 | ||
|
|
47c17932ac | ||
|
|
7c880c6006 | ||
|
|
b9712fc908 | ||
|
|
83463c67be | ||
|
|
3db9bff3da | ||
|
|
752bc5f80d | ||
|
|
ba8931b67d | ||
|
|
9b6992af61 | ||
|
|
a0d7b5c2ba | ||
|
|
e9598a3677 | ||
|
|
e824f5e210 | ||
|
|
ab51bc3cde | ||
|
|
1eaaa56a45 | ||
|
|
6509d0b92f | ||
|
|
a9da5d83d6 | ||
|
|
15b0785ba4 | ||
|
|
437cd4f1b5 | ||
|
|
3f5c2eb60c | ||
|
|
13026e28ac | ||
|
|
967999df79 | ||
|
|
a259654e32 | ||
|
|
bd6bb09320 | ||
|
|
eaf6e9e0a0 | ||
|
|
9960f54166 | ||
|
|
dc959e900f | ||
|
|
ea3215bc0a | ||
|
|
7d24fe7e08 | ||
|
|
17e9ef409a | ||
|
|
f73e7014ce | ||
|
|
097ed6f108 | ||
|
|
812664cbb1 | ||
|
|
299af2172f | ||
|
|
c9dfd41308 | ||
|
|
aa02e79401 | ||
|
|
3b36f34d8f | ||
|
|
0ea88b7d72 | ||
|
|
0c242fe535 | ||
|
|
a72b262b5e | ||
|
|
2173b87b99 | ||
|
|
8f00d347b5 | ||
|
|
c1b0c7ba4c | ||
|
|
e1c21480e4 | ||
|
|
9bd0ed0d56 | ||
|
|
d2da477791 | ||
|
|
0240958c15 | ||
|
|
4328bfc1ff | ||
|
|
817161a33b | ||
|
|
87a0d02b3a | ||
|
|
16c6e36ee4 | ||
|
|
8375840344 | ||
|
|
41b1ebef83 | ||
|
|
4cf807e67a | ||
|
|
6344d9623e | ||
|
|
1a8dd249c0 | ||
|
|
b0dab38c7d | ||
|
|
1f0d4cfccc | ||
|
|
1ae5aff2f8 | ||
|
|
fe151a1c0e | ||
|
|
149926dec4 | ||
|
|
d5a961c7f6 | ||
|
|
428bf48734 | ||
|
|
4d6d20427e | ||
|
|
9bada88f0f | ||
|
|
234fc9d1cd | ||
|
|
79f4b5ceb6 | ||
|
|
1ae0317423 | ||
|
|
cfb24701d2 | ||
|
|
e21a82fcc1 | ||
|
|
ac69e27fd2 | ||
|
|
46ae472cee | ||
|
|
08124045dc | ||
|
|
777302191f | ||
|
|
ebf2682214 | ||
|
|
a773b1effb | ||
|
|
7101965bd6 | ||
|
|
c8cae4a293 | ||
|
|
3047db9709 | ||
|
|
327936d164 | ||
|
|
9fa3c4fa94 | ||
|
|
297eb7168a | ||
|
|
21731c9c2d | ||
|
|
ec649b6d86 | ||
|
|
f60ae50f38 | ||
|
|
4aaed135b8 | ||
|
|
9353328eb8 | ||
|
|
a765c141ca | ||
|
|
776d8f97cc | ||
|
|
1494f9bbea | ||
|
|
45fc484447 | ||
|
|
309654cece | ||
|
|
3d3e947242 | ||
|
|
ba682e58d7 | ||
|
|
f059d8bb33 | ||
|
|
63c42d8dda | ||
|
|
cfdd85cc64 | ||
|
|
6a7d79f8e0 | ||
|
|
92abb563af | ||
|
|
8fd917693b | ||
|
|
d207de224e | ||
|
|
8a71b8af94 | ||
|
|
07d1bffb73 | ||
|
|
5d9d53cc15 | ||
|
|
663245468c | ||
|
|
9639795f89 | ||
|
|
9c40fcb0e3 | ||
|
|
40126b5afa | ||
|
|
d0216905d1 | ||
|
|
a7e8d970ac | ||
|
|
952b98960d | ||
|
|
7f2d513426 | ||
|
|
56fd4596e5 | ||
|
|
1a7ec6dee0 | ||
|
|
793f7289d5 | ||
|
|
a0d4b11a55 | ||
|
|
36c1a15658 | ||
|
|
4e8cfde1a0 | ||
|
|
f9ef32c756 | ||
|
|
06fa661360 | ||
|
|
193cb146bc | ||
|
|
a35a94e5fc | ||
|
|
7cb9d40836 | ||
|
|
a74af1e343 | ||
|
|
0dbf570933 | ||
|
|
75f5985a58 | ||
|
|
d11ac8954e | ||
|
|
254f3b69ae | ||
|
|
eb9d0a52aa | ||
|
|
bff4860563 | ||
|
|
cd171bf66b | ||
|
|
b1eff1a7eb | ||
|
|
6e9e16e80f | ||
|
|
f9a7ea8f27 | ||
|
|
ba10959313 | ||
|
|
c99669e088 | ||
|
|
b73363e015 | ||
|
|
05dce81d09 | ||
|
|
dc69539820 | ||
|
|
7183c9fcb3 | ||
|
|
625e5d2daf | ||
|
|
00937c98e5 | ||
|
|
b5d8d5a02b | ||
|
|
4583ccadc1 | ||
|
|
3f164cf576 | ||
|
|
16613fe96c | ||
|
|
2aaf997b1f | ||
|
|
cac0de1b16 | ||
|
|
00a1ce6bb2 | ||
|
|
23b6bfad55 | ||
|
|
44c882c686 | ||
|
|
896ed12446 | ||
|
|
2833be88f0 | ||
|
|
eba374c618 | ||
|
|
51d34dd951 | ||
|
|
cbfcb19cc7 | ||
|
|
612c415d97 | ||
|
|
e32090f0d4 | ||
|
|
441b583daf | ||
|
|
c68cafa6af | ||
|
|
b00d9b73c3 | ||
|
|
6df4a802f2 | ||
|
|
9480c0f65b | ||
|
|
a73c45fb96 | ||
|
|
8e38b4f2a3 | ||
|
|
43a9457ede | ||
|
|
0a02a63c54 | ||
|
|
90760a5088 | ||
|
|
8e3b915d0e | ||
|
|
ee47700c4d | ||
|
|
06b3a3613d | ||
|
|
2fc7c7da73 | ||
|
|
0303715a33 | ||
|
|
553371b73f | ||
|
|
45744d20be | ||
|
|
22ba8f519a | ||
|
|
44a0d73ecd | ||
|
|
d8cb04e9d7 | ||
|
|
812bbad09b | ||
|
|
53a210eef8 | ||
|
|
9849a17aa9 | ||
|
|
214b980940 |
18
.github/dependabot.yml
vendored
Normal file
18
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
ignore:
|
||||
- dependency-name: k8s.io/*
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/ui/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
70
.github/workflows/ci-build.yaml
vendored
70
.github/workflows/ci-build.yaml
vendored
@@ -18,15 +18,18 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-go:
|
||||
name: Ensure Go modules synchronicity
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Download all Go modules
|
||||
@@ -42,13 +45,13 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -59,17 +62,20 @@ jobs:
|
||||
run: make build-local
|
||||
|
||||
lint-go:
|
||||
permissions:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
|
||||
name: Lint Go code
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
uses: golangci/golangci-lint-action@0ad9a0988b3973e851ab0a07adf248ec2e100376 # v3.3.1
|
||||
with:
|
||||
version: v1.46.2
|
||||
args: --timeout 10m --exclude SA5011 --verbose
|
||||
@@ -86,11 +92,11 @@ jobs:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -110,7 +116,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -127,12 +133,12 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-local
|
||||
- name: Generate code coverage artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
|
||||
with:
|
||||
name: code-coverage
|
||||
path: coverage.out
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results/
|
||||
@@ -149,11 +155,11 @@ jobs:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -173,7 +179,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -190,7 +196,7 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-race-local
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
|
||||
with:
|
||||
name: race-results
|
||||
path: test-results/
|
||||
@@ -200,9 +206,9 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Create symlink in GOPATH
|
||||
@@ -244,14 +250,14 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 # v3.5.1
|
||||
with:
|
||||
node-version: '12.18.4'
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -281,12 +287,12 @@ jobs:
|
||||
sonar_secret: ${{ secrets.SONAR_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -297,16 +303,16 @@ jobs:
|
||||
run: |
|
||||
mkdir -p test-results
|
||||
- name: Get code coverage artifiact
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # v3.0.1
|
||||
with:
|
||||
name: code-coverage
|
||||
- name: Get test result artifact
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # v3.0.1
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results
|
||||
- name: Upload code coverage information to codecov.io
|
||||
uses: codecov/codecov-action@v1
|
||||
uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
|
||||
with:
|
||||
file: coverage.out
|
||||
- name: Perform static code analysis using SonarCloud
|
||||
@@ -360,9 +366,9 @@ jobs:
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: GH actions workaround - Kill XSP4 process
|
||||
@@ -380,7 +386,7 @@ jobs:
|
||||
sudo chown runner $HOME/.kube/config
|
||||
kubectl version
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -406,7 +412,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.35.1-distroless
|
||||
docker pull ghcr.io/dexidp/dex:v2.35.3
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:7.0.5-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
@@ -436,7 +442,7 @@ jobs:
|
||||
set -x
|
||||
make test-e2e-local
|
||||
- name: Upload e2e-server logs
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
|
||||
with:
|
||||
name: e2e-server-k8s${{ matrix.k3s-version }}.log
|
||||
path: /tmp/e2e-server.log
|
||||
|
||||
15
.github/workflows/codeql.yml
vendored
15
.github/workflows/codeql.yml
vendored
@@ -13,8 +13,15 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
permissions:
|
||||
actions: read # for github/codeql-action/init to get workflow details
|
||||
contents: read # for actions/checkout to fetch code
|
||||
security-events: write # for github/codeql-action/autobuild to send a status report
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
|
||||
# CodeQL runs on ubuntu-latest and windows-latest
|
||||
@@ -22,11 +29,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
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
|
||||
@@ -34,7 +41,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@v2
|
||||
uses: github/codeql-action/autobuild@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -48,4 +55,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33
|
||||
|
||||
44
.github/workflows/image.yaml
vendored
44
.github/workflows/image.yaml
vendored
@@ -16,39 +16,45 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
permissions:
|
||||
contents: write # for git to push upgrade commit if not already deployed
|
||||
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
GOPATH: /home/runner/work/argo-cd/argo-cd
|
||||
steps:
|
||||
- uses: actions/setup-go@v1
|
||||
- uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
with:
|
||||
path: src/github.com/argoproj/argo-cd
|
||||
|
||||
# get image tag
|
||||
- run: echo ::set-output name=tag::$(cat ./VERSION)-${GITHUB_SHA::8}
|
||||
- run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
|
||||
working-directory: ./src/github.com/argoproj/argo-cd
|
||||
id: image
|
||||
|
||||
# login
|
||||
- run: |
|
||||
docker login ghcr.io --username $USERNAME --password $PASSWORD
|
||||
docker login quay.io --username "${DOCKER_USERNAME}" --password "${DOCKER_TOKEN}"
|
||||
docker login ghcr.io --username $USERNAME --password-stdin <<< "$PASSWORD"
|
||||
docker login quay.io --username "$DOCKER_USERNAME" --password-stdin <<< "$DOCKER_TOKEN"
|
||||
if: github.event_name == 'push'
|
||||
env:
|
||||
USERNAME: ${{ secrets.USERNAME }}
|
||||
PASSWORD: ${{ secrets.TOKEN }}
|
||||
USERNAME: ${{ github.actor }}
|
||||
PASSWORD: ${{ secrets.GITHUB_TOKEN }}
|
||||
DOCKER_USERNAME: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
||||
DOCKER_TOKEN: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
||||
|
||||
# build
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
- uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
|
||||
- uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # v2.2.1
|
||||
- run: |
|
||||
IMAGE_PLATFORMS=linux/amd64
|
||||
if [[ "${{ github.event_name }}" == "push" || "${{ contains(github.event.pull_request.labels.*.name, 'test-arm-image') }}" == "true" ]]
|
||||
@@ -57,17 +63,33 @@ jobs:
|
||||
fi
|
||||
echo "Building image for platforms: $IMAGE_PLATFORMS"
|
||||
docker buildx build --platform $IMAGE_PLATFORMS --push="${{ github.event_name == 'push' }}" \
|
||||
-t ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }} \
|
||||
-t ghcr.io/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }} \
|
||||
-t quay.io/argoproj/argocd:latest .
|
||||
working-directory: ./src/github.com/argoproj/argo-cd
|
||||
|
||||
# sign container images
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1
|
||||
with:
|
||||
cosign-release: 'v1.13.0'
|
||||
|
||||
- name: Sign Argo CD latest image
|
||||
run: |
|
||||
cosign sign --key env://COSIGN_PRIVATE_KEY quay.io/argoproj/argocd:latest
|
||||
# Displays the public key to share.
|
||||
cosign public-key --key env://COSIGN_PRIVATE_KEY
|
||||
env:
|
||||
COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}}
|
||||
COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
|
||||
# deploy
|
||||
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
|
||||
if: github.event_name == 'push'
|
||||
env:
|
||||
TOKEN: ${{ secrets.TOKEN }}
|
||||
- run: |
|
||||
docker run -u $(id -u):$(id -g) -v $(pwd):/src -w /src --rm -t ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }} kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }}
|
||||
docker run -u $(id -u):$(id -g) -v $(pwd):/src -w /src --rm -t ghcr.io/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }} kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }}
|
||||
git config --global user.email 'ci@argoproj.com'
|
||||
git config --global user.name 'CI'
|
||||
git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ steps.image.outputs.tag }}' && git push)
|
||||
|
||||
52
.github/workflows/release.yaml
vendored
52
.github/workflows/release.yaml
vendored
@@ -14,8 +14,13 @@ on:
|
||||
env:
|
||||
GOLANG_VERSION: '1.18'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prepare-release:
|
||||
permissions:
|
||||
contents: write # To push changes to release branch
|
||||
name: Perform automatic release on trigger ${{ github.ref }}
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -38,7 +43,7 @@ jobs:
|
||||
GIT_EMAIL: argoproj@gmail.com
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -142,7 +147,7 @@ jobs:
|
||||
echo "RELEASE_NOTES=${RELEASE_NOTES}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # v3.3.1
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
@@ -190,13 +195,13 @@ jobs:
|
||||
QUAY_TOKEN: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
||||
run: |
|
||||
set -ue
|
||||
docker login quay.io --username "${QUAY_USERNAME}" --password "${QUAY_TOKEN}"
|
||||
docker login quay.io --username "${QUAY_USERNAME}" --password-stdin <<< "${QUAY_TOKEN}"
|
||||
# Remove the following when Docker Hub is gone
|
||||
docker login --username "${DOCKER_USERNAME}" --password "${DOCKER_TOKEN}"
|
||||
docker login --username "${DOCKER_USERNAME}" --password-stdin <<< "${DOCKER_TOKEN}"
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
- uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
|
||||
- uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # v2.2.1
|
||||
- name: Build and push Docker image for release
|
||||
run: |
|
||||
set -ue
|
||||
@@ -209,9 +214,25 @@ jobs:
|
||||
./dist/argocd-linux-amd64 version --client
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1
|
||||
with:
|
||||
cosign-release: 'v1.13.0'
|
||||
|
||||
- name: Sign Argo CD container images and assets
|
||||
run: |
|
||||
cosign sign --key env://COSIGN_PRIVATE_KEY ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
|
||||
cosign sign-blob --key env://COSIGN_PRIVATE_KEY ./dist/argocd-${TARGET_VERSION}-checksums.txt > ./dist/argocd-${TARGET_VERSION}-checksums.sig
|
||||
# Retrieves the public key to release as an asset
|
||||
cosign public-key --key env://COSIGN_PRIVATE_KEY > ./dist/argocd-cosign.pub
|
||||
env:
|
||||
COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}}
|
||||
COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Read release notes file
|
||||
id: release-notes
|
||||
uses: juliangruber/read-file-action@v1
|
||||
uses: juliangruber/read-file-action@02bbba9876a8f870efd4ad64e3b9088d3fb94d4b # v1.1.6
|
||||
with:
|
||||
path: ${{ env.RELEASE_NOTES }}
|
||||
|
||||
@@ -222,7 +243,7 @@ jobs:
|
||||
git push origin ${RELEASE_TAG}
|
||||
|
||||
- name: Dry run GitHub release
|
||||
uses: actions/create-release@v1
|
||||
uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e # v1.1.4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
id: create_release
|
||||
@@ -265,8 +286,16 @@ jobs:
|
||||
cd /tmp && tar -zcf sbom.tar.gz *.spdx
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Sign sbom
|
||||
run: |
|
||||
cosign sign-blob --key env://COSIGN_PRIVATE_KEY /tmp/sbom.tar.gz > /tmp/sbom.tar.gz.sig
|
||||
env:
|
||||
COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}}
|
||||
COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5 # v0.1.14
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
@@ -274,16 +303,17 @@ jobs:
|
||||
tag_name: ${{ env.RELEASE_TAG }}
|
||||
draft: ${{ env.DRAFT_RELEASE }}
|
||||
prerelease: ${{ env.PRE_RELEASE }}
|
||||
body: ${{ steps.release-notes.outputs.content }}
|
||||
body: ${{ steps.release-notes.outputs.content }} # Pre-pended to the generated notes
|
||||
files: |
|
||||
dist/argocd-*
|
||||
/tmp/sbom.tar.gz
|
||||
/tmp/sbom.tar.gz.sig
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Update homebrew formula
|
||||
env:
|
||||
HOMEBREW_TOKEN: ${{ secrets.RELEASE_HOMEBREW_TOKEN }}
|
||||
uses: dawidd6/action-homebrew-bump-formula@v3
|
||||
uses: dawidd6/action-homebrew-bump-formula@02e79d9da43d79efa846d73695b6052cbbdbf48a # v3.8.3
|
||||
with:
|
||||
token: ${{env.HOMEBREW_TOKEN}}
|
||||
formula: argocd
|
||||
|
||||
21
.github/workflows/update-snyk.yaml
vendored
21
.github/workflows/update-snyk.yaml
vendored
@@ -1,25 +1,36 @@
|
||||
name: Snyk report update
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # midnight every Sunday
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
snyk-report:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
name: Update Snyk report in the docs directory
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build reports
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
run: |
|
||||
make snyk-report
|
||||
pr_branch="snyk-update-$(echo $RANDOM | md5sum | head -c 20)"
|
||||
git checkout -b "$pr_branch"
|
||||
git config --global user.email 'ci@argoproj.com'
|
||||
git config --global user.name 'CI'
|
||||
git add docs/snyk/index.md
|
||||
git add docs/snyk/*/*.html
|
||||
git commit -m "[Bot] Update Snyk reports"
|
||||
git push
|
||||
git add docs/snyk
|
||||
git commit -m "[Bot] Update Snyk reports" --signoff
|
||||
git push --set-upstream origin "$pr_branch"
|
||||
gh pr create -B master -H "$pr_branch" --title '[Bot] docs: Update Snyk report' --body ''
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,6 +17,7 @@ test-results
|
||||
node_modules/
|
||||
.kube/
|
||||
./test/cmp/*.sock
|
||||
.envrc.remote
|
||||
|
||||
# ignore built binaries
|
||||
cmd/argocd/argocd
|
||||
|
||||
6
Makefile
6
Makefile
@@ -512,7 +512,7 @@ build-docs-local:
|
||||
|
||||
.PHONY: build-docs
|
||||
build-docs:
|
||||
docker run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}:/docs ${MKDOCS_DOCKER_IMAGE} 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:
|
||||
@@ -520,7 +520,7 @@ serve-docs-local:
|
||||
|
||||
.PHONY: serve-docs
|
||||
serve-docs:
|
||||
docker run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}:/docs ${MKDOCS_DOCKER_IMAGE} serve -a 0.0.0.0: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
|
||||
@@ -576,7 +576,7 @@ applicationset-controller:
|
||||
|
||||
.PHONY: checksums
|
||||
checksums:
|
||||
for f in ./dist/$(BIN_NAME)-*; do openssl dgst -sha256 "$$f" | awk ' { print $$2 }' > "$$f".sha256 ; done
|
||||
sha256sum ./dist/$(BIN_NAME)-* | awk -F './dist/' '{print $$1 $$2}' > ./dist/$(BIN_NAME)-$(TARGET_VERSION)-checksums.txt
|
||||
|
||||
.PHONY: snyk-container-tests
|
||||
snyk-container-tests:
|
||||
|
||||
2
OWNERS
2
OWNERS
@@ -27,3 +27,5 @@ reviewers:
|
||||
- wanghong230
|
||||
- ciiay
|
||||
- saumeya
|
||||
- zachaller
|
||||
- 34fathombelow
|
||||
|
||||
12
README.md
12
README.md
@@ -1,4 +1,5 @@
|
||||
[](https://github.com/argoproj/argo-cd/actions?query=workflow%3A%22Integration+tests%22) [](https://argoproj.github.io/community/join-slack) [](https://codecov.io/gh/argoproj/argo-cd) [](https://github.com/argoproj/argo-cd/releases/latest) [](https://bestpractices.coreinfrastructure.org/projects/4486) [](https://twitter.com/argoproj)
|
||||
[](https://artifacthub.io/packages/helm/argo/argo-cd)
|
||||
|
||||
# Argo CD - Declarative Continuous Delivery for Kubernetes
|
||||
|
||||
@@ -42,7 +43,6 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
### Blogs and Presentations
|
||||
|
||||
1. [Awesome-Argo: A Curated List of Awesome Projects and Resources Related to Argo](https://github.com/terrytangyuan/awesome-argo)
|
||||
1. [Unveil the Secret Ingredients of Continuous Delivery at Enterprise Scale with Argo CD](https://blog.akuity.io/unveil-the-secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argo-cd-7c5b4057ee49)
|
||||
1. [GitOps Without Pipelines With ArgoCD Image Updater](https://youtu.be/avPUQin9kzU)
|
||||
1. [Combining Argo CD (GitOps), Crossplane (Control Plane), And KubeVela (OAM)](https://youtu.be/eEcgn_gU3SM)
|
||||
1. [How to Apply GitOps to Everything - Combining Argo CD and Crossplane](https://youtu.be/yrj4lmScKHQ)
|
||||
@@ -50,10 +50,8 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
1. [Automation of Everything - How To Combine Argo Events, Workflows & Pipelines, CD, and Rollouts](https://youtu.be/XNXJtxkUKeY)
|
||||
1. [Environments Based On Pull Requests (PRs): Using Argo CD To Apply GitOps Principles On Previews](https://youtu.be/cpAaI8p4R60)
|
||||
1. [Argo CD: Applying GitOps Principles To Manage Production Environment In Kubernetes](https://youtu.be/vpWQeoaiRM4)
|
||||
1. [Creating Temporary Preview Environments Based On Pull Requests With Argo CD And Codefresh](https://codefresh.io/continuous-deployment/creating-temporary-preview-environments-based-pull-requests-argo-cd-codefresh/)
|
||||
1. [Tutorial: Everything You Need To Become A GitOps Ninja](https://www.youtube.com/watch?v=r50tRQjisxw) 90m tutorial on GitOps and Argo CD.
|
||||
1. [Comparison of Argo CD, Spinnaker, Jenkins X, and Tekton](https://www.inovex.de/blog/spinnaker-vs-argo-cd-vs-tekton-vs-jenkins-x/)
|
||||
1. [Simplify and Automate Deployments Using GitOps with IBM Multicloud Manager 3.1.2](https://www.ibm.com/cloud/blog/simplify-and-automate-deployments-using-gitops-with-ibm-multicloud-manager-3-1-2)
|
||||
1. [GitOps for Kubeflow using Argo CD](https://v0-6.kubeflow.org/docs/use-cases/gitops-for-kubeflow/)
|
||||
1. [GitOps Toolsets on Kubernetes with CircleCI and Argo CD](https://www.digitalocean.com/community/tutorials/webinar-series-gitops-tool-sets-on-kubernetes-with-circleci-and-argo-cd)
|
||||
1. [CI/CD in Light Speed with K8s and Argo CD](https://www.youtube.com/watch?v=OdzH82VpMwI&feature=youtu.be)
|
||||
@@ -62,7 +60,6 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
1. [Introduction to Argo CD : Kubernetes DevOps CI/CD](https://www.youtube.com/watch?v=2WSJF7d8dUg&feature=youtu.be)
|
||||
1. [GitOps Deployment and Kubernetes - using Argo CD](https://medium.com/riskified-technology/gitops-deployment-and-kubernetes-f1ab289efa4b)
|
||||
1. [Deploy Argo CD with Ingress and TLS in Three Steps: No YAML Yak Shaving Required](https://itnext.io/deploy-argo-cd-with-ingress-and-tls-in-three-steps-no-yaml-yak-shaving-required-bc536d401491)
|
||||
1. [GitOps Continuous Delivery with Argo and Codefresh](https://codefresh.io/events/cncf-member-webinar-gitops-continuous-delivery-argo-codefresh/)
|
||||
1. [Stay up to date with Argo CD and Renovate](https://mjpitz.com/blog/2020/12/03/renovate-your-gitops/)
|
||||
1. [Setting up Argo CD with Helm](https://www.arthurkoziel.com/setting-up-argocd-with-helm/)
|
||||
1. [Applied GitOps with Argo CD](https://thenewstack.io/applied-gitops-with-argocd/)
|
||||
@@ -72,3 +69,10 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
1. [Getting Started with ArgoCD for GitOps Deployments](https://youtu.be/AvLuplh1skA)
|
||||
1. [Using Argo CD & Datree for Stable Kubernetes CI/CD Deployments](https://youtu.be/17894DTru2Y)
|
||||
|
||||
### Vendor-sponsored community resources
|
||||
|
||||
The following links are to resources hosted by Argo CD vendors (companies which use Argo CD as part of their software offerings).
|
||||
These resources are vendor-neutral (overwhelmingly _about_ Argo CD, but may briefly mention the vendor) and do not require providing information for marketing purposes.
|
||||
|
||||
1. [Unveil the Secret Ingredients of Continuous Delivery at Enterprise Scale with Argo CD](https://blog.akuity.io/unveil-the-secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argo-cd-7c5b4057ee49)
|
||||
1. [Argo CD Sandbox (demo instance)](https://argocd.argo.opsmx.net/) (requires logging into the demo instance with a Google account)
|
||||
|
||||
14
USERS.md
14
USERS.md
@@ -11,9 +11,11 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Adevinta](https://www.adevinta.com/)
|
||||
1. [Adfinis](https://adfinis.com)
|
||||
1. [Adventure](https://jp.adventurekk.com/)
|
||||
1. [AirQo](https://airqo.net/)
|
||||
1. [Akuity](https://akuity.io/)
|
||||
1. [Alibaba Group](https://www.alibabagroup.com/)
|
||||
1. [Allianz Direct](https://www.allianzdirect.de/)
|
||||
1. [Amadeus IT Group](https://amadeus.com/)
|
||||
1. [Ambassador Labs](https://www.getambassador.io/)
|
||||
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
|
||||
1. [Ant Group](https://www.antgroup.com/)
|
||||
@@ -35,6 +37,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Camptocamp](https://camptocamp.com)
|
||||
1. [Capital One](https://www.capitalone.com)
|
||||
1. [CARFAX](https://www.carfax.com)
|
||||
1. [CARFAX Europe](https://www.carfax.eu)
|
||||
1. [Casavo](https://casavo.com)
|
||||
1. [Celonis](https://www.celonis.com/)
|
||||
1. [CERN](https://home.cern/)
|
||||
@@ -53,8 +56,11 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [D2iQ](https://www.d2iq.com)
|
||||
1. [Datarisk](https://www.datarisk.io/)
|
||||
1. [Deloitte](https://www.deloitte.com/)
|
||||
1. [Deutsche Telekom AG](https://telekom.com)
|
||||
1. [Devopsi - Poland Software/DevOps Consulting](https://devopsi.pl/)
|
||||
1. [Devtron Labs](https://github.com/devtron-labs/devtron)
|
||||
1. [Divistant](https://divistant.com)
|
||||
1. [Doximity](https://www.doximity.com/)
|
||||
1. [EDF Renewables](https://www.edf-re.com/)
|
||||
1. [edX](https://edx.org)
|
||||
1. [Elastic](https://elastic.co/)
|
||||
@@ -64,6 +70,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [END.](https://www.endclothing.com/)
|
||||
1. [Energisme](https://energisme.com/)
|
||||
1. [enigmo](https://enigmo.co.jp/)
|
||||
1. [Envoy](https://envoy.com/)
|
||||
1. [Faro](https://www.faro.com/)
|
||||
1. [Fave](https://myfave.com)
|
||||
1. [Flip](https://flip.id)
|
||||
@@ -74,6 +81,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [G DATA CyberDefense AG](https://www.gdata-software.com/)
|
||||
1. [Garner](https://www.garnercorp.com)
|
||||
1. [Generali Deutschland AG](https://www.generali.de/)
|
||||
1. [Gepardec](https://gepardec.com/)
|
||||
1. [Gitpod](https://www.gitpod.io)
|
||||
1. [Gllue](https://gllue.com)
|
||||
1. [gloat](https://gloat.com/)
|
||||
@@ -114,6 +122,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Kurly](https://www.kurly.com/)
|
||||
1. [LexisNexis](https://www.lexisnexis.com/)
|
||||
1. [Lian Chu Securities](https://lczq.com)
|
||||
1. [Liatrio](https://www.liatrio.com)
|
||||
1. [Lightricks](https://www.lightricks.com/)
|
||||
1. [LINE](https://linecorp.com/en/)
|
||||
1. [Lytt](https://www.lytt.co/)
|
||||
@@ -140,6 +149,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Nextdoor](https://nextdoor.com/)
|
||||
1. [Nikkei](https://www.nikkei.co.jp/nikkeiinfo/en/)
|
||||
1. [Nitro](https://gonitro.com)
|
||||
1. [Objective](https://www.objective.com.br/)
|
||||
1. [OCCMundial](https://occ.com.mx)
|
||||
1. [Octadesk](https://octadesk.com)
|
||||
1. [omegaUp](https://omegaUp.com)
|
||||
@@ -157,9 +167,11 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [PagerDuty](https://www.pagerduty.com/)
|
||||
1. [PayPay](https://paypay.ne.jp/)
|
||||
1. [Peloton Interactive](https://www.onepeloton.com/)
|
||||
1. [Pigment](https://www.gopigment.com/)
|
||||
1. [Pipefy](https://www.pipefy.com/)
|
||||
1. [Pismo](https://pismo.io/)
|
||||
1. [Polarpoint.io](https://polarpoint.io)
|
||||
1. [PostFinance](https://github.com/postfinance)
|
||||
1. [Preferred Networks](https://preferred.jp/en/)
|
||||
1. [Productboard](https://www.productboard.com/)
|
||||
1. [Prudential](https://prudential.com.sg)
|
||||
@@ -180,6 +192,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Saloodo! GmbH](https://www.saloodo.com)
|
||||
1. [Sap Labs](http://sap.com)
|
||||
1. [Schwarz IT](https://jobs.schwarz/it-mission)
|
||||
1. [SI Analytics](https://si-analytics.ai)
|
||||
1. [Skit](https://skit.ai/)
|
||||
1. [Skyscanner](https://www.skyscanner.net/)
|
||||
1. [Smilee.io](https://smilee.io)
|
||||
@@ -230,6 +243,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [WeMo Scooter](https://www.wemoscooter.com/)
|
||||
1. [Whitehat Berlin](https://whitehat.berlin) by Guido Maria Serra +Fenaroli
|
||||
1. [Witick](https://witick.io/)
|
||||
1. [Wolffun Game](https://www.wolffungame.com/)
|
||||
1. [WooliesX](https://wooliesx.com.au/)
|
||||
1. [Woolworths Group](https://www.woolworthsgroup.com.au/)
|
||||
1. [WSpot](https://www.wspot.com.br/)
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
log "github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -62,7 +61,6 @@ var (
|
||||
// ApplicationSetReconciler reconciles a ApplicationSet object
|
||||
type ApplicationSetReconciler struct {
|
||||
client.Client
|
||||
Log logr.Logger
|
||||
Scheme *runtime.Scheme
|
||||
Recorder record.EventRecorder
|
||||
Generators map[string]generators.Generator
|
||||
@@ -77,15 +75,14 @@ type ApplicationSetReconciler struct {
|
||||
// +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets/status,verbs=get;update;patch
|
||||
|
||||
func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
_ = r.Log.WithValues("applicationset", req.NamespacedName)
|
||||
_ = log.WithField("applicationset", req.NamespacedName)
|
||||
logCtx := log.WithField("applicationset", req.NamespacedName)
|
||||
|
||||
var applicationSetInfo argov1alpha1.ApplicationSet
|
||||
parametersGenerated := false
|
||||
|
||||
if err := r.Get(ctx, req.NamespacedName, &applicationSetInfo); err != nil {
|
||||
if client.IgnoreNotFound(err) != nil {
|
||||
log.WithError(err).Infof("unable to get ApplicationSet: '%v' ", err)
|
||||
logCtx.WithError(err).Infof("unable to get ApplicationSet: '%v' ", err)
|
||||
}
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
@@ -123,7 +120,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
//
|
||||
// Changes to watched resources will cause this to be reconciled sooner than
|
||||
// the RequeueAfter time.
|
||||
log.Errorf("error occurred during application validation: %s", err.Error())
|
||||
logCtx.Errorf("error occurred during application validation: %s", err.Error())
|
||||
|
||||
_ = r.setApplicationSetStatusCondition(ctx,
|
||||
&applicationSetInfo,
|
||||
@@ -148,7 +145,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
var message string
|
||||
for _, v := range validateErrors {
|
||||
message = v.Error()
|
||||
log.Errorf("validation error found during application validation: %s", message)
|
||||
logCtx.Errorf("validation error found during application validation: %s", message)
|
||||
}
|
||||
if len(validateErrors) > 1 {
|
||||
// Only the last message gets added to the appset status, to keep the size reasonable.
|
||||
@@ -215,7 +212,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
delete(applicationSetInfo.Annotations, common.AnnotationApplicationSetRefresh)
|
||||
err := r.Client.Update(ctx, &applicationSetInfo)
|
||||
if err != nil {
|
||||
log.Warnf("error occurred while updating ApplicationSet: %v", err)
|
||||
logCtx.Warnf("error occurred while updating ApplicationSet: %v", err)
|
||||
_ = r.setApplicationSetStatusCondition(ctx,
|
||||
&applicationSetInfo,
|
||||
argov1alpha1.ApplicationSetCondition{
|
||||
@@ -230,7 +227,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
}
|
||||
|
||||
requeueAfter := r.getMinRequeueAfter(&applicationSetInfo)
|
||||
log.WithField("requeueAfter", requeueAfter).Info("end reconcile")
|
||||
logCtx.WithField("requeueAfter", requeueAfter).Info("end reconcile")
|
||||
|
||||
if len(validateErrors) == 0 {
|
||||
if err := r.setApplicationSetStatusCondition(ctx,
|
||||
@@ -487,7 +484,7 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// ...and if so, return it
|
||||
return []string{owner.Name}
|
||||
}); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error setting up with manager: %w", err)
|
||||
}
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
@@ -570,7 +567,7 @@ func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, applicat
|
||||
var createApps []argov1alpha1.Application
|
||||
current, err := r.getCurrentApplications(ctx, applicationSet)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting current applications: %w", err)
|
||||
}
|
||||
|
||||
m := make(map[string]bool) // Will holds the app names that are current in the cluster
|
||||
@@ -611,13 +608,13 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, applicat
|
||||
// clusterList, err := argoDB.ListClusters(ctx)
|
||||
clusterList, err := utils.ListClusters(ctx, r.KubeClientset, applicationSet.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error listing clusters: %w", err)
|
||||
}
|
||||
|
||||
// Save current applications to be able to delete the ones that are not in appList
|
||||
current, err := r.getCurrentApplications(ctx, applicationSet)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting current applications: %w", err)
|
||||
}
|
||||
|
||||
m := make(map[string]bool) // Will holds the app names in appList for the deletion process
|
||||
@@ -721,7 +718,7 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte
|
||||
|
||||
err := r.Client.Update(ctx, app, &client.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error updating finalizers: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1832,7 +1832,6 @@ func TestReconcilerValidationErrorBehaviour(t *testing.T) {
|
||||
}}, nil)
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Log: ctrl.Log.WithName("controllers").WithName("ApplicationSet"),
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
@@ -1908,7 +1907,6 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Log: ctrl.Log.WithName("controllers").WithName("ApplicationSet"),
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#namePrefix: kustomize-
|
||||
|
||||
resources:
|
||||
- namespace-install.yaml
|
||||
- https://github.com/argoproj/argo-workflows/releases/download/v3.4.0/namespace-install.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
@@ -1,417 +0,0 @@
|
||||
# This is an auto-generated file. DO NOT EDIT
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: clusterworkflowtemplates.argoproj.io
|
||||
spec:
|
||||
group: argoproj.io
|
||||
names:
|
||||
kind: ClusterWorkflowTemplate
|
||||
listKind: ClusterWorkflowTemplateList
|
||||
plural: clusterworkflowtemplates
|
||||
shortNames:
|
||||
- clusterwftmpl
|
||||
- cwft
|
||||
singular: clusterworkflowtemplate
|
||||
scope: Cluster
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: cronworkflows.argoproj.io
|
||||
spec:
|
||||
group: argoproj.io
|
||||
names:
|
||||
kind: CronWorkflow
|
||||
listKind: CronWorkflowList
|
||||
plural: cronworkflows
|
||||
shortNames:
|
||||
- cwf
|
||||
- cronwf
|
||||
singular: cronworkflow
|
||||
scope: Namespaced
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: workfloweventbindings.argoproj.io
|
||||
spec:
|
||||
group: argoproj.io
|
||||
names:
|
||||
kind: WorkflowEventBinding
|
||||
listKind: WorkflowEventBindingList
|
||||
plural: workfloweventbindings
|
||||
shortNames:
|
||||
- wfeb
|
||||
singular: workfloweventbinding
|
||||
scope: Namespaced
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: workflows.argoproj.io
|
||||
spec:
|
||||
additionalPrinterColumns:
|
||||
- JSONPath: .status.phase
|
||||
description: Status of the workflow
|
||||
name: Status
|
||||
type: string
|
||||
- JSONPath: .status.startedAt
|
||||
description: When the workflow was started
|
||||
format: date-time
|
||||
name: Age
|
||||
type: date
|
||||
group: argoproj.io
|
||||
names:
|
||||
kind: Workflow
|
||||
listKind: WorkflowList
|
||||
plural: workflows
|
||||
shortNames:
|
||||
- wf
|
||||
singular: workflow
|
||||
scope: Namespaced
|
||||
subresources: {}
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: workflowtemplates.argoproj.io
|
||||
spec:
|
||||
group: argoproj.io
|
||||
names:
|
||||
kind: WorkflowTemplate
|
||||
listKind: WorkflowTemplateList
|
||||
plural: workflowtemplates
|
||||
shortNames:
|
||||
- wftmpl
|
||||
singular: workflowtemplate
|
||||
scope: Namespaced
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: argo
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: argo-server
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: argo-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/exec
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- watch
|
||||
- list
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- persistentvolumeclaims
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- workflows
|
||||
- workflows/finalizers
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- create
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- workflowtemplates
|
||||
- workflowtemplates/finalizers
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- cronworkflows
|
||||
- cronworkflows/finalizers
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- apiGroups:
|
||||
- policy
|
||||
resources:
|
||||
- poddisruptionbudgets
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- delete
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: argo-server-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- watch
|
||||
- list
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- create
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/exec
|
||||
- pods/log
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- watch
|
||||
- create
|
||||
- patch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- workflows
|
||||
- workfloweventbindings
|
||||
- workflowtemplates
|
||||
- cronworkflows
|
||||
- cronworkflows/finalizers
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: argo-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: argo-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: argo
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: argo-server-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: argo-server-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: argo-server
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: workflow-controller-configmap
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: argo-server
|
||||
spec:
|
||||
ports:
|
||||
- name: web
|
||||
port: 2746
|
||||
targetPort: 2746
|
||||
selector:
|
||||
app: argo-server
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: workflow-controller-metrics
|
||||
spec:
|
||||
ports:
|
||||
- name: metrics
|
||||
port: 9090
|
||||
protocol: TCP
|
||||
targetPort: 9090
|
||||
selector:
|
||||
app: workflow-controller
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: argo-server
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: argo-server
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: argo-server
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- server
|
||||
- --namespaced
|
||||
image: argoproj/argocli:v2.12.5
|
||||
name: argo-server
|
||||
ports:
|
||||
- containerPort: 2746
|
||||
name: web
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 2746
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 20
|
||||
volumeMounts:
|
||||
- mountPath: /tmp
|
||||
name: tmp
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
serviceAccountName: argo-server
|
||||
volumes:
|
||||
- emptyDir: {}
|
||||
name: tmp
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: workflow-controller
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: workflow-controller
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: workflow-controller
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --configmap
|
||||
- workflow-controller-configmap
|
||||
- --executor-image
|
||||
- argoproj/argoexec:v2.12.5
|
||||
- --namespaced
|
||||
command:
|
||||
- workflow-controller
|
||||
image: argoproj/workflow-controller:v2.12.5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: metrics
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
name: workflow-controller
|
||||
ports:
|
||||
- containerPort: 9090
|
||||
name: metrics
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
serviceAccountName: argo
|
||||
@@ -11,4 +11,4 @@ version: 0.1.0
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
appVersion: "1.0"
|
||||
appVersion: "1.0"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dependencies:
|
||||
- name: kube-prometheus-stack
|
||||
version: 9.4.10
|
||||
version: 40.5.0
|
||||
repository: https://prometheus-community.github.io/helm-charts
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#namePrefix: kustomize-
|
||||
|
||||
resources:
|
||||
- namespace-install.yaml
|
||||
- https://github.com/argoproj/argo-workflows/releases/download/v3.4.0/namespace-install.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
@@ -1,417 +0,0 @@
|
||||
# This is an auto-generated file. DO NOT EDIT
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: clusterworkflowtemplates.argoproj.io
|
||||
spec:
|
||||
group: argoproj.io
|
||||
names:
|
||||
kind: ClusterWorkflowTemplate
|
||||
listKind: ClusterWorkflowTemplateList
|
||||
plural: clusterworkflowtemplates
|
||||
shortNames:
|
||||
- clusterwftmpl
|
||||
- cwft
|
||||
singular: clusterworkflowtemplate
|
||||
scope: Cluster
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: cronworkflows.argoproj.io
|
||||
spec:
|
||||
group: argoproj.io
|
||||
names:
|
||||
kind: CronWorkflow
|
||||
listKind: CronWorkflowList
|
||||
plural: cronworkflows
|
||||
shortNames:
|
||||
- cwf
|
||||
- cronwf
|
||||
singular: cronworkflow
|
||||
scope: Namespaced
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: workfloweventbindings.argoproj.io
|
||||
spec:
|
||||
group: argoproj.io
|
||||
names:
|
||||
kind: WorkflowEventBinding
|
||||
listKind: WorkflowEventBindingList
|
||||
plural: workfloweventbindings
|
||||
shortNames:
|
||||
- wfeb
|
||||
singular: workfloweventbinding
|
||||
scope: Namespaced
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: workflows.argoproj.io
|
||||
spec:
|
||||
additionalPrinterColumns:
|
||||
- JSONPath: .status.phase
|
||||
description: Status of the workflow
|
||||
name: Status
|
||||
type: string
|
||||
- JSONPath: .status.startedAt
|
||||
description: When the workflow was started
|
||||
format: date-time
|
||||
name: Age
|
||||
type: date
|
||||
group: argoproj.io
|
||||
names:
|
||||
kind: Workflow
|
||||
listKind: WorkflowList
|
||||
plural: workflows
|
||||
shortNames:
|
||||
- wf
|
||||
singular: workflow
|
||||
scope: Namespaced
|
||||
subresources: {}
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: workflowtemplates.argoproj.io
|
||||
spec:
|
||||
group: argoproj.io
|
||||
names:
|
||||
kind: WorkflowTemplate
|
||||
listKind: WorkflowTemplateList
|
||||
plural: workflowtemplates
|
||||
shortNames:
|
||||
- wftmpl
|
||||
singular: workflowtemplate
|
||||
scope: Namespaced
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: argo
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: argo-server
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: argo-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/exec
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- watch
|
||||
- list
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- persistentvolumeclaims
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- workflows
|
||||
- workflows/finalizers
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- create
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- workflowtemplates
|
||||
- workflowtemplates/finalizers
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- cronworkflows
|
||||
- cronworkflows/finalizers
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- apiGroups:
|
||||
- policy
|
||||
resources:
|
||||
- poddisruptionbudgets
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- delete
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: argo-server-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- watch
|
||||
- list
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- create
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/exec
|
||||
- pods/log
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- watch
|
||||
- create
|
||||
- patch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- workflows
|
||||
- workfloweventbindings
|
||||
- workflowtemplates
|
||||
- cronworkflows
|
||||
- cronworkflows/finalizers
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: argo-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: argo-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: argo
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: argo-server-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: argo-server-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: argo-server
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: workflow-controller-configmap
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: argo-server
|
||||
spec:
|
||||
ports:
|
||||
- name: web
|
||||
port: 2746
|
||||
targetPort: 2746
|
||||
selector:
|
||||
app: argo-server
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: workflow-controller-metrics
|
||||
spec:
|
||||
ports:
|
||||
- name: metrics
|
||||
port: 9090
|
||||
protocol: TCP
|
||||
targetPort: 9090
|
||||
selector:
|
||||
app: workflow-controller
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: argo-server
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: argo-server
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: argo-server
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- server
|
||||
- --namespaced
|
||||
image: argoproj/argocli:v2.12.5
|
||||
name: argo-server
|
||||
ports:
|
||||
- containerPort: 2746
|
||||
name: web
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 2746
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 20
|
||||
volumeMounts:
|
||||
- mountPath: /tmp
|
||||
name: tmp
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
serviceAccountName: argo-server
|
||||
volumes:
|
||||
- emptyDir: {}
|
||||
name: tmp
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: workflow-controller
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: workflow-controller
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: workflow-controller
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --configmap
|
||||
- workflow-controller-configmap
|
||||
- --executor-image
|
||||
- argoproj/argoexec:v2.12.5
|
||||
- --namespaced
|
||||
command:
|
||||
- workflow-controller
|
||||
image: argoproj/workflow-controller:v2.12.5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: metrics
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
name: workflow-controller
|
||||
ports:
|
||||
- containerPort: 9090
|
||||
name: metrics
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
serviceAccountName: argo
|
||||
@@ -1 +1,14 @@
|
||||
apiVersion: v2
|
||||
name: helm-prometheus-operator
|
||||
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
appVersion: "1.0"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dependencies:
|
||||
- name: kube-prometheus-stack
|
||||
version: 9.4.10
|
||||
version: 40.5.0
|
||||
repository: https://prometheus-community.github.io/helm-charts
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: cluster-addons
|
||||
namespace: argocd
|
||||
spec:
|
||||
generators:
|
||||
- git:
|
||||
repoURL: https://github.com/argoproj/argo-cd.git
|
||||
revision: HEAD
|
||||
directories:
|
||||
- path: applicationset/examples/git-generator-directory/excludes/cluster-addons/*
|
||||
- exclude: true
|
||||
path: applicationset/examples/git-generator-directory/excludes/cluster-addons/exclude-helm-guestbook
|
||||
template:
|
||||
metadata:
|
||||
name: '{{path.basename}}'
|
||||
spec:
|
||||
project: "my-project"
|
||||
source:
|
||||
repoURL: https://github.com/argoproj/argo-cd.git
|
||||
targetRevision: HEAD
|
||||
path: '{{path}}'
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: '{{path.basename}}'
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
@@ -2,7 +2,9 @@ apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: cluster-addons
|
||||
namespace: argocd
|
||||
spec:
|
||||
goTemplate: true
|
||||
generators:
|
||||
- git:
|
||||
repoURL: https://github.com/argoproj/argo-cd.git
|
||||
@@ -15,7 +17,7 @@ spec:
|
||||
metadata:
|
||||
name: '{{.path.basename}}'
|
||||
spec:
|
||||
project: default
|
||||
project: "my-project"
|
||||
source:
|
||||
repoURL: https://github.com/argoproj/argo-cd.git
|
||||
targetRevision: HEAD
|
||||
@@ -23,3 +25,6 @@ spec:
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: '{{.path.basename}}'
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
||||
@@ -2,6 +2,7 @@ apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: cluster-addons
|
||||
namespace: argocd
|
||||
spec:
|
||||
generators:
|
||||
- git:
|
||||
@@ -13,7 +14,7 @@ spec:
|
||||
metadata:
|
||||
name: '{{path.basename}}'
|
||||
spec:
|
||||
project: default
|
||||
project: "my-project"
|
||||
source:
|
||||
repoURL: https://github.com/argoproj/argo-cd.git
|
||||
targetRevision: HEAD
|
||||
@@ -21,3 +22,6 @@ spec:
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: '{{path.basename}}'
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
||||
@@ -2,6 +2,7 @@ apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: cluster-addons
|
||||
namespace: argocd
|
||||
spec:
|
||||
goTemplate: true
|
||||
generators:
|
||||
@@ -14,7 +15,7 @@ spec:
|
||||
metadata:
|
||||
name: '{{.path.basename}}'
|
||||
spec:
|
||||
project: default
|
||||
project: "my-project"
|
||||
source:
|
||||
repoURL: https://github.com/argoproj/argo-cd.git
|
||||
targetRevision: HEAD
|
||||
@@ -22,3 +23,6 @@ spec:
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: '{{.path.basename}}'
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
||||
@@ -170,7 +170,7 @@ func appendTemplatedValues(clusterValues map[string]string, params map[string]in
|
||||
result, err := replaceTemplatedString(value, params, appSet)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error replacing templated String: %w", err)
|
||||
}
|
||||
|
||||
if appSet.Spec.GoTemplate {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
@@ -28,7 +29,7 @@ type TransformResult struct {
|
||||
func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}) ([]TransformResult, error) {
|
||||
selector, err := metav1.LabelSelectorAsSelector(requestedGenerator.Selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error parsing label selector: %w", err)
|
||||
}
|
||||
|
||||
res := []TransformResult{}
|
||||
|
||||
@@ -85,12 +85,12 @@ func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revi
|
||||
|
||||
gitRepoClient, err := git.NewClient(repo.Repo, repo.GetGitCreds(a.storecreds), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error creating a new git client: %w", err)
|
||||
}
|
||||
|
||||
err = checkoutRepo(gitRepoClient, revision, a.submoduleEnabled)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error while checking out repo: %w", err)
|
||||
}
|
||||
|
||||
filteredPaths := []string{}
|
||||
@@ -99,7 +99,7 @@ func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revi
|
||||
|
||||
if err := filepath.Walk(repoRoot, func(path string, info os.FileInfo, fnErr error) error {
|
||||
if fnErr != nil {
|
||||
return fnErr
|
||||
return fmt.Errorf("error walking the file tree: %w", fnErr)
|
||||
}
|
||||
if !info.IsDir() { // Skip files: directories only
|
||||
return nil
|
||||
@@ -112,7 +112,7 @@ func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revi
|
||||
|
||||
relativePath, err := filepath.Rel(repoRoot, path)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error constructing relative repo path: %w", err)
|
||||
}
|
||||
|
||||
if relativePath == "." { // Exclude '.' from results
|
||||
|
||||
@@ -35,7 +35,7 @@ func NewGiteaProvider(ctx context.Context, owner, token, url string, allBranches
|
||||
}
|
||||
client, err := gitea.NewClient(url, gitea.SetToken(token), gitea.SetHTTPClient(httpClient))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error creating a new gitea client: %w", err)
|
||||
}
|
||||
return &GiteaProvider{
|
||||
client: client,
|
||||
|
||||
@@ -88,7 +88,7 @@ func CreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f c
|
||||
// mutate wraps a MutateFn and applies validation to its result
|
||||
func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object) error {
|
||||
if err := f(); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error while wrapping using MutateFn: %w", err)
|
||||
}
|
||||
if newKey := client.ObjectKeyFromObject(obj); key != newKey {
|
||||
return fmt.Errorf("MutateFn cannot mutate object name and/or object namespace")
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"text/template"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"github.com/valyala/fasttemplate"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -133,6 +133,16 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Keys can be templated as well as values (e.g. to template something into an annotation).
|
||||
if key.Kind() == reflect.String {
|
||||
templatedKey, err := r.Replace(key.String(), replaceMap, useGoTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key = reflect.ValueOf(templatedKey)
|
||||
}
|
||||
|
||||
copy.SetMapIndex(key, copyValue)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
logtest "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -461,7 +462,49 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderTemplateKeys(t *testing.T) {
|
||||
t.Run("fasttemplate", func(t *testing.T) {
|
||||
application := &argoappsv1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"annotation-{{key}}": "annotation-{{value}}",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
params := map[string]interface{}{
|
||||
"key": "some-key",
|
||||
"value": "some-value",
|
||||
}
|
||||
|
||||
render := Render{}
|
||||
newApplication, err := render.RenderTemplateParams(application, nil, params, false)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, newApplication.ObjectMeta.Annotations, "annotation-some-key")
|
||||
assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-some-key"], "annotation-some-value")
|
||||
})
|
||||
t.Run("gotemplate", func(t *testing.T) {
|
||||
application := &argoappsv1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"annotation-{{ .key }}": "annotation-{{ .value }}",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
params := map[string]interface{}{
|
||||
"key": "some-key",
|
||||
"value": "some-value",
|
||||
}
|
||||
|
||||
render := Render{}
|
||||
newApplication, err := render.RenderTemplateParams(application, nil, params, true)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, newApplication.ObjectMeta.Annotations, "annotation-some-key")
|
||||
assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-some-key"], "annotation-some-value")
|
||||
})
|
||||
}
|
||||
|
||||
func TestRenderTemplateParamsFinalizers(t *testing.T) {
|
||||
|
||||
4
argocd-cosign.pub
Normal file
4
argocd-cosign.pub
Normal file
@@ -0,0 +1,4 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEesHEB7vX5Y2RxXypjMy1nI1z7iRG
|
||||
JI9/gt/sYqzpsa65aaNP4npM43DDxoIy/MQBo9s/mxGxmA+8UXeDpVC9vw==
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -21,6 +21,10 @@ p, role:admin, applications, delete, */*, allow
|
||||
p, role:admin, applications, sync, */*, allow
|
||||
p, role:admin, applications, override, */*, allow
|
||||
p, role:admin, applications, action/*, */*, allow
|
||||
p, role:admin, applicationsets, get, */*, allow
|
||||
p, role:admin, applicationsets, create, */*, allow
|
||||
p, role:admin, applicationsets, update, */*, allow
|
||||
p, role:admin, applicationsets, delete, */*, allow
|
||||
p, role:admin, certificates, create, *, allow
|
||||
p, role:admin, certificates, update, *, allow
|
||||
p, role:admin, certificates, delete, *, allow
|
||||
@@ -39,4 +43,4 @@ p, role:admin, gpgkeys, delete, *, allow
|
||||
p, role:admin, exec, create, */*, allow
|
||||
|
||||
g, role:admin, role:readonly
|
||||
g, admin, role:admin
|
||||
g, admin, role:admin
|
||||
|
@@ -4097,6 +4097,9 @@
|
||||
"appLabelKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"appsInAnyNamespaceEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"configManagementPlugins": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -4505,6 +4508,65 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"repositoryParameterAnnouncement": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"array": {
|
||||
"description": "array is the default value of the parameter if the parameter is an array.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"collectionType": {
|
||||
"description": "collectionType is the type of value this parameter holds - either a single value (a string) or a collection\n(array or map). If collectionType is set, only the field with that type will be used. If collectionType is not\nset, `string` is the default. If collectionType is set to an invalid value, a validation error is thrown.",
|
||||
"type": "string"
|
||||
},
|
||||
"itemType": {
|
||||
"description": "itemType determines the primitive data type represented by the parameter. Parameters are always encoded as\nstrings, but this field lets them be interpreted as other primitive types.",
|
||||
"type": "string"
|
||||
},
|
||||
"map": {
|
||||
"description": "map is the default value of the parameter if the parameter is a map.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"description": "name is the name identifying a parameter.",
|
||||
"type": "string"
|
||||
},
|
||||
"required": {
|
||||
"description": "required defines if this given parameter is mandatory.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"string": {
|
||||
"description": "string is the default value of the parameter if the parameter is a string.",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"description": "title is a human-readable text of the parameter name.",
|
||||
"type": "string"
|
||||
},
|
||||
"tooltip": {
|
||||
"description": "tooltip is a human-readable description of the parameter.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repositoryPluginAppSpec": {
|
||||
"type": "object",
|
||||
"title": "PluginAppSpec contains details about a plugin-type Application",
|
||||
"properties": {
|
||||
"parametersAnnouncement": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repositoryParameterAnnouncement"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"repositoryRefs": {
|
||||
"type": "object",
|
||||
"title": "A subset of the repository's named refs",
|
||||
@@ -4551,6 +4613,9 @@
|
||||
"kustomize": {
|
||||
"$ref": "#/definitions/repositoryKustomizeAppSpec"
|
||||
},
|
||||
"plugin": {
|
||||
"$ref": "#/definitions/repositoryPluginAppSpec"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -5772,6 +5837,39 @@
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parameters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1ApplicationSourcePluginParameter"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1ApplicationSourcePluginParameter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"array": {
|
||||
"description": "Array is the value of an array type parameter.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"map": {
|
||||
"description": "Map is the value of a map type parameter.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"description": "Name is the name identifying a parameter.",
|
||||
"type": "string"
|
||||
},
|
||||
"string": {
|
||||
"description": "String_ is the value of a string type parameter.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6544,6 +6642,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1ManagedNamespaceMetadata": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1MatrixGenerator": {
|
||||
"description": "MatrixGenerator generates the cartesian product of two sets of parameters. The parameters are defined by two nested\ngenerators.",
|
||||
"type": "object",
|
||||
@@ -7423,6 +7538,10 @@
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"syncWave": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -7478,8 +7597,8 @@
|
||||
"$ref": "#/definitions/v1Time"
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"title": "Message contains the message associated with the revision, most likely the commit message.\nThe message is truncated to the first newline or 64 characters (which ever comes first)"
|
||||
"description": "Message contains the message associated with the revision, most likely the commit message.",
|
||||
"type": "string"
|
||||
},
|
||||
"signatureInfo": {
|
||||
"description": "SignatureInfo contains a hint on the signer if the revision was signed with GPG, and signature verification is enabled.",
|
||||
@@ -7823,6 +7942,9 @@
|
||||
"automated": {
|
||||
"$ref": "#/definitions/v1alpha1SyncPolicyAutomated"
|
||||
},
|
||||
"managedNamespaceMetadata": {
|
||||
"$ref": "#/definitions/v1alpha1ManagedNamespaceMetadata"
|
||||
},
|
||||
"retry": {
|
||||
"$ref": "#/definitions/v1alpha1RetryStrategy"
|
||||
},
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/pkg/stats"
|
||||
@@ -16,6 +15,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/generators"
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/webhook"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/askpass"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
@@ -39,7 +39,7 @@ import (
|
||||
argosettings "github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
// TODO: load this using Cobra. https://github.com/argoproj/argo-cd/issues/10157
|
||||
// TODO: load this using Cobra.
|
||||
func getSubmoduleEnabled() bool {
|
||||
return env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true)
|
||||
}
|
||||
@@ -56,8 +56,6 @@ func NewCommand() *cobra.Command {
|
||||
policy string
|
||||
debugLog bool
|
||||
dryRun bool
|
||||
logFormat string
|
||||
logLevel string
|
||||
)
|
||||
scheme := runtime.NewScheme()
|
||||
_ = clientgoscheme.AddToScheme(scheme)
|
||||
@@ -79,6 +77,9 @@ func NewCommand() *cobra.Command {
|
||||
},
|
||||
)
|
||||
|
||||
cli.SetLogFormat(cmdutil.LogFormat)
|
||||
cli.SetLogLevel(cmdutil.LogLevel)
|
||||
|
||||
restConfig, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -86,21 +87,6 @@ func NewCommand() *cobra.Command {
|
||||
|
||||
restConfig.UserAgent = fmt.Sprintf("argocd-applicationset-controller/%s (%s)", vers.Version, vers.Platform)
|
||||
|
||||
level, err := log.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.SetLevel(level)
|
||||
switch strings.ToLower(logFormat) {
|
||||
case "json":
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
case "text":
|
||||
if os.Getenv("FORCE_LOG_COLORS") == "1" {
|
||||
log.SetFormatter(&log.TextFormatter{ForceColors: true})
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Unknown log format '%s'", logFormat)
|
||||
}
|
||||
policyObj, exists := utils.Policies[policy]
|
||||
if !exists {
|
||||
log.Info("Policy value can be: sync, create-only, create-update")
|
||||
@@ -184,7 +170,6 @@ func NewCommand() *cobra.Command {
|
||||
if err = (&controllers.ApplicationSetReconciler{
|
||||
Generators: topLevelGenerators,
|
||||
Client: mgr.GetClient(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("ApplicationSet"),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Recorder: mgr.GetEventRecorderFor("applicationset-controller"),
|
||||
Renderer: &utils.Render{},
|
||||
@@ -210,16 +195,16 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
|
||||
command.Flags().StringVar(&probeBindAddr, "probe-addr", ":8081", "The address the probe endpoint binds to.")
|
||||
command.Flags().StringVar(&webhookAddr, "webhook-addr", ":7000", "The address the webhook endpoint binds to.")
|
||||
command.Flags().BoolVar(&enableLeaderElection, "enable-leader-election", false,
|
||||
command.Flags().BoolVar(&enableLeaderElection, "enable-leader-election", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_LEADER_ELECTION", false),
|
||||
"Enable leader election for controller manager. "+
|
||||
"Enabling this will ensure there is only one active controller manager.")
|
||||
command.Flags().StringVar(&namespace, "namespace", "", "Argo CD repo namespace (default: argocd)")
|
||||
command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", "argocd-repo-server:8081", "Argo CD repo server address")
|
||||
command.Flags().StringVar(&policy, "policy", "sync", "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion)")
|
||||
command.Flags().BoolVar(&debugLog, "debug", false, "Print debug logs. Takes precedence over loglevel")
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().BoolVar(&dryRun, "dry-run", false, "Enable dry run mode")
|
||||
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&namespace, "namespace", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACE", ""), "Argo CD repo namespace (default: argocd)")
|
||||
command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER", common.DefaultRepoServerAddr), "Argo CD repo server address")
|
||||
command.Flags().StringVar(&policy, "policy", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_POLICY", "sync"), "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion)")
|
||||
command.Flags().BoolVar(&debugLog, "debug", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG", false), "Print debug logs. Takes precedence over loglevel")
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().BoolVar(&dryRun, "dry-run", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DRY_RUN", false), "Enable dry run mode")
|
||||
return &command
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ func NewAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
command.AddCommand(NewAccountGenerateTokenCommand(clientOpts))
|
||||
command.AddCommand(NewAccountGetCommand(clientOpts))
|
||||
command.AddCommand(NewAccountDeleteTokenCommand(clientOpts))
|
||||
command.AddCommand(NewBcryptCmd())
|
||||
return command
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ func NewAdminCommand() *cobra.Command {
|
||||
command.AddCommand(NewExportCommand())
|
||||
command.AddCommand(NewDashboardCommand())
|
||||
command.AddCommand(NewNotificationsCommand())
|
||||
command.AddCommand(NewInitialPasswordCommand())
|
||||
|
||||
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")
|
||||
|
||||
@@ -292,11 +292,11 @@ func saveToFile(err error, outputFormat string, result reconcileResults, outputP
|
||||
switch outputFormat {
|
||||
case "yaml":
|
||||
if data, err = yaml.Marshal(result); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error marshalling yaml: %w", err)
|
||||
}
|
||||
case "json":
|
||||
if data, err = json.Marshal(result); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error marshalling json: %w", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("format %s is not supported", outputFormat)
|
||||
|
||||
@@ -221,11 +221,11 @@ func printStatsSummary(clusters []ClusterWithInfo) {
|
||||
func runClusterNamespacesCommand(ctx context.Context, clientConfig clientcmd.ClientConfig, action func(appClient *versioned.Clientset, argoDB db.ArgoDB, clusters map[string][]string) error) error {
|
||||
clientCfg, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error while creating client config: %w", err)
|
||||
}
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error while getting namespace from client config: %w", err)
|
||||
}
|
||||
|
||||
kubeClient := kubernetes.NewForConfigOrDie(clientCfg)
|
||||
@@ -235,17 +235,16 @@ func runClusterNamespacesCommand(ctx context.Context, clientConfig clientcmd.Cli
|
||||
argoDB := db.NewDB(namespace, settingsMgr, kubeClient)
|
||||
clustersList, err := argoDB.ListClusters(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error listing clusters: %w", err)
|
||||
}
|
||||
appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error listing application: %w", err)
|
||||
}
|
||||
apps := appItems.Items
|
||||
for i, app := range apps {
|
||||
err := argo.ValidateDestination(ctx, &app.Spec.Destination, argoDB)
|
||||
if err != nil {
|
||||
return err
|
||||
if err := argo.ValidateDestination(ctx, &app.Spec.Destination, argoDB); err != nil {
|
||||
return fmt.Errorf("error validating application destination: %w", err)
|
||||
}
|
||||
apps[i] = app
|
||||
}
|
||||
@@ -349,15 +348,14 @@ func NewClusterEnableNamespacedMode() *cobra.Command {
|
||||
|
||||
cluster, err := argoDB.GetCluster(ctx, server)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting cluster from server: %w", err)
|
||||
}
|
||||
cluster.Namespaces = namespaces
|
||||
cluster.ClusterResources = clusterResources
|
||||
fmt.Printf("Setting cluster %s namespaces to %v...", server, namespaces)
|
||||
if !dryRun {
|
||||
_, err = argoDB.UpdateCluster(ctx, cluster)
|
||||
if err != nil {
|
||||
return err
|
||||
if _, err = argoDB.UpdateCluster(ctx, cluster); err != nil {
|
||||
return fmt.Errorf("error updating cluster: %w", err)
|
||||
}
|
||||
fmt.Println("done")
|
||||
} else {
|
||||
@@ -405,7 +403,7 @@ func NewClusterDisableNamespacedMode() *cobra.Command {
|
||||
|
||||
cluster, err := argoDB.GetCluster(ctx, server)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting cluster from server: %w", err)
|
||||
}
|
||||
|
||||
if len(cluster.Namespaces) == 0 {
|
||||
@@ -415,9 +413,8 @@ func NewClusterDisableNamespacedMode() *cobra.Command {
|
||||
cluster.Namespaces = nil
|
||||
fmt.Printf("Disabling namespaced mode for cluster %s...", server)
|
||||
if !dryRun {
|
||||
_, err = argoDB.UpdateCluster(ctx, cluster)
|
||||
if err != nil {
|
||||
return err
|
||||
if _, err = argoDB.UpdateCluster(ctx, cluster); err != nil {
|
||||
return fmt.Errorf("error updating cluster: %w", err)
|
||||
}
|
||||
fmt.Println("done")
|
||||
} else {
|
||||
|
||||
@@ -43,7 +43,7 @@ func PrintResources(output string, out io.Writer, resources ...interface{}) erro
|
||||
}
|
||||
filteredResource, err := omitFields(resource)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error omitting filtered fields from the resource: %w", err)
|
||||
}
|
||||
resources[i] = filteredResource
|
||||
}
|
||||
@@ -56,14 +56,14 @@ func PrintResources(output string, out io.Writer, resources ...interface{}) erro
|
||||
case "json":
|
||||
jsonBytes, err := json.MarshalIndent(obj, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error marshaling json: %w", err)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(out, string(jsonBytes))
|
||||
case "yaml":
|
||||
yamlBytes, err := yaml.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error marshaling yaml: %w", err)
|
||||
}
|
||||
// marshaled YAML already ends with the new line character
|
||||
_, _ = fmt.Fprint(out, string(yamlBytes))
|
||||
|
||||
46
cmd/argocd/commands/admin/initial_password.go
Normal file
46
cmd/argocd/commands/admin/initial_password.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
)
|
||||
|
||||
const initialPasswordSecretName = "argocd-initial-admin-secret"
|
||||
|
||||
// NewInitialPasswordCommand defines a new command to retrieve Argo CD initial password.
|
||||
func NewInitialPasswordCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "initial-password",
|
||||
Short: "Prints initial password to log in to Argo CD for the first time",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
|
||||
kubeClientset := kubernetes.NewForConfigOrDie(config)
|
||||
secret, err := kubeClientset.CoreV1().Secrets(namespace).Get(context.Background(), initialPasswordSecretName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
|
||||
if initialPass, ok := secret.Data["password"]; ok {
|
||||
fmt.Println(string(initialPass))
|
||||
fmt.Println("\n This password must be only used for first time login. We strongly recommend you update the password using `argocd account update-password`.")
|
||||
}
|
||||
},
|
||||
}
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
|
||||
return &command
|
||||
}
|
||||
@@ -33,7 +33,7 @@ func NewNotificationsCommand() *cobra.Command {
|
||||
var argocdService service.Service
|
||||
toolsCommand := cmd.NewToolsCommand(
|
||||
"notifications",
|
||||
"notifications",
|
||||
"argocd admin notifications",
|
||||
applications,
|
||||
settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm"), func(clientConfig clientcmd.ClientConfig) {
|
||||
k8sCfg, err := clientConfig.ClientConfig()
|
||||
|
||||
@@ -106,13 +106,13 @@ func saveProject(ctx context.Context, updated v1alpha1.AppProject, orig v1alpha1
|
||||
errors.CheckError(err)
|
||||
live, err := kube.ToUnstructured(&orig)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error converting project to unstructured: %w", err)
|
||||
}
|
||||
_ = cli.PrintDiff(updated.Name, target, live)
|
||||
if !dryRun {
|
||||
_, err = projectsIf.Update(ctx, &updated, v1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error while updating project: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -188,7 +188,7 @@ func NewUpdatePolicyRuleCommand() *cobra.Command {
|
||||
func updateProjects(ctx context.Context, projIf appclient.AppProjectInterface, projectGlob string, rolePattern string, action string, modification func(string, string) string, dryRun bool) error {
|
||||
projects, err := projIf.List(ctx, v1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error listing the projects: %w", err)
|
||||
}
|
||||
for _, proj := range projects.Items {
|
||||
if !globMatch(projectGlob, proj.Name) {
|
||||
@@ -225,7 +225,7 @@ func updateProjects(ctx context.Context, projIf appclient.AppProjectInterface, p
|
||||
if updated {
|
||||
err = saveProject(ctx, proj, *origProj, projIf, dryRun)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error saving the project: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2427,12 +2427,12 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
cli.InteractiveEdit(fmt.Sprintf("%s-*-edit.yaml", appName), appData, func(input []byte) error {
|
||||
input, err = yaml.YAMLToJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error converting YAML to JSON: %w", err)
|
||||
}
|
||||
updatedSpec := argoappv1.ApplicationSpec{}
|
||||
err = json.Unmarshal(input, &updatedSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error unmarshaling input into application spec: %w", err)
|
||||
}
|
||||
|
||||
var appOpts cmdutil.AppOptions
|
||||
@@ -2444,9 +2444,9 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
AppNamespace: &appNs,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to update application spec:\n%v", err)
|
||||
return fmt.Errorf("failed to update application spec: %w", err)
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
36
cmd/argocd/commands/bcrypt.go
Normal file
36
cmd/argocd/commands/bcrypt.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// bcryptCmd represents the bcrypt command
|
||||
func NewBcryptCmd() *cobra.Command {
|
||||
var (
|
||||
password string
|
||||
)
|
||||
var bcryptCmd = &cobra.Command{
|
||||
Use: "bcrypt",
|
||||
Short: "Generate bcrypt hash for the admin password",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
bytePassword := []byte(password)
|
||||
// Hashing the password
|
||||
hash, err := bcrypt.GenerateFromPassword(bytePassword, bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to genarate bcrypt hash: %v", err)
|
||||
}
|
||||
fmt.Fprint(cmd.OutOrStdout(), string(hash))
|
||||
},
|
||||
}
|
||||
|
||||
bcryptCmd.Flags().StringVar(&password, "password", "", "Password for which bcrypt hash is generated")
|
||||
err := bcryptCmd.MarkFlagRequired("password")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return bcryptCmd
|
||||
}
|
||||
22
cmd/argocd/commands/bcrypt_test.go
Normal file
22
cmd/argocd/commands/bcrypt_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func TestGeneratePassword(t *testing.T) {
|
||||
bcryptCmd := NewBcryptCmd()
|
||||
bcryptCmd.SetArgs([]string{"--password", "abc"})
|
||||
output := new(bytes.Buffer)
|
||||
bcryptCmd.SetOutput(output)
|
||||
err := bcryptCmd.Execute()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = bcrypt.CompareHashAndPassword(output.Bytes(), []byte("abc"))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -22,13 +22,13 @@ func PrintResource(resource interface{}, output string) error {
|
||||
case "json":
|
||||
jsonBytes, err := json.MarshalIndent(resource, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unable to marshal resource to json: %w", err)
|
||||
}
|
||||
fmt.Println(string(jsonBytes))
|
||||
case "yaml":
|
||||
yamlBytes, err := yaml.Marshal(resource)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unable to marshal resource to yaml: %w", err)
|
||||
}
|
||||
fmt.Print(string(yamlBytes))
|
||||
default:
|
||||
@@ -56,13 +56,13 @@ func PrintResourceList(resources interface{}, output string, single bool) error
|
||||
case "json":
|
||||
jsonBytes, err := json.MarshalIndent(resources, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unable to marshal resources to json: %w", err)
|
||||
}
|
||||
fmt.Println(string(jsonBytes))
|
||||
case "yaml":
|
||||
yamlBytes, err := yaml.Marshal(resources)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unable to marshal resources to yaml: %w", err)
|
||||
}
|
||||
fmt.Print(string(yamlBytes))
|
||||
default:
|
||||
|
||||
@@ -863,23 +863,23 @@ func NewProjectEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
cli.InteractiveEdit(fmt.Sprintf("%s-*-edit.yaml", projName), projData, func(input []byte) error {
|
||||
input, err = yaml.YAMLToJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error converting YAML to JSON: %w", err)
|
||||
}
|
||||
updatedSpec := v1alpha1.AppProjectSpec{}
|
||||
err = json.Unmarshal(input, &updatedSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error unmarshaling input into application spec: %w", err)
|
||||
}
|
||||
proj, err := projIf.Get(ctx, &projectpkg.ProjectQuery{Name: projName})
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not get project by project name: %w", err)
|
||||
}
|
||||
proj.Spec = updatedSpec
|
||||
_, err = projIf.Update(ctx, &projectpkg.ProjectUpdateRequest{Project: proj})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to update project:\n%v", err)
|
||||
return fmt.Errorf("failed to update project:\n%w", err)
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func NewCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
command.AddCommand(NewCompletionCommand())
|
||||
command.AddCommand(initialize.InitCommand(NewVersionCmd(&clientOpts)))
|
||||
command.AddCommand(initialize.InitCommand(NewVersionCmd(&clientOpts, nil)))
|
||||
command.AddCommand(initialize.InitCommand(NewClusterCommand(&clientOpts, pathOpts)))
|
||||
command.AddCommand(initialize.InitCommand(NewApplicationCommand(&clientOpts)))
|
||||
command.AddCommand(initialize.InitCommand(NewAppSetCommand(&clientOpts)))
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
// NewVersionCmd returns a new `version` command to be used as a sub-command to root
|
||||
func NewVersionCmd(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
func NewVersionCmd(clientOpts *argocdclient.ClientOptions, serverVersion *version.VersionMessage) *cobra.Command {
|
||||
var (
|
||||
short bool
|
||||
client bool
|
||||
@@ -54,7 +54,12 @@ func NewVersionCmd(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
}
|
||||
|
||||
if !client {
|
||||
sv := getServerVersion(ctx, clientOpts, cmd)
|
||||
var sv *version.VersionMessage
|
||||
if serverVersion == nil {
|
||||
sv = getServerVersion(ctx, clientOpts, cmd)
|
||||
} else {
|
||||
sv = serverVersion
|
||||
}
|
||||
|
||||
if short {
|
||||
v["server"] = map[string]string{"argocd-server": sv.Version}
|
||||
@@ -68,8 +73,13 @@ func NewVersionCmd(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
case "wide", "short", "":
|
||||
fmt.Fprint(cmd.OutOrStdout(), printClientVersion(&cv, short || (output == "short")))
|
||||
if !client {
|
||||
sv := getServerVersion(ctx, clientOpts, cmd)
|
||||
printServerVersion(sv, short || (output == "short"))
|
||||
var sv *version.VersionMessage
|
||||
if serverVersion == nil {
|
||||
sv = getServerVersion(ctx, clientOpts, cmd)
|
||||
} else {
|
||||
sv = serverVersion
|
||||
}
|
||||
fmt.Fprint(cmd.OutOrStdout(), printServerVersion(sv, short || (output == "short")))
|
||||
}
|
||||
default:
|
||||
log.Fatalf("unknown output format: %s", output)
|
||||
@@ -109,44 +119,45 @@ func printClientVersion(version *common.Version, short bool) string {
|
||||
return output
|
||||
}
|
||||
|
||||
func printServerVersion(version *version.VersionMessage, short bool) {
|
||||
fmt.Printf("%s: %s\n", "argocd-server", version.Version)
|
||||
func printServerVersion(version *version.VersionMessage, short bool) string {
|
||||
output := fmt.Sprintf("%s: %s\n", "argocd-server", version.Version)
|
||||
|
||||
if short {
|
||||
return
|
||||
return output
|
||||
}
|
||||
|
||||
if version.BuildDate != "" {
|
||||
fmt.Printf(" BuildDate: %s\n", version.BuildDate)
|
||||
output += fmt.Sprintf(" BuildDate: %s\n", version.BuildDate)
|
||||
}
|
||||
if version.GitCommit != "" {
|
||||
fmt.Printf(" GitCommit: %s\n", version.GitCommit)
|
||||
output += fmt.Sprintf(" GitCommit: %s\n", version.GitCommit)
|
||||
}
|
||||
if version.GitTreeState != "" {
|
||||
fmt.Printf(" GitTreeState: %s\n", version.GitTreeState)
|
||||
output += fmt.Sprintf(" GitTreeState: %s\n", version.GitTreeState)
|
||||
}
|
||||
if version.GitTag != "" {
|
||||
fmt.Printf(" GitTag: %s\n", version.GitTag)
|
||||
output += fmt.Sprintf(" GitTag: %s\n", version.GitTag)
|
||||
}
|
||||
if version.GoVersion != "" {
|
||||
fmt.Printf(" GoVersion: %s\n", version.GoVersion)
|
||||
output += fmt.Sprintf(" GoVersion: %s\n", version.GoVersion)
|
||||
}
|
||||
if version.Compiler != "" {
|
||||
fmt.Printf(" Compiler: %s\n", version.Compiler)
|
||||
output += fmt.Sprintf(" Compiler: %s\n", version.Compiler)
|
||||
}
|
||||
if version.Platform != "" {
|
||||
fmt.Printf(" Platform: %s\n", version.Platform)
|
||||
output += fmt.Sprintf(" Platform: %s\n", version.Platform)
|
||||
}
|
||||
if version.KustomizeVersion != "" {
|
||||
fmt.Printf(" Kustomize Version: %s\n", version.KustomizeVersion)
|
||||
output += fmt.Sprintf(" Kustomize Version: %s\n", version.KustomizeVersion)
|
||||
}
|
||||
if version.HelmVersion != "" {
|
||||
fmt.Printf(" Helm Version: %s\n", version.HelmVersion)
|
||||
output += fmt.Sprintf(" Helm Version: %s\n", version.HelmVersion)
|
||||
}
|
||||
if version.KubectlVersion != "" {
|
||||
fmt.Printf(" Kubectl Version: %s\n", version.KubectlVersion)
|
||||
output += fmt.Sprintf(" Kubectl Version: %s\n", version.KubectlVersion)
|
||||
}
|
||||
if version.JsonnetVersion != "" {
|
||||
fmt.Printf(" Jsonnet Version: %s\n", version.JsonnetVersion)
|
||||
output += fmt.Sprintf(" Jsonnet Version: %s\n", version.JsonnetVersion)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ import (
|
||||
"testing"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient/version"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestShortVersion(t *testing.T) {
|
||||
func TestShortVersionClient(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewVersionCmd(&argocdclient.ClientOptions{})
|
||||
cmd := NewVersionCmd(&argocdclient.ClientOptions{}, nil)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.SetArgs([]string{"version", "--short", "--client"})
|
||||
err := cmd.Execute()
|
||||
@@ -20,3 +21,17 @@ func TestShortVersion(t *testing.T) {
|
||||
output := buf.String()
|
||||
assert.Equal(t, output, "argocd: v99.99.99+unknown\n")
|
||||
}
|
||||
|
||||
func TestShortVersion(t *testing.T) {
|
||||
serverVersion := &version.VersionMessage{Version: "v99.99.99+unknown"}
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewVersionCmd(&argocdclient.ClientOptions{}, serverVersion)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.SetArgs([]string{"argocd", "version", "--short"})
|
||||
err := cmd.Execute()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to execute short version command")
|
||||
}
|
||||
output := buf.String()
|
||||
assert.Equal(t, output, "argocd: v99.99.99+unknown\nargocd-server: v99.99.99+unknown\n")
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func readAppsetFromURI(fileURL string, appset *[]*argoprojiov1alpha1.Application
|
||||
|
||||
yml, err := readFilePayload()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error reading file payload: %w", err)
|
||||
}
|
||||
|
||||
return readAppset(yml, appset)
|
||||
@@ -49,18 +49,18 @@ func readAppsetFromURI(fileURL string, appset *[]*argoprojiov1alpha1.Application
|
||||
func readAppset(yml []byte, appsets *[]*argoprojiov1alpha1.ApplicationSet) error {
|
||||
yamls, err := kube.SplitYAMLToString(yml)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error splitting YAML to string: %w", err)
|
||||
}
|
||||
|
||||
for _, yml := range yamls {
|
||||
var appset argoprojiov1alpha1.ApplicationSet
|
||||
err = config.Unmarshal([]byte(yml), &appset)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error unmarshalling appset: %w", err)
|
||||
}
|
||||
*appsets = append(*appsets, &appset)
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
return fmt.Errorf("error reading app set: %w", err)
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ func readProjFromURI(fileURL string, proj *v1alpha1.AppProject) error {
|
||||
} else {
|
||||
err = config.UnmarshalRemoteFile(fileURL, &proj)
|
||||
}
|
||||
return err
|
||||
return fmt.Errorf("error reading proj from uri: %w", err)
|
||||
}
|
||||
|
||||
func SetProjSpecOptions(flags *pflag.FlagSet, spec *v1alpha1.AppProjectSpec, projOpts *ProjectOpts) int {
|
||||
|
||||
@@ -6,6 +6,7 @@ package apiclient
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
apiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
@@ -362,6 +363,55 @@ func (m *RepositoryResponse) GetIsSupported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ParametersAnnouncementResponse contains a list of announcements. This list represents all the parameters which a CMP
|
||||
// is able to accept.
|
||||
type ParametersAnnouncementResponse struct {
|
||||
ParameterAnnouncements []*apiclient.ParameterAnnouncement `protobuf:"bytes,1,rep,name=parameterAnnouncements,proto3" json:"parameterAnnouncements,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ParametersAnnouncementResponse) Reset() { *m = ParametersAnnouncementResponse{} }
|
||||
func (m *ParametersAnnouncementResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ParametersAnnouncementResponse) ProtoMessage() {}
|
||||
func (*ParametersAnnouncementResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_b21875a7079a06ed, []int{5}
|
||||
}
|
||||
func (m *ParametersAnnouncementResponse) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *ParametersAnnouncementResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_ParametersAnnouncementResponse.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *ParametersAnnouncementResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ParametersAnnouncementResponse.Merge(m, src)
|
||||
}
|
||||
func (m *ParametersAnnouncementResponse) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *ParametersAnnouncementResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ParametersAnnouncementResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ParametersAnnouncementResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *ParametersAnnouncementResponse) GetParameterAnnouncements() []*apiclient.ParameterAnnouncement {
|
||||
if m != nil {
|
||||
return m.ParameterAnnouncements
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Chunk []byte `protobuf:"bytes,1,opt,name=chunk,proto3" json:"chunk,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
@@ -373,7 +423,7 @@ func (m *File) Reset() { *m = File{} }
|
||||
func (m *File) String() string { return proto.CompactTextString(m) }
|
||||
func (*File) ProtoMessage() {}
|
||||
func (*File) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_b21875a7079a06ed, []int{5}
|
||||
return fileDescriptor_b21875a7079a06ed, []int{6}
|
||||
}
|
||||
func (m *File) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -415,44 +465,49 @@ func init() {
|
||||
proto.RegisterType((*EnvEntry)(nil), "plugin.EnvEntry")
|
||||
proto.RegisterType((*ManifestResponse)(nil), "plugin.ManifestResponse")
|
||||
proto.RegisterType((*RepositoryResponse)(nil), "plugin.RepositoryResponse")
|
||||
proto.RegisterType((*ParametersAnnouncementResponse)(nil), "plugin.ParametersAnnouncementResponse")
|
||||
proto.RegisterType((*File)(nil), "plugin.File")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("cmpserver/plugin/plugin.proto", fileDescriptor_b21875a7079a06ed) }
|
||||
|
||||
var fileDescriptor_b21875a7079a06ed = []byte{
|
||||
// 483 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0xd1, 0x8e, 0x12, 0x3d,
|
||||
0x14, 0xa6, 0x3f, 0xec, 0x2e, 0x1c, 0x36, 0xf9, 0x49, 0x63, 0xe2, 0x84, 0xec, 0x22, 0x99, 0x2b,
|
||||
0x6e, 0x84, 0x04, 0x8d, 0x77, 0x26, 0xba, 0x66, 0x75, 0xa3, 0xc1, 0x90, 0xe2, 0x95, 0x77, 0xdd,
|
||||
0x72, 0x80, 0xba, 0x33, 0x6d, 0x6d, 0x3b, 0x93, 0xe0, 0x95, 0x6f, 0xe3, 0x2b, 0xf8, 0x08, 0x5e,
|
||||
0xfa, 0x08, 0x86, 0x27, 0x31, 0x53, 0x66, 0x18, 0xe2, 0x46, 0xaf, 0x38, 0xdf, 0x77, 0xce, 0xf9,
|
||||
0xf8, 0xbe, 0x4e, 0x0b, 0x97, 0x22, 0x35, 0x0e, 0x6d, 0x8e, 0x76, 0x62, 0x92, 0x6c, 0x2d, 0x55,
|
||||
0xf9, 0x33, 0x36, 0x56, 0x7b, 0x4d, 0x4f, 0xf7, 0x28, 0xfe, 0x4a, 0xa0, 0xf7, 0xd2, 0x98, 0x85,
|
||||
0xb7, 0xc8, 0x53, 0x86, 0x9f, 0x33, 0x74, 0x9e, 0x3e, 0x87, 0x76, 0x8a, 0x9e, 0x2f, 0xb9, 0xe7,
|
||||
0x11, 0x19, 0x92, 0x51, 0x77, 0xfa, 0x68, 0x5c, 0x6e, 0xcf, 0xb8, 0x92, 0x2b, 0x74, 0xbe, 0x1c,
|
||||
0x9d, 0x95, 0x63, 0x37, 0x0d, 0x76, 0x58, 0xa1, 0x31, 0xb4, 0x56, 0x32, 0xc1, 0xe8, 0xbf, 0xb0,
|
||||
0x7a, 0x5e, 0xad, 0xbe, 0x96, 0x09, 0xde, 0x34, 0x58, 0xe8, 0x5d, 0x75, 0xe0, 0xcc, 0xee, 0x25,
|
||||
0xe2, 0x6f, 0x04, 0x1e, 0xfe, 0x45, 0x96, 0x46, 0x70, 0xc6, 0x8d, 0x79, 0xcf, 0x53, 0x0c, 0x46,
|
||||
0x3a, 0xac, 0x82, 0x74, 0x00, 0xc0, 0x8d, 0x61, 0x98, 0xcc, 0xb9, 0xdf, 0x84, 0xbf, 0xea, 0xb0,
|
||||
0x23, 0x86, 0xf6, 0xa1, 0x2d, 0x36, 0x28, 0xee, 0x5c, 0x96, 0x46, 0xcd, 0xd0, 0x3d, 0x60, 0x4a,
|
||||
0xa1, 0xe5, 0xe4, 0x17, 0x8c, 0x5a, 0x43, 0x32, 0x6a, 0xb2, 0x50, 0xd3, 0x18, 0x9a, 0xa8, 0xf2,
|
||||
0xe8, 0x64, 0xd8, 0x1c, 0x75, 0xa7, 0xbd, 0xca, 0xf3, 0xb5, 0xca, 0xaf, 0x95, 0xb7, 0x5b, 0x56,
|
||||
0x34, 0xe3, 0xa7, 0xd0, 0xae, 0x88, 0x42, 0x43, 0xd5, 0xb6, 0x42, 0x4d, 0x1f, 0xc0, 0x49, 0xce,
|
||||
0x93, 0x0c, 0x4b, 0x3b, 0x7b, 0x10, 0xcf, 0xa1, 0x57, 0xc7, 0x73, 0x46, 0x2b, 0x87, 0xf4, 0x02,
|
||||
0x3a, 0x69, 0xc9, 0xb9, 0x88, 0x0c, 0x9b, 0xa3, 0x0e, 0xab, 0x89, 0x22, 0x9b, 0xd3, 0x99, 0x15,
|
||||
0xf8, 0x61, 0x6b, 0x2a, 0xb1, 0x23, 0x26, 0x7e, 0x06, 0x94, 0xa1, 0xd1, 0x4e, 0x7a, 0x6d, 0xb7,
|
||||
0x07, 0xcd, 0x21, 0x74, 0xa5, 0x5b, 0x64, 0xc6, 0x68, 0xeb, 0x71, 0x19, 0x8c, 0xb5, 0xd9, 0x31,
|
||||
0x15, 0x5f, 0x40, 0xab, 0xf8, 0x08, 0x85, 0x4f, 0xb1, 0xc9, 0xd4, 0x5d, 0x98, 0x39, 0x67, 0x7b,
|
||||
0x30, 0xfd, 0x4e, 0xe0, 0xf2, 0x95, 0x56, 0x2b, 0xb9, 0x9e, 0x71, 0xc5, 0xd7, 0x98, 0xa2, 0xf2,
|
||||
0xf3, 0x70, 0x0c, 0x0b, 0xb4, 0xb9, 0x14, 0x48, 0xdf, 0x42, 0xef, 0x0d, 0x2a, 0xb4, 0xdc, 0x63,
|
||||
0x95, 0x88, 0x46, 0xd5, 0x51, 0xfd, 0x79, 0x8b, 0xfa, 0xd1, 0xfd, 0x3b, 0xb3, 0x77, 0x1a, 0x37,
|
||||
0x46, 0x84, 0xbe, 0x83, 0xff, 0x67, 0xdc, 0x8b, 0x4d, 0x1d, 0xe4, 0x1f, 0x52, 0xfd, 0xaa, 0x73,
|
||||
0x3f, 0x76, 0x21, 0x76, 0xf5, 0xe2, 0xc7, 0x6e, 0x40, 0x7e, 0xee, 0x06, 0xe4, 0xd7, 0x6e, 0x40,
|
||||
0x3e, 0x4e, 0xd7, 0xd2, 0x6f, 0xb2, 0xdb, 0xb1, 0xd0, 0xe9, 0x84, 0xdb, 0xb5, 0x36, 0x56, 0x7f,
|
||||
0x0a, 0xc5, 0x63, 0xb1, 0x9c, 0xe4, 0xd3, 0x49, 0xfd, 0x32, 0xb8, 0x91, 0x22, 0x91, 0xa8, 0xfc,
|
||||
0xed, 0x69, 0x78, 0x16, 0x4f, 0x7e, 0x07, 0x00, 0x00, 0xff, 0xff, 0x83, 0x01, 0x5e, 0x48, 0x37,
|
||||
0x03, 0x00, 0x00,
|
||||
// 558 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0xc1, 0x6e, 0xd3, 0x4c,
|
||||
0x10, 0xae, 0x9b, 0xb4, 0x4d, 0x26, 0x95, 0xfe, 0x68, 0xf5, 0x0b, 0x4c, 0xd4, 0x86, 0xe0, 0x03,
|
||||
0xca, 0x85, 0x44, 0x32, 0x88, 0x1b, 0x12, 0x2d, 0x2a, 0xad, 0x40, 0x41, 0xd1, 0x96, 0x0b, 0xdc,
|
||||
0xb6, 0xce, 0x24, 0x59, 0x6a, 0xef, 0x2e, 0xeb, 0xb5, 0xa5, 0xc0, 0x85, 0xf7, 0xe0, 0x01, 0x78,
|
||||
0x15, 0x8e, 0x3c, 0x02, 0xca, 0x93, 0x20, 0xaf, 0xed, 0xd8, 0xa2, 0x6d, 0x38, 0x79, 0xe6, 0x9b,
|
||||
0x99, 0x6f, 0xbf, 0x9d, 0x99, 0x35, 0x1c, 0x07, 0x91, 0x8a, 0x51, 0xa7, 0xa8, 0xc7, 0x2a, 0x4c,
|
||||
0x16, 0x5c, 0x14, 0x9f, 0x91, 0xd2, 0xd2, 0x48, 0xb2, 0x9f, 0x7b, 0xbd, 0xb3, 0x05, 0x37, 0xcb,
|
||||
0xe4, 0x6a, 0x14, 0xc8, 0x68, 0xcc, 0xf4, 0x42, 0x2a, 0x2d, 0x3f, 0x59, 0xe3, 0x49, 0x30, 0x1b,
|
||||
0xa7, 0xfe, 0x58, 0xa3, 0x92, 0x05, 0x8d, 0x35, 0xb9, 0x91, 0x7a, 0x55, 0x33, 0x73, 0x3a, 0xef,
|
||||
0x9b, 0x03, 0xdd, 0x13, 0xa5, 0x2e, 0x8d, 0x46, 0x16, 0x51, 0xfc, 0x9c, 0x60, 0x6c, 0xc8, 0x0b,
|
||||
0x68, 0x45, 0x68, 0xd8, 0x8c, 0x19, 0xe6, 0x3a, 0x03, 0x67, 0xd8, 0xf1, 0x1f, 0x8e, 0x0a, 0x11,
|
||||
0x13, 0x26, 0xf8, 0x1c, 0x63, 0x53, 0xa4, 0x4e, 0x8a, 0xb4, 0x8b, 0x1d, 0xba, 0x29, 0x21, 0x1e,
|
||||
0x34, 0xe7, 0x3c, 0x44, 0x77, 0xd7, 0x96, 0x1e, 0x96, 0xa5, 0xaf, 0x79, 0x88, 0x17, 0x3b, 0xd4,
|
||||
0xc6, 0x4e, 0xdb, 0x70, 0xa0, 0x73, 0x0a, 0xef, 0x87, 0x03, 0xf7, 0xef, 0xa0, 0x25, 0x2e, 0x1c,
|
||||
0x30, 0xa5, 0xde, 0xb1, 0x08, 0xad, 0x90, 0x36, 0x2d, 0x5d, 0xd2, 0x07, 0x60, 0x4a, 0x51, 0x0c,
|
||||
0xa7, 0xcc, 0x2c, 0xed, 0x51, 0x6d, 0x5a, 0x43, 0x48, 0x0f, 0x5a, 0xc1, 0x12, 0x83, 0xeb, 0x38,
|
||||
0x89, 0xdc, 0x86, 0x8d, 0x6e, 0x7c, 0x42, 0xa0, 0x19, 0xf3, 0x2f, 0xe8, 0x36, 0x07, 0xce, 0xb0,
|
||||
0x41, 0xad, 0x4d, 0x3c, 0x68, 0xa0, 0x48, 0xdd, 0xbd, 0x41, 0x63, 0xd8, 0xf1, 0xbb, 0xa5, 0xe6,
|
||||
0x33, 0x91, 0x9e, 0x09, 0xa3, 0x57, 0x34, 0x0b, 0x7a, 0xcf, 0xa0, 0x55, 0x02, 0x19, 0x87, 0xa8,
|
||||
0x64, 0x59, 0x9b, 0xfc, 0x0f, 0x7b, 0x29, 0x0b, 0x13, 0x2c, 0xe4, 0xe4, 0x8e, 0x37, 0x85, 0x6e,
|
||||
0x75, 0xbd, 0x58, 0x49, 0x11, 0x23, 0x39, 0x82, 0x76, 0x54, 0x60, 0xb1, 0xeb, 0x0c, 0x1a, 0xc3,
|
||||
0x36, 0xad, 0x80, 0xec, 0x6e, 0xb1, 0x4c, 0x74, 0x80, 0xef, 0x57, 0xaa, 0x24, 0xab, 0x21, 0xde,
|
||||
0x73, 0x20, 0x74, 0x33, 0xc8, 0x0d, 0xe7, 0x00, 0x3a, 0x3c, 0xbe, 0x4c, 0x94, 0x92, 0xda, 0xe0,
|
||||
0xcc, 0x0a, 0x6b, 0xd1, 0x3a, 0xe4, 0x7d, 0x85, 0xfe, 0x94, 0x69, 0x16, 0xa1, 0x41, 0x1d, 0x9f,
|
||||
0x08, 0x21, 0x13, 0x11, 0x60, 0x84, 0xa2, 0xd2, 0xf5, 0x01, 0xee, 0xa9, 0x32, 0xa3, 0x9e, 0x90,
|
||||
0x8b, 0xec, 0xf8, 0x8f, 0x46, 0xb5, 0x0d, 0x9a, 0xde, 0x96, 0x49, 0xef, 0x20, 0xf0, 0x8e, 0xa0,
|
||||
0x99, 0x6d, 0x40, 0xd6, 0xa4, 0x60, 0x99, 0x88, 0x6b, 0x2b, 0xf0, 0x90, 0xe6, 0x8e, 0xff, 0x7d,
|
||||
0x17, 0x8e, 0x5f, 0x49, 0x31, 0xe7, 0x8b, 0x09, 0x13, 0x6c, 0x61, 0x6b, 0xa6, 0x76, 0x06, 0x97,
|
||||
0xa8, 0x53, 0x1e, 0x20, 0x79, 0x03, 0xdd, 0x73, 0x14, 0xa8, 0x99, 0xc1, 0xb2, 0x9d, 0xc4, 0x2d,
|
||||
0xe7, 0xf4, 0xf7, 0x0a, 0xf7, 0xdc, 0x9b, 0x0b, 0x9b, 0x5f, 0xd1, 0xdb, 0x19, 0x3a, 0xe4, 0x2d,
|
||||
0xfc, 0x37, 0x61, 0x26, 0x58, 0x56, 0x5d, 0xdc, 0x42, 0xd5, 0x2b, 0x23, 0x37, 0x7b, 0x6e, 0xc9,
|
||||
0x18, 0x3c, 0x38, 0x47, 0x73, 0x7b, 0x63, 0xb7, 0xd0, 0x3e, 0x2e, 0x23, 0xdb, 0x47, 0x92, 0x1d,
|
||||
0x71, 0xfa, 0xf2, 0xe7, 0xba, 0xef, 0xfc, 0x5a, 0xf7, 0x9d, 0xdf, 0xeb, 0xbe, 0xf3, 0xd1, 0xff,
|
||||
0xc7, 0xd3, 0xaf, 0x7e, 0x20, 0x4c, 0xf1, 0x20, 0xe4, 0x28, 0xcc, 0xd5, 0xbe, 0x7d, 0xee, 0x4f,
|
||||
0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x33, 0x34, 0xb3, 0x95, 0x5e, 0x04, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -472,6 +527,8 @@ type ConfigManagementPluginServiceClient interface {
|
||||
GenerateManifest(ctx context.Context, opts ...grpc.CallOption) (ConfigManagementPluginService_GenerateManifestClient, error)
|
||||
// MatchRepository returns whether or not the given application is supported by the plugin
|
||||
MatchRepository(ctx context.Context, opts ...grpc.CallOption) (ConfigManagementPluginService_MatchRepositoryClient, error)
|
||||
// GetParametersAnnouncement gets a list of parameter announcements for the given app
|
||||
GetParametersAnnouncement(ctx context.Context, opts ...grpc.CallOption) (ConfigManagementPluginService_GetParametersAnnouncementClient, error)
|
||||
}
|
||||
|
||||
type configManagementPluginServiceClient struct {
|
||||
@@ -550,6 +607,40 @@ func (x *configManagementPluginServiceMatchRepositoryClient) CloseAndRecv() (*Re
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *configManagementPluginServiceClient) GetParametersAnnouncement(ctx context.Context, opts ...grpc.CallOption) (ConfigManagementPluginService_GetParametersAnnouncementClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_ConfigManagementPluginService_serviceDesc.Streams[2], "/plugin.ConfigManagementPluginService/GetParametersAnnouncement", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &configManagementPluginServiceGetParametersAnnouncementClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type ConfigManagementPluginService_GetParametersAnnouncementClient interface {
|
||||
Send(*AppStreamRequest) error
|
||||
CloseAndRecv() (*ParametersAnnouncementResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type configManagementPluginServiceGetParametersAnnouncementClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *configManagementPluginServiceGetParametersAnnouncementClient) Send(m *AppStreamRequest) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *configManagementPluginServiceGetParametersAnnouncementClient) CloseAndRecv() (*ParametersAnnouncementResponse, error) {
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := new(ParametersAnnouncementResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ConfigManagementPluginServiceServer is the server API for ConfigManagementPluginService service.
|
||||
type ConfigManagementPluginServiceServer interface {
|
||||
// GenerateManifests receive a stream containing a tgz archive with all required files necessary
|
||||
@@ -557,6 +648,8 @@ type ConfigManagementPluginServiceServer interface {
|
||||
GenerateManifest(ConfigManagementPluginService_GenerateManifestServer) error
|
||||
// MatchRepository returns whether or not the given application is supported by the plugin
|
||||
MatchRepository(ConfigManagementPluginService_MatchRepositoryServer) error
|
||||
// GetParametersAnnouncement gets a list of parameter announcements for the given app
|
||||
GetParametersAnnouncement(ConfigManagementPluginService_GetParametersAnnouncementServer) error
|
||||
}
|
||||
|
||||
// UnimplementedConfigManagementPluginServiceServer can be embedded to have forward compatible implementations.
|
||||
@@ -569,6 +662,9 @@ func (*UnimplementedConfigManagementPluginServiceServer) GenerateManifest(srv Co
|
||||
func (*UnimplementedConfigManagementPluginServiceServer) MatchRepository(srv ConfigManagementPluginService_MatchRepositoryServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method MatchRepository not implemented")
|
||||
}
|
||||
func (*UnimplementedConfigManagementPluginServiceServer) GetParametersAnnouncement(srv ConfigManagementPluginService_GetParametersAnnouncementServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method GetParametersAnnouncement not implemented")
|
||||
}
|
||||
|
||||
func RegisterConfigManagementPluginServiceServer(s *grpc.Server, srv ConfigManagementPluginServiceServer) {
|
||||
s.RegisterService(&_ConfigManagementPluginService_serviceDesc, srv)
|
||||
@@ -626,6 +722,32 @@ func (x *configManagementPluginServiceMatchRepositoryServer) Recv() (*AppStreamR
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func _ConfigManagementPluginService_GetParametersAnnouncement_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(ConfigManagementPluginServiceServer).GetParametersAnnouncement(&configManagementPluginServiceGetParametersAnnouncementServer{stream})
|
||||
}
|
||||
|
||||
type ConfigManagementPluginService_GetParametersAnnouncementServer interface {
|
||||
SendAndClose(*ParametersAnnouncementResponse) error
|
||||
Recv() (*AppStreamRequest, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type configManagementPluginServiceGetParametersAnnouncementServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *configManagementPluginServiceGetParametersAnnouncementServer) SendAndClose(m *ParametersAnnouncementResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *configManagementPluginServiceGetParametersAnnouncementServer) Recv() (*AppStreamRequest, error) {
|
||||
m := new(AppStreamRequest)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
var _ConfigManagementPluginService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "plugin.ConfigManagementPluginService",
|
||||
HandlerType: (*ConfigManagementPluginServiceServer)(nil),
|
||||
@@ -641,6 +763,11 @@ var _ConfigManagementPluginService_serviceDesc = grpc.ServiceDesc{
|
||||
Handler: _ConfigManagementPluginService_MatchRepository_Handler,
|
||||
ClientStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "GetParametersAnnouncement",
|
||||
Handler: _ConfigManagementPluginService_GetParametersAnnouncement_Handler,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "cmpserver/plugin/plugin.proto",
|
||||
}
|
||||
@@ -911,6 +1038,47 @@ func (m *RepositoryResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *ParametersAnnouncementResponse) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *ParametersAnnouncementResponse) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *ParametersAnnouncementResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.XXX_unrecognized != nil {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.ParameterAnnouncements) > 0 {
|
||||
for iNdEx := len(m.ParameterAnnouncements) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.ParameterAnnouncements[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintPlugin(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *File) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
@@ -1085,6 +1253,24 @@ func (m *RepositoryResponse) Size() (n int) {
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ParametersAnnouncementResponse) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.ParameterAnnouncements) > 0 {
|
||||
for _, e := range m.ParameterAnnouncements {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovPlugin(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *File) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
@@ -1729,6 +1915,91 @@ func (m *RepositoryResponse) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ParametersAnnouncementResponse) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowPlugin
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: ParametersAnnouncementResponse: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ParametersAnnouncementResponse: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ParameterAnnouncements", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowPlugin
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthPlugin
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthPlugin
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.ParameterAnnouncements = append(m.ParameterAnnouncements, &apiclient.ParameterAnnouncement{})
|
||||
if err := m.ParameterAnnouncements[len(m.ParameterAnnouncements)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipPlugin(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthPlugin
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *File) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
configUtil "github.com/argoproj/argo-cd/v2/util/config"
|
||||
)
|
||||
|
||||
@@ -21,12 +22,11 @@ type PluginConfig struct {
|
||||
}
|
||||
|
||||
type PluginConfigSpec struct {
|
||||
Version string `json:"version"`
|
||||
Init Command `json:"init,omitempty"`
|
||||
Generate Command `json:"generate"`
|
||||
Discover Discover `json:"discover"`
|
||||
AllowConcurrency bool `json:"allowConcurrency"`
|
||||
LockRepo bool `json:"lockRepo"`
|
||||
Version string `json:"version"`
|
||||
Init Command `json:"init,omitempty"`
|
||||
Generate Command `json:"generate"`
|
||||
Discover Discover `json:"discover"`
|
||||
Parameters Parameters `yaml:"parameters"`
|
||||
}
|
||||
|
||||
//Discover holds find and fileName
|
||||
@@ -47,6 +47,17 @@ type Find struct {
|
||||
Glob string `json:"glob"`
|
||||
}
|
||||
|
||||
// Parameters holds static and dynamic configurations
|
||||
type Parameters struct {
|
||||
Static []*apiclient.ParameterAnnouncement `yaml:"static"`
|
||||
Dynamic Command `yaml:"dynamic"`
|
||||
}
|
||||
|
||||
// Dynamic hold the dynamic announcements for CMP's
|
||||
type Dynamic struct {
|
||||
Command
|
||||
}
|
||||
|
||||
func ReadPluginConfig(filePath string) (*PluginConfig, error) {
|
||||
path := fmt.Sprintf("%s/%s", strings.TrimRight(filePath, "/"), common.PluginConfigFileName)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package plugin
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -13,7 +14,9 @@ import (
|
||||
|
||||
"github.com/argoproj/pkg/rand"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmpserver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
repoclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/buffered_context"
|
||||
"github.com/argoproj/argo-cd/v2/util/cmp"
|
||||
"github.com/argoproj/argo-cd/v2/util/io/files"
|
||||
@@ -21,8 +24,6 @@ import (
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/mattn/go-zglob"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmpserver/apiclient"
|
||||
)
|
||||
|
||||
// cmpTimeoutBuffer is the amount of time before the request deadline to timeout server-side work. It makes sure there's
|
||||
@@ -45,15 +46,14 @@ func NewService(initConstants CMPServerInitConstants) *Service {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Init() error {
|
||||
workDir := common.GetCMPWorkDir()
|
||||
func (s *Service) Init(workDir string) error {
|
||||
err := os.RemoveAll(workDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error removing workdir %q: %s", workDir, err)
|
||||
return fmt.Errorf("error removing workdir %q: %w", workDir, err)
|
||||
}
|
||||
err = os.MkdirAll(workDir, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating workdir %q: %s", workDir, err)
|
||||
return fmt.Errorf("error creating workdir %q: %w", workDir, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -143,24 +143,50 @@ func environ(envVars []*apiclient.EnvEntry) []string {
|
||||
return environ
|
||||
}
|
||||
|
||||
// getTempDirMustCleanup creates a temporary directory and returns a cleanup function.
|
||||
func getTempDirMustCleanup(baseDir string) (workDir string, cleanup func(), err error) {
|
||||
workDir, err = files.CreateTempDir(baseDir)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error creating temp dir: %w", err)
|
||||
}
|
||||
cleanup = func() {
|
||||
if err := os.RemoveAll(workDir); err != nil {
|
||||
log.WithFields(map[string]interface{}{
|
||||
common.SecurityField: common.SecurityHigh,
|
||||
common.SecurityCWEField: 459,
|
||||
}).Errorf("Failed to clean up temp directory: %s", err)
|
||||
}
|
||||
}
|
||||
return workDir, cleanup, nil
|
||||
}
|
||||
|
||||
type Stream interface {
|
||||
Recv() (*apiclient.AppStreamRequest, error)
|
||||
Context() context.Context
|
||||
}
|
||||
|
||||
type GenerateManifestStream interface {
|
||||
Stream
|
||||
SendAndClose(response *apiclient.ManifestResponse) error
|
||||
}
|
||||
|
||||
// GenerateManifest runs generate command from plugin config file and returns generated manifest files
|
||||
func (s *Service) GenerateManifest(stream apiclient.ConfigManagementPluginService_GenerateManifestServer) error {
|
||||
return s.generateManifestGeneric(stream)
|
||||
}
|
||||
|
||||
func (s *Service) generateManifestGeneric(stream GenerateManifestStream) error {
|
||||
ctx, cancel := buffered_context.WithEarlierDeadline(stream.Context(), cmpTimeoutBuffer)
|
||||
defer cancel()
|
||||
workDir, err := files.CreateTempDir(common.GetCMPWorkDir())
|
||||
workDir, cleanup, err := getTempDirMustCleanup(common.GetCMPWorkDir())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating temp dir: %s", err)
|
||||
return fmt.Errorf("error creating workdir for manifest generation: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := os.RemoveAll(workDir); err != nil {
|
||||
// we panic here as the workDir may contain sensitive information
|
||||
panic(fmt.Sprintf("error removing generate manifest workdir: %s", err))
|
||||
}
|
||||
}()
|
||||
defer cleanup()
|
||||
|
||||
metadata, err := cmp.ReceiveRepoStream(ctx, stream, workDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate manifest error receiving stream: %s", err)
|
||||
return fmt.Errorf("generate manifest error receiving stream: %w", err)
|
||||
}
|
||||
|
||||
appPath := filepath.Clean(filepath.Join(workDir, metadata.AppRelPath))
|
||||
@@ -169,11 +195,11 @@ func (s *Service) GenerateManifest(stream apiclient.ConfigManagementPluginServic
|
||||
}
|
||||
response, err := s.generateManifest(ctx, appPath, metadata.GetEnv())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating manifests: %s", err)
|
||||
return fmt.Errorf("error generating manifests: %w", err)
|
||||
}
|
||||
err = stream.SendAndClose(response)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error sending manifest response: %s", err)
|
||||
return fmt.Errorf("error sending manifest response: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -203,6 +229,11 @@ func (s *Service) generateManifest(ctx context.Context, appDir string, envEntrie
|
||||
|
||||
manifests, err := kube.SplitYAMLToString([]byte(out))
|
||||
if err != nil {
|
||||
sanitizedManifests := manifests
|
||||
if len(sanitizedManifests) > 1000 {
|
||||
sanitizedManifests = manifests[:1000]
|
||||
}
|
||||
log.Debugf("Failed to split generated manifests. Beginning of generated manifests: %q", sanitizedManifests)
|
||||
return &apiclient.ManifestResponse{}, err
|
||||
}
|
||||
|
||||
@@ -211,42 +242,46 @@ func (s *Service) generateManifest(ctx context.Context, appDir string, envEntrie
|
||||
}, err
|
||||
}
|
||||
|
||||
type MatchRepositoryStream interface {
|
||||
Stream
|
||||
SendAndClose(response *apiclient.RepositoryResponse) error
|
||||
}
|
||||
|
||||
// MatchRepository receives the application stream and checks whether
|
||||
// its repository type is supported by the config management plugin
|
||||
// server.
|
||||
//The checks are implemented in the following order:
|
||||
// 1. If spec.Discover.FileName is provided it finds for a name match in Applications files
|
||||
// 2. If spec.Discover.Find.Glob is provided if finds for a glob match in Applications files
|
||||
// 3. Otherwise it runs the spec.Discover.Find.Command
|
||||
// The checks are implemented in the following order:
|
||||
// 1. If spec.Discover.FileName is provided it finds for a name match in Applications files
|
||||
// 2. If spec.Discover.Find.Glob is provided if finds for a glob match in Applications files
|
||||
// 3. Otherwise it runs the spec.Discover.Find.Command
|
||||
func (s *Service) MatchRepository(stream apiclient.ConfigManagementPluginService_MatchRepositoryServer) error {
|
||||
return s.matchRepositoryGeneric(stream)
|
||||
}
|
||||
|
||||
func (s *Service) matchRepositoryGeneric(stream MatchRepositoryStream) error {
|
||||
bufferedCtx, cancel := buffered_context.WithEarlierDeadline(stream.Context(), cmpTimeoutBuffer)
|
||||
defer cancel()
|
||||
|
||||
workDir, err := files.CreateTempDir(common.GetCMPWorkDir())
|
||||
workDir, cleanup, err := getTempDirMustCleanup(common.GetCMPWorkDir())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating match repository workdir: %s", err)
|
||||
return fmt.Errorf("error creating workdir for repository matching: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := os.RemoveAll(workDir); err != nil {
|
||||
// we panic here as the workDir may contain sensitive information
|
||||
panic(fmt.Sprintf("error removing match repository workdir: %s", err))
|
||||
}
|
||||
}()
|
||||
defer cleanup()
|
||||
|
||||
metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("match repository error receiving stream: %s", err)
|
||||
return fmt.Errorf("match repository error receiving stream: %w", err)
|
||||
}
|
||||
|
||||
isSupported, err := s.matchRepository(bufferedCtx, workDir, metadata.GetEnv())
|
||||
if err != nil {
|
||||
return fmt.Errorf("match repository error: %s", err)
|
||||
return fmt.Errorf("match repository error: %w", err)
|
||||
}
|
||||
repoResponse := &apiclient.RepositoryResponse{IsSupported: isSupported}
|
||||
|
||||
err = stream.SendAndClose(repoResponse)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error sending match repository response: %s", err)
|
||||
return fmt.Errorf("error sending match repository response: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -258,7 +293,7 @@ func (s *Service) matchRepository(ctx context.Context, workdir string, envEntrie
|
||||
pattern := filepath.Join(workdir, config.Spec.Discover.FileName)
|
||||
matches, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
e := fmt.Errorf("error finding filename match for pattern %q: %s", pattern, err)
|
||||
e := fmt.Errorf("error finding filename match for pattern %q: %w", pattern, err)
|
||||
log.Debug(e)
|
||||
return false, e
|
||||
}
|
||||
@@ -272,7 +307,7 @@ func (s *Service) matchRepository(ctx context.Context, workdir string, envEntrie
|
||||
// https://github.com/golang/go/issues/11862
|
||||
matches, err := zglob.Glob(pattern)
|
||||
if err != nil {
|
||||
e := fmt.Errorf("error finding glob match for pattern %q: %s", pattern, err)
|
||||
e := fmt.Errorf("error finding glob match for pattern %q: %w", pattern, err)
|
||||
log.Debug(e)
|
||||
return false, e
|
||||
}
|
||||
@@ -288,7 +323,7 @@ func (s *Service) matchRepository(ctx context.Context, workdir string, envEntrie
|
||||
|
||||
find, err := runCommand(ctx, config.Spec.Discover.Find.Command, workdir, env)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error running find command: %s", err)
|
||||
return false, fmt.Errorf("error running find command: %w", err)
|
||||
}
|
||||
|
||||
if find != "" {
|
||||
@@ -296,3 +331,66 @@ func (s *Service) matchRepository(ctx context.Context, workdir string, envEntrie
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ParametersAnnouncementStream defines an interface able to send/receive a stream of parameter announcements.
|
||||
type ParametersAnnouncementStream interface {
|
||||
Stream
|
||||
SendAndClose(response *apiclient.ParametersAnnouncementResponse) error
|
||||
}
|
||||
|
||||
// GetParametersAnnouncement gets parameter announcements for a given Application and repo contents.
|
||||
func (s *Service) GetParametersAnnouncement(stream apiclient.ConfigManagementPluginService_GetParametersAnnouncementServer) error {
|
||||
bufferedCtx, cancel := buffered_context.WithEarlierDeadline(stream.Context(), cmpTimeoutBuffer)
|
||||
defer cancel()
|
||||
|
||||
workDir, cleanup, err := getTempDirMustCleanup(common.GetCMPWorkDir())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating workdir for generating parameter announcements: %w", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parameters announcement error receiving stream: %w", err)
|
||||
}
|
||||
appPath := filepath.Clean(filepath.Join(workDir, metadata.AppRelPath))
|
||||
if !strings.HasPrefix(appPath, workDir) {
|
||||
return fmt.Errorf("illegal appPath: out of workDir bound")
|
||||
}
|
||||
|
||||
repoResponse, err := getParametersAnnouncement(bufferedCtx, appPath, s.initConstants.PluginConfig.Spec.Parameters.Static, s.initConstants.PluginConfig.Spec.Parameters.Dynamic)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get parameters announcement error: %w", err)
|
||||
}
|
||||
|
||||
err = stream.SendAndClose(repoResponse)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error sending parameters announcement response: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getParametersAnnouncement(ctx context.Context, appDir string, announcements []*repoclient.ParameterAnnouncement, command Command) (*apiclient.ParametersAnnouncementResponse, error) {
|
||||
augmentedAnnouncements := announcements
|
||||
|
||||
if len(command.Command) > 0 {
|
||||
stdout, err := runCommand(ctx, command, appDir, os.Environ())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error executing dynamic parameter output command: %w", err)
|
||||
}
|
||||
|
||||
var dynamicParamAnnouncements []*repoclient.ParameterAnnouncement
|
||||
err = json.Unmarshal([]byte(stdout), &dynamicParamAnnouncements)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshaling dynamic parameter output into ParametersAnnouncementResponse: %w", err)
|
||||
}
|
||||
|
||||
// dynamic goes first, because static should take precedence by being later.
|
||||
augmentedAnnouncements = append(dynamicParamAnnouncements, announcements...)
|
||||
}
|
||||
|
||||
repoResponse := &apiclient.ParametersAnnouncementResponse{
|
||||
ParameterAnnouncements: augmentedAnnouncements,
|
||||
}
|
||||
return repoResponse, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ option go_package = "github.com/argoproj/argo-cd/v2/cmpserver/apiclient";
|
||||
|
||||
package plugin;
|
||||
|
||||
import "github.com/argoproj/argo-cd/v2/reposerver/repository/repository.proto";
|
||||
|
||||
// AppStreamRequest is the request object used to send the application's
|
||||
// files over a stream.
|
||||
message AppStreamRequest {
|
||||
@@ -44,6 +46,12 @@ message RepositoryResponse {
|
||||
bool isSupported = 1;
|
||||
}
|
||||
|
||||
// ParametersAnnouncementResponse contains a list of announcements. This list represents all the parameters which a CMP
|
||||
// is able to accept.
|
||||
message ParametersAnnouncementResponse {
|
||||
repeated repository.ParameterAnnouncement parameterAnnouncements = 1;
|
||||
}
|
||||
|
||||
message File {
|
||||
bytes chunk = 1;
|
||||
}
|
||||
@@ -58,4 +66,8 @@ service ConfigManagementPluginService {
|
||||
// MatchRepository returns whether or not the given application is supported by the plugin
|
||||
rpc MatchRepository(stream AppStreamRequest) returns (RepositoryResponse) {
|
||||
}
|
||||
|
||||
// GetParametersAnnouncement gets a list of parameter announcements for the given app
|
||||
rpc GetParametersAnnouncement(stream AppStreamRequest) returns (ParametersAnnouncementResponse) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"gopkg.in/yaml.v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmpserver/apiclient"
|
||||
repoclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
"github.com/argoproj/argo-cd/v2/util/cmp"
|
||||
"github.com/argoproj/argo-cd/v2/util/tgzstream"
|
||||
)
|
||||
|
||||
func newService(configFilePath string) (*Service, error) {
|
||||
@@ -30,6 +40,11 @@ func newService(configFilePath string) (*Service, error) {
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func (s *Service) WithGenerateCommand(command Command) *Service {
|
||||
s.initConstants.PluginConfig.Spec.Generate = command
|
||||
return s
|
||||
}
|
||||
|
||||
type pluginOpt func(*CMPServerInitConstants)
|
||||
|
||||
func withDiscover(d Discover) pluginOpt {
|
||||
@@ -104,6 +119,19 @@ func TestMatchRepository(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, match)
|
||||
})
|
||||
t.Run("will not match a pattern with a syntax error", func(t *testing.T) {
|
||||
// given
|
||||
d := Discover{
|
||||
FileName: "[",
|
||||
}
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
_, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.ErrorContains(t, err, "syntax error")
|
||||
})
|
||||
t.Run("will match plugin by glob", func(t *testing.T) {
|
||||
// given
|
||||
d := Discover{
|
||||
@@ -136,6 +164,21 @@ func TestMatchRepository(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, match)
|
||||
})
|
||||
t.Run("will throw an error for a bad pattern", func(t *testing.T) {
|
||||
// given
|
||||
d := Discover{
|
||||
Find: Find{
|
||||
Glob: "does-not-exist",
|
||||
},
|
||||
}
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
_, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.ErrorContains(t, err, "error finding glob match for pattern")
|
||||
})
|
||||
t.Run("will match plugin by command when returns any output", func(t *testing.T) {
|
||||
// given
|
||||
d := Discover{
|
||||
@@ -238,17 +281,49 @@ func Test_Negative_ConfigFile_DoesnotExist(t *testing.T) {
|
||||
|
||||
func TestGenerateManifest(t *testing.T) {
|
||||
configFilePath := "./testdata/kustomize/config"
|
||||
|
||||
t.Run("successful generate", func(t *testing.T) {
|
||||
service, err := newService(configFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
res1, err := service.generateManifest(context.Background(), "testdata/kustomize", nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res1)
|
||||
|
||||
expectedOutput := "{\"apiVersion\":\"v1\",\"data\":{\"foo\":\"bar\"},\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"my-map\"}}"
|
||||
if res1 != nil {
|
||||
require.Equal(t, expectedOutput, res1.Manifests[0])
|
||||
}
|
||||
})
|
||||
t.Run("bad generate command", func(t *testing.T) {
|
||||
service, err := newService(configFilePath)
|
||||
require.NoError(t, err)
|
||||
service.WithGenerateCommand(Command{Command: []string{"bad-command"}})
|
||||
|
||||
res, err := service.generateManifest(context.Background(), "testdata/kustomize", nil)
|
||||
assert.ErrorContains(t, err, "executable file not found")
|
||||
assert.Nil(t, res.Manifests)
|
||||
})
|
||||
t.Run("bad yaml output", func(t *testing.T) {
|
||||
service, err := newService(configFilePath)
|
||||
require.NoError(t, err)
|
||||
service.WithGenerateCommand(Command{Command: []string{"echo", "invalid yaml: }"}})
|
||||
|
||||
res, err := service.generateManifest(context.Background(), "testdata/kustomize", nil)
|
||||
assert.ErrorContains(t, err, "failed to unmarshal manifest")
|
||||
assert.Nil(t, res.Manifests)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGenerateManifest_deadline_exceeded(t *testing.T) {
|
||||
configFilePath := "./testdata/kustomize/config"
|
||||
service, err := newService(configFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
res1, err := service.generateManifest(context.Background(), "", nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res1)
|
||||
|
||||
expectedOutput := "{\"apiVersion\":\"v1\",\"data\":{\"foo\":\"bar\"},\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"my-map\"}}"
|
||||
if res1 != nil {
|
||||
require.Equal(t, expectedOutput, res1.Manifests[0])
|
||||
}
|
||||
expiredCtx, cancel := context.WithTimeout(context.Background(), time.Second*0)
|
||||
defer cancel()
|
||||
_, err = service.generateManifest(expiredCtx, "", nil)
|
||||
assert.ErrorContains(t, err, "context deadline exceeded")
|
||||
}
|
||||
|
||||
// TestRunCommandContextTimeout makes sure the command dies at timeout rather than sleeping past the timeout.
|
||||
@@ -266,3 +341,388 @@ func TestRunCommandContextTimeout(t *testing.T) {
|
||||
assert.Error(t, err) // The command should time out, causing an error.
|
||||
assert.Less(t, after.Sub(before), 1*time.Second)
|
||||
}
|
||||
|
||||
func TestRunCommandEmptyCommand(t *testing.T) {
|
||||
_, err := runCommand(context.Background(), Command{}, "", nil)
|
||||
assert.ErrorContains(t, err, "Command is empty")
|
||||
}
|
||||
|
||||
func Test_getParametersAnnouncement_empty_command(t *testing.T) {
|
||||
staticYAML := `
|
||||
- name: static-a
|
||||
- name: static-b
|
||||
`
|
||||
static := &[]*repoclient.ParameterAnnouncement{}
|
||||
err := yaml.Unmarshal([]byte(staticYAML), static)
|
||||
require.NoError(t, err)
|
||||
command := Command{
|
||||
Command: []string{"echo"},
|
||||
Args: []string{`[]`},
|
||||
}
|
||||
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []*repoclient.ParameterAnnouncement{{Name: "static-a"}, {Name: "static-b"}}, res.ParameterAnnouncements)
|
||||
}
|
||||
|
||||
func Test_getParametersAnnouncement_no_command(t *testing.T) {
|
||||
staticYAML := `
|
||||
- name: static-a
|
||||
- name: static-b
|
||||
`
|
||||
static := &[]*repoclient.ParameterAnnouncement{}
|
||||
err := yaml.Unmarshal([]byte(staticYAML), static)
|
||||
require.NoError(t, err)
|
||||
command := Command{}
|
||||
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []*repoclient.ParameterAnnouncement{{Name: "static-a"}, {Name: "static-b"}}, res.ParameterAnnouncements)
|
||||
}
|
||||
|
||||
func Test_getParametersAnnouncement_static_and_dynamic(t *testing.T) {
|
||||
staticYAML := `
|
||||
- name: static-a
|
||||
- name: static-b
|
||||
`
|
||||
static := &[]*repoclient.ParameterAnnouncement{}
|
||||
err := yaml.Unmarshal([]byte(staticYAML), static)
|
||||
require.NoError(t, err)
|
||||
command := Command{
|
||||
Command: []string{"echo"},
|
||||
Args: []string{`[{"name": "dynamic-a"}, {"name": "dynamic-b"}]`},
|
||||
}
|
||||
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
|
||||
require.NoError(t, err)
|
||||
expected := []*repoclient.ParameterAnnouncement{
|
||||
{Name: "dynamic-a"},
|
||||
{Name: "dynamic-b"},
|
||||
{Name: "static-a"},
|
||||
{Name: "static-b"},
|
||||
}
|
||||
assert.Equal(t, expected, res.ParameterAnnouncements)
|
||||
}
|
||||
|
||||
func Test_getParametersAnnouncement_invalid_json(t *testing.T) {
|
||||
command := Command{
|
||||
Command: []string{"echo"},
|
||||
Args: []string{`[`},
|
||||
}
|
||||
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "unexpected end of JSON input")
|
||||
}
|
||||
|
||||
func Test_getParametersAnnouncement_bad_command(t *testing.T) {
|
||||
command := Command{
|
||||
Command: []string{"exit"},
|
||||
Args: []string{"1"},
|
||||
}
|
||||
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "error executing dynamic parameter output command")
|
||||
}
|
||||
|
||||
func Test_getTempDirMustCleanup(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
// Induce a directory create error to verify error handling.
|
||||
err := os.Chmod(tempDir, 0000)
|
||||
require.NoError(t, err)
|
||||
_, _, err = getTempDirMustCleanup(path.Join(tempDir, "test"))
|
||||
assert.ErrorContains(t, err, "error creating temp dir")
|
||||
|
||||
err = os.Chmod(tempDir, 0700)
|
||||
require.NoError(t, err)
|
||||
workDir, cleanup, err := getTempDirMustCleanup(tempDir)
|
||||
require.NoError(t, err)
|
||||
require.DirExists(t, workDir)
|
||||
cleanup()
|
||||
assert.NoDirExists(t, workDir)
|
||||
}
|
||||
|
||||
func TestService_Init(t *testing.T) {
|
||||
// Set up a base directory containing a test directory and a test file.
|
||||
tempDir := t.TempDir()
|
||||
workDir := path.Join(tempDir, "workDir")
|
||||
err := os.MkdirAll(workDir, 0700)
|
||||
require.NoError(t, err)
|
||||
testfile := path.Join(workDir, "testfile")
|
||||
file, err := os.Create(testfile)
|
||||
require.NoError(t, err)
|
||||
err = file.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make the base directory read-only so Init's cleanup fails.
|
||||
err = os.Chmod(tempDir, 0000)
|
||||
require.NoError(t, err)
|
||||
s := NewService(CMPServerInitConstants{PluginConfig: PluginConfig{}})
|
||||
err = s.Init(workDir)
|
||||
assert.ErrorContains(t, err, "error removing workdir", "Init must throw an error if it can't remove the work directory")
|
||||
|
||||
// Make the base directory writable so Init's cleanup succeeds.
|
||||
err = os.Chmod(tempDir, 0700)
|
||||
require.NoError(t, err)
|
||||
err = s.Init(workDir)
|
||||
assert.NoError(t, err)
|
||||
assert.DirExists(t, workDir)
|
||||
assert.NoFileExists(t, testfile)
|
||||
}
|
||||
|
||||
func TestEnviron(t *testing.T) {
|
||||
t.Run("empty environ", func(t *testing.T) {
|
||||
env := environ([]*apiclient.EnvEntry{})
|
||||
assert.Nil(t, env)
|
||||
})
|
||||
t.Run("env vars with empty names or values", func(t *testing.T) {
|
||||
env := environ([]*apiclient.EnvEntry{
|
||||
{Value: "test"},
|
||||
{Name: "test"},
|
||||
})
|
||||
assert.Nil(t, env)
|
||||
})
|
||||
t.Run("proper env vars", func(t *testing.T) {
|
||||
env := environ([]*apiclient.EnvEntry{
|
||||
{Name: "name1", Value: "value1"},
|
||||
{Name: "name2", Value: "value2"},
|
||||
})
|
||||
assert.Equal(t, []string{"name1=value1", "name2=value2"}, env)
|
||||
})
|
||||
}
|
||||
|
||||
type MockGenerateManifestStream struct {
|
||||
metadataSent bool
|
||||
fileSent bool
|
||||
metadataRequest *apiclient.AppStreamRequest
|
||||
fileRequest *apiclient.AppStreamRequest
|
||||
response *apiclient.ManifestResponse
|
||||
}
|
||||
|
||||
func NewMockGenerateManifestStream(repoPath, appPath string, env []string) (*MockGenerateManifestStream, error) {
|
||||
tgz, mr, err := cmp.GetCompressedRepoAndMetadata(repoPath, appPath, env, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tgzstream.CloseAndDelete(tgz)
|
||||
|
||||
tgzBuffer := bytes.NewBuffer(nil)
|
||||
_, err = io.Copy(tgzBuffer, tgz)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to copy manifest targz to a byte buffer: %w", err)
|
||||
}
|
||||
|
||||
return &MockGenerateManifestStream{
|
||||
metadataRequest: mr,
|
||||
fileRequest: cmp.AppFileRequest(tgzBuffer.Bytes()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *MockGenerateManifestStream) SendAndClose(response *apiclient.ManifestResponse) error {
|
||||
m.response = response
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockGenerateManifestStream) Recv() (*apiclient.AppStreamRequest, error) {
|
||||
if !m.metadataSent {
|
||||
m.metadataSent = true
|
||||
return m.metadataRequest, nil
|
||||
}
|
||||
|
||||
if !m.fileSent {
|
||||
m.fileSent = true
|
||||
return m.fileRequest, nil
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func (m *MockGenerateManifestStream) Context() context.Context {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
func TestService_GenerateManifest(t *testing.T) {
|
||||
configFilePath := "./testdata/kustomize/config"
|
||||
service, err := newService(configFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("successful generate", func(t *testing.T) {
|
||||
s, err := NewMockGenerateManifestStream("./testdata/kustomize", "./testdata/kustomize", nil)
|
||||
require.NoError(t, err)
|
||||
err = service.generateManifestGeneric(s)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, s.response)
|
||||
assert.Equal(t, []string{"{\"apiVersion\":\"v1\",\"data\":{\"foo\":\"bar\"},\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"my-map\"}}"}, s.response.Manifests)
|
||||
})
|
||||
|
||||
t.Run("out-of-bounds app path", func(t *testing.T) {
|
||||
s, err := NewMockGenerateManifestStream("./testdata/kustomize", "./testdata/kustomize", nil)
|
||||
require.NoError(t, err)
|
||||
// set a malicious app path on the metadata
|
||||
s.metadataRequest.Request.(*apiclient.AppStreamRequest_Metadata).Metadata.AppRelPath = "../out-of-bounds"
|
||||
err = service.generateManifestGeneric(s)
|
||||
require.ErrorContains(t, err, "illegal appPath")
|
||||
assert.Nil(t, s.response)
|
||||
})
|
||||
}
|
||||
|
||||
type MockMatchRepositoryStream struct {
|
||||
metadataSent bool
|
||||
fileSent bool
|
||||
metadataRequest *apiclient.AppStreamRequest
|
||||
fileRequest *apiclient.AppStreamRequest
|
||||
response *apiclient.RepositoryResponse
|
||||
}
|
||||
|
||||
func NewMockMatchRepositoryStream(repoPath, appPath string, env []string) (*MockMatchRepositoryStream, error) {
|
||||
tgz, mr, err := cmp.GetCompressedRepoAndMetadata(repoPath, appPath, env, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tgzstream.CloseAndDelete(tgz)
|
||||
|
||||
tgzBuffer := bytes.NewBuffer(nil)
|
||||
_, err = io.Copy(tgzBuffer, tgz)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to copy manifest targz to a byte buffer: %w", err)
|
||||
}
|
||||
|
||||
return &MockMatchRepositoryStream{
|
||||
metadataRequest: mr,
|
||||
fileRequest: cmp.AppFileRequest(tgzBuffer.Bytes()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *MockMatchRepositoryStream) SendAndClose(response *apiclient.RepositoryResponse) error {
|
||||
m.response = response
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockMatchRepositoryStream) Recv() (*apiclient.AppStreamRequest, error) {
|
||||
if !m.metadataSent {
|
||||
m.metadataSent = true
|
||||
return m.metadataRequest, nil
|
||||
}
|
||||
|
||||
if !m.fileSent {
|
||||
m.fileSent = true
|
||||
return m.fileRequest, nil
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func (m *MockMatchRepositoryStream) Context() context.Context {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
func TestService_MatchRepository(t *testing.T) {
|
||||
configFilePath := "./testdata/kustomize/config"
|
||||
service, err := newService(configFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("supported app", func(t *testing.T) {
|
||||
s, err := NewMockMatchRepositoryStream("./testdata/kustomize", "./testdata/kustomize", nil)
|
||||
require.NoError(t, err)
|
||||
err = service.matchRepositoryGeneric(s)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, s.response)
|
||||
assert.True(t, s.response.IsSupported)
|
||||
})
|
||||
|
||||
t.Run("unsupported app", func(t *testing.T) {
|
||||
s, err := NewMockMatchRepositoryStream("./testdata/ksonnet", "./testdata/ksonnet", nil)
|
||||
require.NoError(t, err)
|
||||
err = service.matchRepositoryGeneric(s)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, s.response)
|
||||
assert.False(t, s.response.IsSupported)
|
||||
})
|
||||
}
|
||||
|
||||
type MockParametersAnnouncementStream struct {
|
||||
metadataSent bool
|
||||
fileSent bool
|
||||
metadataRequest *apiclient.AppStreamRequest
|
||||
fileRequest *apiclient.AppStreamRequest
|
||||
response *apiclient.ParametersAnnouncementResponse
|
||||
}
|
||||
|
||||
func NewMockParametersAnnouncementStream(repoPath, appPath string, env []string) (*MockParametersAnnouncementStream, error) {
|
||||
tgz, mr, err := cmp.GetCompressedRepoAndMetadata(repoPath, appPath, env, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tgzstream.CloseAndDelete(tgz)
|
||||
|
||||
tgzBuffer := bytes.NewBuffer(nil)
|
||||
_, err = io.Copy(tgzBuffer, tgz)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to copy manifest targz to a byte buffer: %w", err)
|
||||
}
|
||||
|
||||
return &MockParametersAnnouncementStream{
|
||||
metadataRequest: mr,
|
||||
fileRequest: cmp.AppFileRequest(tgzBuffer.Bytes()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *MockParametersAnnouncementStream) SendAndClose(response *apiclient.ParametersAnnouncementResponse) error {
|
||||
m.response = response
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockParametersAnnouncementStream) Recv() (*apiclient.AppStreamRequest, error) {
|
||||
if !m.metadataSent {
|
||||
m.metadataSent = true
|
||||
return m.metadataRequest, nil
|
||||
}
|
||||
|
||||
if !m.fileSent {
|
||||
m.fileSent = true
|
||||
return m.fileRequest, nil
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func (m *MockParametersAnnouncementStream) SetHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockParametersAnnouncementStream) SendHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockParametersAnnouncementStream) SetTrailer(metadata.MD) {}
|
||||
|
||||
func (m *MockParametersAnnouncementStream) Context() context.Context {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
func (m *MockParametersAnnouncementStream) SendMsg(interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockParametersAnnouncementStream) RecvMsg(interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestService_GetParametersAnnouncement(t *testing.T) {
|
||||
configFilePath := "./testdata/kustomize/config"
|
||||
service, err := newService(configFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("successful response", func(t *testing.T) {
|
||||
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", nil)
|
||||
require.NoError(t, err)
|
||||
err = service.GetParametersAnnouncement(s)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, s.response)
|
||||
require.Len(t, s.response.ParameterAnnouncements, 1)
|
||||
assert.Equal(t, repoclient.ParameterAnnouncement{Name: "test-param", String_: "test-value"}, *s.response.ParameterAnnouncements[0])
|
||||
})
|
||||
t.Run("out of bounds app", func(t *testing.T) {
|
||||
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", nil)
|
||||
require.NoError(t, err)
|
||||
// set a malicious app path on the metadata
|
||||
s.metadataRequest.Request.(*apiclient.AppStreamRequest_Metadata).Metadata.AppRelPath = "../out-of-bounds"
|
||||
err = service.GetParametersAnnouncement(s)
|
||||
require.ErrorContains(t, err, "illegal appPath")
|
||||
require.Nil(t, s.response)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,5 +11,3 @@ spec:
|
||||
discover:
|
||||
find:
|
||||
glob: "**/*/main.jsonnet"
|
||||
allowConcurrency: false
|
||||
lockRepo: false
|
||||
@@ -12,5 +12,3 @@ spec:
|
||||
find:
|
||||
command: [sh, -c, find . -name kustomization.yaml]
|
||||
glob: "**/*/kustomization.yaml"
|
||||
allowConcurrency: true
|
||||
lockRepo: false
|
||||
@@ -7,10 +7,12 @@ spec:
|
||||
init:
|
||||
command: [kustomize, version]
|
||||
generate:
|
||||
command: [sh, -c, "cd testdata/kustomize && kustomize build"]
|
||||
command: [sh, -c, "kustomize build"]
|
||||
discover:
|
||||
find:
|
||||
command: [sh, -c, find . -name kustomization.yaml]
|
||||
glob: "**/*/kustomization.yaml"
|
||||
allowConcurrency: true
|
||||
lockRepo: false
|
||||
glob: "**/kustomization.yaml"
|
||||
parameters:
|
||||
static:
|
||||
- name: test-param
|
||||
string: test-value
|
||||
|
||||
@@ -108,7 +108,7 @@ func (a *ArgoCDCMPServer) CreateGRPC() (*grpc.Server, error) {
|
||||
return true, nil
|
||||
}))
|
||||
pluginService := plugin.NewService(a.initConstants)
|
||||
err := pluginService.Init()
|
||||
err := pluginService.Init(common.GetCMPWorkDir())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initializing plugin service: %s", err)
|
||||
}
|
||||
|
||||
@@ -907,7 +907,7 @@ func (ctrl *ApplicationController) processProjectQueueItem() (processNext bool)
|
||||
func (ctrl *ApplicationController) finalizeProjectDeletion(proj *appv1.AppProject) error {
|
||||
apps, err := ctrl.appLister.Applications(ctrl.namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error listing applications: %w", err)
|
||||
}
|
||||
appsCount := 0
|
||||
for i := range apps {
|
||||
@@ -1077,7 +1077,7 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
|
||||
func (ctrl *ApplicationController) removeCascadeFinalizer(app *appv1.Application) error {
|
||||
_, err := ctrl.getAppProj(app)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting project: %w", err)
|
||||
}
|
||||
app.UnSetCascadedDeletion()
|
||||
var patch []byte
|
||||
@@ -1256,12 +1256,12 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta
|
||||
}
|
||||
patchJSON, err := json.Marshal(patch)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error marshaling json: %w", err)
|
||||
}
|
||||
if app.Status.OperationState != nil && app.Status.OperationState.FinishedAt != nil && state.FinishedAt == nil {
|
||||
patchJSON, err = jsonpatch.MergeMergePatches(patchJSON, []byte(`{"status": {"operationState": {"finishedAt": null}}}`))
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error merging operation state patch: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1272,7 +1272,7 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta
|
||||
if apierr.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
return fmt.Errorf("error patching application with operation state: %w", err)
|
||||
}
|
||||
log.Infof("updated '%s' operation (phase: %s)", app.QualifiedName(), state.Phase)
|
||||
if state.Phase.Completed() {
|
||||
@@ -1497,17 +1497,7 @@ func (ctrl *ApplicationController) refreshAppConditions(app *appv1.Application)
|
||||
errorConditions := make([]appv1.ApplicationCondition, 0)
|
||||
proj, err := ctrl.getAppProj(app)
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
errorConditions = append(errorConditions, appv1.ApplicationCondition{
|
||||
Type: appv1.ApplicationConditionInvalidSpecError,
|
||||
Message: fmt.Sprintf("Application referencing project %s which does not exist", app.Spec.Project),
|
||||
})
|
||||
} else {
|
||||
errorConditions = append(errorConditions, appv1.ApplicationCondition{
|
||||
Type: appv1.ApplicationConditionUnknownError,
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
errorConditions = append(errorConditions, ctrl.projectErrorToCondition(err, app))
|
||||
} else {
|
||||
specConditions, err := argo.ValidatePermissions(context.Background(), &app.Spec, proj, ctrl.db)
|
||||
if err != nil {
|
||||
@@ -1798,7 +1788,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
|
||||
// If the application is not allowed to use the project,
|
||||
// log an error.
|
||||
if _, err := ctrl.getAppProj(app); err != nil {
|
||||
ctrl.setAppCondition(app, appv1.ApplicationCondition{Type: appv1.ApplicationConditionUnknownError, Message: err.Error()})
|
||||
ctrl.setAppCondition(app, ctrl.projectErrorToCondition(err, app))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1869,6 +1859,19 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
|
||||
return informer, lister
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) projectErrorToCondition(err error, app *appv1.Application) appv1.ApplicationCondition {
|
||||
var condition appv1.ApplicationCondition
|
||||
if apierr.IsNotFound(err) {
|
||||
condition = appv1.ApplicationCondition{
|
||||
Type: appv1.ApplicationConditionInvalidSpecError,
|
||||
Message: fmt.Sprintf("Application referencing project %s which does not exist", app.Spec.Project),
|
||||
}
|
||||
} else {
|
||||
condition = appv1.ApplicationCondition{Type: appv1.ApplicationConditionUnknownError, Message: err.Error()}
|
||||
}
|
||||
return condition
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) RegisterClusterSecretUpdater(ctx context.Context) {
|
||||
updater := NewClusterInfoUpdater(ctrl.stateCache, ctrl.db, ctrl.appLister.Applications(""), ctrl.cache, ctrl.clusterFilter, ctrl.getAppProj, ctrl.namespace)
|
||||
go updater.Run(ctx)
|
||||
|
||||
@@ -1068,6 +1068,34 @@ func TestUpdateReconciledAt(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestProjectErrorToCondition(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
app.Spec.Project = "wrong project"
|
||||
ctrl := newFakeController(&fakeData{
|
||||
apps: []runtime.Object{app, &defaultProj},
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
Manifests: []string{},
|
||||
Namespace: test.FakeDestNamespace,
|
||||
Server: test.FakeClusterURL,
|
||||
Revision: "abc123",
|
||||
},
|
||||
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
|
||||
})
|
||||
key, _ := cache.MetaNamespaceKeyFunc(app)
|
||||
ctrl.appRefreshQueue.Add(key)
|
||||
ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil)
|
||||
|
||||
ctrl.processAppRefreshQueueItem()
|
||||
|
||||
obj, ok, err := ctrl.appInformer.GetIndexer().GetByKey(key)
|
||||
assert.True(t, ok)
|
||||
assert.NoError(t, err)
|
||||
updatedApp := obj.(*argoappv1.Application)
|
||||
assert.Equal(t, argoappv1.ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[0].Type)
|
||||
assert.Equal(t, "Application referencing project wrong project which does not exist", updatedApp.Status.Conditions[0].Message)
|
||||
assert.Equal(t, argoappv1.ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[0].Type)
|
||||
}
|
||||
|
||||
func TestFinalizeProjectDeletion_HasApplications(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
proj := &argoappv1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: test.FakeArgoCDNamespace}}
|
||||
|
||||
15
controller/cache/cache.go
vendored
15
controller/cache/cache.go
vendored
@@ -382,13 +382,18 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
|
||||
|
||||
cluster, err := c.db.GetCluster(context.Background(), server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting cluster: %w", err)
|
||||
}
|
||||
|
||||
if !c.canHandleCluster(cluster) {
|
||||
return nil, fmt.Errorf("controller is configured to ignore cluster %s", cluster.Server)
|
||||
}
|
||||
|
||||
resourceCustomLabels, err := c.settingsMgr.GetResourceCustomLabels()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting custom label: %w", err)
|
||||
}
|
||||
|
||||
clusterCacheOpts := []clustercache.UpdateSettingsFunc{
|
||||
clustercache.SetListSemaphore(semaphore.NewWeighted(clusterCacheListSemaphoreSize)),
|
||||
clustercache.SetListPageSize(clusterCacheListPageSize),
|
||||
@@ -400,7 +405,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
|
||||
clustercache.SetClusterResources(cluster.ClusterResources),
|
||||
clustercache.SetPopulateResourceInfoHandler(func(un *unstructured.Unstructured, isRoot bool) (interface{}, bool) {
|
||||
res := &ResourceInfo{}
|
||||
populateNodeInfo(un, res)
|
||||
populateNodeInfo(un, res, resourceCustomLabels)
|
||||
c.lock.RLock()
|
||||
cacheSettings := c.cacheSettings
|
||||
c.lock.RUnlock()
|
||||
@@ -456,11 +461,11 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
|
||||
func (c *liveStateCache) getSyncedCluster(server string) (clustercache.ClusterCache, error) {
|
||||
clusterCache, err := c.getCluster(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting cluster: %w", err)
|
||||
}
|
||||
err = clusterCache.EnsureSynced()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error synchronizing cache state : %w", err)
|
||||
}
|
||||
return clusterCache, nil
|
||||
}
|
||||
@@ -594,7 +599,7 @@ func (c *liveStateCache) watchSettings(ctx context.Context) {
|
||||
func (c *liveStateCache) Init() error {
|
||||
cacheSettings, err := c.loadCacheSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error loading cache settings: %w", err)
|
||||
}
|
||||
c.cacheSettings = *cacheSettings
|
||||
return nil
|
||||
|
||||
11
controller/cache/info.go
vendored
11
controller/cache/info.go
vendored
@@ -19,12 +19,21 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/resource"
|
||||
)
|
||||
|
||||
func populateNodeInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
func populateNodeInfo(un *unstructured.Unstructured, res *ResourceInfo, customLabels []string) {
|
||||
gvk := un.GroupVersionKind()
|
||||
revision := resource.GetRevision(un)
|
||||
if revision > 0 {
|
||||
res.Info = append(res.Info, v1alpha1.InfoItem{Name: "Revision", Value: fmt.Sprintf("Rev:%v", revision)})
|
||||
}
|
||||
if len(customLabels) > 0 {
|
||||
if labels := un.GetLabels(); labels != nil {
|
||||
for _, customLabel := range customLabels {
|
||||
if value, ok := labels[customLabel]; ok {
|
||||
res.Info = append(res.Info, v1alpha1.InfoItem{Name: customLabel, Value: value})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
switch gvk.Group {
|
||||
case "":
|
||||
switch gvk.Kind {
|
||||
|
||||
76
controller/cache/info_test.go
vendored
76
controller/cache/info_test.go
vendored
@@ -271,7 +271,7 @@ func TestGetPodInfo(t *testing.T) {
|
||||
`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info)
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/1"},
|
||||
@@ -302,7 +302,7 @@ status:
|
||||
`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(node, info)
|
||||
populateNodeInfo(node, info, []string{})
|
||||
assert.Equal(t, &NodeInfo{
|
||||
Name: "minikube",
|
||||
Capacity: v1.ResourceList{v1.ResourceMemory: resource.MustParse("6091320Ki"), v1.ResourceCPU: resource.MustParse("6")},
|
||||
@@ -312,7 +312,7 @@ status:
|
||||
|
||||
func TestGetServiceInfo(t *testing.T) {
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(testService, info)
|
||||
populateNodeInfo(testService, info, []string{})
|
||||
assert.Equal(t, 0, len(info.Info))
|
||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
|
||||
TargetLabels: map[string]string{"app": "guestbook"},
|
||||
@@ -322,7 +322,7 @@ func TestGetServiceInfo(t *testing.T) {
|
||||
|
||||
func TestGetLinkAnnotatedServiceInfo(t *testing.T) {
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(testLinkAnnotatedService, info)
|
||||
populateNodeInfo(testLinkAnnotatedService, info, []string{})
|
||||
assert.Equal(t, 0, len(info.Info))
|
||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
|
||||
TargetLabels: map[string]string{"app": "guestbook"},
|
||||
@@ -333,7 +333,7 @@ func TestGetLinkAnnotatedServiceInfo(t *testing.T) {
|
||||
|
||||
func TestGetIstioVirtualServiceInfo(t *testing.T) {
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(testIstioVirtualService, info)
|
||||
populateNodeInfo(testIstioVirtualService, info, []string{})
|
||||
assert.Equal(t, 0, len(info.Info))
|
||||
require.NotNil(t, info.NetworkingInfo)
|
||||
require.NotNil(t, info.NetworkingInfo.TargetRefs)
|
||||
@@ -363,7 +363,7 @@ func TestGetIngressInfo(t *testing.T) {
|
||||
}
|
||||
for _, tc := range tests {
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(tc.Ingress, info)
|
||||
populateNodeInfo(tc.Ingress, info, []string{})
|
||||
assert.Equal(t, 0, len(info.Info))
|
||||
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
|
||||
return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0
|
||||
@@ -388,7 +388,7 @@ func TestGetIngressInfo(t *testing.T) {
|
||||
|
||||
func TestGetLinkAnnotatedIngressInfo(t *testing.T) {
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(testLinkAnnotatedIngress, info)
|
||||
populateNodeInfo(testLinkAnnotatedIngress, info, []string{})
|
||||
assert.Equal(t, 0, len(info.Info))
|
||||
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
|
||||
return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0
|
||||
@@ -412,7 +412,7 @@ func TestGetLinkAnnotatedIngressInfo(t *testing.T) {
|
||||
|
||||
func TestGetIngressInfoWildCardPath(t *testing.T) {
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(testIngressWildCardPath, info)
|
||||
populateNodeInfo(testIngressWildCardPath, info, []string{})
|
||||
assert.Equal(t, 0, len(info.Info))
|
||||
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
|
||||
return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0
|
||||
@@ -436,7 +436,7 @@ func TestGetIngressInfoWildCardPath(t *testing.T) {
|
||||
|
||||
func TestGetIngressInfoWithoutTls(t *testing.T) {
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(testIngressWithoutTls, info)
|
||||
populateNodeInfo(testIngressWithoutTls, info, []string{})
|
||||
assert.Equal(t, 0, len(info.Info))
|
||||
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
|
||||
return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0
|
||||
@@ -481,7 +481,7 @@ func TestGetIngressInfoWithHost(t *testing.T) {
|
||||
- ip: 107.178.210.11`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
populateNodeInfo(ingress, info, []string{})
|
||||
|
||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
|
||||
Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}},
|
||||
@@ -514,7 +514,7 @@ func TestGetIngressInfoNoHost(t *testing.T) {
|
||||
`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
populateNodeInfo(ingress, info, []string{})
|
||||
|
||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
|
||||
TargetRefs: []v1alpha1.ResourceRef{{
|
||||
@@ -549,7 +549,7 @@ func TestExternalUrlWithSubPath(t *testing.T) {
|
||||
- ip: 107.178.210.11`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
populateNodeInfo(ingress, info, []string{})
|
||||
|
||||
expectedExternalUrls := []string{"https://107.178.210.11/my/sub/path/"}
|
||||
assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
|
||||
@@ -585,7 +585,7 @@ func TestExternalUrlWithMultipleSubPaths(t *testing.T) {
|
||||
- ip: 107.178.210.11`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
populateNodeInfo(ingress, info, []string{})
|
||||
|
||||
expectedExternalUrls := []string{"https://helm-guestbook.com/my/sub/path/", "https://helm-guestbook.com/my/sub/path/2", "https://helm-guestbook.com"}
|
||||
actualURLs := info.NetworkingInfo.ExternalURLs
|
||||
@@ -615,7 +615,7 @@ func TestExternalUrlWithNoSubPath(t *testing.T) {
|
||||
- ip: 107.178.210.11`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
populateNodeInfo(ingress, info, []string{})
|
||||
|
||||
expectedExternalUrls := []string{"https://107.178.210.11"}
|
||||
assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
|
||||
@@ -643,8 +643,54 @@ func TestExternalUrlWithNetworkingApi(t *testing.T) {
|
||||
- ip: 107.178.210.11`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
populateNodeInfo(ingress, info, []string{})
|
||||
|
||||
expectedExternalUrls := []string{"https://107.178.210.11"}
|
||||
assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
|
||||
}
|
||||
|
||||
func TestCustomLabel(t *testing.T) {
|
||||
configmap := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(configmap, info, []string{"my-label"})
|
||||
|
||||
assert.Equal(t, 0, len(info.Info))
|
||||
|
||||
configmap = strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm
|
||||
labels:
|
||||
my-label: value`)
|
||||
|
||||
info = &ResourceInfo{}
|
||||
populateNodeInfo(configmap, info, []string{"my-label", "other-label"})
|
||||
|
||||
assert.Equal(t, 1, len(info.Info))
|
||||
assert.Equal(t, "my-label", info.Info[0].Name)
|
||||
assert.Equal(t, "value", info.Info[0].Value)
|
||||
|
||||
configmap = strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm
|
||||
labels:
|
||||
my-label: value
|
||||
other-label: value2`)
|
||||
|
||||
info = &ResourceInfo{}
|
||||
populateNodeInfo(configmap, info, []string{"my-label", "other-label"})
|
||||
|
||||
assert.Equal(t, 2, len(info.Info))
|
||||
assert.Equal(t, "my-label", info.Info[0].Name)
|
||||
assert.Equal(t, "value", info.Info[0].Value)
|
||||
assert.Equal(t, "other-label", info.Info[1].Name)
|
||||
assert.Equal(t, "value2", info.Info[1].Value)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package controller
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
"github.com/argoproj/gitops-engine/pkg/cache"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -93,7 +93,7 @@ func (c *clusterInfoUpdater) updateClusters() {
|
||||
func (c *clusterInfoUpdater) updateClusterInfo(cluster appv1.Cluster, info *cache.ClusterInfo) error {
|
||||
apps, err := c.appLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error while fetching the apps list: %w", err)
|
||||
}
|
||||
var appCount int64
|
||||
for _, a := range apps {
|
||||
|
||||
@@ -41,27 +41,30 @@ func setApplicationHealth(resources []managedResource, statuses []appv1.Resource
|
||||
savedErr = err
|
||||
}
|
||||
}
|
||||
if healthStatus != nil {
|
||||
if persistResourceHealth {
|
||||
resHealth := appv1.HealthStatus{Status: healthStatus.Status, Message: healthStatus.Message}
|
||||
statuses[i].Health = &resHealth
|
||||
} else {
|
||||
statuses[i].Health = nil
|
||||
}
|
||||
|
||||
// Is health status is missing but resource has not built-in/custom health check then it should not affect parent app health
|
||||
if _, hasOverride := healthOverrides[lua.GetConfigMapKey(gvk)]; healthStatus.Status == health.HealthStatusMissing && !hasOverride && health.GetHealthCheckFunc(gvk) == nil {
|
||||
continue
|
||||
}
|
||||
if healthStatus == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Missing or Unknown health status of child Argo CD app should not affect parent
|
||||
if res.Kind == application.ApplicationKind && res.Group == application.Group && (healthStatus.Status == health.HealthStatusMissing || healthStatus.Status == health.HealthStatusUnknown) {
|
||||
continue
|
||||
}
|
||||
if persistResourceHealth {
|
||||
resHealth := appv1.HealthStatus{Status: healthStatus.Status, Message: healthStatus.Message}
|
||||
statuses[i].Health = &resHealth
|
||||
} else {
|
||||
statuses[i].Health = nil
|
||||
}
|
||||
|
||||
if health.IsWorse(appHealth.Status, healthStatus.Status) {
|
||||
appHealth.Status = healthStatus.Status
|
||||
}
|
||||
// Is health status is missing but resource has not built-in/custom health check then it should not affect parent app health
|
||||
if _, hasOverride := healthOverrides[lua.GetConfigMapKey(gvk)]; healthStatus.Status == health.HealthStatusMissing && !hasOverride && health.GetHealthCheckFunc(gvk) == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Missing or Unknown health status of child Argo CD app should not affect parent
|
||||
if res.Kind == application.ApplicationKind && res.Group == application.Group && (healthStatus.Status == health.HealthStatusMissing || healthStatus.Status == health.HealthStatusUnknown) {
|
||||
continue
|
||||
}
|
||||
|
||||
if health.IsWorse(appHealth.Status, healthStatus.Status) {
|
||||
appHealth.Status = healthStatus.Status
|
||||
}
|
||||
}
|
||||
if persistResourceHealth {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/robfig/cron"
|
||||
"github.com/robfig/cron/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
@@ -193,7 +193,10 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFil
|
||||
redisRequestCounter: redisRequestCounter,
|
||||
redisRequestHistogram: redisRequestHistogram,
|
||||
hostname: hostname,
|
||||
cron: cron.New(),
|
||||
// This cron is used to expire the metrics cache.
|
||||
// Currently clearing the metrics cache is logging and deleting from the map
|
||||
// so there is no possibility of panic, but we will add a chain to keep robfig/cron v1 behavior.
|
||||
cron: cron.New(cron.WithChain(cron.Recover(cron.PrintfLogger(log.StandardLogger())))),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -281,7 +284,7 @@ func (m *MetricsServer) SetExpiration(cacheExpiration time.Duration) error {
|
||||
return errors.New("Expiration is already set")
|
||||
}
|
||||
|
||||
err := m.cron.AddFunc(fmt.Sprintf("@every %s", cacheExpiration), func() {
|
||||
_, err := m.cron.AddFunc(fmt.Sprintf("@every %s", cacheExpiration), func() {
|
||||
log.Infof("Reset Prometheus metrics based on existing expiration '%v'", cacheExpiration)
|
||||
m.syncCounter.Reset()
|
||||
m.kubectlExecCounter.Reset()
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
hookutil "github.com/argoproj/gitops-engine/pkg/sync/hook"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
|
||||
resourceutil "github.com/argoproj/gitops-engine/pkg/sync/resource"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/syncwaves"
|
||||
kubeutil "github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -513,7 +514,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
}
|
||||
gvk := obj.GroupVersionKind()
|
||||
|
||||
isSelfReferencedObj := m.isSelfReferencedObj(liveObj, appLabelKey, trackingMethod)
|
||||
isSelfReferencedObj := m.isSelfReferencedObj(liveObj, targetObj, app.GetName(), appLabelKey, trackingMethod)
|
||||
|
||||
resState := v1alpha1.ResourceStatus{
|
||||
Namespace: obj.GetNamespace(),
|
||||
@@ -524,6 +525,9 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
Hook: hookutil.IsHook(obj),
|
||||
RequiresPruning: targetObj == nil && liveObj != nil && isSelfReferencedObj,
|
||||
}
|
||||
if targetObj != nil {
|
||||
resState.SyncWave = int64(syncwaves.Wave(targetObj))
|
||||
}
|
||||
|
||||
var diffResult diff.DiffResult
|
||||
if i < len(diffResults.Diffs) {
|
||||
@@ -655,7 +659,7 @@ func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revi
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error marshaling revision history patch: %w", err)
|
||||
}
|
||||
_, err = m.appclientset.ArgoprojV1alpha1().Applications(app.Namespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{})
|
||||
return err
|
||||
@@ -695,12 +699,13 @@ func NewAppStateManager(
|
||||
}
|
||||
|
||||
// isSelfReferencedObj returns whether the given obj is managed by the application
|
||||
// according to the values in the tracking annotation. It returns true when all
|
||||
// of the properties in the annotation (name, namespace, group and kind) match
|
||||
// the properties of the inspected object, or if the tracking method used does
|
||||
// not provide the required properties for matching.
|
||||
func (m *appStateManager) isSelfReferencedObj(obj *unstructured.Unstructured, appLabelKey string, trackingMethod v1alpha1.TrackingMethod) bool {
|
||||
if obj == nil {
|
||||
// according to the values of the tracking id (aka app instance value) annotation.
|
||||
// It returns true when all of the properties of the tracking id (app name, namespace,
|
||||
// group and kind) match the properties of the live object, or if the tracking method
|
||||
// used does not provide the required properties for matching.
|
||||
// Reference: https://github.com/argoproj/argo-cd/issues/8683
|
||||
func (m *appStateManager) isSelfReferencedObj(live, config *unstructured.Unstructured, appName, appLabelKey string, trackingMethod v1alpha1.TrackingMethod) bool {
|
||||
if live == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -710,17 +715,42 @@ func (m *appStateManager) isSelfReferencedObj(obj *unstructured.Unstructured, ap
|
||||
return true
|
||||
}
|
||||
|
||||
// In order for us to assume obj to be managed by this application, the
|
||||
// values from the annotation have to match the properties from the live
|
||||
// object. Cluster scoped objects carry the app's destination namespace
|
||||
// in the tracking annotation, but are unique in GVK + name combination.
|
||||
appInstance := m.resourceTracking.GetAppInstance(obj, appLabelKey, trackingMethod)
|
||||
if appInstance != nil {
|
||||
return (obj.GetNamespace() == appInstance.Namespace || obj.GetNamespace() == "") &&
|
||||
obj.GetName() == appInstance.Name &&
|
||||
obj.GetObjectKind().GroupVersionKind().Group == appInstance.Group &&
|
||||
obj.GetObjectKind().GroupVersionKind().Kind == appInstance.Kind
|
||||
// config != nil is the best-case scenario for constructing an accurate
|
||||
// Tracking ID. `config` is the "desired state" (from git/helm/etc.).
|
||||
// Using the desired state is important when there is an ApiGroup upgrade.
|
||||
// When upgrading, the comparison must be made with the new tracking ID.
|
||||
// Example:
|
||||
// live resource annotation will be:
|
||||
// ingress-app:extensions/Ingress:default/some-ingress
|
||||
// when it should be:
|
||||
// ingress-app:networking.k8s.io/Ingress:default/some-ingress
|
||||
// More details in: https://github.com/argoproj/argo-cd/pull/11012
|
||||
var aiv argo.AppInstanceValue
|
||||
if config != nil {
|
||||
aiv = argo.UnstructuredToAppInstanceValue(config, appName, "")
|
||||
return isSelfReferencedObj(live, aiv)
|
||||
}
|
||||
|
||||
// If config is nil then compare the live resource with the value
|
||||
// of the annotation. In this case, in order to validate if obj is
|
||||
// managed by this application, the values from the annotation have
|
||||
// to match the properties from the live object. Cluster scoped objects
|
||||
// carry the app's destination namespace in the tracking annotation,
|
||||
// but are unique in GVK + name combination.
|
||||
appInstance := m.resourceTracking.GetAppInstance(live, appLabelKey, trackingMethod)
|
||||
if appInstance != nil {
|
||||
return isSelfReferencedObj(live, *appInstance)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isSelfReferencedObj returns true if the given Tracking ID (`aiv`) matches
|
||||
// the given object. It returns false when the ID doesn't match. This sometimes
|
||||
// happens when a tracking label or annotation gets accidentally copied to a
|
||||
// different resource.
|
||||
func isSelfReferencedObj(obj *unstructured.Unstructured, aiv argo.AppInstanceValue) bool {
|
||||
return (obj.GetNamespace() == aiv.Namespace || obj.GetNamespace() == "") &&
|
||||
obj.GetName() == aiv.Name &&
|
||||
obj.GetObjectKind().GroupVersionKind().Group == aiv.Group &&
|
||||
obj.GetObjectKind().GroupVersionKind().Kind == aiv.Kind
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -852,6 +853,19 @@ func TestIsLiveResourceManaged(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
managedWrongAPIGroup := kube.MustToUnstructured(&networkingv1.Ingress{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "networking.k8s.io/v1",
|
||||
Kind: "Ingress",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "some-ingress",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: "guestbook:extensions/Ingress:default/some-ingress",
|
||||
},
|
||||
},
|
||||
})
|
||||
ctrl := newFakeController(&fakeData{
|
||||
apps: []runtime.Object{app, &defaultProj},
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
@@ -870,30 +884,69 @@ func TestIsLiveResourceManaged(t *testing.T) {
|
||||
})
|
||||
|
||||
manager := ctrl.appStateManager.(*appStateManager)
|
||||
appName := "guestbook"
|
||||
|
||||
// Managed resource w/ annotations
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObj, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObj, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
t.Run("will return true if trackingid matches the resource", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
configObj := managedObj.DeepCopy()
|
||||
|
||||
// Managed resource w/ label
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObjWithLabel, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObj, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObj, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
})
|
||||
t.Run("will return true if tracked with label", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
configObj := managedObjWithLabel.DeepCopy()
|
||||
|
||||
// Wrong resource name
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObjWithLabel, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
})
|
||||
t.Run("will handle if trackingId has wrong resource name and config is nil", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
|
||||
// Wrong resource group
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongName, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongName, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
})
|
||||
t.Run("will handle if trackingId has wrong resource group and config is nil", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
|
||||
// Wrong resource kind
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
})
|
||||
t.Run("will handle if trackingId has wrong kind and config is nil", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
|
||||
// Wrong resource namespace
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotationAndLabel))
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
})
|
||||
t.Run("will handle if trackingId has wrong namespace and config is nil", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
|
||||
// Nil resource
|
||||
assert.True(t, manager.isSelfReferencedObj(nil, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotationAndLabel))
|
||||
})
|
||||
t.Run("will return true if live is nil", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.True(t, manager.isSelfReferencedObj(nil, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
})
|
||||
|
||||
t.Run("will handle upgrade in desired state APIGroup", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
config := managedWrongAPIGroup.DeepCopy()
|
||||
delete(config.GetAnnotations(), common.AnnotationKeyAppInstance)
|
||||
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(managedWrongAPIGroup, config, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
cdcommon "github.com/argoproj/argo-cd/v2/common"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
@@ -20,7 +21,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/managedfields"
|
||||
"k8s.io/kubectl/pkg/util/openapi"
|
||||
|
||||
cdcommon "github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/controller/metrics"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
listersv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
|
||||
@@ -212,14 +212,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
}
|
||||
trackingMethod := argo.GetTrackingMethod(m.settingsMgr)
|
||||
|
||||
syncCtx, cleanup, err := sync.NewSyncContext(
|
||||
compareResult.syncStatus.Revision,
|
||||
reconciliationResult,
|
||||
restConfig,
|
||||
rawConfig,
|
||||
m.kubectl,
|
||||
app.Spec.Destination.Namespace,
|
||||
openAPISchema,
|
||||
opts := []sync.SyncOpt{
|
||||
sync.WithLogr(logutils.NewLogrusLogger(logEntry)),
|
||||
sync.WithHealthOverride(lua.ResourceHealthOverrides(resourceOverrides)),
|
||||
sync.WithPermissionValidator(func(un *unstructured.Unstructured, res *v1.APIResource) error {
|
||||
@@ -246,16 +239,9 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
sync.WithResourcesFilter(func(key kube.ResourceKey, target *unstructured.Unstructured, live *unstructured.Unstructured) bool {
|
||||
return (len(syncOp.Resources) == 0 ||
|
||||
argo.ContainsSyncResource(key.Name, key.Namespace, schema.GroupVersionKind{Kind: key.Kind, Group: key.Group}, syncOp.Resources)) &&
|
||||
m.isSelfReferencedObj(live, appLabelKey, trackingMethod)
|
||||
m.isSelfReferencedObj(live, target, app.GetName(), appLabelKey, trackingMethod)
|
||||
}),
|
||||
sync.WithManifestValidation(!syncOp.SyncOptions.HasOption(common.SyncOptionsDisableValidation)),
|
||||
sync.WithNamespaceCreation(syncOp.SyncOptions.HasOption("CreateNamespace=true"), func(un *unstructured.Unstructured) bool {
|
||||
if un != nil && kube.GetAppInstanceLabel(un, cdcommon.LabelKeyAppInstance) != "" {
|
||||
kube.UnsetLabel(un, cdcommon.LabelKeyAppInstance)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}),
|
||||
sync.WithSyncWaveHook(delayBetweenSyncWaves),
|
||||
sync.WithPruneLast(syncOp.SyncOptions.HasOption(common.SyncOptionPruneLast)),
|
||||
sync.WithResourceModificationChecker(syncOp.SyncOptions.HasOption("ApplyOutOfSyncOnly=true"), compareResult.diffResultList),
|
||||
@@ -263,6 +249,21 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
sync.WithReplace(syncOp.SyncOptions.HasOption(common.SyncOptionReplace)),
|
||||
sync.WithServerSideApply(syncOp.SyncOptions.HasOption(common.SyncOptionServerSideApply)),
|
||||
sync.WithServerSideApplyManager(cdcommon.ArgoCDSSAManager),
|
||||
}
|
||||
|
||||
if syncOp.SyncOptions.HasOption("CreateNamespace=true") {
|
||||
opts = append(opts, sync.WithNamespaceModifier(syncNamespace(m.resourceTracking, appLabelKey, trackingMethod, app.Name, app.Spec.SyncPolicy)))
|
||||
}
|
||||
|
||||
syncCtx, cleanup, err := sync.NewSyncContext(
|
||||
compareResult.syncStatus.Revision,
|
||||
reconciliationResult,
|
||||
restConfig,
|
||||
rawConfig,
|
||||
m.kubectl,
|
||||
app.Spec.Destination.Namespace,
|
||||
openAPISchema,
|
||||
opts...,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
||||
55
controller/sync_namespace.go
Normal file
55
controller/sync_namespace.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
gitopscommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// syncNamespace determine if Argo CD should create and/or manage the namespace
|
||||
// where the application will be deployed.
|
||||
func syncNamespace(resourceTracking argo.ResourceTracking, appLabelKey string, trackingMethod v1alpha1.TrackingMethod, appName string, syncPolicy *v1alpha1.SyncPolicy) func(m, l *unstructured.Unstructured) (bool, error) {
|
||||
// This function must return true for the managed namespace to be synced.
|
||||
return func(managedNs, liveNs *unstructured.Unstructured) (bool, error) {
|
||||
if managedNs == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
isNewNamespace := liveNs == nil
|
||||
isManagedNamespace := syncPolicy != nil && syncPolicy.ManagedNamespaceMetadata != nil
|
||||
|
||||
// should only sync the namespace if it doesn't exist in k8s or if
|
||||
// syncPolicy is defined to manage the metadata
|
||||
if !isManagedNamespace && !isNewNamespace {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if isManagedNamespace {
|
||||
managedNamespaceMetadata := syncPolicy.ManagedNamespaceMetadata
|
||||
managedNs.SetLabels(managedNamespaceMetadata.Labels)
|
||||
// managedNamespaceMetadata relies on SSA in order to avoid overriding
|
||||
// existing labels and annotations in namespaces
|
||||
managedNs.SetAnnotations(appendSSAAnnotation(managedNamespaceMetadata.Annotations))
|
||||
}
|
||||
|
||||
// TODO: https://github.com/argoproj/argo-cd/issues/11196
|
||||
// err := resourceTracking.SetAppInstance(managedNs, appLabelKey, appName, "", trackingMethod)
|
||||
// if err != nil {
|
||||
// return false, fmt.Errorf("failed to set app instance tracking on the namespace %s: %s", managedNs.GetName(), err)
|
||||
// }
|
||||
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// appendSSAAnnotation will set the managed namespace to be synced
|
||||
// with server-side apply
|
||||
func appendSSAAnnotation(in map[string]string) map[string]string {
|
||||
r := map[string]string{}
|
||||
for k, v := range in {
|
||||
r[k] = v
|
||||
}
|
||||
r[gitopscommon.AnnotationSyncOptions] = gitopscommon.SyncOptionServerSideApply
|
||||
return r
|
||||
}
|
||||
261
controller/sync_namespace_test.go
Normal file
261
controller/sync_namespace_test.go
Normal file
@@ -0,0 +1,261 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func createFakeNamespace(uid string, resourceVersion string, labels map[string]string, annotations map[string]string) *unstructured.Unstructured {
|
||||
un := unstructured.Unstructured{}
|
||||
un.SetUID(types.UID(uid))
|
||||
un.SetResourceVersion(resourceVersion)
|
||||
un.SetLabels(labels)
|
||||
un.SetAnnotations(annotations)
|
||||
un.SetKind("Namespace")
|
||||
un.SetName("some-namespace")
|
||||
return &un
|
||||
}
|
||||
|
||||
func Test_shouldNamespaceSync(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
syncPolicy *v1alpha1.SyncPolicy
|
||||
managedNs *unstructured.Unstructured
|
||||
liveNs *unstructured.Unstructured
|
||||
expected bool
|
||||
expectedLabels map[string]string
|
||||
expectedAnnotations map[string]string
|
||||
}{
|
||||
{
|
||||
name: "liveNs is nil and syncPolicy is nil",
|
||||
expected: false,
|
||||
managedNs: nil,
|
||||
liveNs: nil,
|
||||
syncPolicy: nil,
|
||||
},
|
||||
{
|
||||
name: "liveNs is nil and syncPolicy is not nil",
|
||||
expected: false,
|
||||
managedNs: nil,
|
||||
liveNs: nil,
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "liveNs is nil and syncPolicy has labels and annotations",
|
||||
expected: false,
|
||||
managedNs: nil,
|
||||
liveNs: nil,
|
||||
expectedLabels: map[string]string{"my-cool-label": "some-value"},
|
||||
expectedAnnotations: map[string]string{"my-cool-annotation": "some-value"},
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: map[string]string{"my-cool-label": "some-value"},
|
||||
Annotations: map[string]string{"my-cool-annotation": "some-value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace does not yet exist and managedNamespaceMetadata nil",
|
||||
expected: true,
|
||||
expectedLabels: map[string]string{},
|
||||
expectedAnnotations: map[string]string{},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: nil,
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace does not yet exist and managedNamespaceMetadata not nil",
|
||||
expected: true,
|
||||
expectedAnnotations: map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: nil,
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace does not yet exist and managedNamespaceMetadata has empty labels map",
|
||||
expected: true,
|
||||
expectedLabels: map[string]string{},
|
||||
expectedAnnotations: map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: nil,
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace does not yet exist and managedNamespaceMetadata has empty annotations map",
|
||||
expected: true,
|
||||
expectedAnnotations: map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: nil,
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace does not yet exist and managedNamespaceMetadata has empty annotations and labels map",
|
||||
expected: true,
|
||||
expectedLabels: map[string]string{},
|
||||
expectedAnnotations: map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: nil,
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: map[string]string{},
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace does not yet exist and managedNamespaceMetadata has labels",
|
||||
expected: true,
|
||||
expectedLabels: map[string]string{"my-cool-label": "some-value"},
|
||||
expectedAnnotations: map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: nil,
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: map[string]string{"my-cool-label": "some-value"},
|
||||
Annotations: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace does not yet exist and managedNamespaceMetadata has annotations",
|
||||
expected: true,
|
||||
expectedAnnotations: map[string]string{"my-cool-annotation": "some-value", "argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: nil,
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: nil,
|
||||
Annotations: map[string]string{"my-cool-annotation": "some-value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace does not yet exist and managedNamespaceMetadata has annotations and labels",
|
||||
expected: true,
|
||||
expectedLabels: map[string]string{"my-cool-label": "some-value"},
|
||||
expectedAnnotations: map[string]string{"my-cool-annotation": "some-value", "argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: nil,
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: map[string]string{"my-cool-label": "some-value"},
|
||||
Annotations: map[string]string{"my-cool-annotation": "some-value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace exists with no labels or annotations and managedNamespaceMetadata has labels",
|
||||
expected: true,
|
||||
expectedLabels: map[string]string{"my-cool-label": "some-value"},
|
||||
expectedAnnotations: map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: createFakeNamespace("something", "1", map[string]string{}, map[string]string{}),
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: map[string]string{"my-cool-label": "some-value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace exists with no labels or annotations and managedNamespaceMetadata has annotations",
|
||||
expected: true,
|
||||
expectedAnnotations: map[string]string{"my-cool-annotation": "some-value", "argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: createFakeNamespace("something", "1", map[string]string{}, map[string]string{}),
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Annotations: map[string]string{"my-cool-annotation": "some-value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace exists with no labels or annotations and managedNamespaceMetadata has annotations and labels",
|
||||
expected: true,
|
||||
expectedLabels: map[string]string{"my-cool-label": "some-value"},
|
||||
expectedAnnotations: map[string]string{"my-cool-annotation": "some-value", "argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: createFakeNamespace("something", "1", map[string]string{}, map[string]string{}),
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: map[string]string{"my-cool-label": "some-value"},
|
||||
Annotations: map[string]string{"my-cool-annotation": "some-value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace exists with labels and managedNamespaceMetadata has mismatching labels",
|
||||
expected: true,
|
||||
expectedAnnotations: map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
expectedLabels: map[string]string{"my-cool-label": "some-value", "my-other-label": "some-other-value"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: createFakeNamespace("something", "1", map[string]string{"my-cool-label": "some-value"}, map[string]string{}),
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: map[string]string{"my-cool-label": "some-value", "my-other-label": "some-other-value"},
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace exists with annotations and managedNamespaceMetadata has mismatching annotations",
|
||||
expected: true,
|
||||
expectedLabels: map[string]string{},
|
||||
expectedAnnotations: map[string]string{"my-cool-annotation": "some-value", "argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: createFakeNamespace("something", "1", map[string]string{}, map[string]string{"my-cool-annotation": "some-value", "my-other-annotation": "some-other-value"}),
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: map[string]string{},
|
||||
Annotations: map[string]string{"my-cool-annotation": "some-value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespace exists with annotations and labels managedNamespaceMetadata has mismatching annotations and labels",
|
||||
expected: true,
|
||||
expectedLabels: map[string]string{"my-cool-label": "some-value", "my-other-label": "some-other-value"},
|
||||
expectedAnnotations: map[string]string{"my-cool-annotation": "some-value", "my-other-annotation": "some-other-value", "argocd.argoproj.io/sync-options": "ServerSideApply=true"},
|
||||
managedNs: createFakeNamespace("", "", map[string]string{}, map[string]string{}),
|
||||
liveNs: createFakeNamespace("something", "1", map[string]string{"my-cool-label": "some-value"}, map[string]string{"my-cool-annotation": "some-value"}),
|
||||
syncPolicy: &v1alpha1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &v1alpha1.ManagedNamespaceMetadata{
|
||||
Labels: map[string]string{"my-cool-label": "some-value", "my-other-label": "some-other-value"},
|
||||
Annotations: map[string]string{"my-cool-annotation": "some-value", "my-other-annotation": "some-other-value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual, err := syncNamespace(argo.NewResourceTracking(), common.LabelKeyAppInstance, argo.TrackingMethodAnnotation, "some-app", tt.syncPolicy)(tt.managedNs, tt.liveNs)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if tt.managedNs != nil {
|
||||
assert.Equal(t, tt.expectedLabels, tt.managedNs.GetLabels())
|
||||
assert.Equal(t, tt.expectedAnnotations, tt.managedNs.GetAnnotations())
|
||||
}
|
||||
|
||||
assert.Equalf(t, tt.expected, actual, "syncNamespace(%v)", tt.syncPolicy)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,6 @@ $ curl $ARGOCD_SERVER/api/v1/session -d $'{"username":"admin","password":"passwo
|
||||
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1Njc4MTIzODcsImlzcyI6ImFyZ29jZCIsIm5iZiI6MTU2NzgxMjM4Nywic3ViIjoiYWRtaW4ifQ.ejyTgFxLhuY9mOBtKhcnvobg3QZXJ4_RusN_KIdVwao"}
|
||||
```
|
||||
|
||||
> <=v1.2
|
||||
|
||||
Then pass using the HTTP `SetCookie` header, prefixing with `argocd.token`:
|
||||
|
||||
```bash
|
||||
$ curl $ARGOCD_SERVER/api/v1/applications --cookie "argocd.token=$ARGOCD_TOKEN"
|
||||
{"metadata":{"selfLink":"/apis/argoproj.io/v1alpha1/namespaces/argocd/applications","resourceVersion":"37755"},"items":...}
|
||||
```
|
||||
|
||||
Then pass using the HTTP `Authorization` header, prefixing with `Bearer `:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -65,12 +65,12 @@ make builder-image IMAGE_NAMESPACE=argoproj IMAGE_TAG=v1.0.0
|
||||
|
||||
## Public CD
|
||||
|
||||
Every commit to master is built and published to `ghcr.io/argoproj/argocd:<version>-<short-sha>`. The list of images is available at
|
||||
Every commit to master is built and published to `ghcr.io/argoproj/argo-cd/argocd:<version>-<short-sha>`. The list of images is available at
|
||||
https://github.com/argoproj/argo-cd/packages.
|
||||
|
||||
!!! note
|
||||
GitHub docker registry [requires](https://github.community/t5/GitHub-Actions/docker-pull-from-public-GitHub-Package-Registry-fail-with-quot/m-p/32888#M1294) authentication to read
|
||||
even publicly available packages. Follow the steps from Kubernetes [documentation](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry)
|
||||
to configure image pull secret if you want to use `ghcr.io/argoproj/argocd` image.
|
||||
to configure image pull secret if you want to use `ghcr.io/argoproj/argo-cd/argocd` image.
|
||||
|
||||
The image is automatically deployed to the dev Argo CD instance: [https://cd.apps.argoproj.io/](https://cd.apps.argoproj.io/)
|
||||
|
||||
@@ -103,7 +103,7 @@ Design documents are usually submitted as PR and use [this template](https://git
|
||||
|
||||
Our community regularly meets virtually to discuss issues, ideas and enhancements around Argo CD. We do invite you to join this virtual meetings if you want to bring up certain things (including your enhancement proposals), participate in our triaging or just want to get to know other contributors.
|
||||
|
||||
The current cadence of our meetings is weekly, every Thursday at 4pm UTC (9am Pacific, 12pm Eastern, 6pm Central European, 9:30pm Indian). We use Zoom to conduct these meetings.
|
||||
The current cadence of our meetings is weekly, every Thursday at 4:15pm UTC (8:15am Pacific, 11:15am Eastern, 5:15pm Central European, 9:45pm Indian). We use Zoom to conduct these meetings.
|
||||
|
||||
* [Agenda document (Google Docs, includes Zoom link)](https://docs.google.com/document/d/1xkoFkVviB70YBzSEa4bDnu-rUZ1sIFtwKKG1Uw8XsY8)
|
||||
|
||||
|
||||
112
docs/developer-guide/contributors-quickstart.md
Normal file
112
docs/developer-guide/contributors-quickstart.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Contributors Quick-Start
|
||||
|
||||
This guide is a starting point for first-time contributors running Argo CD locally for the first time.
|
||||
|
||||
It skips advanced topics such as codegen, which are covered in the [running locally guide](running-locally.md)
|
||||
and the [toolchain guide](toolchain-guide.md).
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Install Go
|
||||
|
||||
- Install version 1.18 or newer (Verify version by running `go version`)
|
||||
|
||||
- Get current value of `GOPATH` env:
|
||||
```shell
|
||||
go env | grep path
|
||||
```
|
||||
- Change directory into that path
|
||||
```shell
|
||||
cd <path>
|
||||
```
|
||||
|
||||
### Clone the Argo CD repo
|
||||
|
||||
```shell
|
||||
mkdir -p src/github.com/argoproj/ &&
|
||||
cd src/github.com/argoproj &&
|
||||
git clone https://github.com/argoproj/argo-cd.git
|
||||
```
|
||||
|
||||
### Install Docker
|
||||
|
||||
<https://docs.docker.com/engine/install/>
|
||||
|
||||
### Install or Upgrade `kind` (Optional - Should work with any local cluster)
|
||||
|
||||
<https://kind.sigs.k8s.io/docs/user/quick-start/>
|
||||
|
||||
### Start Your Local Cluster
|
||||
|
||||
```shell
|
||||
kind create cluster
|
||||
```
|
||||
|
||||
### Install Argo CD
|
||||
|
||||
```shell
|
||||
kubectl create namespace argocd &&
|
||||
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/install.yaml
|
||||
```
|
||||
|
||||
Set kubectl config to avoid specifying the namespace in every kubectl command.
|
||||
All following commands in this guide assume the namespace is already set.
|
||||
|
||||
```shell
|
||||
kubectl config set-context --current --namespace=argocd
|
||||
```
|
||||
|
||||
### Install `yarn`
|
||||
|
||||
<https://classic.yarnpkg.com/lang/en/docs/install/>
|
||||
|
||||
### Install `goreman`
|
||||
|
||||
<https://github.com/mattn/goreman#getting-started>
|
||||
|
||||
### Run Argo CD
|
||||
|
||||
```shell
|
||||
cd argo-cd
|
||||
make start-local ARGOCD_GPG_ENABLED=false
|
||||
```
|
||||
|
||||
- Navigate to <localhost:4000> to the ArgoCD UI on browser
|
||||
- It may take a few minutes for the UI to be responsive
|
||||
|
||||
!!! note
|
||||
If the UI is not working, check the logs from `make start-local`. The logs are `DEBUG` level by default. If the logs are
|
||||
too noisy to find the problem, try editing log levels for the commands in the `Procfile` in the root of the Argo CD repo.
|
||||
|
||||
## Making Changes
|
||||
|
||||
### UI Changes
|
||||
|
||||
Modifying the User-Interface (by editing .tsx or .scss files) auto-reloads the changes on port 4000.
|
||||
|
||||
### Backend Changes
|
||||
|
||||
Modifying the API server, repo server, or a controller requires restarting the current `make start-local` session to reflect the changes.
|
||||
|
||||
### CLI Changes
|
||||
|
||||
Modifying the CLI requires restarting the current `make start-local` session to reflect the changes.
|
||||
|
||||
To test most CLI commands, you will need to log in.
|
||||
|
||||
First, get the auto-generated secret:
|
||||
|
||||
```shell
|
||||
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
|
||||
```
|
||||
|
||||
Then log in using that password and username `admin`:
|
||||
|
||||
```shell
|
||||
dist/argocd login localhost:8080
|
||||
```
|
||||
|
||||
---
|
||||
Congrats on making it to the end of this runbook! 🚀
|
||||
|
||||
For more on Argo CD, find us in Slack - <https://slack.cncf.io/> [#argo-contributors](https://cloud-native.slack.com/archives/C020XM04CUW)
|
||||
@@ -20,6 +20,36 @@ curl -sSfL https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/i
|
||||
## Connect
|
||||
Connect to one of the services, for example, to debug the main ArgoCD server run:
|
||||
```shell
|
||||
kubectl config set-context --current --namespace argocd
|
||||
telepresence helm install # Installs telepresence into your cluster
|
||||
telepresence connect # Starts the connection to your cluster (bound to the current namespace)
|
||||
telepresence intercept argocd-server --port 8080:http --env-file .envrc.remote # Starts the interception
|
||||
```
|
||||
* `--port` forwards traffic of remote port http to 8080 locally (use `--port 8080:https` if argocd-server terminates TLS)
|
||||
* `--env-file` writes all the environment variables of the remote pod into a local file, the variables are also set on the subprocess of the `--run` command
|
||||
|
||||
With this, any traffic that hits your argocd-server service in the cluster (e.g through a LB / ingress) will be forwarded to your laptop on port 8080. So that you can now start argocd-server locally to debug or test new code. If you launch argocd-server using the environment variables in `.envrc.remote`, he is able to fetch all the configmaps, secrets and so one from the cluster and transparently connect to the other microservices so that no further configuration should be neccesary and he behaves exactly the same as in the cluster.
|
||||
|
||||
List current status of Telepresence using:
|
||||
```shell
|
||||
telepresence status
|
||||
```
|
||||
|
||||
Stop the intercept using:
|
||||
```shell
|
||||
telepresence leave argocd-server-argocd
|
||||
```
|
||||
|
||||
And uninstall telepresence from your cluster:
|
||||
```shell
|
||||
telepresence helm uninstall
|
||||
```
|
||||
|
||||
See [this quickstart](https://www.telepresence.io/docs/latest/howtos/intercepts/) for more information on how to intercept services using Telepresence.
|
||||
|
||||
### Connect (telepresence v1)
|
||||
Use the following command instead:
|
||||
```shell
|
||||
telepresence --swap-deployment argocd-server --namespace argocd --env-file .envrc.remote --expose 8080:8080 --expose 8083:8083 --run bash
|
||||
```
|
||||
* `--swap-deployment` changes the argocd-server deployment
|
||||
@@ -27,7 +57,6 @@ telepresence --swap-deployment argocd-server --namespace argocd --env-file .envr
|
||||
* `--env-file` writes all the environment variables of the remote pod into a local file, the variables are also set on the subprocess of the `--run` command
|
||||
* `--run` defines which command to run once a connection is established, use `bash`, `zsh` or others
|
||||
|
||||
|
||||
## Debug
|
||||
Once a connection is established, use your favorite tools to start the server locally.
|
||||
|
||||
@@ -36,21 +65,23 @@ Once a connection is established, use your favorite tools to start the server lo
|
||||
* Run `./dist/argocd-server`
|
||||
|
||||
### VSCode
|
||||
In VSCode use the integrated terminal to run the Telepresence command to connect. Then, to run argocd-server service use the following configuration.
|
||||
Update the configuration file to point to kubeconfig file: `KUBECONFIG=` (required)
|
||||
In VSCode use the following launch configuration to run argocd-server:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Launch",
|
||||
"name": "Launch argocd-server",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/cmd/argocd-server",
|
||||
"program": "${workspaceFolder}/cmd/main.go",
|
||||
"envFile": [
|
||||
"${workspaceFolder}/.envrc.remote",
|
||||
],
|
||||
"env": {
|
||||
"ARGOCD_BINARY_NAME": "argocd-server",
|
||||
"CGO_ENABLED": "0",
|
||||
"KUBECONFIG": "/path/to/kube/config"
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
### Can I discuss my contribution ideas somewhere?
|
||||
|
||||
Sure thing! You can either open an Enhancement Proposal in our GitHub issue tracker or you can [join us on Slack](https://argoproj.github.io/community/join-slack) in channel #argo-dev to discuss your ideas and get guidance for submitting a PR.
|
||||
Sure thing! You can either open an Enhancement Proposal in our GitHub issue tracker or you can [join us on Slack](https://argoproj.github.io/community/join-slack) in channel #argo-contributors to discuss your ideas and get guidance for submitting a PR.
|
||||
|
||||
### No one has looked at my PR yet. Why?
|
||||
|
||||
|
||||
10
docs/faq.md
10
docs/faq.md
@@ -45,10 +45,9 @@ a secret named `argocd-initial-admin-secret`.
|
||||
To change the password, edit the `argocd-secret` secret and update the `admin.password` field with a new bcrypt hash.
|
||||
|
||||
!!! note "Generating a bcrypt hash"
|
||||
Use a trustworthy, offline `bcrypt` implementation such as the [Python bcrypt library](https://pypi.org/project/bcrypt/) to generate the hash.
|
||||
Use the following command to generate a bcrypt hash for `admin.password`
|
||||
|
||||
pip3 install bcrypt
|
||||
python3 -c "import bcrypt; print(bcrypt.hashpw(b'YOUR-PASSWORD-HERE', bcrypt.gensalt()).decode())"
|
||||
argocd account bcrypt --password <YOUR-PASSWORD-HERE>
|
||||
|
||||
To apply the new password hash, use the following command (replacing the hash with your own):
|
||||
|
||||
@@ -81,11 +80,6 @@ might decide to refresh `stable` repo. As workaround override
|
||||
|
||||
```yaml
|
||||
data:
|
||||
# v1.2 or earlier use `helm.repositories`
|
||||
helm.repositories: |
|
||||
- url: http://<internal-helm-repo-host>:8080
|
||||
name: stable
|
||||
# v1.3 or later use `repositories` with `type: helm`
|
||||
repositories: |
|
||||
- type: helm
|
||||
url: http://<internal-helm-repo-host>:8080
|
||||
|
||||
@@ -78,10 +78,10 @@ The API server can then be accessed using https://localhost:8080
|
||||
The initial password for the `admin` account is auto-generated and stored as
|
||||
clear text in the field `password` in a secret named `argocd-initial-admin-secret`
|
||||
in your Argo CD installation namespace. You can simply retrieve this password
|
||||
using `kubectl`:
|
||||
using the `argocd` CLI:
|
||||
|
||||
```bash
|
||||
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
|
||||
argocd admin initial-password
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
||||
@@ -99,13 +99,16 @@ spec:
|
||||
value: bar
|
||||
# Exclude contains a glob pattern to match paths against that should be explicitly excluded from being used during
|
||||
# manifest generation. This takes precedence over the `include` field.
|
||||
exclude: string
|
||||
# To match multiple patterns, wrap the patterns in {} and separate them with commas. For example: '{config.yaml,env-use2/*}'
|
||||
exclude: 'config.yaml'
|
||||
# Include contains a glob pattern to match paths against that should be explicitly included during manifest
|
||||
# generation. If this field is set, only matching manifests will be included.
|
||||
include: string
|
||||
# To match multiple patterns, wrap the patterns in {} and separate them with commas. For example: '{*.yml,*.yaml}'
|
||||
include: '*.yaml'
|
||||
|
||||
# plugin specific config
|
||||
plugin:
|
||||
# NOTE: this field is deprecated in v2.5 and must be removed to use sidecar-based plugins.
|
||||
# Only set the plugin name if the plugin is defined in argocd-cm.
|
||||
# If the plugin is defined as a sidecar, omit the name. The plugin will be automatically matched with the
|
||||
# Application according to the plugin's discovery rules.
|
||||
@@ -114,6 +117,15 @@ spec:
|
||||
env:
|
||||
- name: FOO
|
||||
value: bar
|
||||
# Plugin parameters are new in v2.5.
|
||||
parameters:
|
||||
- name: string-param
|
||||
string: example-string
|
||||
- name: array-param
|
||||
array: [item1, item2]
|
||||
- name: map-param
|
||||
map:
|
||||
param-name: param-value
|
||||
|
||||
# Destination cluster and namespace to deploy the application
|
||||
destination:
|
||||
@@ -132,6 +144,15 @@ spec:
|
||||
- CreateNamespace=true # Namespace Auto-Creation ensures that namespace specified as the application destination exists in the destination cluster.
|
||||
- PrunePropagationPolicy=foreground # Supported policies are background, foreground and orphan.
|
||||
- PruneLast=true # Allow the ability for resource pruning to happen as a final, implicit wave of a sync operation
|
||||
managedNamespaceMetadata: # Sets the metadata for the application namespace. Only valid if CreateNamespace=true (see above), otherwise it's a no-op.
|
||||
labels: # The labels to set on the application namespace
|
||||
any: label
|
||||
you: like
|
||||
annotations: # The annotations to set on the application namespace
|
||||
the: same
|
||||
applies: for
|
||||
annotations: on-the-namespace
|
||||
|
||||
# The retry feature is available since v1.7
|
||||
retry:
|
||||
limit: 5 # number of failed sync attempt retries; unlimited number of attempts if less than 0
|
||||
|
||||
@@ -45,7 +45,7 @@ spec:
|
||||
- path: applicationset/examples/git-generator-directory/cluster-addons/*
|
||||
template:
|
||||
metadata:
|
||||
name: '{{path[0]}}'
|
||||
name: '{{path.basename}}'
|
||||
spec:
|
||||
project: "my-project"
|
||||
source:
|
||||
@@ -55,6 +55,9 @@ spec:
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: '{{path.basename}}'
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
```
|
||||
(*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/git-generator-directory).*)
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ metadata:
|
||||
spec:
|
||||
generators:
|
||||
- pullRequest:
|
||||
# When using a Pull Request generator, the ApplicationSet controller polls every `requeueAfterSeconds` interval (defaulting to every 30 minutes) to detect changes.
|
||||
requeueAfterSeconds: 1800
|
||||
# See below for provider specific options.
|
||||
github:
|
||||
# ...
|
||||
@@ -181,7 +183,7 @@ If you want to access a private repository, you must also provide the credential
|
||||
## Filters
|
||||
|
||||
Filters allow selecting which pull requests to generate for. Each filter can declare one or more conditions, all of which must pass. If multiple filters are present, any can match for a repository to be included. If no filters are specified, all pull requests will be processed.
|
||||
Currently, only a subset of filters is available when comparing with SCM provider filters.
|
||||
Currently, only a subset of filters is available when comparing with [SCM provider](Generators-SCM-Provider.md) filters.
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
@@ -190,7 +192,7 @@ metadata:
|
||||
name: myapps
|
||||
spec:
|
||||
generators:
|
||||
- scmProvider:
|
||||
- pullRequest:
|
||||
# ...
|
||||
# Include any pull request ending with "argocd". (optional)
|
||||
filters:
|
||||
@@ -201,6 +203,7 @@ spec:
|
||||
|
||||
* `branchMatch`: A regexp matched against source branch names.
|
||||
|
||||
[GitHub](#github) and [GitLab](#gitlab) also support a `labels` filter.
|
||||
|
||||
## Template
|
||||
|
||||
|
||||
@@ -74,6 +74,13 @@ All your templates must replace parameters with GoTemplate Syntax:
|
||||
|
||||
Example: `{{ some.value }}` becomes `{{ .some.value }}`
|
||||
|
||||
### Cluster Generators
|
||||
|
||||
By activating Go Templating, `{{ .metadata }}` becomes an object.
|
||||
|
||||
- `{{ metadata.labels.my-label }}` becomes `{{ index .metadata.labels "my-label" }}`
|
||||
- `{{ metadata.annotations.my/annotation }}` becomes `{{ index .metadata.annotations "my/annotation" }}`
|
||||
|
||||
### Git Generators
|
||||
|
||||
By activating Go Templating, `{{ .path }}` becomes an object. Therefore, some changes must be made to the Git
|
||||
|
||||
@@ -190,6 +190,9 @@ data:
|
||||
clusters:
|
||||
- "*.local"
|
||||
|
||||
# An optional comma-separated list of metadata.labels to observe in the UI.
|
||||
resource.customLabels: tier
|
||||
|
||||
resource.compareoptions: |
|
||||
# if ignoreAggregatedRoles set to true then differences caused by aggregated roles in RBAC resources are ignored.
|
||||
ignoreAggregatedRoles: true
|
||||
|
||||
@@ -139,6 +139,26 @@ data:
|
||||
reposerver.streamed.manifest.max.tar.size: "100M"
|
||||
# Maximum size of extracted manifests when streaming manifests to the repo server for generation
|
||||
reposerver.streamed.manifest.max.extracted.size: "1G"
|
||||
# Enable git submodule support
|
||||
reposerver.enable.git.submodule: "true"
|
||||
|
||||
# Disable TLS on the HTTP endpoint
|
||||
dexserver.disable.tls: "false"
|
||||
|
||||
## ApplicationSet Controller Properties
|
||||
# Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.
|
||||
applicationsetcontroller.enable.leader.election: "false"
|
||||
# Argo CD repo namespace (default: argocd)
|
||||
applicationsetcontroller.namespace: ""
|
||||
# "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion)"
|
||||
applicationsetcontroller.policy: "sync"
|
||||
# Print debug logs. Takes precedence over loglevel
|
||||
applicationsetcontroller.debug: "false"
|
||||
# Set the logging format. One of: text|json (default "text")
|
||||
applicationsetcontroller.log.format: "text"
|
||||
# Set the logging level. One of: debug|info|warn|error (default "info")
|
||||
applicationsetcontroller.log.level: "info"
|
||||
# Enable dry run mode
|
||||
applicationsetcontroller.dryrun: "false"
|
||||
# Enable git submodule support
|
||||
applicationsetcontroller.enable.git.submodule: "true"
|
||||
|
||||
@@ -100,7 +100,7 @@ experience, you may wish to build a separate project using the [Argo CD UI dev s
|
||||
|
||||
## Banners
|
||||
|
||||
Argo CD can optionally display a banner that can be used to notify your users of upcoming maintenance and operational changes. This feature can be enabled by specifying the banner message using the `ui.bannercontent` field in the `argocd-cm` ConfigMap and Argo CD will display this message at the top of every UI page. You can optionally add a link to this message by setting `ui.bannerurl`.
|
||||
Argo CD can optionally display a banner that can be used to notify your users of upcoming maintenance and operational changes. This feature can be enabled by specifying the banner message using the `ui.bannercontent` field in the `argocd-cm` ConfigMap and Argo CD will display this message at the top of every UI page. You can optionally add a link to this message by setting `ui.bannerurl`. You can also make the banner sticky (permanent) by setting `ui.bannerpermanent` to `true` and change it's position to the bottom by using `ui.bannerposition: "bottom"`
|
||||
|
||||
### argocd-cm
|
||||
```yaml
|
||||
@@ -113,6 +113,8 @@ metadata:
|
||||
data:
|
||||
ui.bannercontent: "Banner message linked to a URL"
|
||||
ui.bannerurl: "www.bannerlink.com"
|
||||
ui.bannerpermanent: "true"
|
||||
ui.bannerposition: "bottom"
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -178,7 +178,7 @@ Consider using [bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sea
|
||||
Each repository must have a `url` field and, depending on whether you connect using HTTPS, SSH, or GitHub App, `username` and `password` (for HTTPS), `sshPrivateKey` (for SSH), or `githubAppPrivateKey` (for GitHub App).
|
||||
|
||||
!!!warning
|
||||
When using [bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets) the labels will be removed and have to be readded as descibed here: https://github.com/bitnami-labs/sealed-secrets#sealedsecrets-as-templates-for-secrets
|
||||
When using [bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets) the labels will be removed and have to be readded as described here: https://github.com/bitnami-labs/sealed-secrets#sealedsecrets-as-templates-for-secrets
|
||||
|
||||
Example for HTTPS:
|
||||
|
||||
@@ -682,6 +682,10 @@ Notes:
|
||||
* Invalid globs result in the whole rule being ignored.
|
||||
* If you add a rule that matches existing resources, these will appear in the interface as `OutOfSync`.
|
||||
|
||||
## Resource Custom Labels
|
||||
|
||||
Custom Labels configured with `resource.customLabels` (comma separated string) will be displayed in the UI (for any resource that defines them).
|
||||
|
||||
## SSO & RBAC
|
||||
|
||||
* SSO configuration details: [SSO](./user-management/index.md)
|
||||
|
||||
@@ -36,19 +36,21 @@ metadata:
|
||||
app.kubernetes.io/name: argocd-cm
|
||||
app.kubernetes.io/part-of: argocd
|
||||
data:
|
||||
resource.customizations.health.argoproj.io_Application: |
|
||||
hs = {}
|
||||
hs.status = "Progressing"
|
||||
hs.message = ""
|
||||
if obj.status ~= nil then
|
||||
if obj.status.health ~= nil then
|
||||
hs.status = obj.status.health.status
|
||||
if obj.status.health.message ~= nil then
|
||||
hs.message = obj.status.health.message
|
||||
resource.customizations: |
|
||||
argoproj.io/Application:
|
||||
health.lua: |
|
||||
hs = {}
|
||||
hs.status = "Progressing"
|
||||
hs.message = ""
|
||||
if obj.status ~= nil then
|
||||
if obj.status.health ~= nil then
|
||||
hs.status = obj.status.health.status
|
||||
if obj.status.health.message ~= nil then
|
||||
hs.message = obj.status.health.message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return hs
|
||||
return hs
|
||||
```
|
||||
|
||||
## Custom Health Checks
|
||||
@@ -62,34 +64,50 @@ There are two ways to configure a custom health check. The next two sections des
|
||||
|
||||
### Way 1. Define a Custom Health Check in `argocd-cm` ConfigMap
|
||||
|
||||
Custom health checks can be defined in `resource.customizations.health.<group_kind>` field of `argocd-cm`. If you are using argocd-operator, this is overridden by [the argocd-operator resourceCustomizations](https://argocd-operator.readthedocs.io/en/latest/reference/argocd/#resource-customizations).
|
||||
Custom health checks can be defined in
|
||||
```yaml
|
||||
resource.customizations: |
|
||||
<group/kind>:
|
||||
health.lua: |
|
||||
```
|
||||
field of `argocd-cm`. If you are using argocd-operator, this is overridden by [the argocd-operator resourceCustomizations](https://argocd-operator.readthedocs.io/en/latest/reference/argocd/#resource-customizations).
|
||||
|
||||
The following example demonstrates a health check for `cert-manager.io/Certificate`.
|
||||
|
||||
```yaml
|
||||
data:
|
||||
resource.customizations.health.cert-manager.io_Certificate: |
|
||||
hs = {}
|
||||
if obj.status ~= nil then
|
||||
if obj.status.conditions ~= nil then
|
||||
for i, condition in ipairs(obj.status.conditions) do
|
||||
if condition.type == "Ready" and condition.status == "False" then
|
||||
hs.status = "Degraded"
|
||||
hs.message = condition.message
|
||||
return hs
|
||||
end
|
||||
if condition.type == "Ready" and condition.status == "True" then
|
||||
hs.status = "Healthy"
|
||||
hs.message = condition.message
|
||||
return hs
|
||||
resource.customizations: |
|
||||
cert-manager.io/Certificate:
|
||||
health.lua: |
|
||||
hs = {}
|
||||
if obj.status ~= nil then
|
||||
if obj.status.conditions ~= nil then
|
||||
for i, condition in ipairs(obj.status.conditions) do
|
||||
if condition.type == "Ready" and condition.status == "False" then
|
||||
hs.status = "Degraded"
|
||||
hs.message = condition.message
|
||||
return hs
|
||||
end
|
||||
if condition.type == "Ready" and condition.status == "True" then
|
||||
hs.status = "Healthy"
|
||||
hs.message = condition.message
|
||||
return hs
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Waiting for certificate"
|
||||
return hs
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Waiting for certificate"
|
||||
return hs
|
||||
```
|
||||
In order to prevent duplication of the same custom health check for potentially multiple resources, it is also possible to specify a wildcard in the resource kind, like this:
|
||||
|
||||
```yaml
|
||||
resource.customizations: |
|
||||
ec2.aws.crossplane.io/*:
|
||||
health.lua: |
|
||||
...
|
||||
```
|
||||
|
||||
The `obj` is a global variable which contains the resource. The script must return an object with status and optional message field.
|
||||
@@ -139,3 +157,5 @@ tests:
|
||||
To test the implemented custom health checks, run `go test -v ./util/lua/`.
|
||||
|
||||
The [PR#1139](https://github.com/argoproj/argo-cd/pull/1139) is an example of Cert Manager CRDs custom health check.
|
||||
|
||||
Please note that bundled health checks with wildcards are not supported.
|
||||
|
||||
@@ -61,7 +61,7 @@ number of allowed concurrent kubectl fork/execs.
|
||||
* The controller uses Kubernetes watch APIs to maintain lightweight Kubernetes cluster cache. This allows to avoid querying Kubernetes during app reconciliation and significantly improve
|
||||
performance. For performance reasons controller monitors and caches only preferred the version of a resource. During reconciliation, the controller might have to convert cached resource from
|
||||
preferred version into a version of the resource stored in Git. If `kubectl convert` fails because conversion is not supported then controller falls back to Kubernetes API query which slows down
|
||||
reconciliation. In this case advice user-preferred resource version in Git.
|
||||
reconciliation. In this case, we advise you to use the preferred resource version in Git.
|
||||
|
||||
* The controller polls Git every 3m by default. You can increase this duration using `timeout.reconciliation` setting in the `argocd-cm` ConfigMap. The value of `timeout.reconciliation` is a duration string e.g `60s`, `1m`, `1h` or `1d`.
|
||||
|
||||
@@ -126,20 +126,17 @@ If the manifest generation has no side effects then requests are processed in pa
|
||||
|
||||
### Webhook and Manifest Paths Annotation
|
||||
|
||||
Argo CD aggressively caches generated manifests and uses repository commit SHA as a cache key. A new commit to the Git repository invalidates cache for all applications configured in the repository
|
||||
that again negatively affect mono repositories with multiple applications. You might use [webhooks ⧉](https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/webhook.md) and `argocd.argoproj.io/manifest-generate-paths` Application
|
||||
CRD annotation to solve this problem and improve performance.
|
||||
Argo CD aggressively caches generated manifests and uses the repository commit SHA as a cache key. A new commit to the Git repository invalidates the cache for all applications configured in the repository.
|
||||
This can negatively affect repositories with multiple applications. You can use [webhooks](https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/webhook.md) and the `argocd.argoproj.io/manifest-generate-paths` Application CRD annotation to solve this problem and improve performance.
|
||||
|
||||
The `argocd.argoproj.io/manifest-generate-paths` contains a semicolon-separated list of paths within the Git repository that are used during manifest generation. The webhook compares paths specified in the annotation
|
||||
with the changed files specified in the webhook payload. If non of the changed files are located in the paths then webhook don't trigger application reconciliation and re-uses previously generated manifests cache for a new commit.
|
||||
The `argocd.argoproj.io/manifest-generate-paths` annotation contains a semicolon-separated list of paths within the Git repository that are used during manifest generation. The webhook compares paths specified in the annotation with the changed files specified in the webhook payload. If no modified files match the paths specified in `argocd.argoproj.io/manifest-generate-paths`, then the webhook will not trigger application reconciliation and the existing cache will be considered valid for the new commit.
|
||||
|
||||
Installations that use a different repo for each app are **not** subject to this behavior and will likely get no benefit from using these annotations.
|
||||
Installations that use a different repository for each application are **not** subject to this behavior and will likely get no benefit from using these annotations.
|
||||
|
||||
!!! note
|
||||
Application manifest paths annotation support depends on the git provider used for the Application. It is currently only supported for GitHub, GitLab, and Gogs based repos
|
||||
I'm using `.Second()` modifier to avoid distracting users who already rely on `--app-resync` flag.
|
||||
Application manifest paths annotation support depends on the git provider used for the Application. It is currently only supported for GitHub, GitLab, and Gogs based repos.
|
||||
|
||||
* **Relative path** The annotation might contains relative path. In this case the path is considered relative to the path specified in the application source:
|
||||
* **Relative path** The annotation might contain a relative path. In this case the path is considered relative to the path specified in the application source:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
@@ -157,7 +154,8 @@ spec:
|
||||
path: guestbook
|
||||
# ...
|
||||
```
|
||||
* **Absolute path** The annotation value might be an absolute path started from '/'. In this case path is considered as an absolute path within the Git repository:
|
||||
|
||||
* **Absolute path** The annotation value might be an absolute path starting with '/'. In this case path is considered as an absolute path within the Git repository:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
|
||||
@@ -9,7 +9,7 @@ Metrics about applications. Scraped at the `argocd-metrics:8082/metrics` endpoin
|
||||
|--------|:----:|-------------|
|
||||
| `argocd_app_info` | gauge | Information about Applications. It contains labels such as `sync_status` and `health_status` that reflect the application state in ArgoCD. |
|
||||
| `argocd_app_k8s_request_total` | counter | Number of kubernetes requests executed during application reconciliation |
|
||||
| `argocd_app_labels` | gauge | Argo Application labels converted to Prometheus labels. Disabled by default. See section bellow about how to enable it. |
|
||||
| `argocd_app_labels` | gauge | Argo Application labels converted to Prometheus labels. Disabled by default. See section below about how to enable it. |
|
||||
| `argocd_app_reconcile` | histogram | Application reconciliation performance. |
|
||||
| `argocd_app_sync_total` | counter | Counter for application sync history |
|
||||
| `argocd_cluster_api_resource_objects` | gauge | Number of k8s resource objects in the cache. |
|
||||
@@ -41,7 +41,7 @@ Some examples are:
|
||||
As the Application labels are specific to each company, this feature is disabled by default. To enable it, add the
|
||||
`--metrics-application-labels` flag to the ArgoCD application controller.
|
||||
|
||||
The example bellow will expose the ArgoCD Application labels `team-name` and `business-unit` to Prometheus:
|
||||
The example below will expose the ArgoCD Application labels `team-name` and `business-unit` to Prometheus:
|
||||
|
||||
containers:
|
||||
- command:
|
||||
@@ -67,8 +67,7 @@ Scraped at the `argocd-server-metrics:8083/metrics` endpoint.
|
||||
| Metric | Type | Description |
|
||||
|--------|:----:|-------------|
|
||||
| `argocd_redis_request_duration` | histogram | Redis requests duration. |
|
||||
| `argocd_redis_request_total` | counter | Number of kubernetes requests executed during application
|
||||
reconciliation. |
|
||||
| `argocd_redis_request_total` | counter | Number of kubernetes requests executed during application reconciliation. |
|
||||
| `grpc_server_handled_total` | counter | Total number of RPCs completed on the server, regardless of success or failure. |
|
||||
| `grpc_server_msg_sent_total` | counter | Total number of gRPC stream messages sent by the server. |
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
## notifications template get
|
||||
## argocd admin notifications template get
|
||||
|
||||
Prints information about configured templates
|
||||
|
||||
```
|
||||
notifications template get [flags]
|
||||
argocd admin notifications template get [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -11,9 +11,9 @@ notifications template get [flags]
|
||||
```
|
||||
|
||||
# prints all templates
|
||||
notifications template get
|
||||
argocd admin notifications template get
|
||||
# print YAML formatted app-sync-succeeded template definition
|
||||
notifications template get app-sync-succeeded -o=yaml
|
||||
argocd admin notifications template get app-sync-succeeded -o=yaml
|
||||
|
||||
```
|
||||
|
||||
@@ -53,12 +53,12 @@ notifications template get app-sync-succeeded -o=yaml
|
||||
--username string Username for basic authentication to the API server
|
||||
```
|
||||
|
||||
## notifications template notify
|
||||
## argocd admin notifications template notify
|
||||
|
||||
Generates notification using the specified template and send it to specified recipients
|
||||
|
||||
```
|
||||
notifications template notify NAME RESOURCE_NAME [flags]
|
||||
argocd admin notifications template notify NAME RESOURCE_NAME [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -66,10 +66,10 @@ notifications template notify NAME RESOURCE_NAME [flags]
|
||||
```
|
||||
|
||||
# Trigger notification using in-cluster config map and secret
|
||||
notifications template notify app-sync-succeeded guestbook --recipient slack:my-slack-channel
|
||||
argocd admin notifications template notify app-sync-succeeded guestbook --recipient slack:my-slack-channel
|
||||
|
||||
# Render notification render generated notification in console
|
||||
notifications template notify app-sync-succeeded guestbook
|
||||
argocd admin notifications template notify app-sync-succeeded guestbook
|
||||
|
||||
```
|
||||
|
||||
@@ -109,12 +109,12 @@ notifications template notify app-sync-succeeded guestbook
|
||||
--username string Username for basic authentication to the API server
|
||||
```
|
||||
|
||||
## notifications trigger get
|
||||
## argocd admin notifications trigger get
|
||||
|
||||
Prints information about configured triggers
|
||||
|
||||
```
|
||||
notifications trigger get [flags]
|
||||
argocd admin notifications trigger get [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -122,9 +122,9 @@ notifications trigger get [flags]
|
||||
```
|
||||
|
||||
# prints all triggers
|
||||
notifications trigger get
|
||||
argocd admin notifications trigger get
|
||||
# print YAML formatted on-sync-failed trigger definition
|
||||
notifications trigger get on-sync-failed -o=yaml
|
||||
argocd admin notifications trigger get on-sync-failed -o=yaml
|
||||
|
||||
```
|
||||
|
||||
@@ -164,12 +164,12 @@ notifications trigger get on-sync-failed -o=yaml
|
||||
--username string Username for basic authentication to the API server
|
||||
```
|
||||
|
||||
## notifications trigger run
|
||||
## argocd admin notifications trigger run
|
||||
|
||||
Evaluates specified trigger condition and prints the result
|
||||
|
||||
```
|
||||
notifications trigger run NAME RESOURCE_NAME [flags]
|
||||
argocd admin notifications trigger run NAME RESOURCE_NAME [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -177,10 +177,10 @@ notifications trigger run NAME RESOURCE_NAME [flags]
|
||||
```
|
||||
|
||||
# Execute trigger configured in 'argocd-notification-cm' ConfigMap
|
||||
notifications trigger run on-sync-status-unknown ./sample-app.yaml
|
||||
argocd admin notifications trigger run on-sync-status-unknown ./sample-app.yaml
|
||||
|
||||
# Execute trigger using my-config-map.yaml instead of 'argocd-notifications-cm' ConfigMap
|
||||
notifications trigger run on-sync-status-unknown ./sample-app.yaml \
|
||||
argocd admin notifications trigger run on-sync-status-unknown ./sample-app.yaml \
|
||||
--config-map ./my-config-map.yaml
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Troubleshooting
|
||||
|
||||
The `argocd-notifications` binary includes a set of CLI commands that helps to configure the controller
|
||||
The `argocd admin notifications` is a CLI command group that helps to configure the controller
|
||||
settings and troubleshoot issues.
|
||||
|
||||
## Global flags
|
||||
@@ -17,15 +17,15 @@ Additionally, you can specify `:empty` value to use empty secret with no notific
|
||||
* Get list of triggers configured in the local config map:
|
||||
|
||||
```bash
|
||||
argocd-notifications trigger get \
|
||||
--config-map ./argocd-notifications-cm.yaml --secret :empty
|
||||
argocd admin notifications trigger get \
|
||||
--config-map ./argocd admin notifications-cm.yaml --secret :empty
|
||||
```
|
||||
|
||||
* Trigger notification using in-cluster config map and secret:
|
||||
|
||||
```bash
|
||||
argocd-notifications template notify \
|
||||
app-sync-succeeded guestbook --recipient slack:argocd-notifications
|
||||
argocd admin notifications template notify \
|
||||
app-sync-succeeded guestbook --recipient slack:argocd admin notifications
|
||||
```
|
||||
|
||||
## Kustomize
|
||||
@@ -44,18 +44,18 @@ kustomize build ./argocd-notifications | \
|
||||
|
||||
### On your laptop
|
||||
|
||||
You can download `argocd-notifications` from the github [release](https://github.com/argoproj-labs/argocd-notifications/releases)
|
||||
You can download the `argocd` CLI from the github [release](https://github.com/argoproj/argo-cd/releases)
|
||||
attachments.
|
||||
|
||||
The binary is available in `argoprojlabs/argocd-notifications` image. Use the `docker run` and volume mount
|
||||
The binary is available in `argoproj/argo-cd` image. Use the `docker run` and volume mount
|
||||
to execute binary on any platform.
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
docker run --rm -it -w /src -v $(pwd):/src \
|
||||
argoprojlabs/argocd-notifications:<version> \
|
||||
/app/argocd-notifications trigger get \
|
||||
argoproj/argo-cd:<version> \
|
||||
/app/argocd admin notifications trigger get \
|
||||
--config-map ./argocd-notifications-cm.yaml --secret :empty
|
||||
```
|
||||
|
||||
@@ -67,7 +67,7 @@ configuration.
|
||||
**Example**
|
||||
```bash
|
||||
kubectl exec -it argocd-notifications-controller-<pod-hash> \
|
||||
/app/argocd-notifications trigger get
|
||||
/app/argocd admin notifications trigger get
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
@@ -46,7 +46,7 @@ subresources of an application.
|
||||
#### The `action` action
|
||||
|
||||
The `action` action corresponds to either built-in resource customizations defined
|
||||
[in the Argo CD repository](https://github.com/argoproj/argo-cd/search?q=filename%3Aaction.lua+path%3Aresource_customizations),
|
||||
[in the Argo CD repository](https://github.com/argoproj/argo-cd/tree/master/resource_customizations),
|
||||
or to [custom resource actions](resource_actions.md#custom-resource-actions) defined by you.
|
||||
The `action` path is of the form `action/<api-group>/<Kind>/<action-name>`. For
|
||||
example, a resource customization path
|
||||
@@ -86,6 +86,10 @@ configures a custom role, named `org-admin`. The role is assigned to any user wh
|
||||
`your-github-org:your-team` group. All other users get the default policy of `role:readonly`,
|
||||
which cannot modify Argo CD settings.
|
||||
|
||||
!!! warning
|
||||
All authenticated users get _at least_ the permissions granted by the default policy. This access cannot be blocked
|
||||
by a `deny` rule. Instead, restrict the default policy and then grant permissions to individual roles as needed.
|
||||
|
||||
*ArgoCD ConfigMap `argocd-rbac-cm` Example:*
|
||||
|
||||
```yaml
|
||||
|
||||
32
docs/operator-manual/signed-release-assets.md
Normal file
32
docs/operator-manual/signed-release-assets.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Verification of Argo CD signatures
|
||||
|
||||
All Argo CD container images are signed by cosign. Checksums are created for the CLI binaries and then signed to ensure integrity.
|
||||
|
||||
## Prerequisites
|
||||
- Cosign [installation instructions](https://docs.sigstore.dev/cosign/installation)
|
||||
- Obtain or have a copy of the [public key](https://github.com/argoproj/argo-cd/blob/master/argocd-cosign.pub) ```argocd-cosign.pub```
|
||||
|
||||
Once you have installed cosign, you can use [argocd-cosign.pub](https://github.com/argoproj/argo-cd/blob/master/argocd-cosign.pub) to verify the signed assets or container images.
|
||||
```
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEesHEB7vX5Y2RxXypjMy1nI1z7iRG
|
||||
JI9/gt/sYqzpsa65aaNP4npM43DDxoIy/MQBo9s/mxGxmA+8UXeDpVC9vw==
|
||||
-----END PUBLIC KEY-----
|
||||
```
|
||||
## Verification of container images
|
||||
|
||||
```bash
|
||||
cosign verify --key argocd-cosign.pub quay.io/argoproj/argocd:latest
|
||||
|
||||
Verification for quay.io/argoproj/argocd:latest --
|
||||
The following checks were performed on each of these signatures:
|
||||
* The cosign claims were validated
|
||||
* The signatures were verified against the specified public key
|
||||
...
|
||||
```
|
||||
## Verification of signed assets
|
||||
|
||||
```bash
|
||||
cosign verify-blob --key cosign.pub --signature $(cat argocd-$VERSION-checksums.sig) argocd-$VERSION-checksums.txt
|
||||
Verified OK
|
||||
```
|
||||
@@ -112,7 +112,7 @@ The bundled Kustomize version has been upgraded from 4.4.1 to 4.5.7.
|
||||
|
||||
## Upgraded Helm Version
|
||||
|
||||
Note that bundled Helm version has been upgraded from 3.9.0 to 3.10.0.
|
||||
Note that bundled Helm version has been upgraded from 3.9.0 to 3.10.1.
|
||||
|
||||
## Upgraded HAProxy version
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user