mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-18 22:38:48 +01:00
Compare commits
360 Commits
reggie-k-p
...
release-3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21e13a621e | ||
|
|
226178c1a5 | ||
|
|
d91a2ab3bf | ||
|
|
458837ee21 | ||
|
|
8fb91682db | ||
|
|
63ddc3c3df | ||
|
|
b5a08fac7a | ||
|
|
f7dbfbd9b4 | ||
|
|
f0c694d9f0 | ||
|
|
06719071d6 | ||
|
|
2653860882 | ||
|
|
59f229b9fd | ||
|
|
22327073ef | ||
|
|
76a14132f5 | ||
|
|
bd9c12cbba | ||
|
|
d875c6170e | ||
|
|
c86e32b9d6 | ||
|
|
e208035ac7 | ||
|
|
b7e2027f35 | ||
|
|
b8e6a797cc | ||
|
|
3a4b0a0fed | ||
|
|
8739f91afd | ||
|
|
6df1a5bbc2 | ||
|
|
d099c24655 | ||
|
|
f1c5201e0c | ||
|
|
4524dc997c | ||
|
|
3cb4955d44 | ||
|
|
f8c0d7efcf | ||
|
|
4c59c3c7ec | ||
|
|
305ecb623b | ||
|
|
d1089f8b55 | ||
|
|
7477ecb5b9 | ||
|
|
79a11cb745 | ||
|
|
58993ecabc | ||
|
|
7ccb6cecc1 | ||
|
|
6041e85df0 | ||
|
|
95c17b68f5 | ||
|
|
18fe8c4e34 | ||
|
|
8a507b783e | ||
|
|
054538b069 | ||
|
|
845d1c9c40 | ||
|
|
d9c83da78f | ||
|
|
36e8c52829 | ||
|
|
63698bd4d2 | ||
|
|
cf53ed7ad1 | ||
|
|
6eea558023 | ||
|
|
6740282c3a | ||
|
|
3250327f01 | ||
|
|
de99b70071 | ||
|
|
984a29c921 | ||
|
|
92e4b2c877 | ||
|
|
1762a59f30 | ||
|
|
b3c5f4b1c6 | ||
|
|
60ee21377c | ||
|
|
37f29e517d | ||
|
|
fd1307d454 | ||
|
|
d8b33bd437 | ||
|
|
330077c5a8 | ||
|
|
0ee21afe37 | ||
|
|
3b34d2a56a | ||
|
|
472d50cb19 | ||
|
|
560d428381 | ||
|
|
0d22f39603 | ||
|
|
08602c27c1 | ||
|
|
ffeacae414 | ||
|
|
1f6d54ce04 | ||
|
|
ede9229353 | ||
|
|
d6e36fc5ea | ||
|
|
cbbb19ddef | ||
|
|
d8d7009a4f | ||
|
|
30e30a126b | ||
|
|
f46c387c68 | ||
|
|
063d3b6d80 | ||
|
|
3321498f0b | ||
|
|
13bc5ae0c4 | ||
|
|
11ec675138 | ||
|
|
e01714590d | ||
|
|
1f124986d9 | ||
|
|
abb60a9c27 | ||
|
|
14e69bfa0c | ||
|
|
6baa4bcbcc | ||
|
|
d11c99a516 | ||
|
|
8ed52b2ca1 | ||
|
|
0af9b6761e | ||
|
|
6e89ed9867 | ||
|
|
7f877a68a9 | ||
|
|
1d2d032642 | ||
|
|
b61b08d18b | ||
|
|
c85d65511b | ||
|
|
251c366f8a | ||
|
|
7825b4bce3 | ||
|
|
5e3feb4463 | ||
|
|
f50f88fb66 | ||
|
|
564d17d57f | ||
|
|
91253dcf46 | ||
|
|
2c6d97a89f | ||
|
|
9e9a49d95f | ||
|
|
858f159cf9 | ||
|
|
a903bc46e6 | ||
|
|
945fb13fe6 | ||
|
|
6a10ffe833 | ||
|
|
db8c801b0d | ||
|
|
739bc5812f | ||
|
|
a3b4c8327f | ||
|
|
38345d6185 | ||
|
|
796b722807 | ||
|
|
887736534f | ||
|
|
ff8305694f | ||
|
|
af05c56bc4 | ||
|
|
19b41b9d31 | ||
|
|
4e71de3ef7 | ||
|
|
b70d1444f0 | ||
|
|
d77f8d8667 | ||
|
|
adf88c56ef | ||
|
|
986cdf0ef3 | ||
|
|
98430658d8 | ||
|
|
5fff3b3640 | ||
|
|
acc4bb7078 | ||
|
|
ff45972ec8 | ||
|
|
8c1a815b78 | ||
|
|
f6b0c8146f | ||
|
|
c0a2a579c5 | ||
|
|
5e6449fbba | ||
|
|
4ffad60095 | ||
|
|
3ce6f14b74 | ||
|
|
6795b80cfc | ||
|
|
6ab9426cf5 | ||
|
|
630c6e86ea | ||
|
|
51327cb818 | ||
|
|
1d50c48dec | ||
|
|
3dd71ea14d | ||
|
|
987768c07d | ||
|
|
4519fdf650 | ||
|
|
956a6a1352 | ||
|
|
2ae64ba04b | ||
|
|
fd3c8840c9 | ||
|
|
5e974b03d3 | ||
|
|
973c3f281d | ||
|
|
3e29c4c752 | ||
|
|
a21fcf32ae | ||
|
|
a8676aeb46 | ||
|
|
8aea5612ab | ||
|
|
296100fe13 | ||
|
|
a4f9f57b5f | ||
|
|
09fac7b7f3 | ||
|
|
ccdcf6f885 | ||
|
|
7a3d6f1f6a | ||
|
|
5e7bb39b02 | ||
|
|
46ff85af31 | ||
|
|
3d97fc9ede | ||
|
|
ee4d6e0efe | ||
|
|
bbb13df56f | ||
|
|
9515e68413 | ||
|
|
ae08a3ed6c | ||
|
|
cc302bab90 | ||
|
|
0c2fa1ea23 | ||
|
|
afdb3fe047 | ||
|
|
401b1a8b3c | ||
|
|
b85887fc3a | ||
|
|
e1aeb792a4 | ||
|
|
bdc3447649 | ||
|
|
7db8ab20cb | ||
|
|
b5e09212cf | ||
|
|
326831aee4 | ||
|
|
205241f350 | ||
|
|
9851b84f97 | ||
|
|
f2fdc65386 | ||
|
|
7180deb937 | ||
|
|
ed6d2c525e | ||
|
|
7acd9305df | ||
|
|
6a902023b2 | ||
|
|
043544c197 | ||
|
|
650fa6a10e | ||
|
|
03556db586 | ||
|
|
47eb7ee4d6 | ||
|
|
b40a443b6a | ||
|
|
f4852f70af | ||
|
|
adbf9cd237 | ||
|
|
0f069ee767 | ||
|
|
a1d68ca465 | ||
|
|
3db8a412e0 | ||
|
|
67c5fe27cd | ||
|
|
9e19a8f0bc | ||
|
|
510af5c4d7 | ||
|
|
ac0c63eb43 | ||
|
|
8dfbdec941 | ||
|
|
f1ce190671 | ||
|
|
139ecf610b | ||
|
|
dcf0a1586d | ||
|
|
b48ed499c0 | ||
|
|
b0b9658e04 | ||
|
|
3eeebd8346 | ||
|
|
2253481c84 | ||
|
|
fb6a46c8de | ||
|
|
86e42fb223 | ||
|
|
d66ad45500 | ||
|
|
5cfcd0f88f | ||
|
|
c13ba1ee98 | ||
|
|
09b3856177 | ||
|
|
53a5cd3420 | ||
|
|
8b2ad9ba66 | ||
|
|
43ea991a25 | ||
|
|
8e636b78e5 | ||
|
|
a46baf4863 | ||
|
|
0cdd44bda7 | ||
|
|
18efd0cf1b | ||
|
|
40412aaba3 | ||
|
|
a544bc0920 | ||
|
|
a2e0b6f902 | ||
|
|
a22f33dbe2 | ||
|
|
1391e2f95f | ||
|
|
21acbb861d | ||
|
|
1418e87420 | ||
|
|
e8539bea12 | ||
|
|
716339c799 | ||
|
|
971bf5769a | ||
|
|
a76a8762e4 | ||
|
|
fdbe19adaf | ||
|
|
8f9ee6d1da | ||
|
|
9cfcd2d261 | ||
|
|
262360e0ee | ||
|
|
6c043d3acb | ||
|
|
2793097480 | ||
|
|
2c5f7317a5 | ||
|
|
66835b6ec7 | ||
|
|
d655bf6453 | ||
|
|
5345a2aa22 | ||
|
|
89fe752eb4 | ||
|
|
6be8ac10e6 | ||
|
|
b337855e22 | ||
|
|
ad186cfe54 | ||
|
|
235bf2aace | ||
|
|
49d0b48984 | ||
|
|
8515358413 | ||
|
|
2615be441d | ||
|
|
b0488f5a9e | ||
|
|
1b4398b5ba | ||
|
|
847b8b203c | ||
|
|
a108a84f62 | ||
|
|
27433929c0 | ||
|
|
1a62c87d29 | ||
|
|
34ccf865f6 | ||
|
|
0cff632502 | ||
|
|
ac83f1d8da | ||
|
|
34eeede822 | ||
|
|
f2c69c1628 | ||
|
|
39fcff7bad | ||
|
|
7ccc6f94c7 | ||
|
|
9b10fe9774 | ||
|
|
76162a9310 | ||
|
|
1d65d8be6c | ||
|
|
2b3eae62c4 | ||
|
|
88223b09e4 | ||
|
|
0c9039ecd9 | ||
|
|
2bea8c0deb | ||
|
|
342aea457f | ||
|
|
6b2b0668be | ||
|
|
be08693409 | ||
|
|
24615c8ce8 | ||
|
|
257ebc5f3e | ||
|
|
286c6b8725 | ||
|
|
a369ae2cf6 | ||
|
|
e2f7e7d27f | ||
|
|
6e0c949dd2 | ||
|
|
f85ad11b2a | ||
|
|
2e1d81d62c | ||
|
|
849815ee1a | ||
|
|
bea1139e96 | ||
|
|
e4a97bde49 | ||
|
|
bd284b1d29 | ||
|
|
b4421672f6 | ||
|
|
96bfc1e20b | ||
|
|
feab8bde77 | ||
|
|
9cc0c77eb5 | ||
|
|
f45ee221e5 | ||
|
|
12149466c8 | ||
|
|
84f9446d3a | ||
|
|
f8cab49a07 | ||
|
|
46e9022ab8 | ||
|
|
05620cea27 | ||
|
|
cd65bc0585 | ||
|
|
f255cef326 | ||
|
|
e168d8a9fd | ||
|
|
28b90a75ba | ||
|
|
7925bf7916 | ||
|
|
bf1f836ece | ||
|
|
ae34305d18 | ||
|
|
d8635ca27b | ||
|
|
57e9e5e201 | ||
|
|
1d66593195 | ||
|
|
01629ccd7c | ||
|
|
8ef0fd7068 | ||
|
|
d883412df8 | ||
|
|
d35a2518e6 | ||
|
|
807ff53fd5 | ||
|
|
7f34ee84fd | ||
|
|
a7c771fbdd | ||
|
|
0a70881921 | ||
|
|
e20affd6f0 | ||
|
|
72085781dc | ||
|
|
b912405c16 | ||
|
|
f5de841d97 | ||
|
|
c9a5a7e62c | ||
|
|
989fa6fecd | ||
|
|
28ca821978 | ||
|
|
8c30738f6f | ||
|
|
fe93731777 | ||
|
|
d064cec17a | ||
|
|
e548ec76c7 | ||
|
|
69c2620aef | ||
|
|
2f4c5bb972 | ||
|
|
e3f616d6dc | ||
|
|
9d459c8afe | ||
|
|
7879dc0f6e | ||
|
|
afdf99f33a | ||
|
|
c2e594c5f5 | ||
|
|
9c108cbaa0 | ||
|
|
e42727bcaf | ||
|
|
5ab598e0fd | ||
|
|
19a74df8dc | ||
|
|
3811a30949 | ||
|
|
928543527d | ||
|
|
e10a395f34 | ||
|
|
c7574970d0 | ||
|
|
24434907af | ||
|
|
988e4f48cf | ||
|
|
82be819e9a | ||
|
|
aadf2af7e1 | ||
|
|
92c5389388 | ||
|
|
d60b41da5e | ||
|
|
81d109fb7a | ||
|
|
3ddf722517 | ||
|
|
65350789e8 | ||
|
|
82ee09a930 | ||
|
|
4e827c80cf | ||
|
|
4d66a209d3 | ||
|
|
3e26594009 | ||
|
|
90ce1782ec | ||
|
|
e5e00d3f6f | ||
|
|
559dc16e72 | ||
|
|
1b85a688ac | ||
|
|
9182e6696f | ||
|
|
1a1b142cea | ||
|
|
5c2a417257 | ||
|
|
6a597c9e62 | ||
|
|
e8895b8d68 | ||
|
|
b8d4216438 | ||
|
|
507289925b | ||
|
|
fc6379b90e | ||
|
|
c626b202b7 | ||
|
|
2f7d36bc38 | ||
|
|
c879b6da2f | ||
|
|
4e997f0d5e | ||
|
|
8b1415a6b7 | ||
|
|
acb30b4e06 | ||
|
|
408e99e9e9 | ||
|
|
228378474a | ||
|
|
a66fe2af24 | ||
|
|
088ef94d78 | ||
|
|
b911650616 |
1
.github/configs/renovate-config.js
vendored
1
.github/configs/renovate-config.js
vendored
@@ -4,6 +4,7 @@ module.exports = {
|
||||
autodiscover: false,
|
||||
allowPostUpgradeCommandTemplating: true,
|
||||
allowedPostUpgradeCommands: ["make mockgen"],
|
||||
binarySource: 'install',
|
||||
extends: [
|
||||
"github>argoproj/argo-cd//renovate-presets/commons.json5",
|
||||
"github>argoproj/argo-cd//renovate-presets/custom-managers/shell.json5",
|
||||
|
||||
26
.github/pr-title-checker-config.json
vendored
26
.github/pr-title-checker-config.json
vendored
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"LABEL": {
|
||||
"name": "title needs formatting",
|
||||
"color": "EEEEEE"
|
||||
},
|
||||
"CHECKS": {
|
||||
"prefixes": ["[Bot] docs: "],
|
||||
"regexp": "^(feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*"
|
||||
},
|
||||
"MESSAGES": {
|
||||
"success": "PR title is valid",
|
||||
"failure": "PR title is invalid",
|
||||
"notice": "PR Title needs to pass regex '^(feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*"
|
||||
}
|
||||
"LABEL": {
|
||||
"name": "title needs formatting",
|
||||
"color": "EEEEEE"
|
||||
},
|
||||
"CHECKS": {
|
||||
"prefixes": ["[Bot] docs: "],
|
||||
"regexp": "^(refactor|feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*"
|
||||
},
|
||||
"MESSAGES": {
|
||||
"success": "PR title is valid",
|
||||
"failure": "PR title is invalid",
|
||||
"notice": "PR Title needs to pass regex '^(refactor|feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*"
|
||||
}
|
||||
}
|
||||
|
||||
1
.github/workflows/README.md
vendored
1
.github/workflows/README.md
vendored
@@ -11,6 +11,7 @@
|
||||
| release.yaml | Build images, cli-binaries, provenances, and post actions |
|
||||
| scorecard.yaml | Generate scorecard for supply-chain security |
|
||||
| update-snyk.yaml | Scheduled snyk reports |
|
||||
| stale.yaml | Labels stale issues and PRs |
|
||||
|
||||
# Reusable workflows
|
||||
|
||||
|
||||
6
.github/workflows/bump-major-version.yaml
vendored
6
.github/workflows/bump-major-version.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Add ~/go/bin to PATH
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
rsync -a --exclude=.git /home/runner/go/src/github.com/argoproj/argo-cd/ ../argo-cd
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
with:
|
||||
commit-message: "Bump major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}"
|
||||
title: "Bump major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}"
|
||||
|
||||
4
.github/workflows/cherry-pick-single.yml
vendored
4
.github/workflows/cherry-pick-single.yml
vendored
@@ -32,13 +32,13 @@ jobs:
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1
|
||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||
with:
|
||||
app-id: ${{ secrets.CHERRYPICK_APP_ID }}
|
||||
private-key: ${{ secrets.CHERRYPICK_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
90
.github/workflows/ci-build.yaml
vendored
90
.github/workflows/ci-build.yaml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
env:
|
||||
# Golang version to use across CI steps
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
GOLANG_VERSION: '1.25.6'
|
||||
GOLANG_VERSION: '1.26.0'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -31,8 +31,8 @@ jobs:
|
||||
frontend: ${{ steps.filter.outputs.frontend_any_changed }}
|
||||
docs: ${{ steps.filter.outputs.docs_any_changed }}
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
id: filter
|
||||
with:
|
||||
# Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file
|
||||
@@ -55,9 +55,9 @@ jobs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Download all Go modules
|
||||
@@ -75,13 +75,13 @@ jobs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -102,16 +102,16 @@ jobs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
|
||||
with:
|
||||
# renovate: datasource=go packageName=github.com/golangci/golangci-lint versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
|
||||
version: v2.5.0
|
||||
# renovate: datasource=go packageName=github.com/golangci/golangci-lint/v2 versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
|
||||
version: v2.11.3
|
||||
args: --verbose
|
||||
|
||||
test-go:
|
||||
@@ -128,11 +128,11 @@ jobs:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-local
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results
|
||||
@@ -192,11 +192,11 @@ jobs:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -216,7 +216,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -237,7 +237,7 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-race-local
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: race-results
|
||||
path: test-results/
|
||||
@@ -250,9 +250,9 @@ jobs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Create symlink in GOPATH
|
||||
@@ -307,15 +307,15 @@ jobs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
# renovate: datasource=node-version packageName=node versioning=node
|
||||
node-version: '22.9.0'
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -340,7 +340,7 @@ jobs:
|
||||
shellcheck:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- run: |
|
||||
sudo apt-get install shellcheck
|
||||
shellcheck -e SC2059 -e SC2154 -e SC2034 -e SC2016 -e SC1091 $(find . -type f -name '*.sh' | grep -v './ui/node_modules') | tee sc.log
|
||||
@@ -360,32 +360,28 @@ jobs:
|
||||
codecov_secret: ${{ secrets.CODECOV_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
if: env.codecov_secret != ''
|
||||
- name: Remove other node_modules directory
|
||||
run: |
|
||||
rm -rf ui/node_modules/argo-ui/node_modules
|
||||
if: env.codecov_secret != ''
|
||||
- name: Get e2e code coverage
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: e2e-code-coverage
|
||||
path: e2e-code-coverage
|
||||
if: env.codecov_secret != ''
|
||||
- name: Get unit test code coverage
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results
|
||||
if: env.codecov_secret != ''
|
||||
- name: combine-go-coverage
|
||||
# We generate coverage reports for all Argo CD components, but only the applicationset-controller,
|
||||
# app-controller, repo-server, and commit-server report contain coverage data. The other components currently
|
||||
@@ -393,18 +389,18 @@ jobs:
|
||||
# references to their coverage output directories.
|
||||
run: |
|
||||
go tool covdata percent -i=test-results,e2e-code-coverage/applicationset-controller,e2e-code-coverage/repo-server,e2e-code-coverage/app-controller,e2e-code-coverage/commit-server -o test-results/full-coverage.out
|
||||
if: env.codecov_secret != ''
|
||||
- name: Upload code coverage information to codecov.io
|
||||
# Only run when the workflow is for upstream (PR target or push is in argoproj/argo-cd).
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
||||
with:
|
||||
files: test-results/full-coverage.out
|
||||
fail_ci_if_error: true
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
if: env.codecov_secret != ''
|
||||
- name: Upload test results to Codecov
|
||||
# Codecov uploads test results to Codecov.io on upstream master branch and on fork master branch if the token is configured.
|
||||
if: env.codecov_secret != '' && github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
# Codecov uploads test results to Codecov.io on upstream master branch.
|
||||
if: github.repository == 'argoproj/argo-cd' && github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
||||
with:
|
||||
files: test-results/junit.xml
|
||||
@@ -427,14 +423,14 @@ jobs:
|
||||
# latest: true means that this version mush upload the coverage report to codecov.io
|
||||
# We designate the latest version because we only collect code coverage for that version.
|
||||
k3s:
|
||||
- version: v1.34.2
|
||||
- version: v1.35.0
|
||||
latest: true
|
||||
- version: v1.34.2
|
||||
latest: false
|
||||
- version: v1.33.1
|
||||
latest: false
|
||||
- version: v1.32.1
|
||||
latest: false
|
||||
- version: v1.31.0
|
||||
latest: false
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
@@ -456,9 +452,9 @@ jobs:
|
||||
swap-storage: false
|
||||
tool-cache: false
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Set GOPATH
|
||||
@@ -480,7 +476,7 @@ jobs:
|
||||
sudo chmod go-r $HOME/.kube/config
|
||||
kubectl version
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -506,7 +502,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.43.0
|
||||
docker pull ghcr.io/dexidp/dex:v2.45.0
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:8.2.3-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
@@ -538,13 +534,13 @@ jobs:
|
||||
goreman run stop-all || echo "goreman trouble"
|
||||
sleep 30
|
||||
- name: Upload e2e coverage report
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: e2e-code-coverage
|
||||
path: /tmp/coverage
|
||||
if: ${{ matrix.k3s.latest }}
|
||||
- name: Upload e2e-server logs
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
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
@@ -6,7 +6,18 @@ on:
|
||||
branches-ignore:
|
||||
- 'dependabot/**'
|
||||
- 'cherry-pick-*'
|
||||
|
||||
# Skip CodeQL for documentation-only changes
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
|
||||
pull_request:
|
||||
# Skip CodeQL for documentation-only changes
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
- '**/*.md'
|
||||
- 'docs/assets/**'
|
||||
schedule:
|
||||
- cron: '0 19 * * 0'
|
||||
|
||||
@@ -29,11 +40,11 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
|
||||
20
.github/workflows/image-reuse.yaml
vendored
20
.github/workflows/image-reuse.yaml
vendored
@@ -56,27 +56,27 @@ jobs:
|
||||
image-digest: ${{ steps.image.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
if: ${{ github.ref_type == 'tag'}}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
if: ${{ github.ref_type != 'tag'}}
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
cache: false
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0
|
||||
|
||||
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
|
||||
- name: Setup tags for container image as a CSV type
|
||||
run: |
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
echo 'EOF' >> $GITHUB_ENV
|
||||
|
||||
- name: Login to Quay.io
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ secrets.quay_username }}
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
if: ${{ inputs.quay_image_name && inputs.push }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.ghcr_username }}
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
if: ${{ inputs.ghcr_image_name && inputs.push }}
|
||||
|
||||
- name: Login to dockerhub Container Registry
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
username: ${{ secrets.docker_username }}
|
||||
password: ${{ secrets.docker_password }}
|
||||
@@ -142,7 +142,7 @@ jobs:
|
||||
|
||||
- name: Build and push container image
|
||||
id: image
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 #v6.18.0
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 #v7.0.0
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ inputs.platforms }}
|
||||
|
||||
8
.github/workflows/image.yaml
vendored
8
.github/workflows/image.yaml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
ghcr_provenance_image: ${{ steps.image.outputs.ghcr_provenance_image }}
|
||||
allow_ghcr_publish: ${{ steps.image.outputs.allow_ghcr_publish }}
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set image tag and names
|
||||
run: |
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
with:
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.25.6
|
||||
go-version: 1.26.0
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: false
|
||||
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
ghcr_image_name: ${{ needs.set-vars.outputs.ghcr_image_name }}
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.25.6
|
||||
go-version: 1.26.0
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: true
|
||||
secrets:
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
|
||||
env:
|
||||
TOKEN: ${{ secrets.TOKEN }}
|
||||
|
||||
4
.github/workflows/init-release.yaml
vendored
4
.github/workflows/init-release.yaml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
IMAGE_REPOSITORY: ${{ vars.IMAGE_REPOSITORY || 'argocd' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
git stash pop
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
with:
|
||||
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
|
||||
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
|
||||
|
||||
4
.github/workflows/pr-title-check.yml
vendored
4
.github/workflows/pr-title-check.yml
vendored
@@ -1,11 +1,9 @@
|
||||
name: "Lint PR"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
# IMPORTANT: No checkout actions, scripts, or builds should be added to this workflow. Permissions should always be used
|
||||
# with extreme caution. https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
|
||||
permissions: {}
|
||||
|
||||
# PR updates can happen in quick succession leading to this
|
||||
|
||||
20
.github/workflows/release.yaml
vendored
20
.github/workflows/release.yaml
vendored
@@ -11,7 +11,7 @@ permissions: {}
|
||||
|
||||
env:
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
GOLANG_VERSION: '1.25.6' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
GOLANG_VERSION: '1.26.0' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
|
||||
jobs:
|
||||
argocd-image:
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
quay_image_name: ${{ needs.setup-variables.outputs.quay_image_name }}
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.25.6
|
||||
go-version: 1.26.0
|
||||
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
|
||||
push: true
|
||||
secrets:
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
allow_fork_release: ${{ steps.var.outputs.allow_fork_release }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
hashes: ${{ steps.hash.outputs.hashes }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -133,7 +133,7 @@ jobs:
|
||||
run: git fetch --force --tags
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
cache: false
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
tool-cache: false
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
|
||||
id: run-goreleaser
|
||||
with:
|
||||
version: latest
|
||||
@@ -213,13 +213,13 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
cache: false
|
||||
@@ -300,7 +300,7 @@ jobs:
|
||||
TAG_STABLE: ${{ needs.setup-variables.outputs.is_latest_release }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -344,7 +344,7 @@ jobs:
|
||||
if: ${{ env.UPDATE_VERSION == 'true' }}
|
||||
|
||||
- name: Create PR to update VERSION on master branch
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
with:
|
||||
commit-message: Bump version in master
|
||||
title: 'chore: Bump version in master'
|
||||
|
||||
11
.github/workflows/renovate.yaml
vendored
11
.github/workflows/renovate.yaml
vendored
@@ -20,17 +20,10 @@ jobs:
|
||||
private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
|
||||
|
||||
# Some codegen commands require Go to be setup
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.25.6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||
|
||||
- name: Self-hosted Renovate
|
||||
uses: renovatebot/github-action@66387ab8c2464d575b933fa44e9e5a86b2822809 #44.2.4
|
||||
uses: renovatebot/github-action@abd08c7549b2a864af5df4a2e369c43f035a6a9d #46.1.5
|
||||
with:
|
||||
configurationFile: .github/configs/renovate-config.js
|
||||
token: '${{ steps.get_token.outputs.token }}'
|
||||
|
||||
4
.github/workflows/scorecard.yaml
vendored
4
.github/workflows/scorecard.yaml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
||||
33
.github/workflows/stale.yaml
vendored
Normal file
33
.github/workflows/stale.yaml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: "Label stale issues and PRs"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" #Runs midnight 12AM UTC
|
||||
|
||||
#Added Recommended permissions
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
stale-issue-message: >
|
||||
This issue has been marked as stale because it has had no activity for 90 days. Please comment if this is still relevant.
|
||||
|
||||
stale-pr-message: >
|
||||
This pull request has been marked as stale because it has had no activity for 90 days. Please comment if this is still relevant.
|
||||
|
||||
days-before-stale: 90
|
||||
days-before-close: -1 # Auto-close diabled
|
||||
|
||||
exempt-issue-labels: >
|
||||
bug, security, breaking/high, breaking/medium, breaking/low
|
||||
|
||||
# General configuration
|
||||
operations-per-run: 200
|
||||
remove-stale-when-updated: true #Remove stale label when issue/pr is updated
|
||||
2
.github/workflows/update-snyk.yaml
vendored
2
.github/workflows/update-snyk.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build reports
|
||||
|
||||
@@ -22,6 +22,7 @@ linters:
|
||||
- govet
|
||||
- importas
|
||||
- misspell
|
||||
- modernize
|
||||
- noctx
|
||||
- perfsprint
|
||||
- revive
|
||||
@@ -121,6 +122,13 @@ linters:
|
||||
- pkg: github.com/argoproj/argo-cd/v3/util/io
|
||||
alias: utilio
|
||||
|
||||
modernize:
|
||||
disable:
|
||||
# Suggest replacing omitempty with omitzero for struct fields.
|
||||
- omitzero
|
||||
# Simplify code by using go1.26's new(expr). - generates lots of false positives.
|
||||
- newexpr
|
||||
|
||||
nolintlint:
|
||||
require-specific: true
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ packages:
|
||||
interfaces:
|
||||
RepoServerServiceClient: {}
|
||||
RepoServerService_GenerateManifestWithFilesClient: {}
|
||||
github.com/argoproj/argo-cd/v3/server/application:
|
||||
github.com/argoproj/argo-cd/v3/server/broadcast:
|
||||
interfaces:
|
||||
Broadcaster: {}
|
||||
github.com/argoproj/argo-cd/v3/server/extension:
|
||||
@@ -79,10 +79,10 @@ packages:
|
||||
github.com/argoproj/argo-cd/v3/util/workloadidentity:
|
||||
interfaces:
|
||||
TokenProvider: {}
|
||||
github.com/argoproj/gitops-engine/pkg/cache:
|
||||
github.com/argoproj/argo-cd/gitops-engine/pkg/cache:
|
||||
interfaces:
|
||||
ClusterCache: {}
|
||||
github.com/argoproj/gitops-engine/pkg/diff:
|
||||
github.com/argoproj/argo-cd/gitops-engine/pkg/diff:
|
||||
interfaces:
|
||||
ServerSideDryRunner: {}
|
||||
github.com/microsoft/azure-devops-go-api/azuredevops/v7/git:
|
||||
|
||||
19
Dockerfile
19
Dockerfile
@@ -1,10 +1,10 @@
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:25.10@sha256:5922638447b1e3ba114332c896a2c7288c876bb94adec923d70d58a17d2fec5e
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:25.10@sha256:4a9232cc47bf99defcc8860ef6222c99773330367fcecbf21ba2edb0b810a31e
|
||||
####################################################################################################
|
||||
# Builder image
|
||||
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
||||
# Also used as the image in CI jobs so needs all dependencies
|
||||
####################################################################################################
|
||||
FROM docker.io/library/golang:1.25.6@sha256:fc24d3881a021e7b968a4610fc024fba749f98fe5c07d4f28e6cfa14dc65a84c AS builder
|
||||
FROM docker.io/library/golang:1.26.0@sha256:fb612b7831d53a89cbc0aaa7855b69ad7b0caf603715860cf538df854d047b84 AS builder
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
@@ -16,7 +16,6 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
unzip \
|
||||
fcgiwrap \
|
||||
git \
|
||||
git-lfs \
|
||||
make \
|
||||
wget \
|
||||
gcc \
|
||||
@@ -29,7 +28,8 @@ COPY hack/install.sh hack/tool-versions.sh ./
|
||||
COPY hack/installers installers
|
||||
|
||||
RUN ./install.sh helm && \
|
||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize
|
||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize && \
|
||||
./install.sh git-lfs
|
||||
|
||||
####################################################################################################
|
||||
# Argo CD Base - used as the base for both the release and dev argocd images
|
||||
@@ -51,7 +51,7 @@ RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
||||
apt-get update && \
|
||||
apt-get dist-upgrade -y && \
|
||||
apt-get install --no-install-recommends -y \
|
||||
git git-lfs tini ca-certificates gpg gpg-agent tzdata connect-proxy openssh-client && \
|
||||
git tini ca-certificates gpg gpg-agent tzdata connect-proxy openssh-client && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
|
||||
|
||||
@@ -61,6 +61,7 @@ COPY hack/gpg-wrapper.sh \
|
||||
/usr/local/bin/
|
||||
COPY --from=builder /usr/local/bin/helm /usr/local/bin/helm
|
||||
COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize
|
||||
COPY --from=builder /usr/local/bin/git-lfs /usr/local/bin/git-lfs
|
||||
|
||||
# keep uid_entrypoint.sh for backward compatibility
|
||||
RUN ln -s /usr/local/bin/entrypoint.sh /usr/local/bin/uid_entrypoint.sh
|
||||
@@ -79,6 +80,12 @@ RUN mkdir -p tls && \
|
||||
|
||||
ENV USER=argocd
|
||||
|
||||
# Disable gRPC service config lookups via DNS TXT records to prevent excessive
|
||||
# DNS queries for _grpc_config.<hostname> which can cause timeouts in dual-stack
|
||||
# environments. This can be overridden via argocd-cmd-params-cm ConfigMap.
|
||||
# See https://github.com/argoproj/argo-cd/issues/24991
|
||||
ENV GRPC_ENABLE_TXT_SERVICE_CONFIG=false
|
||||
|
||||
USER $ARGOCD_USER_ID
|
||||
WORKDIR /home/argocd
|
||||
|
||||
@@ -103,7 +110,7 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP
|
||||
####################################################################################################
|
||||
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
||||
####################################################################################################
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.25.6@sha256:fc24d3881a021e7b968a4610fc024fba749f98fe5c07d4f28e6cfa14dc65a84c AS argocd-build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26.0@sha256:fb612b7831d53a89cbc0aaa7855b69ad7b0caf603715860cf538df854d047b84 AS argocd-build
|
||||
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/library/golang:1.25.6@sha256:fc24d3881a021e7b968a4610fc024fba749f98fe5c07d4f28e6cfa14dc65a84c
|
||||
FROM docker.io/library/golang:1.26.0@sha256:fb612b7831d53a89cbc0aaa7855b69ad7b0caf603715860cf538df854d047b84
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -11,7 +11,6 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
unzip \
|
||||
fcgiwrap \
|
||||
git \
|
||||
git-lfs \
|
||||
make \
|
||||
wget \
|
||||
gcc \
|
||||
@@ -28,7 +27,8 @@ COPY hack/install.sh hack/tool-versions.sh ./
|
||||
COPY hack/installers installers
|
||||
|
||||
RUN ./install.sh helm && \
|
||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize
|
||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize && \
|
||||
./install.sh git-lfs
|
||||
|
||||
COPY hack/gpg-wrapper.sh \
|
||||
hack/git-verify-wrapper.sh \
|
||||
|
||||
@@ -18,14 +18,14 @@ This document lists the maintainers of the Argo CD project.
|
||||
| Dan Garfield | [todaywasawesome](https://github.com/todaywasawesome) | Approver(docs) | [Octopus Deploy](https://octopus.com/) |
|
||||
| Alexandre Gaudreault | [agaudreault](https://github.com/agaudreault) | Approver | [Intuit](https://www.github.com/intuit/) |
|
||||
| Christian Hernandez | [christianh814](https://github.com/christianh814) | Reviewer(docs) | [Akuity](https://akuity.io/) |
|
||||
| Peter Jiang | [pjiang](https://github.com/pjiang) | Reviewer | [Intuit](https://www.intuit.com/) |
|
||||
| Peter Jiang | [pjiang-dev](https://github.com/pjiang-dev) | Approver(docs) | [Intuit](https://www.intuit.com/) |
|
||||
| Andrii Korotkov | [andrii-korotkov](https://github.com/andrii-korotkov) | Reviewer | [Verkada](https://www.verkada.com/) |
|
||||
| Pasha Kostohrys | [pasha-codefresh](https://github.com/pasha-codefresh) | Approver | [Codefresh](https://www.github.com/codefresh/) |
|
||||
| Nitish Kumar | [nitishfy](https://github.com/nitishfy) | Approver(cli,docs) | [Akuity](https://akuity.io/) |
|
||||
| Justin Marquis | [34fathombelow](https://github.com/34fathombelow) | Approver(docs/ci) | [Akuity](https://akuity.io/) |
|
||||
| Alexander Matyushentsev | [alexmt](https://github.com/alexmt) | Lead | [Akuity](https://akuity.io/) |
|
||||
| Nicholas Morey | [morey-tech](https://github.com/morey-tech) | Reviewer(docs) | [Akuity](https://akuity.io/) |
|
||||
| Papapetrou Patroklos | [ppapapetrou76](https://github.com/ppapapetrou76) | Reviewer | [Octopus Deploy](https://octopus.com/) |
|
||||
| Papapetrou Patroklos | [ppapapetrou76](https://github.com/ppapapetrou76) | Approver(docs,cli) | [Octopus Deploy](https://octopus.com/) |
|
||||
| Blake Pettersson | [blakepettersson](https://github.com/blakepettersson) | Approver | [Akuity](https://akuity.io/) |
|
||||
| Ishita Sequeira | [ishitasequeira](https://github.com/ishitasequeira) | Approver | [Red Hat](https://redhat.com/) |
|
||||
| Ashutosh Singh | [ashutosh16](https://github.com/ashutosh16) | Approver(docs) | [Intuit](https://www.github.com/intuit/) |
|
||||
@@ -37,3 +37,7 @@ This document lists the maintainers of the Argo CD project.
|
||||
| Regina Voloshin | [reggie-k](https://github.com/reggie-k) | Approver | [Octopus Deploy](https://octopus.com/) |
|
||||
| Hong Wang | [wanghong230](https://github.com/wanghong230) | Reviewer | [Akuity](https://akuity.io/) |
|
||||
| Jonathan West | [jgwest](https://github.com/jgwest) | Approver | [Red Hat](https://redhat.com/) |
|
||||
| Jaewoo Choi | [choejwoo](https://github.com/choejwoo) | Reviewer | [Hyundai-Autoever](https://www.hyundai-autoever.com/eng/) |
|
||||
| Alexy Mantha | [alexymantha](https://github.com/alexymantha) | Reviewer | GoTo |
|
||||
| Kanika Rana | [ranakan19](https://github.com/ranakan19) | Reviewer | [Red Hat](https://redhat.com/) |
|
||||
| Jonathan Winters | [jwinters01](https://github.com/jwinters01) | Reviewer | [Intuit](https://www.github.com/intuit/) |
|
||||
|
||||
8
Makefile
8
Makefile
@@ -353,7 +353,7 @@ controller:
|
||||
build-ui:
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build -t argocd-ui --platform=$(TARGET_ARCH) --target argocd-ui .
|
||||
find ./ui/dist -type f -not -name gitkeep -delete
|
||||
$(DOCKER) run -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/'
|
||||
$(DOCKER) run -u $(CONTAINER_UID):$(CONTAINER_GID) -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/'
|
||||
|
||||
.PHONY: image
|
||||
ifeq ($(DEV_IMAGE), true)
|
||||
@@ -444,17 +444,23 @@ test: test-tools-image
|
||||
# Run all unit tests (local version)
|
||||
.PHONY: test-local
|
||||
test-local: test-gitops-engine
|
||||
# run if TEST_MODULE is empty or does not point to gitops-engine tests
|
||||
ifneq ($(if $(TEST_MODULE),,ALL)$(filter-out github.com/argoproj/argo-cd/gitops-engine% ./gitops-engine%,$(TEST_MODULE)),)
|
||||
if test "$(TEST_MODULE)" = ""; then \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -args -test.gocoverdir="$(PWD)/test-results"; \
|
||||
else \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -args -test.gocoverdir="$(PWD)/test-results" "$(TEST_MODULE)"; \
|
||||
fi
|
||||
endif
|
||||
|
||||
# Run gitops-engine unit tests
|
||||
.PHONY: test-gitops-engine
|
||||
test-gitops-engine:
|
||||
# run if TEST_MODULE is empty or points to gitops-engine tests
|
||||
ifneq ($(if $(TEST_MODULE),,ALL)$(filter github.com/argoproj/argo-cd/gitops-engine% ./gitops-engine%,$(TEST_MODULE)),)
|
||||
mkdir -p $(PWD)/test-results
|
||||
cd gitops-engine && go test -race -cover ./... -args -test.gocoverdir="$(PWD)/test-results"
|
||||
endif
|
||||
|
||||
.PHONY: test-race
|
||||
test-race: test-tools-image
|
||||
|
||||
2
Procfile
2
Procfile
@@ -1,6 +1,6 @@
|
||||
controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/app-controller} HOSTNAME=testappcontroller-1 FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --commit-server localhost:${ARGOCD_E2E_COMMITSERVER_PORT:-8086} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --server-side-diff-enabled=${ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF:-'false'} --hydrator-enabled=${ARGOCD_HYDRATOR_ENABLED:='false'}"
|
||||
api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/api-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --hydrator-enabled=${ARGOCD_HYDRATOR_ENABLED:='false'}"
|
||||
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v3/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml"
|
||||
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v3/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex:v2.45.0" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml"
|
||||
redis: hack/start-redis-with-password.sh
|
||||
repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "export PATH=./dist:\$PATH && [ -n \"\$ARGOCD_GIT_CONFIG\" ] && export GIT_CONFIG_GLOBAL=\$ARGOCD_GIT_CONFIG && export GIT_CONFIG_NOSYSTEM=1; GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/repo-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
||||
cmp-server: [ "$ARGOCD_E2E_TEST" = 'true' ] && exit 0 || [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
||||
|
||||
17
SECURITY.md
17
SECURITY.md
@@ -48,6 +48,23 @@ otherwise very intrusive, and there's a workaround available, we may decide to
|
||||
provide a forward-fix only, e.g. to be released the next minor release, instead
|
||||
of releasing it within a patch branch for the currently supported releases.
|
||||
|
||||
## Dependency Upgrade Policy
|
||||
|
||||
Argo CD relies on certain binaries and libraries that might appear in security scanners.
|
||||
|
||||
Upgrading certain dependencies, such as Helm, Kustomize, and git, may have negative impacts
|
||||
on users, as they may include breaking changes or changes in behavior. For this reason,
|
||||
we will only upgrade to new patch versions within the same minor version series within
|
||||
a supported Argo CD version. For example, if we are currently on Helm 3.12.0 and Argo CD
|
||||
3.4.0, we will only upgrade to Helm 3.12.x within Argo CD 3.4.x, and not to Helm 3.13.0
|
||||
or later.
|
||||
|
||||
If there is a critical, _exploitable_ vulnerability in a dependency that will not be fixed
|
||||
in a patch release, we will evaluate the impact of the vulnerability and the risk of not
|
||||
upgrading the dependency. We ask that, if you believe a version bump is justified, please
|
||||
open an issue _describing how the vulnerability is exploitable in the context of Argo CD_,
|
||||
and we will evaluate it and decide whether or not to upgrade the dependency.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find a security related bug in Argo CD, we kindly ask you for responsible
|
||||
|
||||
7
Tiltfile
7
Tiltfile
@@ -1,6 +1,10 @@
|
||||
load('ext://restart_process', 'docker_build_with_restart')
|
||||
load('ext://uibutton', 'cmd_button', 'location')
|
||||
|
||||
# tilt version should be >= v0.37.0 for k8s_server_side_apply tilt-dev/tilt#6680
|
||||
update_settings(
|
||||
k8s_server_side_apply="true",
|
||||
)
|
||||
# add ui button in web ui to run make codegen-local (top nav)
|
||||
cmd_button(
|
||||
'make codegen-local',
|
||||
@@ -52,6 +56,7 @@ local_resource(
|
||||
'build',
|
||||
'CGO_ENABLED=0 GOOS=linux GOARCH=' + arch + ' go build -gcflags="all=-N -l" -mod=readonly -o .tilt-bin/argocd_linux cmd/main.go',
|
||||
deps = code_deps,
|
||||
ignore = ['**/*_test.go'],
|
||||
allow_parallel=True,
|
||||
)
|
||||
|
||||
@@ -60,7 +65,7 @@ k8s_yaml(kustomize('manifests/dev-tilt'))
|
||||
|
||||
# build dev image
|
||||
docker_build_with_restart(
|
||||
'argocd',
|
||||
'quay.io/argoproj/argocd:latest',
|
||||
context='.',
|
||||
dockerfile='Dockerfile.tilt',
|
||||
entrypoint=[
|
||||
|
||||
29
USERS.md
29
USERS.md
@@ -7,7 +7,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
|
||||
1. [100ms](https://www.100ms.ai/)
|
||||
1. [127Labs](https://127labs.com/)
|
||||
1. [3Rein](https://www.3rein.com/)
|
||||
1. [3Rein](https://3reinmedia.com/)
|
||||
1. [42 School](https://42.fr/)
|
||||
1. [4data](https://4data.ch/)
|
||||
1. [7shifts](https://www.7shifts.com/)
|
||||
@@ -31,8 +31,8 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
|
||||
1. [Ant Group](https://www.antgroup.com/)
|
||||
1. [AppDirect](https://www.appdirect.com)
|
||||
1. [Arcadia](https://www.arcadia.io)
|
||||
1. [Arctiq Inc.](https://www.arctiq.ca)
|
||||
1. [Arcadia](https://arcadia.io/)
|
||||
1. [Arctiq Inc.](https://arctiq.com/)
|
||||
1. [Artemis Health by Nomi Health](https://www.artemishealth.com/)
|
||||
1. [Arturia](https://www.arturia.com)
|
||||
1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/)
|
||||
@@ -75,6 +75,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Chargetrip](https://chargetrip.com)
|
||||
1. [Chime](https://www.chime.com)
|
||||
1. [Chronicle Labs](https://chroniclelabs.org)
|
||||
1. [C.H.Robinson ](https://www.chrobinson.com)
|
||||
1. [Cisco ET&I](https://eti.cisco.com/)
|
||||
1. [Close](https://www.close.com/)
|
||||
1. [Cloud Posse](https://www.cloudposse.com/)
|
||||
@@ -147,14 +148,14 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Getir](https://getir.com)
|
||||
1. [GetYourGuide](https://www.getyourguide.com/)
|
||||
1. [Gitpod](https://www.gitpod.io)
|
||||
1. [Gllue](https://gllue.com)
|
||||
1. [Gllue](https://www.gllue.com/)
|
||||
1. [gloat](https://gloat.com/)
|
||||
1. [GLOBIS](https://globis.com)
|
||||
1. [Glovo](https://www.glovoapp.com)
|
||||
1. [GlueOps](https://glueops.dev)
|
||||
1. [GMETRI](https://gmetri.com/)
|
||||
1. [Gojek](https://www.gojek.io/)
|
||||
1. [GoTo Financial](https://gotofinancial.com/)
|
||||
1. [GoTo Financial](https://www.gotocompany.com/en/products/goto-financial)
|
||||
1. [GoTo](https://www.goto.com/)
|
||||
1. [Greenpass](https://www.greenpass.com.br/)
|
||||
1. [Gridfuse](https://gridfuse.com/)
|
||||
@@ -165,7 +166,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Healy](https://www.healyworld.net)
|
||||
1. [Helio](https://helio.exchange)
|
||||
1. [hetao101](https://www.hetao101.com/)
|
||||
1. [Hetki](https://hetki.ai)
|
||||
1. [Hetki](https://hetki.io/)
|
||||
1. [hipages](https://hipages.com.au/)
|
||||
1. [Hiya](https://hiya.com)
|
||||
1. [Honestbank](https://honestbank.com)
|
||||
@@ -177,7 +178,8 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Icelandair](https://www.icelandair.com)
|
||||
1. [IFS](https://www.ifs.com)
|
||||
1. [IITS-Consulting](https://iits-consulting.de)
|
||||
1. [IllumiDesk](https://www.illumidesk.com)
|
||||
1. [IllumiDesk](https://illumichat.com/)
|
||||
1. [Imagine Learning](https://www.imaginelearning.com/)
|
||||
1. [imaware](https://imaware.health)
|
||||
1. [Indeed](https://indeed.com)
|
||||
1. [Index Exchange](https://www.indexexchange.com/)
|
||||
@@ -212,7 +214,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [LeFigaro](https://www.lefigaro.fr/)
|
||||
1. [Lely](https://www.lely.com/)
|
||||
1. [LexisNexis](https://www.lexisnexis.com/)
|
||||
1. [Lian Chu Securities](https://lczq.com)
|
||||
1. [Lian Chu Securities](https://www.lczq.com/)
|
||||
1. [Liatrio](https://www.liatrio.com)
|
||||
1. [Lightricks](https://www.lightricks.com/)
|
||||
1. [Loom](https://www.loom.com/)
|
||||
@@ -245,7 +247,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [MTN Group](https://www.mtn.com/)
|
||||
1. [Municipality of The Hague](https://www.denhaag.nl/)
|
||||
1. [My Job Glasses](https://myjobglasses.com)
|
||||
1. [Natura &Co](https://naturaeco.com/)
|
||||
1. [Natura &Co](https://ri.natura.com.br/)
|
||||
1. [Netease Cloud Music](https://music.163.com/)
|
||||
1. [Nethopper](https://nethopper.io)
|
||||
1. [New Relic](https://newrelic.com/)
|
||||
@@ -276,7 +278,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Orbital Insight](https://orbitalinsight.com/)
|
||||
1. [Oscar Health Insurance](https://hioscar.com/)
|
||||
1. [Outpost24](https://outpost24.com/)
|
||||
1. [p3r](https://www.p3r.one/)
|
||||
1. [p3r](https://persona.atlus.com/p3r/)
|
||||
1. [Packlink](https://www.packlink.com/)
|
||||
1. [PagerDuty](https://www.pagerduty.com/)
|
||||
1. [Pandosearch](https://www.pandosearch.com/en/home)
|
||||
@@ -330,6 +332,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Salad Technologies](https://salad.com/)
|
||||
1. [Saloodo! GmbH](https://www.saloodo.com)
|
||||
1. [Sap Labs](http://sap.com)
|
||||
1. [SAP Signavio](https://www.signavio.com)
|
||||
1. [Sauce Labs](https://saucelabs.com/)
|
||||
1. [Schneider Electric](https://www.se.com)
|
||||
1. [Schwarz IT](https://jobs.schwarz/it-mission)
|
||||
@@ -417,18 +420,18 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [VSHN - The DevOps Company](https://vshn.ch/)
|
||||
1. [Wakacje.pl](https://www.wakacje.pl/)
|
||||
1. [Walkbase](https://www.walkbase.com/)
|
||||
1. [Webstores](https://www.webstores.nl)
|
||||
1. [Webstores](https://www.friday.nl/)
|
||||
1. [Wehkamp](https://www.wehkamp.nl/)
|
||||
1. [WeMaintain](https://www.wemaintain.com/)
|
||||
1. [WeMo Scooter](https://www.wemoscooter.com/)
|
||||
1. [Whitehat Berlin](https://whitehat.berlin) by Guido Maria Serra +Fenaroli
|
||||
1. [Witick](https://witick.io/)
|
||||
1. [Witick](https://www.witik.io/en/)
|
||||
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/)
|
||||
1. [X3M ads](https://x3mads.com)
|
||||
1. [Yieldlab](https://www.yieldlab.de/)
|
||||
1. [Yieldlab](https://virtualminds.com/)
|
||||
1. [Youverify](https://youverify.co/)
|
||||
1. [Yubo](https://www.yubo.live/)
|
||||
1. [Yuno](https://y.uno/)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -37,7 +38,6 @@ import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"k8s.io/utils/ptr"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -47,7 +47,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/controllers/template"
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/generators"
|
||||
@@ -243,11 +243,6 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err)
|
||||
}
|
||||
|
||||
err = r.updateResourcesStatus(ctx, logCtx, &applicationSetInfo, currentApplications)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("failed to get update resources status for application set: %w", err)
|
||||
}
|
||||
|
||||
// appSyncMap tracks which apps will be synced during this reconciliation.
|
||||
appSyncMap := map[string]bool{}
|
||||
|
||||
@@ -371,6 +366,16 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
}
|
||||
}
|
||||
|
||||
// Update resources status after create/update/delete so it reflects the actual cluster state.
|
||||
currentApplications, err = r.getCurrentApplications(ctx, applicationSetInfo)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err)
|
||||
}
|
||||
err = r.updateResourcesStatus(ctx, logCtx, &applicationSetInfo, currentApplications)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("failed to update resources status for application set: %w", err)
|
||||
}
|
||||
|
||||
if applicationSetInfo.RefreshRequired() {
|
||||
delete(applicationSetInfo.Annotations, common.AnnotationApplicationSetRefresh)
|
||||
err := r.Update(ctx, &applicationSetInfo)
|
||||
@@ -1046,12 +1051,10 @@ func labelMatchedExpression(logCtx *log.Entry, val string, matchExpression argov
|
||||
// if operator == NotIn, default to true
|
||||
valueMatched := matchExpression.Operator == "NotIn"
|
||||
|
||||
for _, value := range matchExpression.Values {
|
||||
if val == value {
|
||||
// first "In" match returns true
|
||||
// first "NotIn" match returns false
|
||||
return matchExpression.Operator == "In"
|
||||
}
|
||||
if slices.Contains(matchExpression.Values, val) {
|
||||
// first "In" match returns true
|
||||
// first "NotIn" match returns false
|
||||
return matchExpression.Operator == "In"
|
||||
}
|
||||
return valueMatched
|
||||
}
|
||||
@@ -1583,8 +1586,8 @@ func (r *ApplicationSetReconciler) syncDesiredApplications(logCtx *log.Entry, ap
|
||||
|
||||
// ensure that Applications generated with RollingSync do not have an automated sync policy, since the AppSet controller will handle triggering the sync operation instead
|
||||
if desiredApplications[i].Spec.SyncPolicy != nil && desiredApplications[i].Spec.SyncPolicy.IsAutomatedSyncEnabled() {
|
||||
pruneEnabled = desiredApplications[i].Spec.SyncPolicy.Automated.Prune
|
||||
desiredApplications[i].Spec.SyncPolicy.Automated.Enabled = ptr.To(false)
|
||||
pruneEnabled = desiredApplications[i].Spec.SyncPolicy.Automated.GetPrune()
|
||||
desiredApplications[i].Spec.SyncPolicy.Automated.Enabled = new(false)
|
||||
}
|
||||
|
||||
appSetStatusPending := false
|
||||
|
||||
@@ -28,8 +28,8 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/sync/common"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/generators"
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/generators/mocks"
|
||||
@@ -2950,6 +2950,112 @@ func TestUpdatePerformedWithSyncPolicySync(t *testing.T) {
|
||||
assert.Equal(t, map[string]string{"label-key": "label-value"}, app.Labels)
|
||||
}
|
||||
|
||||
// TestReconcilePopulatesResourcesStatusOnFirstRun verifies that status.resources and status.resourcesCount
|
||||
// are populated after the first reconcile, when applications are created.
|
||||
func TestReconcilePopulatesResourcesStatusOnFirstRun(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
err = corev1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
|
||||
defaultProject := v1alpha1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"},
|
||||
Spec: v1alpha1.AppProjectSpec{SourceRepos: []string{"*"}, Destinations: []v1alpha1.ApplicationDestination{{Namespace: "*", Server: "https://good-cluster"}}},
|
||||
}
|
||||
applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicySync
|
||||
appSet := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
List: &v1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{{
|
||||
Raw: []byte(`{"cluster": "good-cluster","url": "https://good-cluster"}`),
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
SyncPolicy: &v1alpha1.ApplicationSetSyncPolicy{
|
||||
ApplicationsSync: &applicationsSyncPolicy,
|
||||
},
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||
Name: "{{cluster}}",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"},
|
||||
Project: "default",
|
||||
Destination: v1alpha1.ApplicationDestination{Server: "{{url}}"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster",
|
||||
Namespace: "argocd",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"name": []byte("good-cluster"),
|
||||
"server": []byte("https://good-cluster"),
|
||||
"config": []byte("{\"username\":\"foo\",\"password\":\"foo\"}"),
|
||||
},
|
||||
}
|
||||
|
||||
kubeclientset := getDefaultTestClientSet(secret)
|
||||
client := fake.NewClientBuilder().
|
||||
WithScheme(scheme).
|
||||
WithObjects(&appSet, &defaultProject, secret).
|
||||
WithStatusSubresource(&appSet).
|
||||
WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).
|
||||
Build()
|
||||
metrics := appsetmetrics.NewFakeAppsetMetrics()
|
||||
|
||||
argodb := db.NewDB("argocd", settings.NewSettingsManager(t.Context(), kubeclientset, "argocd"), kubeclientset)
|
||||
clusterInformer, err := settings.NewClusterInformer(kubeclientset, "argocd")
|
||||
require.NoError(t, err)
|
||||
|
||||
defer startAndSyncInformer(t, clusterInformer)()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Generators: map[string]generators.Generator{"List": generators.NewListGenerator()},
|
||||
ArgoDB: argodb,
|
||||
ArgoCDNamespace: "argocd",
|
||||
KubeClientset: kubeclientset,
|
||||
Policy: v1alpha1.ApplicationsSyncPolicySync,
|
||||
Metrics: metrics,
|
||||
ClusterInformer: clusterInformer,
|
||||
}
|
||||
|
||||
req := ctrl.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "name"},
|
||||
}
|
||||
|
||||
_, err = r.Reconcile(t.Context(), req)
|
||||
require.NoError(t, err)
|
||||
|
||||
var retrievedAppSet v1alpha1.ApplicationSet
|
||||
err = r.Get(t.Context(), crtclient.ObjectKey{Namespace: "argocd", Name: "name"}, &retrievedAppSet)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, retrievedAppSet.Status.Resources, 1, "status.resources should have 1 item after first reconcile")
|
||||
assert.Equal(t, int64(1), retrievedAppSet.Status.ResourcesCount, "status.resourcesCount should be 1 after first reconcile")
|
||||
assert.Equal(t, "good-cluster", retrievedAppSet.Status.Resources[0].Name)
|
||||
}
|
||||
|
||||
func TestUpdatePerformedWithSyncPolicyCreateOnlyAndAllowPolicyOverrideFalse(t *testing.T) {
|
||||
applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateOnly
|
||||
|
||||
@@ -4652,7 +4758,7 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
|
||||
newDefaultAppSet := func(stepsCount int, status []v1alpha1.ApplicationSetApplicationStatus) v1alpha1.ApplicationSet {
|
||||
steps := []v1alpha1.ApplicationSetRolloutStep{}
|
||||
for i := 0; i < stepsCount; i++ {
|
||||
for range stepsCount {
|
||||
steps = append(steps, v1alpha1.ApplicationSetRolloutStep{MatchExpressions: []v1alpha1.ApplicationMatchExpression{}})
|
||||
}
|
||||
return v1alpha1.ApplicationSet{
|
||||
@@ -6365,7 +6471,7 @@ func TestUpdateResourceStatus(t *testing.T) {
|
||||
|
||||
func generateNAppResourceStatuses(n int) []v1alpha1.ResourceStatus {
|
||||
var r []v1alpha1.ResourceStatus
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
r = append(r, v1alpha1.ResourceStatus{
|
||||
Name: "app" + strconv.Itoa(i),
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
@@ -6380,7 +6486,7 @@ func generateNAppResourceStatuses(n int) []v1alpha1.ResourceStatus {
|
||||
|
||||
func generateNHealthyApps(n int) []v1alpha1.Application {
|
||||
var r []v1alpha1.Application
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
r = append(r, v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app" + strconv.Itoa(i),
|
||||
|
||||
@@ -96,7 +96,7 @@ func Test_ApplyTemplatePatch(t *testing.T) {
|
||||
},
|
||||
SyncPolicy: &appv1.SyncPolicy{
|
||||
Automated: &appv1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
Prune: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -172,7 +172,7 @@ spec:
|
||||
},
|
||||
SyncPolicy: &appv1.SyncPolicy{
|
||||
Automated: &appv1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
Prune: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -124,7 +124,7 @@ func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSet
|
||||
}
|
||||
|
||||
func flattenParameters(in map[string]any) (map[string]string, error) {
|
||||
flat, err := flatten.Flatten(in, "", flatten.DotStyle)
|
||||
flat, err := flatten.Flatten(normalizeMapForFlatten(in), "", flatten.DotStyle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error flatenning parameters: %w", err)
|
||||
}
|
||||
@@ -137,6 +137,31 @@ func flattenParameters(in map[string]any) (map[string]string, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// normalizeMapForFlatten recursively converts map[string]string values to
|
||||
// map[string]interface{} so that flatten.Flatten can recurse into them.
|
||||
// flatten.Flatten only recurses into map[string]interface{} and []interface{};
|
||||
// map[string]string is treated as an opaque leaf, causing individual keys
|
||||
// (e.g. metadata.labels.environment) to be absent from the flat output and
|
||||
// silently breaking post-generator selector matching in GoTemplate mode.
|
||||
func normalizeMapForFlatten(in map[string]any) map[string]any {
|
||||
out := make(map[string]any, len(in))
|
||||
for k, v := range in {
|
||||
switch cast := v.(type) {
|
||||
case map[string]string:
|
||||
inner := make(map[string]any, len(cast))
|
||||
for ik, iv := range cast {
|
||||
inner[ik] = iv
|
||||
}
|
||||
out[k] = inner
|
||||
case map[string]any:
|
||||
out[k] = normalizeMapForFlatten(cast)
|
||||
default:
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetTemplate argoprojiov1alpha1.ApplicationSetTemplate) (argoprojiov1alpha1.ApplicationSetTemplate, error) {
|
||||
// Make a copy of the value from `GetTemplate()` before merge, rather than copying directly into
|
||||
// the provided parameter (which will touch the original resource object returned by client-go)
|
||||
|
||||
@@ -249,6 +249,231 @@ func TestTransForm(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransFormGoTemplate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
selector *metav1.LabelSelector
|
||||
values map[string]string
|
||||
expected []map[string]any
|
||||
}{
|
||||
{
|
||||
name: "server filter",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"server": "https://production-01.example.com"},
|
||||
},
|
||||
expected: []map[string]any{{
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "label MatchLabels",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"metadata.labels.environment": "staging"},
|
||||
},
|
||||
expected: []map[string]any{{
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "staging",
|
||||
"org": "foo",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "staging",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "label NotIn matchExpression",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "metadata.labels.environment",
|
||||
Operator: metav1.LabelSelectorOpNotIn,
|
||||
Values: []string{"staging"},
|
||||
},
|
||||
},
|
||||
},
|
||||
// staging-01 is excluded; production-01, some-really-long-server-url, and
|
||||
// in-cluster (no labels) are all included because NotIn vacuously passes
|
||||
// when the key is absent.
|
||||
expected: []map[string]any{
|
||||
{
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "some-really-long-server-url",
|
||||
"nameNormalized": "some-really-long-server-url",
|
||||
"server": "https://some-really-long-url-that-will-exceed-63-characters.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// in-cluster (local cluster) has no labels; NotIn vacuously passes.
|
||||
"name": "in-cluster",
|
||||
"nameNormalized": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
"project": "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "slash key in label",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"metadata.labels.argocd.argoproj.io/secret-type": "cluster"},
|
||||
},
|
||||
expected: []map[string]any{
|
||||
{
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "staging",
|
||||
"org": "foo",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "staging",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "some-really-long-server-url",
|
||||
"nameNormalized": "some-really-long-server-url",
|
||||
"server": "https://some-really-long-url-that-will-exceed-63-characters.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "values filter",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"values.env": "staging"},
|
||||
},
|
||||
values: map[string]string{"env": "{{.metadata.labels.environment}}"},
|
||||
expected: []map[string]any{{
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "staging",
|
||||
"org": "foo",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "staging",
|
||||
},
|
||||
},
|
||||
"values": map[string]string{
|
||||
"env": "staging",
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
testGenerators := map[string]Generator{
|
||||
"Clusters": getMockClusterGenerator(),
|
||||
}
|
||||
|
||||
applicationSetInfo := argov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
},
|
||||
}
|
||||
|
||||
results, err := Transform(
|
||||
argov1alpha1.ApplicationSetGenerator{
|
||||
Selector: testCase.selector,
|
||||
Clusters: &argov1alpha1.ClusterGenerator{
|
||||
Selector: metav1.LabelSelector{},
|
||||
Template: argov1alpha1.ApplicationSetTemplate{},
|
||||
Values: testCase.values,
|
||||
},
|
||||
},
|
||||
testGenerators,
|
||||
emptyTemplate(),
|
||||
&applicationSetInfo, nil, nil)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, results[0].Params)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func emptyTemplate() argov1alpha1.ApplicationSetTemplate {
|
||||
return argov1alpha1.ApplicationSetTemplate{
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
@@ -528,7 +753,7 @@ func TestInterpolateGeneratorError(t *testing.T) {
|
||||
{name: "Error templating", args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "foo",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "bar/"}},
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "path/{{ index .rmap (default .override .test) }}"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
"git_test": "{{ toPrettyJson . }}",
|
||||
@@ -542,7 +767,7 @@ func TestInterpolateGeneratorError(t *testing.T) {
|
||||
},
|
||||
useGoTemplate: true,
|
||||
goTemplateOptions: []string{},
|
||||
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "failed to replace parameters in generator: failed to execute go template {{ index .rmap (default .override .test) }}: template: base:1:3: executing \"base\" at <index .rmap (default .override .test)>: error calling index: index of untyped nil"},
|
||||
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "failed to replace parameters in generator: failed to execute go template path/{{ index .rmap (default .override .test) }}: template: base:1:8: executing \"base\" at <index .rmap (default .override .test)>: error calling index: index of untyped nil"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -556,3 +781,178 @@ func TestInterpolateGeneratorError(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterpolateGeneratorValuesHandling(t *testing.T) {
|
||||
applicationSetTemplate := argov1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{
|
||||
Labels: map[string]string{},
|
||||
Annotations: map[string]string{},
|
||||
Finalizers: []string{},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
IgnoreDifferences: argov1alpha1.IgnoreDifferences{},
|
||||
Info: []argov1alpha1.Info{},
|
||||
Sources: argov1alpha1.ApplicationSources{},
|
||||
},
|
||||
}
|
||||
type args struct {
|
||||
requestedGenerator *argov1alpha1.ApplicationSetGenerator
|
||||
params map[string]any
|
||||
useGoTemplate bool
|
||||
goTemplateOptions []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want argov1alpha1.ApplicationSetGenerator
|
||||
expectedErrStr string
|
||||
}{
|
||||
{
|
||||
name: "Generator with values set",
|
||||
args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/{{.name}}.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
"appname": "{{ .path.basenameNormalized }}",
|
||||
},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
params: map[string]any{
|
||||
"name": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
},
|
||||
useGoTemplate: true,
|
||||
goTemplateOptions: []string{},
|
||||
},
|
||||
want: argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/in-cluster.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
// must stay not interpolated
|
||||
"appname": "{{ .path.basenameNormalized }}",
|
||||
},
|
||||
Directories: []argov1alpha1.GitDirectoryGeneratorItem{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
expectedErrStr: "",
|
||||
},
|
||||
{
|
||||
name: "Generator with no values set",
|
||||
args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/{{.name}}.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
params: map[string]any{
|
||||
"name": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
},
|
||||
useGoTemplate: true,
|
||||
goTemplateOptions: []string{},
|
||||
},
|
||||
want: argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/in-cluster.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{},
|
||||
Directories: []argov1alpha1.GitDirectoryGeneratorItem{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
expectedErrStr: "",
|
||||
},
|
||||
{
|
||||
name: "Generator with values set without go templates",
|
||||
args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/{{name}}.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
"appname": "{{ .path.basenameNormalized }}",
|
||||
},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
params: map[string]any{
|
||||
"name": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
},
|
||||
useGoTemplate: false,
|
||||
},
|
||||
want: argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/in-cluster.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
"appname": "{{ .path.basenameNormalized }}",
|
||||
},
|
||||
Directories: []argov1alpha1.GitDirectoryGeneratorItem{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
expectedErrStr: "",
|
||||
},
|
||||
{
|
||||
name: "Generator without values set and no go templates",
|
||||
args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/{{name}}.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
params: map[string]any{
|
||||
"name": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
},
|
||||
useGoTemplate: false,
|
||||
},
|
||||
want: argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/in-cluster.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{},
|
||||
Directories: []argov1alpha1.GitDirectoryGeneratorItem{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
expectedErrStr: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := InterpolateGenerator(tt.args.requestedGenerator, tt.args.params, tt.args.useGoTemplate, tt.args.goTemplateOptions)
|
||||
if tt.expectedErrStr != "" {
|
||||
require.EqualError(t, err, tt.expectedErrStr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, tt.want.Git.Values, got.Git.Values)
|
||||
assert.Equal(t, tt.want.Git.Revision, got.Git.Revision)
|
||||
assert.Equal(t, tt.want.Git.RepoURL, got.Git.RepoURL)
|
||||
assert.Equal(t, tt.want.Git.Files, got.Git.Files)
|
||||
assert.Equal(t, tt.want.Git.Directories, got.Git.Directories)
|
||||
assert.Equal(t, tt.want.Git.Template, got.Git.Template)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package generators
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -168,9 +169,7 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for absPath, content := range retrievedFiles {
|
||||
fileContentMap[absPath] = content
|
||||
}
|
||||
maps.Copy(fileContentMap, retrievedFiles)
|
||||
}
|
||||
|
||||
// Now remove files matching any exclude pattern
|
||||
@@ -242,9 +241,7 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
||||
params := map[string]any{}
|
||||
|
||||
if useGoTemplate {
|
||||
for k, v := range objectFound {
|
||||
params[k] = v
|
||||
}
|
||||
maps.Copy(params, objectFound)
|
||||
|
||||
paramPath := map[string]any{}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/mocks"
|
||||
@@ -111,13 +110,18 @@ foo:
|
||||
{
|
||||
name: "file parameters are added to params with go template",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{},
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{
|
||||
"somekey": "{{.path.basename}}",
|
||||
},
|
||||
useGoTemplate: true,
|
||||
},
|
||||
want: []map[string]any{
|
||||
{
|
||||
"values": map[string]string{
|
||||
"somekey": "dir",
|
||||
},
|
||||
"foo": map[string]any{
|
||||
"bar": "baz",
|
||||
},
|
||||
@@ -165,6 +169,41 @@ foo:
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path parameter are prefixed with go template and values",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{
|
||||
"somekey": "{{.myRepo.path.basename}}",
|
||||
},
|
||||
useGoTemplate: true,
|
||||
pathParamPrefix: "myRepo",
|
||||
},
|
||||
want: []map[string]any{
|
||||
{
|
||||
"foo": map[string]any{
|
||||
"bar": "baz",
|
||||
},
|
||||
"myRepo": map[string]any{
|
||||
"path": map[string]any{
|
||||
"path": "path/dir",
|
||||
"basename": "dir",
|
||||
"filename": "file_name.yaml",
|
||||
"basenameNormalized": "dir",
|
||||
"filenameNormalized": "file-name.yaml",
|
||||
"segments": []string{
|
||||
"path",
|
||||
"dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
"values": map[string]string{
|
||||
"somekey": "dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -2423,7 +2462,7 @@ func TestGitGenerator_GenerateParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: []map[string]any{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}},
|
||||
expectedProject: ptr.To("project"),
|
||||
expectedProject: new("project"),
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
@@ -2457,7 +2496,7 @@ func TestGitGenerator_GenerateParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: []map[string]any{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}},
|
||||
expectedProject: ptr.To(""),
|
||||
expectedProject: new(""),
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1100,3 +1100,85 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||
"test": "content",
|
||||
}}, params)
|
||||
}
|
||||
|
||||
func TestGitGenerator_GenerateParams_list_x_git_matrix_generator_go_templates_values(t *testing.T) {
|
||||
// Given a matrix generator over a list generator and a git generator with values,
|
||||
// that contain a template that refers to got generator output parameters.
|
||||
// This tests for a specific bug where the second generator in the matrix
|
||||
// failed to evaluate value templates that referred to generator output parameters.
|
||||
|
||||
listGeneratorMock := &generatorsMock.Generator{}
|
||||
listGeneratorMock.EXPECT().GenerateParams(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).Return([]map[string]any{
|
||||
{"some": "value"},
|
||||
}, nil)
|
||||
listGeneratorMock.EXPECT().GetTemplate(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
|
||||
gitGeneratorSpec := &v1alpha1.GitGenerator{
|
||||
RepoURL: "https://git.example.com",
|
||||
Files: []v1alpha1.GitFileGeneratorItem{
|
||||
{Path: "some/path.json"},
|
||||
},
|
||||
Values: map[string]string{
|
||||
"foo": "{{.path.basename}}",
|
||||
},
|
||||
}
|
||||
|
||||
repoServiceMock := &servicesMocks.Repos{}
|
||||
repoServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
||||
"some/path.json": []byte("test: content"),
|
||||
}, nil).Maybe()
|
||||
gitGenerator := NewGitGenerator(repoServiceMock, "")
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(map[string]Generator{
|
||||
"List": listGeneratorMock,
|
||||
"Git": gitGenerator,
|
||||
})
|
||||
|
||||
matrixGeneratorSpec := &v1alpha1.MatrixGenerator{
|
||||
Generators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
List: &v1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{
|
||||
{
|
||||
Raw: []byte(`{"some": "value"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Git: gitGeneratorSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
appProject := v1alpha1.AppProject{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
||||
|
||||
params, err := matrixGenerator.GenerateParams(&v1alpha1.ApplicationSetGenerator{
|
||||
Matrix: matrixGeneratorSpec,
|
||||
}, &v1alpha1.ApplicationSet{
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
},
|
||||
}, client)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []map[string]any{{
|
||||
"path": map[string]any{
|
||||
"basename": "some",
|
||||
"basenameNormalized": "some",
|
||||
"filename": "path.json",
|
||||
"filenameNormalized": "path.json",
|
||||
"path": "some",
|
||||
"segments": []string{"some"},
|
||||
},
|
||||
"some": "value",
|
||||
"test": "content",
|
||||
"values": map[string]string{
|
||||
"foo": "some",
|
||||
},
|
||||
}}, params)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -115,9 +116,7 @@ func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.App
|
||||
params := map[string]any{}
|
||||
|
||||
if useGoTemplate {
|
||||
for k, v := range objectFound {
|
||||
params[k] = v
|
||||
}
|
||||
maps.Copy(params, objectFound)
|
||||
} else {
|
||||
flat, err := flatten.Flatten(objectFound, "", flatten.DotStyle)
|
||||
if err != nil {
|
||||
|
||||
@@ -96,15 +96,9 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
var shortSHALength int
|
||||
var shortSHALength7 int
|
||||
for _, pull := range pulls {
|
||||
shortSHALength = 8
|
||||
if len(pull.HeadSHA) < 8 {
|
||||
shortSHALength = len(pull.HeadSHA)
|
||||
}
|
||||
shortSHALength = min(len(pull.HeadSHA), 8)
|
||||
|
||||
shortSHALength7 = 7
|
||||
if len(pull.HeadSHA) < 7 {
|
||||
shortSHALength7 = len(pull.HeadSHA)
|
||||
}
|
||||
shortSHALength7 = min(len(pull.HeadSHA), 7)
|
||||
|
||||
paramMap := map[string]any{
|
||||
"number": strconv.FormatInt(pull.Number, 10),
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -105,10 +106,8 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, g
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, allowedScmProvider := range allowedScmProviders {
|
||||
if url == allowedScmProvider {
|
||||
return nil
|
||||
}
|
||||
if slices.Contains(allowedScmProviders, url) {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
@@ -244,15 +243,9 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
var shortSHALength int
|
||||
var shortSHALength7 int
|
||||
for _, repo := range repos {
|
||||
shortSHALength = 8
|
||||
if len(repo.SHA) < 8 {
|
||||
shortSHALength = len(repo.SHA)
|
||||
}
|
||||
shortSHALength = min(len(repo.SHA), 8)
|
||||
|
||||
shortSHALength7 = 7
|
||||
if len(repo.SHA) < 7 {
|
||||
shortSHALength7 = len(repo.SHA)
|
||||
}
|
||||
shortSHALength7 = min(len(repo.SHA), 7)
|
||||
|
||||
params := map[string]any{
|
||||
"organization": repo.Organization,
|
||||
|
||||
@@ -2,6 +2,7 @@ package generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
)
|
||||
|
||||
func appendTemplatedValues(values map[string]string, params map[string]any, useGoTemplate bool, goTemplateOptions []string) error {
|
||||
@@ -26,9 +27,7 @@ func appendTemplatedValues(values map[string]string, params map[string]any, useG
|
||||
}
|
||||
}
|
||||
|
||||
for key, value := range tmp {
|
||||
params[key] = value
|
||||
}
|
||||
maps.Copy(params, tmp)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -151,9 +151,9 @@ spec:
|
||||
func newFakeAppsets(fakeAppsetYAML string) []argoappv1.ApplicationSet {
|
||||
var results []argoappv1.ApplicationSet
|
||||
|
||||
appsetRawYamls := strings.Split(fakeAppsetYAML, "---")
|
||||
appsetRawYamls := strings.SplitSeq(fakeAppsetYAML, "---")
|
||||
|
||||
for _, appsetRawYaml := range appsetRawYamls {
|
||||
for appsetRawYaml := range appsetRawYamls {
|
||||
var appset argoappv1.ApplicationSet
|
||||
err := yaml.Unmarshal([]byte(appsetRawYaml), &appset)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package pull_request
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7"
|
||||
@@ -136,13 +137,7 @@ func convertLabels(tags *[]core.WebApiTagDefinition) []string {
|
||||
// containAzureDevOpsLabels returns true if gotLabels contains expectedLabels
|
||||
func containAzureDevOpsLabels(expectedLabels []string, gotLabels []string) bool {
|
||||
for _, expected := range expectedLabels {
|
||||
found := false
|
||||
for _, got := range gotLabels {
|
||||
if expected == got {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
found := slices.Contains(gotLabels, expected)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -15,26 +15,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider/mocks"
|
||||
)
|
||||
|
||||
func createBoolPtr(x bool) *bool {
|
||||
return &x
|
||||
}
|
||||
|
||||
func createStringPtr(x string) *string {
|
||||
return &x
|
||||
}
|
||||
|
||||
func createIntPtr(x int) *int {
|
||||
return &x
|
||||
}
|
||||
|
||||
func createLabelsPtr(x []core.WebApiTagDefinition) *[]core.WebApiTagDefinition {
|
||||
return &x
|
||||
}
|
||||
|
||||
func createUniqueNamePtr(x string) *string {
|
||||
return &x
|
||||
}
|
||||
|
||||
func TestListPullRequest(t *testing.T) {
|
||||
teamProject := "myorg_project"
|
||||
repoName := "myorg_project_repo"
|
||||
@@ -46,19 +26,19 @@ func TestListPullRequest(t *testing.T) {
|
||||
|
||||
pullRequestMock := []git.GitPullRequest{
|
||||
{
|
||||
PullRequestId: createIntPtr(prID),
|
||||
Title: createStringPtr(prTitle),
|
||||
SourceRefName: createStringPtr("refs/heads/feature-branch"),
|
||||
TargetRefName: createStringPtr("refs/heads/main"),
|
||||
PullRequestId: new(prID),
|
||||
Title: new(prTitle),
|
||||
SourceRefName: new("refs/heads/feature-branch"),
|
||||
TargetRefName: new("refs/heads/main"),
|
||||
LastMergeSourceCommit: &git.GitCommitRef{
|
||||
CommitId: createStringPtr(prHeadSha),
|
||||
CommitId: new(prHeadSha),
|
||||
},
|
||||
Labels: &[]core.WebApiTagDefinition{},
|
||||
Repository: &git.GitRepository{
|
||||
Name: createStringPtr(repoName),
|
||||
Name: new(repoName),
|
||||
},
|
||||
CreatedBy: &webapi.IdentityRef{
|
||||
UniqueName: createUniqueNamePtr(uniqueName + "@example.com"),
|
||||
UniqueName: new(uniqueName + "@example.com"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -99,27 +79,27 @@ func TestConvertLabes(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "empty labels",
|
||||
gotLabels: createLabelsPtr([]core.WebApiTagDefinition{}),
|
||||
gotLabels: &[]core.WebApiTagDefinition{},
|
||||
expectedLabels: []string{},
|
||||
},
|
||||
{
|
||||
name: "nil labels",
|
||||
gotLabels: createLabelsPtr(nil),
|
||||
gotLabels: nil,
|
||||
expectedLabels: []string{},
|
||||
},
|
||||
{
|
||||
name: "one label",
|
||||
gotLabels: createLabelsPtr([]core.WebApiTagDefinition{
|
||||
{Name: createStringPtr("label1"), Active: createBoolPtr(true)},
|
||||
}),
|
||||
gotLabels: &[]core.WebApiTagDefinition{
|
||||
{Name: new("label1"), Active: new(true)},
|
||||
},
|
||||
expectedLabels: []string{"label1"},
|
||||
},
|
||||
{
|
||||
name: "two label",
|
||||
gotLabels: createLabelsPtr([]core.WebApiTagDefinition{
|
||||
{Name: createStringPtr("label1"), Active: createBoolPtr(true)},
|
||||
{Name: createStringPtr("label2"), Active: createBoolPtr(true)},
|
||||
}),
|
||||
gotLabels: &[]core.WebApiTagDefinition{
|
||||
{Name: new("label1"), Active: new(true)},
|
||||
{Name: new("label2"), Active: new(true)},
|
||||
},
|
||||
expectedLabels: []string{"label1", "label2"},
|
||||
},
|
||||
}
|
||||
@@ -216,7 +196,7 @@ func TestBuildURL(t *testing.T) {
|
||||
|
||||
func TestAzureDevOpsListReturnsRepositoryNotFoundError(t *testing.T) {
|
||||
args := git.GetPullRequestsByProjectArgs{
|
||||
Project: createStringPtr("nonexistent"),
|
||||
Project: new("nonexistent"),
|
||||
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
||||
}
|
||||
|
||||
|
||||
@@ -268,7 +268,6 @@ func TestListPullRequestTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defaultHandler(t)(w, r)
|
||||
|
||||
@@ -83,7 +83,7 @@ func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
// containLabels returns true if gotLabels contains expectedLabels
|
||||
func giteaContainLabels(expectedLabels []string, gotLabels []*gitea.Label) bool {
|
||||
gotLabelNamesMap := make(map[string]bool)
|
||||
for i := 0; i < len(gotLabels); i++ {
|
||||
for i := range gotLabels {
|
||||
gotLabelNamesMap[gotLabels[i].Name] = true
|
||||
}
|
||||
for _, expected := range expectedLabels {
|
||||
|
||||
@@ -10,10 +10,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func toPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestContainLabels(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
@@ -25,9 +21,9 @@ func TestContainLabels(t *testing.T) {
|
||||
Name: "Match labels",
|
||||
Labels: []string{"label1", "label2"},
|
||||
PullLabels: []*github.Label{
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
{Name: new("label1")},
|
||||
{Name: new("label2")},
|
||||
{Name: new("label3")},
|
||||
},
|
||||
Expect: true,
|
||||
},
|
||||
@@ -35,9 +31,9 @@ func TestContainLabels(t *testing.T) {
|
||||
Name: "Not match labels",
|
||||
Labels: []string{"label1", "label4"},
|
||||
PullLabels: []*github.Label{
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
{Name: new("label1")},
|
||||
{Name: new("label2")},
|
||||
{Name: new("label3")},
|
||||
},
|
||||
Expect: false,
|
||||
},
|
||||
@@ -45,9 +41,9 @@ func TestContainLabels(t *testing.T) {
|
||||
Name: "No specify",
|
||||
Labels: []string{},
|
||||
PullLabels: []*github.Label{
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
{Name: new("label1")},
|
||||
{Name: new("label2")},
|
||||
{Name: new("label3")},
|
||||
},
|
||||
Expect: true,
|
||||
},
|
||||
@@ -70,9 +66,9 @@ func TestGetGitHubPRLabelNames(t *testing.T) {
|
||||
{
|
||||
Name: "PR has labels",
|
||||
PullLabels: []*github.Label{
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
{Name: new("label1")},
|
||||
{Name: new("label2")},
|
||||
{Name: new("label3")},
|
||||
},
|
||||
ExpectedResult: []string{"label1", "label2", "label3"},
|
||||
},
|
||||
|
||||
@@ -158,7 +158,6 @@ func TestListWithStateTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
writeMRListResponse(t, w)
|
||||
|
||||
@@ -9,10 +9,6 @@ import (
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func strp(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestFilterBranchMatchBadRegexp(t *testing.T) {
|
||||
provider, _ := NewFakeService(
|
||||
t.Context(),
|
||||
@@ -30,7 +26,7 @@ func TestFilterBranchMatchBadRegexp(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: strp("("),
|
||||
BranchMatch: new("("),
|
||||
},
|
||||
}
|
||||
_, err := ListPullRequests(t.Context(), provider, filters)
|
||||
@@ -78,7 +74,7 @@ func TestFilterBranchMatch(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: strp("w"),
|
||||
BranchMatch: new("w"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
@@ -128,7 +124,7 @@ func TestFilterTargetBranchMatch(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
TargetBranchMatch: strp("1"),
|
||||
TargetBranchMatch: new("1"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
@@ -178,7 +174,7 @@ func TestFilterTitleMatch(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
TitleMatch: strp("\\[filter]"),
|
||||
TitleMatch: new("\\[filter]"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
@@ -228,10 +224,10 @@ func TestMultiFilterOrWithTitle(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
TitleMatch: strp("\\[filter]"),
|
||||
TitleMatch: new("\\[filter]"),
|
||||
},
|
||||
{
|
||||
TitleMatch: strp("- filter"),
|
||||
TitleMatch: new("- filter"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
@@ -282,10 +278,10 @@ func TestMultiFilterOr(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: strp("w"),
|
||||
BranchMatch: new("w"),
|
||||
},
|
||||
{
|
||||
BranchMatch: strp("r"),
|
||||
BranchMatch: new("r"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
@@ -345,19 +341,19 @@ func TestMultiFilterOrWithTargetBranchFilterOrWithTitleFilter(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: strp("w"),
|
||||
TargetBranchMatch: strp("1"),
|
||||
BranchMatch: new("w"),
|
||||
TargetBranchMatch: new("1"),
|
||||
},
|
||||
{
|
||||
BranchMatch: strp("r"),
|
||||
TargetBranchMatch: strp("3"),
|
||||
BranchMatch: new("r"),
|
||||
TargetBranchMatch: new("3"),
|
||||
},
|
||||
{
|
||||
TitleMatch: strp("two"),
|
||||
TitleMatch: new("two"),
|
||||
},
|
||||
{
|
||||
BranchMatch: strp("five"),
|
||||
TitleMatch: strp("PR title is different than branch name"),
|
||||
BranchMatch: new("five"),
|
||||
TitleMatch: new("PR title is different than branch name"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
|
||||
@@ -10,16 +10,19 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/arn"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/codecommit"
|
||||
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
|
||||
rgsatypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/aws/arn"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go-v2/service/codecommit"
|
||||
codecommittypes "github.com/aws/aws-sdk-go-v2/service/codecommit/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
|
||||
application "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
@@ -32,16 +35,16 @@ const (
|
||||
// AWSCodeCommitClient is a lean facade to the codecommitiface.CodeCommitAPI
|
||||
// it helps to reduce the mockery generated code.
|
||||
type AWSCodeCommitClient interface {
|
||||
ListRepositoriesWithContext(aws.Context, *codecommit.ListRepositoriesInput, ...request.Option) (*codecommit.ListRepositoriesOutput, error)
|
||||
GetRepositoryWithContext(aws.Context, *codecommit.GetRepositoryInput, ...request.Option) (*codecommit.GetRepositoryOutput, error)
|
||||
ListBranchesWithContext(aws.Context, *codecommit.ListBranchesInput, ...request.Option) (*codecommit.ListBranchesOutput, error)
|
||||
GetFolderWithContext(aws.Context, *codecommit.GetFolderInput, ...request.Option) (*codecommit.GetFolderOutput, error)
|
||||
ListRepositories(context.Context, *codecommit.ListRepositoriesInput, ...func(*codecommit.Options)) (*codecommit.ListRepositoriesOutput, error)
|
||||
GetRepository(context.Context, *codecommit.GetRepositoryInput, ...func(*codecommit.Options)) (*codecommit.GetRepositoryOutput, error)
|
||||
ListBranches(context.Context, *codecommit.ListBranchesInput, ...func(*codecommit.Options)) (*codecommit.ListBranchesOutput, error)
|
||||
GetFolder(context.Context, *codecommit.GetFolderInput, ...func(*codecommit.Options)) (*codecommit.GetFolderOutput, error)
|
||||
}
|
||||
|
||||
// AWSTaggingClient is a lean facade to the resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI
|
||||
// it helps to reduce the mockery generated code.
|
||||
type AWSTaggingClient interface {
|
||||
GetResourcesWithContext(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)
|
||||
GetResources(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error)
|
||||
}
|
||||
|
||||
type AWSCodeCommitProvider struct {
|
||||
@@ -73,7 +76,7 @@ func (p *AWSCodeCommitProvider) ListRepos(ctx context.Context, cloneProtocol str
|
||||
}
|
||||
|
||||
for _, repoName := range repoNames {
|
||||
repo, err := p.codeCommitClient.GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{
|
||||
repo, err := p.codeCommitClient.GetRepository(ctx, &codecommit.GetRepositoryInput{
|
||||
RepositoryName: aws.String(repoName),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -85,7 +88,7 @@ func (p *AWSCodeCommitProvider) ListRepos(ctx context.Context, cloneProtocol str
|
||||
log.Warnf("codecommit returned invalid response for repository %s, skipped", repoName)
|
||||
continue
|
||||
}
|
||||
if aws.StringValue(repo.RepositoryMetadata.DefaultBranch) == "" {
|
||||
if aws.ToString(repo.RepositoryMetadata.DefaultBranch) == "" {
|
||||
// if a codecommit repo doesn't have default branch, it's uninitialized. not going to bother with it.
|
||||
log.Warnf("repository %s does not have default branch, skipped", repoName)
|
||||
continue
|
||||
@@ -94,11 +97,11 @@ func (p *AWSCodeCommitProvider) ListRepos(ctx context.Context, cloneProtocol str
|
||||
switch cloneProtocol {
|
||||
// default to SSH if unspecified (i.e. if "").
|
||||
case "", "ssh":
|
||||
url = aws.StringValue(repo.RepositoryMetadata.CloneUrlSsh)
|
||||
url = aws.ToString(repo.RepositoryMetadata.CloneUrlSsh)
|
||||
case "https":
|
||||
url = aws.StringValue(repo.RepositoryMetadata.CloneUrlHttp)
|
||||
url = aws.ToString(repo.RepositoryMetadata.CloneUrlHttp)
|
||||
case "https-fips":
|
||||
url, err = getCodeCommitFIPSEndpoint(aws.StringValue(repo.RepositoryMetadata.CloneUrlHttp))
|
||||
url, err = getCodeCommitFIPSEndpoint(aws.ToString(repo.RepositoryMetadata.CloneUrlHttp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("https-fips is provided but repoUrl can't be transformed to FIPS endpoint: %w", err)
|
||||
}
|
||||
@@ -108,13 +111,13 @@ func (p *AWSCodeCommitProvider) ListRepos(ctx context.Context, cloneProtocol str
|
||||
repos = append(repos, &Repository{
|
||||
// there's no "organization" level at codecommit.
|
||||
// we are just using AWS accountId for now.
|
||||
Organization: aws.StringValue(repo.RepositoryMetadata.AccountId),
|
||||
Repository: aws.StringValue(repo.RepositoryMetadata.RepositoryName),
|
||||
Organization: aws.ToString(repo.RepositoryMetadata.AccountId),
|
||||
Repository: aws.ToString(repo.RepositoryMetadata.RepositoryName),
|
||||
URL: url,
|
||||
Branch: aws.StringValue(repo.RepositoryMetadata.DefaultBranch),
|
||||
Branch: aws.ToString(repo.RepositoryMetadata.DefaultBranch),
|
||||
// we could propagate repo tag keys, but without value not sure if it's any useful.
|
||||
Labels: []string{},
|
||||
RepositoryId: aws.StringValue(repo.RepositoryMetadata.RepositoryId),
|
||||
RepositoryId: aws.ToString(repo.RepositoryMetadata.RepositoryId),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -142,13 +145,9 @@ func (p *AWSCodeCommitProvider) RepoHasPath(ctx context.Context, repo *Repositor
|
||||
FolderPath: aws.String(parentPath),
|
||||
RepositoryName: aws.String(repo.Repository),
|
||||
}
|
||||
output, err := p.codeCommitClient.GetFolderWithContext(ctx, input)
|
||||
output, err := p.codeCommitClient.GetFolder(ctx, input)
|
||||
if err != nil {
|
||||
if hasAwsError(err,
|
||||
codecommit.ErrCodeRepositoryDoesNotExistException,
|
||||
codecommit.ErrCodeCommitDoesNotExistException,
|
||||
codecommit.ErrCodeFolderDoesNotExistException,
|
||||
) {
|
||||
if hasAwsError(err) {
|
||||
return false, nil
|
||||
}
|
||||
// unhandled exception, propagate out
|
||||
@@ -157,22 +156,22 @@ func (p *AWSCodeCommitProvider) RepoHasPath(ctx context.Context, repo *Repositor
|
||||
|
||||
// anything that matches.
|
||||
for _, submodule := range output.SubModules {
|
||||
if basePath == aws.StringValue(submodule.RelativePath) {
|
||||
if basePath == aws.ToString(submodule.RelativePath) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
for _, subpath := range output.SubFolders {
|
||||
if basePath == aws.StringValue(subpath.RelativePath) {
|
||||
if basePath == aws.ToString(subpath.RelativePath) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
for _, subpath := range output.Files {
|
||||
if basePath == aws.StringValue(subpath.RelativePath) {
|
||||
if basePath == aws.ToString(subpath.RelativePath) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
for _, subpath := range output.SymbolicLinks {
|
||||
if basePath == aws.StringValue(subpath.RelativePath) {
|
||||
if basePath == aws.ToString(subpath.RelativePath) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
@@ -182,7 +181,7 @@ func (p *AWSCodeCommitProvider) RepoHasPath(ctx context.Context, repo *Repositor
|
||||
func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repository) ([]*Repository, error) {
|
||||
repos := make([]*Repository, 0)
|
||||
if !p.allBranches {
|
||||
output, err := p.codeCommitClient.GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{
|
||||
output, err := p.codeCommitClient.GetRepository(ctx, &codecommit.GetRepositoryInput{
|
||||
RepositoryName: aws.String(repo.Repository),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -192,7 +191,7 @@ func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repositor
|
||||
Organization: repo.Organization,
|
||||
Repository: repo.Repository,
|
||||
URL: repo.URL,
|
||||
Branch: aws.StringValue(output.RepositoryMetadata.DefaultBranch),
|
||||
Branch: aws.ToString(output.RepositoryMetadata.DefaultBranch),
|
||||
RepositoryId: repo.RepositoryId,
|
||||
Labels: repo.Labels,
|
||||
// getting SHA of the branch requires a separate GetBranch call.
|
||||
@@ -204,7 +203,7 @@ func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repositor
|
||||
RepositoryName: aws.String(repo.Repository),
|
||||
}
|
||||
for {
|
||||
output, err := p.codeCommitClient.ListBranchesWithContext(ctx, input)
|
||||
output, err := p.codeCommitClient.ListBranches(ctx, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -213,7 +212,7 @@ func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repositor
|
||||
Organization: repo.Organization,
|
||||
Repository: repo.Repository,
|
||||
URL: repo.URL,
|
||||
Branch: aws.StringValue(branch),
|
||||
Branch: branch,
|
||||
RepositoryId: repo.RepositoryId,
|
||||
Labels: repo.Labels,
|
||||
// getting SHA of the branch requires a separate GetBranch call.
|
||||
@@ -222,7 +221,7 @@ func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repositor
|
||||
})
|
||||
}
|
||||
input.NextToken = output.NextToken
|
||||
if aws.StringValue(output.NextToken) == "" {
|
||||
if aws.ToString(output.NextToken) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -241,32 +240,32 @@ func (p *AWSCodeCommitProvider) listRepoNames(ctx context.Context) ([]string, er
|
||||
listReposInput := &codecommit.ListRepositoriesInput{}
|
||||
var output *codecommit.ListRepositoriesOutput
|
||||
for {
|
||||
output, err = p.codeCommitClient.ListRepositoriesWithContext(ctx, listReposInput)
|
||||
output, err = p.codeCommitClient.ListRepositories(ctx, listReposInput)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
for _, repo := range output.Repositories {
|
||||
repoNames = append(repoNames, aws.StringValue(repo.RepositoryName))
|
||||
repoNames = append(repoNames, aws.ToString(repo.RepositoryName))
|
||||
}
|
||||
listReposInput.NextToken = output.NextToken
|
||||
if aws.StringValue(output.NextToken) == "" {
|
||||
if aws.ToString(output.NextToken) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Debugf("tag filer is specified, calling tagging api to list repos")
|
||||
discoveryInput := &resourcegroupstaggingapi.GetResourcesInput{
|
||||
ResourceTypeFilters: aws.StringSlice([]string{resourceTypeCodeCommitRepository}),
|
||||
ResourceTypeFilters: []string{resourceTypeCodeCommitRepository},
|
||||
TagFilters: tagFilters,
|
||||
}
|
||||
var output *resourcegroupstaggingapi.GetResourcesOutput
|
||||
for {
|
||||
output, err = p.taggingClient.GetResourcesWithContext(ctx, discoveryInput)
|
||||
output, err = p.taggingClient.GetResources(ctx, discoveryInput)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
for _, resource := range output.ResourceTagMappingList {
|
||||
repoArn := aws.StringValue(resource.ResourceARN)
|
||||
repoArn := aws.ToString(resource.ResourceARN)
|
||||
log.Debugf("discovered codecommit repo with arn %s", repoArn)
|
||||
repoName, extractErr := getCodeCommitRepoName(repoArn)
|
||||
if extractErr != nil {
|
||||
@@ -276,7 +275,7 @@ func (p *AWSCodeCommitProvider) listRepoNames(ctx context.Context) ([]string, er
|
||||
repoNames = append(repoNames, repoName)
|
||||
}
|
||||
discoveryInput.PaginationToken = output.PaginationToken
|
||||
if aws.StringValue(output.PaginationToken) == "" {
|
||||
if aws.ToString(output.PaginationToken) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -284,19 +283,20 @@ func (p *AWSCodeCommitProvider) listRepoNames(ctx context.Context) ([]string, er
|
||||
return repoNames, err
|
||||
}
|
||||
|
||||
func (p *AWSCodeCommitProvider) getTagFilters() []*resourcegroupstaggingapi.TagFilter {
|
||||
filters := make(map[string]*resourcegroupstaggingapi.TagFilter)
|
||||
// getTagFilters filters by tag
|
||||
func (p *AWSCodeCommitProvider) getTagFilters() []rgsatypes.TagFilter {
|
||||
filters := make(map[string]rgsatypes.TagFilter)
|
||||
for _, tagFilter := range p.tagFilters {
|
||||
filter, hasKey := filters[tagFilter.Key]
|
||||
if !hasKey {
|
||||
filter = &resourcegroupstaggingapi.TagFilter{
|
||||
filter := filters[tagFilter.Key]
|
||||
if filter.Key == nil {
|
||||
filter = rgsatypes.TagFilter{
|
||||
Key: aws.String(tagFilter.Key),
|
||||
}
|
||||
filters[tagFilter.Key] = filter
|
||||
}
|
||||
if tagFilter.Value != "" {
|
||||
filter.Values = append(filter.Values, aws.String(tagFilter.Value))
|
||||
filter.Values = append(filter.Values, tagFilter.Value)
|
||||
}
|
||||
filters[tagFilter.Key] = filter
|
||||
}
|
||||
return slices.Collect(maps.Values(filters))
|
||||
}
|
||||
@@ -326,12 +326,15 @@ func getCodeCommitFIPSEndpoint(repoURL string) (string, error) {
|
||||
return strings.Replace(repoURL, prefixGitURLHTTPS, prefixGitURLHTTPSFIPS, 1), nil
|
||||
}
|
||||
|
||||
func hasAwsError(err error, codes ...string) bool {
|
||||
var awsErr awserr.Error
|
||||
if errors.As(err, &awsErr) {
|
||||
return slices.Contains(codes, awsErr.Code())
|
||||
}
|
||||
return false
|
||||
func hasAwsError(err error) bool {
|
||||
// Check for common CodeCommit exceptions using SDK v2 typed errors
|
||||
var repoNotFound *codecommittypes.RepositoryDoesNotExistException
|
||||
var commitNotFound *codecommittypes.CommitDoesNotExistException
|
||||
var folderNotFound *codecommittypes.FolderDoesNotExistException
|
||||
|
||||
return errors.As(err, &repoNotFound) ||
|
||||
errors.As(err, &commitNotFound) ||
|
||||
errors.As(err, &folderNotFound)
|
||||
}
|
||||
|
||||
// toAbsolutePath transforms a path input to absolute path, as required by AWS CodeCommit
|
||||
@@ -343,37 +346,32 @@ func toAbsolutePath(path string) string {
|
||||
return filepath.ToSlash(filepath.Join("/", path)) //nolint:gocritic // Prepend slash to have an absolute path
|
||||
}
|
||||
|
||||
func createAWSDiscoveryClients(_ context.Context, role string, region string) (*resourcegroupstaggingapi.ResourceGroupsTaggingAPI, *codecommit.CodeCommit, error) {
|
||||
podSession, err := session.NewSession()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error creating new AWS pod session: %w", err)
|
||||
}
|
||||
discoverySession := podSession
|
||||
// assume role if provided - this allows cross account CodeCommit repo discovery.
|
||||
if role != "" {
|
||||
log.Debugf("role %s is provided for AWS CodeCommit discovery", role)
|
||||
assumeRoleCreds := stscreds.NewCredentials(podSession, role)
|
||||
discoverySession, err = session.NewSession(&aws.Config{
|
||||
Credentials: assumeRoleCreds,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error creating new AWS discovery session: %w", err)
|
||||
}
|
||||
} else {
|
||||
log.Debugf("role is not provided for AWS CodeCommit discovery, using pod role")
|
||||
}
|
||||
// use region explicitly if provided - this allows cross region CodeCommit repo discovery.
|
||||
// createAWSDiscoveryClients creates AWS clients
|
||||
func createAWSDiscoveryClients(ctx context.Context, role string, region string) (*resourcegroupstaggingapi.Client, *codecommit.Client, error) {
|
||||
var configOpts []func(*config.LoadOptions) error
|
||||
if region != "" {
|
||||
log.Debugf("region %s is provided for AWS CodeCommit discovery", region)
|
||||
discoverySession = discoverySession.Copy(&aws.Config{
|
||||
Region: aws.String(region),
|
||||
})
|
||||
configOpts = append(configOpts, config.WithRegion(region))
|
||||
} else {
|
||||
log.Debugf("region is not provided for AWS CodeCommit discovery, using pod region")
|
||||
}
|
||||
// Load the default config based on config options
|
||||
cfg, err := config.LoadDefaultConfig(ctx, configOpts...)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error loading default config: %w", err)
|
||||
}
|
||||
client := sts.NewFromConfig(cfg)
|
||||
// assume role if provided - this allows cross account CodeCommit repo discovery.
|
||||
if role != "" {
|
||||
log.Debugf("role %s is provided for AWS CodeCommit discovery", role)
|
||||
assumeRoleCreds := stscreds.NewAssumeRoleProvider(client, role)
|
||||
cfg.Credentials = aws.NewCredentialsCache(assumeRoleCreds)
|
||||
} else {
|
||||
log.Debugf("role is not provided for AWS CodeCommit discovery, using pod role")
|
||||
}
|
||||
|
||||
taggingClient := resourcegroupstaggingapi.New(discoverySession)
|
||||
codeCommitClient := codecommit.New(discoverySession)
|
||||
taggingClient := resourcegroupstaggingapi.NewFromConfig(cfg)
|
||||
codeCommitClient := codecommit.NewFromConfig(cfg)
|
||||
|
||||
return taggingClient, codeCommitClient, nil
|
||||
}
|
||||
|
||||
@@ -5,10 +5,13 @@ import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/codecommit"
|
||||
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/codecommit"
|
||||
codecommittypes "github.com/aws/aws-sdk-go-v2/service/codecommit/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
|
||||
rgsatypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
@@ -34,7 +37,7 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
repositories []*awsCodeCommitTestRepository
|
||||
cloneProtocol string
|
||||
tagFilters []*v1alpha1.TagFilter
|
||||
expectTagFilters []*resourcegroupstaggingapi.TagFilter
|
||||
expectTagFilters []rgsatypes.TagFilter
|
||||
listRepositoryError error
|
||||
expectOverallError bool
|
||||
expectListAtCodeCommit bool
|
||||
@@ -57,8 +60,8 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
{Key: "key1", Value: "value2"},
|
||||
{Key: "key2"},
|
||||
},
|
||||
expectTagFilters: []*resourcegroupstaggingapi.TagFilter{
|
||||
{Key: aws.String("key1"), Values: aws.StringSlice([]string{"value1", "value2"})},
|
||||
expectTagFilters: []rgsatypes.TagFilter{
|
||||
{Key: aws.String("key1"), Values: []string{"value1", "value2"}},
|
||||
{Key: aws.String("key2")},
|
||||
},
|
||||
expectOverallError: false,
|
||||
@@ -80,7 +83,7 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
tagFilters: []*v1alpha1.TagFilter{
|
||||
{Key: "key1"},
|
||||
},
|
||||
expectTagFilters: []*resourcegroupstaggingapi.TagFilter{
|
||||
expectTagFilters: []rgsatypes.TagFilter{
|
||||
{Key: aws.String("key1")},
|
||||
},
|
||||
expectOverallError: false,
|
||||
@@ -160,12 +163,12 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
codeCommitClient := mocks.NewAWSCodeCommitClient(t)
|
||||
taggingClient := mocks.NewAWSTaggingClient(t)
|
||||
ctx := t.Context()
|
||||
codecommitRepoNameIdPairs := make([]*codecommit.RepositoryNameIdPair, 0)
|
||||
resourceTaggings := make([]*resourcegroupstaggingapi.ResourceTagMapping, 0)
|
||||
codecommitRepoNameIdPairs := make([]codecommittypes.RepositoryNameIdPair, 0)
|
||||
resourceTaggings := make([]rgsatypes.ResourceTagMapping, 0)
|
||||
validRepositories := make([]*awsCodeCommitTestRepository, 0)
|
||||
|
||||
for _, repo := range testCase.repositories {
|
||||
repoMetadata := &codecommit.RepositoryMetadata{
|
||||
repoMetadata := &codecommittypes.RepositoryMetadata{
|
||||
AccountId: aws.String(repo.accountId),
|
||||
Arn: aws.String(repo.arn),
|
||||
CloneUrlHttp: aws.String("https://git-codecommit.us-east-1.amazonaws.com/v1/repos/" + repo.name),
|
||||
@@ -177,13 +180,13 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
if repo.getRepositoryNilMetadata {
|
||||
repoMetadata = nil
|
||||
}
|
||||
codeCommitClient.EXPECT().GetRepositoryWithContext(mock.Anything, &codecommit.GetRepositoryInput{RepositoryName: aws.String(repo.name)}).
|
||||
codeCommitClient.EXPECT().GetRepository(mock.Anything, &codecommit.GetRepositoryInput{RepositoryName: aws.String(repo.name)}).
|
||||
Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: repoMetadata}, repo.getRepositoryError).Maybe()
|
||||
codecommitRepoNameIdPairs = append(codecommitRepoNameIdPairs, &codecommit.RepositoryNameIdPair{
|
||||
codecommitRepoNameIdPairs = append(codecommitRepoNameIdPairs, codecommittypes.RepositoryNameIdPair{
|
||||
RepositoryId: aws.String(repo.id),
|
||||
RepositoryName: aws.String(repo.name),
|
||||
})
|
||||
resourceTaggings = append(resourceTaggings, &resourcegroupstaggingapi.ResourceTagMapping{
|
||||
resourceTaggings = append(resourceTaggings, rgsatypes.ResourceTagMapping{
|
||||
ResourceARN: aws.String(repo.arn),
|
||||
})
|
||||
if repo.valid {
|
||||
@@ -192,14 +195,14 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
}
|
||||
|
||||
if testCase.expectListAtCodeCommit {
|
||||
codeCommitClient.EXPECT().ListRepositoriesWithContext(mock.Anything, &codecommit.ListRepositoriesInput{}).
|
||||
codeCommitClient.EXPECT().ListRepositories(mock.Anything, &codecommit.ListRepositoriesInput{}).
|
||||
Return(&codecommit.ListRepositoriesOutput{
|
||||
Repositories: codecommitRepoNameIdPairs,
|
||||
}, testCase.listRepositoryError).Maybe()
|
||||
} else {
|
||||
taggingClient.EXPECT().GetResourcesWithContext(mock.Anything, mock.MatchedBy(equalIgnoringTagFilterOrder(&resourcegroupstaggingapi.GetResourcesInput{
|
||||
taggingClient.EXPECT().GetResources(mock.Anything, mock.MatchedBy(equalIgnoringTagFilterOrder(&resourcegroupstaggingapi.GetResourcesInput{
|
||||
TagFilters: testCase.expectTagFilters,
|
||||
ResourceTypeFilters: aws.StringSlice([]string{resourceTypeCodeCommitRepository}),
|
||||
ResourceTypeFilters: []string{resourceTypeCodeCommitRepository},
|
||||
}))).
|
||||
Return(&resourcegroupstaggingapi.GetResourcesOutput{
|
||||
ResourceTagMappingList: resourceTaggings,
|
||||
@@ -249,7 +252,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
path: "lib/config.yaml",
|
||||
expectedGetFolderPath: "/lib",
|
||||
getFolderOutput: &codecommit.GetFolderOutput{
|
||||
Files: []*codecommit.File{
|
||||
Files: []codecommittypes.File{
|
||||
{RelativePath: aws.String("config.yaml")},
|
||||
},
|
||||
},
|
||||
@@ -261,7 +264,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
path: "lib/config",
|
||||
expectedGetFolderPath: "/lib",
|
||||
getFolderOutput: &codecommit.GetFolderOutput{
|
||||
SubFolders: []*codecommit.Folder{
|
||||
SubFolders: []codecommittypes.Folder{
|
||||
{RelativePath: aws.String("config")},
|
||||
},
|
||||
},
|
||||
@@ -273,7 +276,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
path: "/lib/submodule/",
|
||||
expectedGetFolderPath: "/lib",
|
||||
getFolderOutput: &codecommit.GetFolderOutput{
|
||||
SubModules: []*codecommit.SubModule{
|
||||
SubModules: []codecommittypes.SubModule{
|
||||
{RelativePath: aws.String("submodule")},
|
||||
},
|
||||
},
|
||||
@@ -285,7 +288,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
path: "./lib/service.json",
|
||||
expectedGetFolderPath: "/lib",
|
||||
getFolderOutput: &codecommit.GetFolderOutput{
|
||||
SymbolicLinks: []*codecommit.SymbolicLink{
|
||||
SymbolicLinks: []codecommittypes.SymbolicLink{
|
||||
{RelativePath: aws.String("service.json")},
|
||||
},
|
||||
},
|
||||
@@ -297,16 +300,16 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
path: "no-match.json",
|
||||
expectedGetFolderPath: "/",
|
||||
getFolderOutput: &codecommit.GetFolderOutput{
|
||||
Files: []*codecommit.File{
|
||||
Files: []codecommittypes.File{
|
||||
{RelativePath: aws.String("config.yaml")},
|
||||
},
|
||||
SubFolders: []*codecommit.Folder{
|
||||
SubFolders: []codecommittypes.Folder{
|
||||
{RelativePath: aws.String("config")},
|
||||
},
|
||||
SubModules: []*codecommit.SubModule{
|
||||
SubModules: []codecommittypes.SubModule{
|
||||
{RelativePath: aws.String("submodule")},
|
||||
},
|
||||
SymbolicLinks: []*codecommit.SymbolicLink{
|
||||
SymbolicLinks: []codecommittypes.SymbolicLink{
|
||||
{RelativePath: aws.String("service.json")},
|
||||
},
|
||||
},
|
||||
@@ -317,7 +320,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
name: "RepoHasPath when parent folder not found",
|
||||
path: "lib/submodule",
|
||||
expectedGetFolderPath: "/lib",
|
||||
getFolderError: &codecommit.FolderDoesNotExistException{},
|
||||
getFolderError: &codecommittypes.FolderDoesNotExistException{},
|
||||
expectOverallError: false,
|
||||
},
|
||||
{
|
||||
@@ -347,7 +350,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
taggingClient := mocks.NewAWSTaggingClient(t)
|
||||
ctx := t.Context()
|
||||
if testCase.expectedGetFolderPath != "" {
|
||||
codeCommitClient.EXPECT().GetFolderWithContext(mock.Anything, &codecommit.GetFolderInput{
|
||||
codeCommitClient.EXPECT().GetFolder(mock.Anything, &codecommit.GetFolderInput{
|
||||
CommitSpecifier: aws.String(branch),
|
||||
FolderPath: aws.String(testCase.expectedGetFolderPath),
|
||||
RepositoryName: aws.String(repoName),
|
||||
@@ -419,13 +422,15 @@ func TestAWSCodeCommitGetBranches(t *testing.T) {
|
||||
taggingClient := mocks.NewAWSTaggingClient(t)
|
||||
ctx := t.Context()
|
||||
if testCase.allBranches {
|
||||
codeCommitClient.EXPECT().ListBranchesWithContext(mock.Anything, &codecommit.ListBranchesInput{
|
||||
branches := make([]string, len(testCase.branches))
|
||||
copy(branches, testCase.branches)
|
||||
codeCommitClient.EXPECT().ListBranches(mock.Anything, &codecommit.ListBranchesInput{
|
||||
RepositoryName: aws.String(name),
|
||||
}).
|
||||
Return(&codecommit.ListBranchesOutput{Branches: aws.StringSlice(testCase.branches)}, testCase.apiError).Maybe()
|
||||
Return(&codecommit.ListBranchesOutput{Branches: branches}, testCase.apiError).Maybe()
|
||||
} else {
|
||||
codeCommitClient.EXPECT().GetRepositoryWithContext(mock.Anything, &codecommit.GetRepositoryInput{RepositoryName: aws.String(name)}).
|
||||
Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: &codecommit.RepositoryMetadata{
|
||||
codeCommitClient.EXPECT().GetRepository(mock.Anything, &codecommit.GetRepositoryInput{RepositoryName: aws.String(name)}).
|
||||
Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: &codecommittypes.RepositoryMetadata{
|
||||
AccountId: aws.String(organization),
|
||||
DefaultBranch: aws.String(defaultBranch),
|
||||
}}, testCase.apiError).Maybe()
|
||||
@@ -470,8 +475,22 @@ func TestAWSCodeCommitGetBranches(t *testing.T) {
|
||||
func equalIgnoringTagFilterOrder(expected *resourcegroupstaggingapi.GetResourcesInput) func(*resourcegroupstaggingapi.GetResourcesInput) bool {
|
||||
return func(actual *resourcegroupstaggingapi.GetResourcesInput) bool {
|
||||
sort.Slice(actual.TagFilters, func(i, j int) bool {
|
||||
return *actual.TagFilters[i].Key < *actual.TagFilters[j].Key
|
||||
keyI := ""
|
||||
keyJ := ""
|
||||
if actual.TagFilters[i].Key != nil {
|
||||
keyI = *actual.TagFilters[i].Key
|
||||
}
|
||||
if actual.TagFilters[j].Key != nil {
|
||||
keyJ = *actual.TagFilters[j].Key
|
||||
}
|
||||
return keyI < keyJ
|
||||
})
|
||||
return cmp.Equal(expected, actual)
|
||||
// Ignore unexported fields in AWS SDK v2 types
|
||||
return cmp.Equal(expected, actual,
|
||||
cmpopts.IgnoreUnexported(
|
||||
rgsatypes.TagFilter{},
|
||||
resourcegroupstaggingapi.GetResourcesInput{},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7"
|
||||
azureGit "github.com/microsoft/azure-devops-go-api/azuredevops/v7/git"
|
||||
@@ -18,10 +17,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider/mocks"
|
||||
)
|
||||
|
||||
func s(input string) *string {
|
||||
return ptr.To(input)
|
||||
}
|
||||
|
||||
func TestAzureDevopsRepoHasPath(t *testing.T) {
|
||||
organization := "myorg"
|
||||
teamProject := "myorg_project"
|
||||
@@ -51,12 +46,12 @@ func TestAzureDevopsRepoHasPath(t *testing.T) {
|
||||
{
|
||||
name: "RepoHasPath when no path found returns false",
|
||||
pathFound: false,
|
||||
azureDevopsError: azuredevops.WrappedError{TypeKey: s(AzureDevOpsErrorsTypeKeyValues.GitItemNotFound)},
|
||||
azureDevopsError: azuredevops.WrappedError{TypeKey: new(AzureDevOpsErrorsTypeKeyValues.GitItemNotFound)},
|
||||
},
|
||||
{
|
||||
name: "RepoHasPath when unknown Azure DevOps WrappedError occurs returns error",
|
||||
pathFound: false,
|
||||
azureDevopsError: azuredevops.WrappedError{TypeKey: s("OtherAzureDevopsException")},
|
||||
azureDevopsError: azuredevops.WrappedError{TypeKey: new("OtherAzureDevopsException")},
|
||||
returnError: true,
|
||||
errorMessage: "failed to check for path existence",
|
||||
},
|
||||
@@ -124,12 +119,12 @@ func TestGetDefaultBranchOnDisabledRepo(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "azure devops error when disabled repo causes empty return value",
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: s(AzureDevOpsErrorsTypeKeyValues.GitRepositoryNotFound)},
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: new(AzureDevOpsErrorsTypeKeyValues.GitRepositoryNotFound)},
|
||||
shouldReturnError: false,
|
||||
},
|
||||
{
|
||||
name: "azure devops error with unknown error type returns error",
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: s("OtherError")},
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: new("OtherError")},
|
||||
shouldReturnError: true,
|
||||
},
|
||||
{
|
||||
@@ -181,12 +176,12 @@ func TestGetAllBranchesOnDisabledRepo(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "azure devops error when disabled repo causes empty return value",
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: s(AzureDevOpsErrorsTypeKeyValues.GitRepositoryNotFound)},
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: new(AzureDevOpsErrorsTypeKeyValues.GitRepositoryNotFound)},
|
||||
shouldReturnError: false,
|
||||
},
|
||||
{
|
||||
name: "azure devops error with unknown error type returns error",
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: s("OtherError")},
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: new("OtherError")},
|
||||
shouldReturnError: true,
|
||||
},
|
||||
{
|
||||
@@ -234,7 +229,7 @@ func TestAzureDevOpsGetDefaultBranchStripsRefsName(t *testing.T) {
|
||||
strippedBranchName := "somebranch"
|
||||
defaultBranch := fmt.Sprintf("refs/heads/%v", strippedBranchName)
|
||||
|
||||
branchReturn := &azureGit.GitBranchStats{Name: &strippedBranchName, Commit: &azureGit.GitCommitRef{CommitId: s("abc123233223")}}
|
||||
branchReturn := &azureGit.GitBranchStats{Name: &strippedBranchName, Commit: &azureGit.GitCommitRef{CommitId: new("abc123233223")}}
|
||||
repo := &Repository{Organization: organization, Repository: repoName, RepositoryId: uuid, Branch: defaultBranch}
|
||||
|
||||
gitClientMock := &azureMock.Client{}
|
||||
@@ -273,7 +268,7 @@ func TestAzureDevOpsGetBranchesDefultBranchOnly(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "GetBranches AllBranches false when single branch returned returns branch",
|
||||
expectedBranch: &azureGit.GitBranchStats{Name: &defaultBranch, Commit: &azureGit.GitCommitRef{CommitId: s("abc123233223")}},
|
||||
expectedBranch: &azureGit.GitBranchStats{Name: &defaultBranch, Commit: &azureGit.GitCommitRef{CommitId: new("abc123233223")}},
|
||||
},
|
||||
{
|
||||
name: "GetBranches AllBranches false when request fails returns error and empty result",
|
||||
@@ -285,7 +280,7 @@ func TestAzureDevOpsGetBranchesDefultBranchOnly(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "GetBranches AllBranches false when branch returned with long commit SHA",
|
||||
expectedBranch: &azureGit.GitBranchStats{Name: &defaultBranch, Commit: &azureGit.GitCommitRef{CommitId: s("53863052ADF24229AB72154B4D83DAB7")}},
|
||||
expectedBranch: &azureGit.GitBranchStats{Name: &defaultBranch, Commit: &azureGit.GitCommitRef{CommitId: new("53863052ADF24229AB72154B4D83DAB7")}},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -344,7 +339,7 @@ func TestAzureDevopsGetBranches(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "GetBranches when single branch returned returns this branch info",
|
||||
expectedBranches: &[]azureGit.GitBranchStats{{Name: s("feature-feat1"), Commit: &azureGit.GitCommitRef{CommitId: s("abc123233223")}}},
|
||||
expectedBranches: &[]azureGit.GitBranchStats{{Name: new("feature-feat1"), Commit: &azureGit.GitCommitRef{CommitId: new("abc123233223")}}},
|
||||
allBranches: true,
|
||||
},
|
||||
{
|
||||
@@ -365,9 +360,9 @@ func TestAzureDevopsGetBranches(t *testing.T) {
|
||||
{
|
||||
name: "GetBranches when multiple branches returned returns branch info for all branches",
|
||||
expectedBranches: &[]azureGit.GitBranchStats{
|
||||
{Name: s("feature-feat1"), Commit: &azureGit.GitCommitRef{CommitId: s("abc123233223")}},
|
||||
{Name: s("feature/feat2"), Commit: &azureGit.GitCommitRef{CommitId: s("4334")}},
|
||||
{Name: s("feature/feat2"), Commit: &azureGit.GitCommitRef{CommitId: s("53863052ADF24229AB72154B4D83DAB7")}},
|
||||
{Name: new("feature-feat1"), Commit: &azureGit.GitCommitRef{CommitId: new("abc123233223")}},
|
||||
{Name: new("feature/feat2"), Commit: &azureGit.GitCommitRef{CommitId: new("4334")}},
|
||||
{Name: new("feature/feat2"), Commit: &azureGit.GitCommitRef{CommitId: new("53863052ADF24229AB72154B4D83DAB7")}},
|
||||
},
|
||||
allBranches: true,
|
||||
},
|
||||
@@ -434,12 +429,12 @@ func TestGetAzureDevopsRepositories(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "ListRepos when single repo found returns repo info",
|
||||
repositories: []azureGit.GitRepository{{Name: s("repo1"), DefaultBranch: s("main"), RemoteUrl: s("https://remoteurl.u"), Id: repoId}},
|
||||
repositories: []azureGit.GitRepository{{Name: new("repo1"), DefaultBranch: new("main"), RemoteUrl: new("https://remoteurl.u"), Id: repoId}},
|
||||
expectedNumberOfRepos: 1,
|
||||
},
|
||||
{
|
||||
name: "ListRepos when repo has no default branch returns empty list",
|
||||
repositories: []azureGit.GitRepository{{Name: s("repo2"), RemoteUrl: s("https://remoteurl.u"), Id: repoId}},
|
||||
repositories: []azureGit.GitRepository{{Name: new("repo2"), RemoteUrl: new("https://remoteurl.u"), Id: repoId}},
|
||||
},
|
||||
{
|
||||
name: "ListRepos when Azure DevOps request fails returns error",
|
||||
@@ -447,24 +442,24 @@ func TestGetAzureDevopsRepositories(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ListRepos when repo has no name returns empty list",
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: s("main"), RemoteUrl: s("https://remoteurl.u"), Id: repoId}},
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: new("main"), RemoteUrl: new("https://remoteurl.u"), Id: repoId}},
|
||||
},
|
||||
{
|
||||
name: "ListRepos when repo has no remote URL returns empty list",
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: s("main"), Name: s("repo_name"), Id: repoId}},
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: new("main"), Name: new("repo_name"), Id: repoId}},
|
||||
},
|
||||
{
|
||||
name: "ListRepos when repo has no ID returns empty list",
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: s("main"), Name: s("repo_name"), RemoteUrl: s("https://remoteurl.u")}},
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: new("main"), Name: new("repo_name"), RemoteUrl: new("https://remoteurl.u")}},
|
||||
},
|
||||
{
|
||||
name: "ListRepos when multiple repos returned returns list of eligible repos only",
|
||||
repositories: []azureGit.GitRepository{
|
||||
{Name: s("returned1"), DefaultBranch: s("main"), RemoteUrl: s("https://remoteurl.u"), Id: repoId},
|
||||
{Name: s("missing_default_branch"), RemoteUrl: s("https://remoteurl.u"), Id: repoId},
|
||||
{DefaultBranch: s("missing_name"), RemoteUrl: s("https://remoteurl.u"), Id: repoId},
|
||||
{Name: s("missing_remote_url"), DefaultBranch: s("main"), Id: repoId},
|
||||
{Name: s("missing_id"), DefaultBranch: s("main"), RemoteUrl: s("https://remoteurl.u")},
|
||||
{Name: new("returned1"), DefaultBranch: new("main"), RemoteUrl: new("https://remoteurl.u"), Id: repoId},
|
||||
{Name: new("missing_default_branch"), RemoteUrl: new("https://remoteurl.u"), Id: repoId},
|
||||
{DefaultBranch: new("missing_name"), RemoteUrl: new("https://remoteurl.u"), Id: repoId},
|
||||
{Name: new("missing_remote_url"), DefaultBranch: new("main"), Id: repoId},
|
||||
{Name: new("missing_id"), DefaultBranch: new("main"), RemoteUrl: new("https://remoteurl.u")},
|
||||
},
|
||||
expectedNumberOfRepos: 1,
|
||||
},
|
||||
@@ -473,7 +468,7 @@ func TestGetAzureDevopsRepositories(t *testing.T) {
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
gitClientMock := azureMock.NewClient(t)
|
||||
gitClientMock.EXPECT().GetRepositories(mock.Anything, azureGit.GetRepositoriesArgs{Project: s(teamProject)}).Return(&testCase.repositories, testCase.getRepositoriesError)
|
||||
gitClientMock.EXPECT().GetRepositories(mock.Anything, azureGit.GetRepositoriesArgs{Project: new(teamProject)}).Return(&testCase.repositories, testCase.getRepositoriesError)
|
||||
|
||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, nil)
|
||||
|
||||
@@ -445,7 +445,6 @@ func TestListReposTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defaultHandler(t)(w, r)
|
||||
|
||||
@@ -1100,7 +1100,7 @@ func TestGitlabListRepos(t *testing.T) {
|
||||
url: "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
filters: []v1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
LabelMatch: strp("test-topic"),
|
||||
LabelMatch: new("test-topic"),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1301,7 +1301,6 @@ func TestGetBranchesTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gitlabMockHandler(t)(w, r)
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/codecommit"
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/codecommit"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
@@ -38,71 +38,71 @@ func (_m *AWSCodeCommitClient) EXPECT() *AWSCodeCommitClient_Expecter {
|
||||
return &AWSCodeCommitClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetFolderWithContext provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) GetFolderWithContext(v aws.Context, getFolderInput *codecommit.GetFolderInput, options ...request.Option) (*codecommit.GetFolderOutput, error) {
|
||||
// request.Option
|
||||
_va := make([]interface{}, len(options))
|
||||
for _i := range options {
|
||||
_va[_i] = options[_i]
|
||||
// GetFolder provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) GetFolder(context1 context.Context, getFolderInput *codecommit.GetFolderInput, fns ...func(*codecommit.Options)) (*codecommit.GetFolderOutput, error) {
|
||||
// func(*codecommit.Options)
|
||||
_va := make([]interface{}, len(fns))
|
||||
for _i := range fns {
|
||||
_va[_i] = fns[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, v, getFolderInput)
|
||||
_ca = append(_ca, context1, getFolderInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetFolderWithContext")
|
||||
panic("no return value specified for GetFolder")
|
||||
}
|
||||
|
||||
var r0 *codecommit.GetFolderOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.GetFolderInput, ...request.Option) (*codecommit.GetFolderOutput, error)); ok {
|
||||
return returnFunc(v, getFolderInput, options...)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.GetFolderInput, ...func(*codecommit.Options)) (*codecommit.GetFolderOutput, error)); ok {
|
||||
return returnFunc(context1, getFolderInput, fns...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.GetFolderInput, ...request.Option) *codecommit.GetFolderOutput); ok {
|
||||
r0 = returnFunc(v, getFolderInput, options...)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.GetFolderInput, ...func(*codecommit.Options)) *codecommit.GetFolderOutput); ok {
|
||||
r0 = returnFunc(context1, getFolderInput, fns...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.GetFolderOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(aws.Context, *codecommit.GetFolderInput, ...request.Option) error); ok {
|
||||
r1 = returnFunc(v, getFolderInput, options...)
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *codecommit.GetFolderInput, ...func(*codecommit.Options)) error); ok {
|
||||
r1 = returnFunc(context1, getFolderInput, fns...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_GetFolderWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFolderWithContext'
|
||||
type AWSCodeCommitClient_GetFolderWithContext_Call struct {
|
||||
// AWSCodeCommitClient_GetFolder_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFolder'
|
||||
type AWSCodeCommitClient_GetFolder_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetFolderWithContext is a helper method to define mock.On call
|
||||
// - v aws.Context
|
||||
// GetFolder is a helper method to define mock.On call
|
||||
// - context1 context.Context
|
||||
// - getFolderInput *codecommit.GetFolderInput
|
||||
// - options ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) GetFolderWithContext(v interface{}, getFolderInput interface{}, options ...interface{}) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
return &AWSCodeCommitClient_GetFolderWithContext_Call{Call: _e.mock.On("GetFolderWithContext",
|
||||
append([]interface{}{v, getFolderInput}, options...)...)}
|
||||
// - fns ...func(*codecommit.Options)
|
||||
func (_e *AWSCodeCommitClient_Expecter) GetFolder(context1 interface{}, getFolderInput interface{}, fns ...interface{}) *AWSCodeCommitClient_GetFolder_Call {
|
||||
return &AWSCodeCommitClient_GetFolder_Call{Call: _e.mock.On("GetFolder",
|
||||
append([]interface{}{context1, getFolderInput}, fns...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) Run(run func(v aws.Context, getFolderInput *codecommit.GetFolderInput, options ...request.Option)) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_GetFolder_Call) Run(run func(context1 context.Context, getFolderInput *codecommit.GetFolderInput, fns ...func(*codecommit.Options))) *AWSCodeCommitClient_GetFolder_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 aws.Context
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(aws.Context)
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *codecommit.GetFolderInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.GetFolderInput)
|
||||
}
|
||||
var arg2 []request.Option
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
var arg2 []func(*codecommit.Options)
|
||||
variadicArgs := make([]func(*codecommit.Options), len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
variadicArgs[i] = a.(func(*codecommit.Options))
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
@@ -115,81 +115,81 @@ func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) Run(run func(v aws.Cont
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) Return(getFolderOutput *codecommit.GetFolderOutput, err error) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_GetFolder_Call) Return(getFolderOutput *codecommit.GetFolderOutput, err error) *AWSCodeCommitClient_GetFolder_Call {
|
||||
_c.Call.Return(getFolderOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) RunAndReturn(run func(v aws.Context, getFolderInput *codecommit.GetFolderInput, options ...request.Option) (*codecommit.GetFolderOutput, error)) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_GetFolder_Call) RunAndReturn(run func(context1 context.Context, getFolderInput *codecommit.GetFolderInput, fns ...func(*codecommit.Options)) (*codecommit.GetFolderOutput, error)) *AWSCodeCommitClient_GetFolder_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetRepositoryWithContext provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) GetRepositoryWithContext(v aws.Context, getRepositoryInput *codecommit.GetRepositoryInput, options ...request.Option) (*codecommit.GetRepositoryOutput, error) {
|
||||
// request.Option
|
||||
_va := make([]interface{}, len(options))
|
||||
for _i := range options {
|
||||
_va[_i] = options[_i]
|
||||
// GetRepository provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) GetRepository(context1 context.Context, getRepositoryInput *codecommit.GetRepositoryInput, fns ...func(*codecommit.Options)) (*codecommit.GetRepositoryOutput, error) {
|
||||
// func(*codecommit.Options)
|
||||
_va := make([]interface{}, len(fns))
|
||||
for _i := range fns {
|
||||
_va[_i] = fns[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, v, getRepositoryInput)
|
||||
_ca = append(_ca, context1, getRepositoryInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetRepositoryWithContext")
|
||||
panic("no return value specified for GetRepository")
|
||||
}
|
||||
|
||||
var r0 *codecommit.GetRepositoryOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.GetRepositoryInput, ...request.Option) (*codecommit.GetRepositoryOutput, error)); ok {
|
||||
return returnFunc(v, getRepositoryInput, options...)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.GetRepositoryInput, ...func(*codecommit.Options)) (*codecommit.GetRepositoryOutput, error)); ok {
|
||||
return returnFunc(context1, getRepositoryInput, fns...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.GetRepositoryInput, ...request.Option) *codecommit.GetRepositoryOutput); ok {
|
||||
r0 = returnFunc(v, getRepositoryInput, options...)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.GetRepositoryInput, ...func(*codecommit.Options)) *codecommit.GetRepositoryOutput); ok {
|
||||
r0 = returnFunc(context1, getRepositoryInput, fns...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.GetRepositoryOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(aws.Context, *codecommit.GetRepositoryInput, ...request.Option) error); ok {
|
||||
r1 = returnFunc(v, getRepositoryInput, options...)
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *codecommit.GetRepositoryInput, ...func(*codecommit.Options)) error); ok {
|
||||
r1 = returnFunc(context1, getRepositoryInput, fns...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_GetRepositoryWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRepositoryWithContext'
|
||||
type AWSCodeCommitClient_GetRepositoryWithContext_Call struct {
|
||||
// AWSCodeCommitClient_GetRepository_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRepository'
|
||||
type AWSCodeCommitClient_GetRepository_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetRepositoryWithContext is a helper method to define mock.On call
|
||||
// - v aws.Context
|
||||
// GetRepository is a helper method to define mock.On call
|
||||
// - context1 context.Context
|
||||
// - getRepositoryInput *codecommit.GetRepositoryInput
|
||||
// - options ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) GetRepositoryWithContext(v interface{}, getRepositoryInput interface{}, options ...interface{}) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
return &AWSCodeCommitClient_GetRepositoryWithContext_Call{Call: _e.mock.On("GetRepositoryWithContext",
|
||||
append([]interface{}{v, getRepositoryInput}, options...)...)}
|
||||
// - fns ...func(*codecommit.Options)
|
||||
func (_e *AWSCodeCommitClient_Expecter) GetRepository(context1 interface{}, getRepositoryInput interface{}, fns ...interface{}) *AWSCodeCommitClient_GetRepository_Call {
|
||||
return &AWSCodeCommitClient_GetRepository_Call{Call: _e.mock.On("GetRepository",
|
||||
append([]interface{}{context1, getRepositoryInput}, fns...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) Run(run func(v aws.Context, getRepositoryInput *codecommit.GetRepositoryInput, options ...request.Option)) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_GetRepository_Call) Run(run func(context1 context.Context, getRepositoryInput *codecommit.GetRepositoryInput, fns ...func(*codecommit.Options))) *AWSCodeCommitClient_GetRepository_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 aws.Context
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(aws.Context)
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *codecommit.GetRepositoryInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.GetRepositoryInput)
|
||||
}
|
||||
var arg2 []request.Option
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
var arg2 []func(*codecommit.Options)
|
||||
variadicArgs := make([]func(*codecommit.Options), len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
variadicArgs[i] = a.(func(*codecommit.Options))
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
@@ -202,81 +202,81 @@ func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) Run(run func(v aws.
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) Return(getRepositoryOutput *codecommit.GetRepositoryOutput, err error) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_GetRepository_Call) Return(getRepositoryOutput *codecommit.GetRepositoryOutput, err error) *AWSCodeCommitClient_GetRepository_Call {
|
||||
_c.Call.Return(getRepositoryOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) RunAndReturn(run func(v aws.Context, getRepositoryInput *codecommit.GetRepositoryInput, options ...request.Option) (*codecommit.GetRepositoryOutput, error)) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_GetRepository_Call) RunAndReturn(run func(context1 context.Context, getRepositoryInput *codecommit.GetRepositoryInput, fns ...func(*codecommit.Options)) (*codecommit.GetRepositoryOutput, error)) *AWSCodeCommitClient_GetRepository_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListBranchesWithContext provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) ListBranchesWithContext(v aws.Context, listBranchesInput *codecommit.ListBranchesInput, options ...request.Option) (*codecommit.ListBranchesOutput, error) {
|
||||
// request.Option
|
||||
_va := make([]interface{}, len(options))
|
||||
for _i := range options {
|
||||
_va[_i] = options[_i]
|
||||
// ListBranches provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) ListBranches(context1 context.Context, listBranchesInput *codecommit.ListBranchesInput, fns ...func(*codecommit.Options)) (*codecommit.ListBranchesOutput, error) {
|
||||
// func(*codecommit.Options)
|
||||
_va := make([]interface{}, len(fns))
|
||||
for _i := range fns {
|
||||
_va[_i] = fns[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, v, listBranchesInput)
|
||||
_ca = append(_ca, context1, listBranchesInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListBranchesWithContext")
|
||||
panic("no return value specified for ListBranches")
|
||||
}
|
||||
|
||||
var r0 *codecommit.ListBranchesOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.ListBranchesInput, ...request.Option) (*codecommit.ListBranchesOutput, error)); ok {
|
||||
return returnFunc(v, listBranchesInput, options...)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.ListBranchesInput, ...func(*codecommit.Options)) (*codecommit.ListBranchesOutput, error)); ok {
|
||||
return returnFunc(context1, listBranchesInput, fns...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.ListBranchesInput, ...request.Option) *codecommit.ListBranchesOutput); ok {
|
||||
r0 = returnFunc(v, listBranchesInput, options...)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.ListBranchesInput, ...func(*codecommit.Options)) *codecommit.ListBranchesOutput); ok {
|
||||
r0 = returnFunc(context1, listBranchesInput, fns...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.ListBranchesOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(aws.Context, *codecommit.ListBranchesInput, ...request.Option) error); ok {
|
||||
r1 = returnFunc(v, listBranchesInput, options...)
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *codecommit.ListBranchesInput, ...func(*codecommit.Options)) error); ok {
|
||||
r1 = returnFunc(context1, listBranchesInput, fns...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_ListBranchesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListBranchesWithContext'
|
||||
type AWSCodeCommitClient_ListBranchesWithContext_Call struct {
|
||||
// AWSCodeCommitClient_ListBranches_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListBranches'
|
||||
type AWSCodeCommitClient_ListBranches_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListBranchesWithContext is a helper method to define mock.On call
|
||||
// - v aws.Context
|
||||
// ListBranches is a helper method to define mock.On call
|
||||
// - context1 context.Context
|
||||
// - listBranchesInput *codecommit.ListBranchesInput
|
||||
// - options ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) ListBranchesWithContext(v interface{}, listBranchesInput interface{}, options ...interface{}) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
return &AWSCodeCommitClient_ListBranchesWithContext_Call{Call: _e.mock.On("ListBranchesWithContext",
|
||||
append([]interface{}{v, listBranchesInput}, options...)...)}
|
||||
// - fns ...func(*codecommit.Options)
|
||||
func (_e *AWSCodeCommitClient_Expecter) ListBranches(context1 interface{}, listBranchesInput interface{}, fns ...interface{}) *AWSCodeCommitClient_ListBranches_Call {
|
||||
return &AWSCodeCommitClient_ListBranches_Call{Call: _e.mock.On("ListBranches",
|
||||
append([]interface{}{context1, listBranchesInput}, fns...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) Run(run func(v aws.Context, listBranchesInput *codecommit.ListBranchesInput, options ...request.Option)) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_ListBranches_Call) Run(run func(context1 context.Context, listBranchesInput *codecommit.ListBranchesInput, fns ...func(*codecommit.Options))) *AWSCodeCommitClient_ListBranches_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 aws.Context
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(aws.Context)
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *codecommit.ListBranchesInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.ListBranchesInput)
|
||||
}
|
||||
var arg2 []request.Option
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
var arg2 []func(*codecommit.Options)
|
||||
variadicArgs := make([]func(*codecommit.Options), len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
variadicArgs[i] = a.(func(*codecommit.Options))
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
@@ -289,81 +289,81 @@ func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) Run(run func(v aws.C
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) Return(listBranchesOutput *codecommit.ListBranchesOutput, err error) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_ListBranches_Call) Return(listBranchesOutput *codecommit.ListBranchesOutput, err error) *AWSCodeCommitClient_ListBranches_Call {
|
||||
_c.Call.Return(listBranchesOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) RunAndReturn(run func(v aws.Context, listBranchesInput *codecommit.ListBranchesInput, options ...request.Option) (*codecommit.ListBranchesOutput, error)) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_ListBranches_Call) RunAndReturn(run func(context1 context.Context, listBranchesInput *codecommit.ListBranchesInput, fns ...func(*codecommit.Options)) (*codecommit.ListBranchesOutput, error)) *AWSCodeCommitClient_ListBranches_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListRepositoriesWithContext provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) ListRepositoriesWithContext(v aws.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, options ...request.Option) (*codecommit.ListRepositoriesOutput, error) {
|
||||
// request.Option
|
||||
_va := make([]interface{}, len(options))
|
||||
for _i := range options {
|
||||
_va[_i] = options[_i]
|
||||
// ListRepositories provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) ListRepositories(context1 context.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, fns ...func(*codecommit.Options)) (*codecommit.ListRepositoriesOutput, error) {
|
||||
// func(*codecommit.Options)
|
||||
_va := make([]interface{}, len(fns))
|
||||
for _i := range fns {
|
||||
_va[_i] = fns[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, v, listRepositoriesInput)
|
||||
_ca = append(_ca, context1, listRepositoriesInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListRepositoriesWithContext")
|
||||
panic("no return value specified for ListRepositories")
|
||||
}
|
||||
|
||||
var r0 *codecommit.ListRepositoriesOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.ListRepositoriesInput, ...request.Option) (*codecommit.ListRepositoriesOutput, error)); ok {
|
||||
return returnFunc(v, listRepositoriesInput, options...)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.ListRepositoriesInput, ...func(*codecommit.Options)) (*codecommit.ListRepositoriesOutput, error)); ok {
|
||||
return returnFunc(context1, listRepositoriesInput, fns...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.ListRepositoriesInput, ...request.Option) *codecommit.ListRepositoriesOutput); ok {
|
||||
r0 = returnFunc(v, listRepositoriesInput, options...)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.ListRepositoriesInput, ...func(*codecommit.Options)) *codecommit.ListRepositoriesOutput); ok {
|
||||
r0 = returnFunc(context1, listRepositoriesInput, fns...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.ListRepositoriesOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(aws.Context, *codecommit.ListRepositoriesInput, ...request.Option) error); ok {
|
||||
r1 = returnFunc(v, listRepositoriesInput, options...)
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *codecommit.ListRepositoriesInput, ...func(*codecommit.Options)) error); ok {
|
||||
r1 = returnFunc(context1, listRepositoriesInput, fns...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_ListRepositoriesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRepositoriesWithContext'
|
||||
type AWSCodeCommitClient_ListRepositoriesWithContext_Call struct {
|
||||
// AWSCodeCommitClient_ListRepositories_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRepositories'
|
||||
type AWSCodeCommitClient_ListRepositories_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListRepositoriesWithContext is a helper method to define mock.On call
|
||||
// - v aws.Context
|
||||
// ListRepositories is a helper method to define mock.On call
|
||||
// - context1 context.Context
|
||||
// - listRepositoriesInput *codecommit.ListRepositoriesInput
|
||||
// - options ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) ListRepositoriesWithContext(v interface{}, listRepositoriesInput interface{}, options ...interface{}) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
return &AWSCodeCommitClient_ListRepositoriesWithContext_Call{Call: _e.mock.On("ListRepositoriesWithContext",
|
||||
append([]interface{}{v, listRepositoriesInput}, options...)...)}
|
||||
// - fns ...func(*codecommit.Options)
|
||||
func (_e *AWSCodeCommitClient_Expecter) ListRepositories(context1 interface{}, listRepositoriesInput interface{}, fns ...interface{}) *AWSCodeCommitClient_ListRepositories_Call {
|
||||
return &AWSCodeCommitClient_ListRepositories_Call{Call: _e.mock.On("ListRepositories",
|
||||
append([]interface{}{context1, listRepositoriesInput}, fns...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) Run(run func(v aws.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, options ...request.Option)) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_ListRepositories_Call) Run(run func(context1 context.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, fns ...func(*codecommit.Options))) *AWSCodeCommitClient_ListRepositories_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 aws.Context
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(aws.Context)
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *codecommit.ListRepositoriesInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.ListRepositoriesInput)
|
||||
}
|
||||
var arg2 []request.Option
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
var arg2 []func(*codecommit.Options)
|
||||
variadicArgs := make([]func(*codecommit.Options), len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
variadicArgs[i] = a.(func(*codecommit.Options))
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
@@ -376,12 +376,12 @@ func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) Run(run func(v a
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) Return(listRepositoriesOutput *codecommit.ListRepositoriesOutput, err error) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_ListRepositories_Call) Return(listRepositoriesOutput *codecommit.ListRepositoriesOutput, err error) *AWSCodeCommitClient_ListRepositories_Call {
|
||||
_c.Call.Return(listRepositoriesOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) RunAndReturn(run func(v aws.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, options ...request.Option) (*codecommit.ListRepositoriesOutput, error)) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
func (_c *AWSCodeCommitClient_ListRepositories_Call) RunAndReturn(run func(context1 context.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, fns ...func(*codecommit.Options)) (*codecommit.ListRepositoriesOutput, error)) *AWSCodeCommitClient_ListRepositories_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
@@ -38,71 +38,71 @@ func (_m *AWSTaggingClient) EXPECT() *AWSTaggingClient_Expecter {
|
||||
return &AWSTaggingClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetResourcesWithContext provides a mock function for the type AWSTaggingClient
|
||||
func (_mock *AWSTaggingClient) GetResourcesWithContext(v aws.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, options ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
|
||||
// request.Option
|
||||
_va := make([]interface{}, len(options))
|
||||
for _i := range options {
|
||||
_va[_i] = options[_i]
|
||||
// GetResources provides a mock function for the type AWSTaggingClient
|
||||
func (_mock *AWSTaggingClient) GetResources(context1 context.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, fns ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
|
||||
// func(*resourcegroupstaggingapi.Options)
|
||||
_va := make([]interface{}, len(fns))
|
||||
for _i := range fns {
|
||||
_va[_i] = fns[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, v, getResourcesInput)
|
||||
_ca = append(_ca, context1, getResourcesInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetResourcesWithContext")
|
||||
panic("no return value specified for GetResources")
|
||||
}
|
||||
|
||||
var r0 *resourcegroupstaggingapi.GetResourcesOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)); ok {
|
||||
return returnFunc(v, getResourcesInput, options...)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error)); ok {
|
||||
return returnFunc(context1, getResourcesInput, fns...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) *resourcegroupstaggingapi.GetResourcesOutput); ok {
|
||||
r0 = returnFunc(v, getResourcesInput, options...)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...func(*resourcegroupstaggingapi.Options)) *resourcegroupstaggingapi.GetResourcesOutput); ok {
|
||||
r0 = returnFunc(context1, getResourcesInput, fns...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*resourcegroupstaggingapi.GetResourcesOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) error); ok {
|
||||
r1 = returnFunc(v, getResourcesInput, options...)
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...func(*resourcegroupstaggingapi.Options)) error); ok {
|
||||
r1 = returnFunc(context1, getResourcesInput, fns...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSTaggingClient_GetResourcesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetResourcesWithContext'
|
||||
type AWSTaggingClient_GetResourcesWithContext_Call struct {
|
||||
// AWSTaggingClient_GetResources_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetResources'
|
||||
type AWSTaggingClient_GetResources_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetResourcesWithContext is a helper method to define mock.On call
|
||||
// - v aws.Context
|
||||
// GetResources is a helper method to define mock.On call
|
||||
// - context1 context.Context
|
||||
// - getResourcesInput *resourcegroupstaggingapi.GetResourcesInput
|
||||
// - options ...request.Option
|
||||
func (_e *AWSTaggingClient_Expecter) GetResourcesWithContext(v interface{}, getResourcesInput interface{}, options ...interface{}) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
return &AWSTaggingClient_GetResourcesWithContext_Call{Call: _e.mock.On("GetResourcesWithContext",
|
||||
append([]interface{}{v, getResourcesInput}, options...)...)}
|
||||
// - fns ...func(*resourcegroupstaggingapi.Options)
|
||||
func (_e *AWSTaggingClient_Expecter) GetResources(context1 interface{}, getResourcesInput interface{}, fns ...interface{}) *AWSTaggingClient_GetResources_Call {
|
||||
return &AWSTaggingClient_GetResources_Call{Call: _e.mock.On("GetResources",
|
||||
append([]interface{}{context1, getResourcesInput}, fns...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResourcesWithContext_Call) Run(run func(v aws.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, options ...request.Option)) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
func (_c *AWSTaggingClient_GetResources_Call) Run(run func(context1 context.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, fns ...func(*resourcegroupstaggingapi.Options))) *AWSTaggingClient_GetResources_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 aws.Context
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(aws.Context)
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *resourcegroupstaggingapi.GetResourcesInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*resourcegroupstaggingapi.GetResourcesInput)
|
||||
}
|
||||
var arg2 []request.Option
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
var arg2 []func(*resourcegroupstaggingapi.Options)
|
||||
variadicArgs := make([]func(*resourcegroupstaggingapi.Options), len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
variadicArgs[i] = a.(func(*resourcegroupstaggingapi.Options))
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
@@ -115,12 +115,12 @@ func (_c *AWSTaggingClient_GetResourcesWithContext_Call) Run(run func(v aws.Cont
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResourcesWithContext_Call) Return(getResourcesOutput *resourcegroupstaggingapi.GetResourcesOutput, err error) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
func (_c *AWSTaggingClient_GetResources_Call) Return(getResourcesOutput *resourcegroupstaggingapi.GetResourcesOutput, err error) *AWSTaggingClient_GetResources_Call {
|
||||
_c.Call.Return(getResourcesOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResourcesWithContext_Call) RunAndReturn(run func(v aws.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, options ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
func (_c *AWSTaggingClient_GetResources_Call) RunAndReturn(run func(context1 context.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, fns ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error)) *AWSTaggingClient_GetResources_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
@@ -58,13 +59,7 @@ func matchFilter(ctx context.Context, provider SCMProviderService, repo *Reposit
|
||||
}
|
||||
|
||||
if filter.LabelMatch != nil {
|
||||
found := false
|
||||
for _, label := range repo.Labels {
|
||||
if filter.LabelMatch.MatchString(label) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
found := slices.ContainsFunc(repo.Labels, filter.LabelMatch.MatchString)
|
||||
if !found {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ import (
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func strp(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestFilterRepoMatch(t *testing.T) {
|
||||
provider := &MockProvider{
|
||||
Repos: []*Repository{
|
||||
@@ -33,7 +29,7 @@ func TestFilterRepoMatch(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
RepositoryMatch: strp("n|hr"),
|
||||
RepositoryMatch: new("n|hr"),
|
||||
},
|
||||
}
|
||||
repos, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -62,7 +58,7 @@ func TestFilterLabelMatch(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
LabelMatch: strp("^prod-.*$"),
|
||||
LabelMatch: new("^prod-.*$"),
|
||||
},
|
||||
}
|
||||
repos, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -131,7 +127,7 @@ func TestFilterRepoMatchBadRegexp(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
RepositoryMatch: strp("("),
|
||||
RepositoryMatch: new("("),
|
||||
},
|
||||
}
|
||||
_, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -148,7 +144,7 @@ func TestFilterLabelMatchBadRegexp(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
LabelMatch: strp("("),
|
||||
LabelMatch: new("("),
|
||||
},
|
||||
}
|
||||
_, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -182,7 +178,7 @@ func TestFilterBranchMatch(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
BranchMatch: strp("w"),
|
||||
BranchMatch: new("w"),
|
||||
},
|
||||
}
|
||||
repos, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -213,8 +209,8 @@ func TestMultiFilterAnd(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
RepositoryMatch: strp("w"),
|
||||
LabelMatch: strp("^prod-.*$"),
|
||||
RepositoryMatch: new("w"),
|
||||
LabelMatch: new("^prod-.*$"),
|
||||
},
|
||||
}
|
||||
repos, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -242,10 +238,10 @@ func TestMultiFilterOr(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
RepositoryMatch: strp("e"),
|
||||
RepositoryMatch: new("e"),
|
||||
},
|
||||
{
|
||||
LabelMatch: strp("^prod-.*$"),
|
||||
LabelMatch: new("^prod-.*$"),
|
||||
},
|
||||
}
|
||||
repos, err := ListRepos(t.Context(), provider, filters, "")
|
||||
|
||||
137
applicationset/utils/client.go
Normal file
137
applicationset/utils/client.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
k8scache "k8s.io/client-go/tools/cache"
|
||||
ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
application "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
// NewCacheSyncingClient returns a client that wraps the given client and syncs the cache after each Create, Update, Patch, or Delete operation on Application objects.
|
||||
func NewCacheSyncingClient(c client.Client, cache ctrlcache.Cache) client.Client {
|
||||
res := &cacheSyncingClient{Client: c, storesByNs: make(map[string]k8scache.Store)}
|
||||
// The k8s controller runtime's cache does not expose a way to get the underlying store, so we have to use reflection to access it.
|
||||
// This is necessary to keep the cache in sync with the client operations.
|
||||
field := reflect.ValueOf(cache).Elem().FieldByName("namespaceToCache")
|
||||
if field.Kind() == reflect.Map {
|
||||
namespaceToCache := *(*map[string]ctrlcache.Cache)(unsafe.Pointer(field.UnsafeAddr()))
|
||||
res.getNSCache = func(_ context.Context, obj client.Object) (ctrlcache.Cache, error) {
|
||||
res, ok := namespaceToCache[obj.GetNamespace()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cache for namespace %s not found", obj.GetNamespace())
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
} else {
|
||||
res.getNSCache = func(_ context.Context, _ client.Object) (ctrlcache.Cache, error) {
|
||||
return cache, nil
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type cacheSyncingClient struct {
|
||||
client.Client
|
||||
getNSCache func(ctx context.Context, obj client.Object) (ctrlcache.Cache, error)
|
||||
|
||||
storesByNs map[string]k8scache.Store
|
||||
storesByNsLock sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) getStore(ctx context.Context, obj client.Object) (k8scache.Store, error) {
|
||||
c.storesByNsLock.RLock()
|
||||
store, ok := c.storesByNs[obj.GetNamespace()]
|
||||
c.storesByNsLock.RUnlock()
|
||||
if ok {
|
||||
return store, nil
|
||||
}
|
||||
|
||||
store, err := c.retrieveStore(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.storesByNsLock.Lock()
|
||||
c.storesByNs[obj.GetNamespace()] = store
|
||||
c.storesByNsLock.Unlock()
|
||||
|
||||
return store, nil
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) retrieveStore(ctx context.Context, obj client.Object) (k8scache.Store, error) {
|
||||
nsCache, err := c.getNSCache(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get namespace cache: %w", err)
|
||||
}
|
||||
|
||||
informer, err := nsCache.GetInformerForKind(ctx, application.ApplicationSchemaGroupVersionKind)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get informer: %w", err)
|
||||
}
|
||||
indexInformer, ok := informer.(k8scache.SharedIndexInformer)
|
||||
if !ok {
|
||||
return nil, errors.New("informer is not a SharedIndexInformer")
|
||||
}
|
||||
|
||||
return indexInformer.GetStore(), nil
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) execAndSyncCache(ctx context.Context, op func() error, obj client.Object, deleteObj bool) error {
|
||||
// execute the operation first and only sync cache if it succeeds
|
||||
if err := op(); err != nil {
|
||||
return err
|
||||
}
|
||||
// sync cache for applications only
|
||||
if _, ok := obj.(*application.Application); !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
logger := log.WithField("namespace", obj.GetNamespace()).WithField("name", obj.GetName())
|
||||
store, err := c.getStore(ctx, obj)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get cache store: %v", err)
|
||||
} else {
|
||||
if deleteObj {
|
||||
err = store.Delete(obj)
|
||||
} else {
|
||||
err = store.Update(obj)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
logger.Errorf("failed to sync cache for object: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
|
||||
return c.execAndSyncCache(ctx, func() error {
|
||||
return c.Client.Create(ctx, obj, opts...)
|
||||
}, obj, false)
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
|
||||
return c.execAndSyncCache(ctx, func() error {
|
||||
return c.Client.Update(ctx, obj, opts...)
|
||||
}, obj, false)
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
|
||||
return c.execAndSyncCache(ctx, func() error {
|
||||
return c.Client.Patch(ctx, obj, patch, opts...)
|
||||
}, obj, false)
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
|
||||
return c.execAndSyncCache(ctx, func() error {
|
||||
return c.Client.Delete(ctx, obj, opts...)
|
||||
}, obj, true)
|
||||
}
|
||||
111
applicationset/utils/client_test.go
Normal file
111
applicationset/utils/client_test.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
k8scache "k8s.io/client-go/tools/cache"
|
||||
ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
application "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
type fakeMultiNamespaceCache struct {
|
||||
//nolint:unused
|
||||
namespaceToCache map[string]ctrlcache.Cache
|
||||
|
||||
ctrlcache.Cache
|
||||
k8scache.SharedIndexInformer
|
||||
|
||||
Store k8scache.Store
|
||||
}
|
||||
|
||||
func (f *fakeMultiNamespaceCache) GetInformerForKind(_ context.Context, _ schema.GroupVersionKind, _ ...ctrlcache.InformerGetOption) (ctrlcache.Informer, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *fakeMultiNamespaceCache) GetStore() k8scache.Store {
|
||||
return f.Store
|
||||
}
|
||||
|
||||
func newClient(objs ...client.Object) (*cacheSyncingClient, k8scache.Store, error) {
|
||||
scheme := runtime.NewScheme()
|
||||
if err := application.AddToScheme(scheme); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
store := k8scache.NewStore(func(obj any) (string, error) {
|
||||
return obj.(client.Object).GetName(), nil
|
||||
})
|
||||
for _, obj := range objs {
|
||||
if err := store.Add(obj); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
c := &cacheSyncingClient{
|
||||
Client: fake.NewClientBuilder().WithScheme(scheme).WithObjects(objs...).Build(),
|
||||
storesByNs: map[string]k8scache.Store{},
|
||||
getNSCache: func(_ context.Context, _ client.Object) (ctrlcache.Cache, error) {
|
||||
return &fakeMultiNamespaceCache{Store: store}, nil
|
||||
},
|
||||
}
|
||||
return c, store, nil
|
||||
}
|
||||
|
||||
func TestCreateSyncsCache(t *testing.T) {
|
||||
c, store, err := newClient()
|
||||
require.NoError(t, err)
|
||||
|
||||
app := &application.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "argocd"},
|
||||
}
|
||||
require.NoError(t, c.Create(context.Background(), app))
|
||||
|
||||
require.Contains(t, store.List(), app)
|
||||
}
|
||||
|
||||
func TestUpdateSyncsCache(t *testing.T) {
|
||||
app := &application.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "argocd",
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
}
|
||||
c, store, err := newClient(app)
|
||||
require.NoError(t, err)
|
||||
|
||||
updatedApp := app.DeepCopy()
|
||||
updatedApp.Labels["foo"] = "bar-UPDATED"
|
||||
require.NoError(t, c.Update(context.Background(), updatedApp))
|
||||
|
||||
updated, _, err := store.GetByKey("test")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "bar-UPDATED", updated.(*application.Application).Labels["foo"])
|
||||
}
|
||||
|
||||
func TestDeleteSyncsCache(t *testing.T) {
|
||||
app := &application.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "argocd",
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
}
|
||||
c, store, err := newClient(app)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, c.Delete(context.Background(), app))
|
||||
|
||||
require.Empty(t, store.List())
|
||||
}
|
||||
|
||||
func TestNewClientDoesNotCrashWithMultiNamespaceCache(_ *testing.T) {
|
||||
_ = NewCacheSyncingClient(nil, &fakeMultiNamespaceCache{})
|
||||
}
|
||||
@@ -216,7 +216,6 @@ spec:
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
foundApp := v1alpha1.Application{TypeMeta: appMeta}
|
||||
|
||||
@@ -2,6 +2,7 @@ package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -207,12 +208,7 @@ type Requirement struct {
|
||||
}
|
||||
|
||||
func (r *Requirement) hasValue(value string) bool {
|
||||
for i := range r.strValues {
|
||||
if r.strValues[i] == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(r.strValues, value)
|
||||
}
|
||||
|
||||
func (r *Requirement) Matches(ls labels.Labels) bool {
|
||||
|
||||
@@ -90,7 +90,7 @@ func ConvertYAMLToJSON(str string) (string, error) {
|
||||
|
||||
// This function is in charge of searching all String fields of the object recursively and apply templating
|
||||
// thanks to https://gist.github.com/randallmlough/1fd78ec8a1034916ca52281e3b886dc7
|
||||
func (r *Render) deeplyReplace(destination, original reflect.Value, replaceMap map[string]any, useGoTemplate bool, goTemplateOptions []string) error {
|
||||
func (r *Render) deeplyReplaceWithFilter(destination, original reflect.Value, replaceMap map[string]any, useGoTemplate bool, goTemplateOptions []string, filter func(destination, original, parent reflect.Value, field reflect.StructField) (bool, error)) error {
|
||||
switch original.Kind() {
|
||||
// The first cases handle nested structures and translate them recursively
|
||||
// If it is a pointer we need to unwrap and call once again
|
||||
@@ -110,7 +110,7 @@ func (r *Render) deeplyReplace(destination, original reflect.Value, replaceMap m
|
||||
copyUnexported(destination, original)
|
||||
}
|
||||
// Unwrap the newly created pointer
|
||||
if err := r.deeplyReplace(destination.Elem(), originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
if err := r.deeplyReplaceWithFilter(destination.Elem(), originalValue, replaceMap, useGoTemplate, goTemplateOptions, filter); err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
@@ -131,7 +131,7 @@ func (r *Render) deeplyReplace(destination, original reflect.Value, replaceMap m
|
||||
reflectValue := reflect.New(reflectType)
|
||||
|
||||
copyValue := reflectValue.Elem()
|
||||
if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
if err := r.deeplyReplaceWithFilter(copyValue, originalValue, replaceMap, useGoTemplate, goTemplateOptions, filter); err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
@@ -142,6 +142,16 @@ func (r *Render) deeplyReplace(destination, original reflect.Value, replaceMap m
|
||||
case reflect.Struct:
|
||||
for i := 0; i < original.NumField(); i++ {
|
||||
currentType := fmt.Sprintf("%s.%s", original.Type().Field(i).Name, original.Type().PkgPath())
|
||||
if filter != nil {
|
||||
matched, filterErr := filter(destination.Field(i), original.Field(i), original,
|
||||
original.Type().Field(i))
|
||||
if matched {
|
||||
if filterErr != nil {
|
||||
return filterErr
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
// specific case time
|
||||
if currentType == "time.Time" {
|
||||
destination.Field(i).Set(original.Field(i))
|
||||
@@ -158,7 +168,7 @@ func (r *Render) deeplyReplace(destination, original reflect.Value, replaceMap m
|
||||
}
|
||||
jsonOriginal := reflect.ValueOf(&unmarshaled)
|
||||
jsonCopy := reflect.New(jsonOriginal.Type()).Elem()
|
||||
err = r.deeplyReplace(jsonCopy, jsonOriginal, replaceMap, useGoTemplate, goTemplateOptions)
|
||||
err = r.deeplyReplaceWithFilter(jsonCopy, jsonOriginal, replaceMap, useGoTemplate, goTemplateOptions, filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deeply replace JSON field contents: %w", err)
|
||||
}
|
||||
@@ -168,7 +178,7 @@ func (r *Render) deeplyReplace(destination, original reflect.Value, replaceMap m
|
||||
return fmt.Errorf("failed to marshal templated JSON field: %w", err)
|
||||
}
|
||||
destination.Field(i).Set(reflect.ValueOf(data))
|
||||
} else if err := r.deeplyReplace(destination.Field(i), original.Field(i), replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
} else if err := r.deeplyReplaceWithFilter(destination.Field(i), original.Field(i), replaceMap, useGoTemplate, goTemplateOptions, filter); err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
@@ -183,7 +193,7 @@ func (r *Render) deeplyReplace(destination, original reflect.Value, replaceMap m
|
||||
}
|
||||
|
||||
for i := 0; i < original.Len(); i++ {
|
||||
if err := r.deeplyReplace(destination.Index(i), original.Index(i), replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
if err := r.deeplyReplaceWithFilter(destination.Index(i), original.Index(i), replaceMap, useGoTemplate, goTemplateOptions, filter); err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
@@ -204,7 +214,7 @@ func (r *Render) deeplyReplace(destination, original reflect.Value, replaceMap m
|
||||
// New gives us a pointer, but again we want the value
|
||||
copyValue := reflect.New(originalValue.Type()).Elem()
|
||||
|
||||
if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
if err := r.deeplyReplaceWithFilter(copyValue, originalValue, replaceMap, useGoTemplate, goTemplateOptions, filter); err != nil {
|
||||
// Not wrapping the error, since this is a recursive function. Avoids excessively long error messages.
|
||||
return err
|
||||
}
|
||||
@@ -249,6 +259,10 @@ func (r *Render) deeplyReplace(destination, original reflect.Value, replaceMap m
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Render) deeplyReplace(destination, original reflect.Value, replaceMap map[string]any, useGoTemplate bool, goTemplateOptions []string) error {
|
||||
return r.deeplyReplaceWithFilter(destination, original, replaceMap, useGoTemplate, goTemplateOptions, nil)
|
||||
}
|
||||
|
||||
// isNillable returns true if the value is something which may be set to nil. This function is meant to guard against a
|
||||
// panic from calling IsNil on a non-pointer type.
|
||||
func isNillable(v reflect.Value) bool {
|
||||
@@ -290,6 +304,27 @@ func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *
|
||||
return replacedTmpl, nil
|
||||
}
|
||||
|
||||
// Generator types that have Value field
|
||||
var filteredGeneratorTypes = getFilteredGeneratorTypes()
|
||||
|
||||
// find generator types that have Values field
|
||||
func getFilteredGeneratorTypes() map[string]bool {
|
||||
result := map[string]bool{}
|
||||
t := reflect.TypeFor[argoappsv1.ApplicationSetGenerator]()
|
||||
for field := range t.Fields() {
|
||||
genPtrType := field.Type
|
||||
if genPtrType.Kind() == reflect.Ptr && strings.HasSuffix(genPtrType.String(), "Generator") {
|
||||
genType := genPtrType.Elem()
|
||||
for field := range genType.Fields() {
|
||||
if field.Name == "Values" && field.Type.String() == "map[string]string" {
|
||||
result[genType.Name()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (r *Render) RenderGeneratorParams(gen *argoappsv1.ApplicationSetGenerator, params map[string]any, useGoTemplate bool, goTemplateOptions []string) (*argoappsv1.ApplicationSetGenerator, error) {
|
||||
if gen == nil {
|
||||
return nil, errors.New("generator is empty")
|
||||
@@ -302,7 +337,17 @@ func (r *Render) RenderGeneratorParams(gen *argoappsv1.ApplicationSetGenerator,
|
||||
original := reflect.ValueOf(gen)
|
||||
destination := reflect.New(original.Type()).Elem()
|
||||
|
||||
if err := r.deeplyReplace(destination, original, params, useGoTemplate, goTemplateOptions); err != nil {
|
||||
filter := func(destination, original, parent reflect.Value, field reflect.StructField) (bool, error) {
|
||||
if field.Name == "Values" && field.Type.String() == "map[string]string" && filteredGeneratorTypes[parent.Type().Name()] {
|
||||
if !destination.CanSet() {
|
||||
return false, fmt.Errorf("cannot copy %s.Values, this cannot happen", parent.Type().Name())
|
||||
}
|
||||
destination.Set(original)
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
if err := r.deeplyReplaceWithFilter(destination, original, params, useGoTemplate, goTemplateOptions, filter); err != nil {
|
||||
return nil, fmt.Errorf("failed to replace parameters in generator: %w", err)
|
||||
}
|
||||
|
||||
@@ -388,8 +433,7 @@ func invalidGenerators(applicationSetInfo *argoappsv1.ApplicationSet) (bool, map
|
||||
for index, generator := range applicationSetInfo.Spec.Generators {
|
||||
v := reflect.Indirect(reflect.ValueOf(generator))
|
||||
found := false
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Field(i)
|
||||
for _, field := range v.Fields() {
|
||||
if !field.CanInterface() {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -1390,3 +1391,12 @@ WkBKOclmOV2xlTVuPw==
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getFilteredGeneratorTypes(t *testing.T) {
|
||||
generators := getFilteredGeneratorTypes()
|
||||
assert.Less(t, 1, len(generators))
|
||||
for name, val := range generators {
|
||||
assert.True(t, val)
|
||||
assert.True(t, strings.HasSuffix(name, "Generator"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,10 +107,8 @@ func NewWebhookHandler(webhookParallelism int, argocdSettingsMgr *argosettings.S
|
||||
|
||||
func (h *WebhookHandler) startWorkerPool(webhookParallelism int) {
|
||||
compLog := log.WithField("component", "applicationset-webhook")
|
||||
for i := 0; i < webhookParallelism; i++ {
|
||||
h.Add(1)
|
||||
go func() {
|
||||
defer h.Done()
|
||||
for range webhookParallelism {
|
||||
h.Go(func() {
|
||||
for {
|
||||
payload, ok := <-h.queue
|
||||
if !ok {
|
||||
@@ -118,7 +116,7 @@ func (h *WebhookHandler) startWorkerPool(webhookParallelism int) {
|
||||
}
|
||||
guard.RecoverAndLog(func() { h.HandleEvent(payload) }, compLog, panicMsgAppSet)
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ func TestWebhookHandler(t *testing.T) {
|
||||
h, err := NewWebhookHandler(webhookParallelism, set, fc, mockGenerators())
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", http.NoBody)
|
||||
req := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/api/webhook", http.NoBody)
|
||||
req.Header.Set(test.headerKey, test.headerValue)
|
||||
eventJSON, err := os.ReadFile(filepath.Join("testdata", test.payloadFile))
|
||||
require.NoError(t, err)
|
||||
@@ -609,7 +609,7 @@ func fakeAppWithMatrixAndNestedGitGenerator(name, namespace, repo string) *v1alp
|
||||
},
|
||||
{
|
||||
Matrix: &apiextensionsv1.JSON{
|
||||
Raw: []byte(fmt.Sprintf(`{
|
||||
Raw: fmt.Appendf(nil, `{
|
||||
"Generators": [
|
||||
{
|
||||
"List": {
|
||||
@@ -626,7 +626,7 @@ func fakeAppWithMatrixAndNestedGitGenerator(name, namespace, repo string) *v1alp
|
||||
}
|
||||
}
|
||||
]
|
||||
}`, repo)),
|
||||
}`, repo),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -707,7 +707,7 @@ func fakeAppWithMergeAndNestedGitGenerator(name, namespace, repo string) *v1alph
|
||||
},
|
||||
{
|
||||
Merge: &apiextensionsv1.JSON{
|
||||
Raw: []byte(fmt.Sprintf(`{
|
||||
Raw: fmt.Appendf(nil, `{
|
||||
"MergeKeys": ["server"],
|
||||
"Generators": [
|
||||
{
|
||||
@@ -719,7 +719,7 @@ func fakeAppWithMergeAndNestedGitGenerator(name, namespace, repo string) *v1alph
|
||||
}
|
||||
}
|
||||
]
|
||||
}`, repo)),
|
||||
}`, repo),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
82
assets/swagger.json
generated
82
assets/swagger.json
generated
@@ -4385,6 +4385,69 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/stream/applicationsets": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"ApplicationSetService"
|
||||
],
|
||||
"operationId": "ApplicationSetService_Watch",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "name",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"name": "projects",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "selector",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "appSetNamespace",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "when specified with a watch call, shows changes that occur after that particular version of a resource.",
|
||||
"name": "resourceVersion",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"title": "Stream result of v1alpha1ApplicationSetWatchEvent",
|
||||
"properties": {
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
},
|
||||
"result": {
|
||||
"$ref": "#/definitions/v1alpha1ApplicationSetWatchEvent"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/runtimeError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/write-repocreds": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -7459,6 +7522,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1ApplicationSetWatchEvent": {
|
||||
"description": "ApplicationSetWatchEvent contains information about application change.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"applicationSet": {
|
||||
"$ref": "#/definitions/v1alpha1ApplicationSet"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "Type represents the Kubernetes watch event type. The protobuf tag uses\ncasttype to ensure the generated Go code keeps this field as\nwatch.EventType (a strong Go type) instead of falling back to a plain string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1alpha1ApplicationSource": {
|
||||
"type": "object",
|
||||
"title": "ApplicationSource contains all required information about the source of an application",
|
||||
@@ -7695,11 +7771,11 @@
|
||||
},
|
||||
"namePrefix": {
|
||||
"type": "string",
|
||||
"title": "NamePrefix is a prefix appended to resources for Kustomize apps"
|
||||
"title": "NamePrefix overrides the namePrefix in the kustomization.yaml for Kustomize apps"
|
||||
},
|
||||
"nameSuffix": {
|
||||
"type": "string",
|
||||
"title": "NameSuffix is a suffix appended to resources for Kustomize apps"
|
||||
"title": "NameSuffix overrides the nameSuffix in the kustomization.yaml for Kustomize apps"
|
||||
},
|
||||
"namespace": {
|
||||
"type": "string",
|
||||
@@ -9756,7 +9832,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"diff": {
|
||||
"description": "Diff contains the JSON patch representing the difference between the live and target resource.\nDeprecated: Use NormalizedLiveState and PredictedLiveState instead to compute differences.",
|
||||
"description": "Diff contains the JSON patch representing the difference between the live and target resource.\n\nDeprecated: Use NormalizedLiveState and PredictedLiveState instead to compute differences.",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
|
||||
@@ -41,8 +41,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// CLIName is the name of the CLI
|
||||
cliName = common.ApplicationController
|
||||
// Default time in seconds for application resync period
|
||||
defaultAppResyncPeriod = 120
|
||||
// Default time in seconds for application resync period jitter
|
||||
@@ -99,7 +97,7 @@ func NewCommand() *cobra.Command {
|
||||
hydratorEnabled bool
|
||||
)
|
||||
command := cobra.Command{
|
||||
Use: cliName,
|
||||
Use: common.CommandApplicationController,
|
||||
Short: "Run ArgoCD Application Controller",
|
||||
Long: "ArgoCD application controller is a Kubernetes controller that continuously monitors running applications and compares the current, live state against the desired target state (as specified in the repo). This command runs Application Controller in the foreground. It can be configured by following options.",
|
||||
DisableAutoGenTag: true,
|
||||
|
||||
@@ -49,10 +49,6 @@ import (
|
||||
|
||||
var gitSubmoduleEnabled = env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true)
|
||||
|
||||
const (
|
||||
cliName = common.ApplicationSetController
|
||||
)
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
@@ -82,12 +78,13 @@ func NewCommand() *cobra.Command {
|
||||
webhookParallelism int
|
||||
tokenRefStrictMode bool
|
||||
maxResourcesStatusCount int
|
||||
cacheSyncPeriod time.Duration
|
||||
)
|
||||
scheme := runtime.NewScheme()
|
||||
_ = clientgoscheme.AddToScheme(scheme)
|
||||
_ = appv1alpha1.AddToScheme(scheme)
|
||||
command := cobra.Command{
|
||||
Use: cliName,
|
||||
Use: common.CommandApplicationSetController,
|
||||
Short: "Starts Argo CD ApplicationSet controller",
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(c *cobra.Command, _ []string) error {
|
||||
@@ -143,13 +140,11 @@ func NewCommand() *cobra.Command {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var cacheOpt ctrlcache.Options
|
||||
cacheOpt := ctrlcache.Options{SyncPeriod: &cacheSyncPeriod}
|
||||
|
||||
if watchedNamespace != "" {
|
||||
cacheOpt = ctrlcache.Options{
|
||||
DefaultNamespaces: map[string]ctrlcache.Config{
|
||||
watchedNamespace: {},
|
||||
},
|
||||
cacheOpt.DefaultNamespaces = map[string]ctrlcache.Config{
|
||||
watchedNamespace: {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +240,7 @@ func NewCommand() *cobra.Command {
|
||||
|
||||
if err = (&controllers.ApplicationSetReconciler{
|
||||
Generators: topLevelGenerators,
|
||||
Client: mgr.GetClient(),
|
||||
Client: utils.NewCacheSyncingClient(mgr.GetClient(), mgr.GetCache()),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Recorder: mgr.GetEventRecorderFor("applicationset-controller"),
|
||||
Renderer: &utils.Render{},
|
||||
@@ -306,7 +301,8 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().IntVar(&webhookParallelism, "webhook-parallelism-limit", env.ParseNumFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_WEBHOOK_PARALLELISM_LIMIT", 50, 1, 1000), "Number of webhook requests processed concurrently")
|
||||
command.Flags().StringSliceVar(&metricsAplicationsetLabels, "metrics-applicationset-labels", []string{}, "List of Application labels that will be added to the argocd_applicationset_labels metric")
|
||||
command.Flags().BoolVar(&enableGitHubAPIMetrics, "enable-github-api-metrics", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_GITHUB_API_METRICS", false), "Enable GitHub API metrics for generators that use the GitHub API")
|
||||
command.Flags().IntVar(&maxResourcesStatusCount, "max-resources-status-count", env.ParseNumFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_MAX_RESOURCES_STATUS_COUNT", 0, 0, math.MaxInt), "Max number of resources stored in appset status.")
|
||||
command.Flags().IntVar(&maxResourcesStatusCount, "max-resources-status-count", env.ParseNumFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_MAX_RESOURCES_STATUS_COUNT", 5000, 0, math.MaxInt), "Max number of resources stored in appset status.")
|
||||
command.Flags().DurationVar(&cacheSyncPeriod, "cache-sync-period", env.ParseDurationFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_CACHE_SYNC_PERIOD", time.Hour*10, 0, time.Hour*24), "Period at which the manager client cache is forcefully resynced with the Kubernetes API server. 0 disables periodic resync.")
|
||||
|
||||
return &command
|
||||
}
|
||||
|
||||
@@ -18,11 +18,6 @@ import (
|
||||
traceutil "github.com/argoproj/argo-cd/v3/util/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
// CLIName is the name of the CLI
|
||||
cliName = "argocd-cmp-server"
|
||||
)
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
configFilePath string
|
||||
@@ -32,7 +27,7 @@ func NewCommand() *cobra.Command {
|
||||
otlpAttrs []string
|
||||
)
|
||||
command := cobra.Command{
|
||||
Use: cliName,
|
||||
Use: common.CommandCMPServer,
|
||||
Short: "Run ArgoCD ConfigManagementPlugin Server",
|
||||
Long: "ArgoCD ConfigManagementPlugin Server is an internal service which runs as sidecar container in reposerver deployment. The following configuration options are available:",
|
||||
DisableAutoGenTag: true,
|
||||
|
||||
@@ -35,7 +35,7 @@ func NewCommand() *cobra.Command {
|
||||
metricsHost string
|
||||
)
|
||||
command := &cobra.Command{
|
||||
Use: "argocd-commit-server",
|
||||
Use: common.CommandCommitServer,
|
||||
Short: "Run Argo CD Commit Server",
|
||||
Long: "Argo CD Commit Server is an internal service which commits and pushes hydrated manifests to git. This command runs Commit Server in the foreground.",
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
@@ -91,13 +91,11 @@ func NewCommand() *cobra.Command {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
wg.Go(func() {
|
||||
s := <-sigCh
|
||||
log.Printf("got signal %v, attempting graceful shutdown", s)
|
||||
grpc.GracefulStop()
|
||||
wg.Done()
|
||||
}()
|
||||
})
|
||||
|
||||
log.Println("starting grpc server")
|
||||
err = grpc.Serve(listener)
|
||||
|
||||
@@ -25,13 +25,9 @@ import (
|
||||
"github.com/argoproj/argo-cd/v3/util/tls"
|
||||
)
|
||||
|
||||
const (
|
||||
cliName = "argocd-dex"
|
||||
)
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: cliName,
|
||||
Use: common.CommandDex,
|
||||
Short: "argocd-dex tools used by Argo CD",
|
||||
Long: "argocd-dex has internal utility tools used by Argo CD",
|
||||
DisableAutoGenTag: true,
|
||||
|
||||
@@ -9,20 +9,16 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/common"
|
||||
"github.com/argoproj/argo-cd/v3/util/askpass"
|
||||
"github.com/argoproj/argo-cd/v3/util/errors"
|
||||
grpc_util "github.com/argoproj/argo-cd/v3/util/grpc"
|
||||
utilio "github.com/argoproj/argo-cd/v3/util/io"
|
||||
)
|
||||
|
||||
const (
|
||||
// cliName is the name of the CLI
|
||||
cliName = "argocd-git-ask-pass"
|
||||
)
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
command := cobra.Command{
|
||||
Use: cliName,
|
||||
Use: common.CommandGitAskPass,
|
||||
Short: "Argo CD git credential helper",
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(c *cobra.Command, _ []string) {
|
||||
|
||||
@@ -2,15 +2,13 @@ package commands
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
cliName = "argocd-k8s-auth"
|
||||
"github.com/argoproj/argo-cd/v3/common"
|
||||
)
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: cliName,
|
||||
Use: common.CommandK8sAuth,
|
||||
Short: "argocd-k8s-auth a set of commands to generate k8s auth token",
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
|
||||
@@ -6,12 +6,14 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/sts"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
clientauthv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
||||
@@ -58,13 +60,13 @@ func newAWSCommand() *cobra.Command {
|
||||
return command
|
||||
}
|
||||
|
||||
type getSignedRequestFunc func(clusterName, roleARN string, profile string) (string, error)
|
||||
type getSignedRequestFunc func(ctx context.Context, clusterName, roleARN string, profile string) (string, error)
|
||||
|
||||
func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Duration, clusterName, roleARN string, profile string, fn getSignedRequestFunc) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
for {
|
||||
signed, err := fn(clusterName, roleARN, profile)
|
||||
signed, err := fn(ctx, clusterName, roleARN, profile)
|
||||
if err == nil {
|
||||
return signed, nil
|
||||
}
|
||||
@@ -76,25 +78,53 @@ func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Durat
|
||||
}
|
||||
}
|
||||
|
||||
func getSignedRequest(clusterName, roleARN string, profile string) (string, error) {
|
||||
sess, err := session.NewSessionWithOptions(session.Options{
|
||||
Profile: profile,
|
||||
})
|
||||
func getSignedRequest(ctx context.Context, clusterName, roleARN string, profile string) (string, error) {
|
||||
cfg, err := loadAWSConfig(ctx, profile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating new AWS session: %w", err)
|
||||
return "", err
|
||||
}
|
||||
stsAPI := sts.New(sess)
|
||||
return getSignedRequestWithConfig(ctx, clusterName, roleARN, cfg)
|
||||
}
|
||||
|
||||
func loadAWSConfig(ctx context.Context, profile string) (aws.Config, error) {
|
||||
var opts []func(*config.LoadOptions) error
|
||||
if profile != "" {
|
||||
opts = append(opts, config.WithSharedConfigProfile(profile))
|
||||
}
|
||||
cfg, err := config.LoadDefaultConfig(ctx, opts...)
|
||||
if err != nil {
|
||||
return aws.Config{}, fmt.Errorf("error loading AWS configuration: %w", err)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// getSignedRequestWithConfig presigns GetCallerIdentity using the given config. Used by getSignedRequest and by tests
|
||||
// that inject a config with static credentials to exercise the roleARN path without real AWS credentials.
|
||||
func getSignedRequestWithConfig(ctx context.Context, clusterName, roleARN string, cfg aws.Config) (string, error) {
|
||||
// Use PresignOptions.ClientOptions + SetHeaderValue (same as aws-iam-authenticator) so the
|
||||
// canonical request matches what EKS sends when validating. Build middleware can produce
|
||||
// a different canonical form and thus an invalid signature for EKS.
|
||||
// See kubernetes-sigs/aws-iam-authenticator pkg/token/token.go GetWithSTS().
|
||||
client := sts.NewFromConfig(cfg)
|
||||
if roleARN != "" {
|
||||
creds := stscreds.NewCredentials(sess, roleARN)
|
||||
stsAPI = sts.New(sess, &aws.Config{Credentials: creds})
|
||||
appCreds := stscreds.NewAssumeRoleProvider(client, roleARN)
|
||||
cfg.Credentials = aws.NewCredentialsCache(appCreds)
|
||||
client = sts.NewFromConfig(cfg)
|
||||
}
|
||||
request, _ := stsAPI.GetCallerIdentityRequest(&sts.GetCallerIdentityInput{})
|
||||
request.HTTPRequest.Header.Add(clusterIDHeader, clusterName)
|
||||
signed, err := request.Presign(requestPresignParam)
|
||||
|
||||
presignClient := sts.NewPresignClient(client)
|
||||
presigned, err := presignClient.PresignGetCallerIdentity(ctx, &sts.GetCallerIdentityInput{},
|
||||
func(presignOptions *sts.PresignOptions) {
|
||||
presignOptions.ClientOptions = append(presignOptions.ClientOptions, func(stsOptions *sts.Options) {
|
||||
stsOptions.APIOptions = append(stsOptions.APIOptions,
|
||||
smithyhttp.SetHeaderValue(clusterIDHeader, clusterName),
|
||||
smithyhttp.SetHeaderValue("X-Amz-Expires", strconv.Itoa(requestPresignParam)))
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error presigning AWS request: %w", err)
|
||||
}
|
||||
return signed, nil
|
||||
return presigned.URL, nil
|
||||
}
|
||||
|
||||
func formatJSON(token string, expiration time.Time) string {
|
||||
|
||||
@@ -1,14 +1,60 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetSignedRequest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("returns error when context is cancelled", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
url, err := getSignedRequest(ctx, "my-cluster", "", "")
|
||||
|
||||
require.ErrorIs(t, err, context.Canceled)
|
||||
assert.Empty(t, url)
|
||||
})
|
||||
|
||||
t.Run("returns error for non-existent profile", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
profile := "argocd-k8s-auth-test-nonexistent-profile-12345"
|
||||
|
||||
url, err := getSignedRequest(ctx, "my-cluster", "", profile)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, url)
|
||||
assert.Contains(t, err.Error(), "configuration", "error should mention configuration load failed")
|
||||
})
|
||||
|
||||
t.Run("returns error when roleARN is provided and assume role fails", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
cfg, err := config.LoadDefaultConfig(ctx,
|
||||
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("test", "test", "")),
|
||||
config.WithRegion("us-east-1"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
url, err := getSignedRequestWithConfig(ctx, "my-cluster", "arn:aws:iam::123456789012:role/NonExistentRole", cfg)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, url)
|
||||
assert.Contains(t, err.Error(), "presigning", "error should mention presigning failed when assume role is used")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetSignedRequestWithRetry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -72,7 +118,7 @@ type signedRequestMock struct {
|
||||
returnFunc func(m *signedRequestMock) (string, error)
|
||||
}
|
||||
|
||||
func (m *signedRequestMock) getSignedRequestMock(_, _ string, _ string) (string, error) {
|
||||
func (m *signedRequestMock) getSignedRequestMock(_ context.Context, _, _ string, _ string) (string, error) {
|
||||
m.getSignedRequestCalls++
|
||||
return m.returnFunc(m)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package commands
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -52,7 +53,7 @@ func NewCommand() *cobra.Command {
|
||||
selfServiceNotificationEnabled bool
|
||||
)
|
||||
command := cobra.Command{
|
||||
Use: "controller",
|
||||
Use: common.CommandNotifications,
|
||||
Short: "Starts Argo CD Notifications controller",
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -126,7 +127,7 @@ func NewCommand() *cobra.Command {
|
||||
tlsConfig.Certificates = pool
|
||||
}
|
||||
repoClientset := apiclient.NewRepoServerClientset(argocdRepoServer, 5, tlsConfig)
|
||||
argocdService, err := service.NewArgoCDService(k8sClient, namespace, repoClientset)
|
||||
argocdService, err := service.NewArgoCDService(k8sClient, dynamicClient, namespace, repoClientset)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize Argo CD service: %w", err)
|
||||
}
|
||||
@@ -150,13 +151,11 @@ func NewCommand() *cobra.Command {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
wg.Go(func() {
|
||||
s := <-sigCh
|
||||
log.Printf("got signal %v, attempting graceful shutdown", s)
|
||||
cancel()
|
||||
}()
|
||||
})
|
||||
|
||||
go ctrl.Run(ctx, processorsCount)
|
||||
<-ctx.Done()
|
||||
@@ -164,7 +163,7 @@ func NewCommand() *cobra.Command {
|
||||
},
|
||||
}
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().IntVar(&processorsCount, "processors-count", 1, "Processors count.")
|
||||
command.Flags().IntVar(&processorsCount, "processors-count", env.ParseNumFromEnv("ARGOCD_NOTIFICATION_CONTROLLER_PROCESSORS_COUNT", 1, 1, math.MaxInt32), "Processors count.")
|
||||
command.Flags().StringVar(&appLabelSelector, "app-label-selector", "", "App label selector.")
|
||||
command.Flags().StringVar(&logLevel, "loglevel", env.StringFromEnv("ARGOCD_NOTIFICATIONS_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().StringVar(&logFormat, "logformat", env.StringFromEnv("ARGOCD_NOTIFICATIONS_CONTROLLER_LOGFORMAT", "json"), "Set the logging format. One of: json|text")
|
||||
28
cmd/argocd-notification/commands/argocd_notification_test.go
Normal file
28
cmd/argocd-notification/commands/argocd_notification_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewCommandProcessorsCountDefault(t *testing.T) {
|
||||
t.Setenv("ARGOCD_NOTIFICATION_CONTROLLER_PROCESSORS_COUNT", "4")
|
||||
|
||||
cmd := NewCommand()
|
||||
|
||||
processorsCount, err := cmd.Flags().GetInt("processors-count")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 4, processorsCount)
|
||||
}
|
||||
|
||||
func TestNewCommandProcessorsCountInvalidEnvFallsBackToDefault(t *testing.T) {
|
||||
t.Setenv("ARGOCD_NOTIFICATION_CONTROLLER_PROCESSORS_COUNT", "0")
|
||||
|
||||
cmd := NewCommand()
|
||||
|
||||
processorsCount, err := cmd.Flags().GetInt("processors-count")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, processorsCount)
|
||||
}
|
||||
@@ -34,21 +34,18 @@ import (
|
||||
"github.com/argoproj/argo-cd/v3/util/gpg"
|
||||
"github.com/argoproj/argo-cd/v3/util/healthz"
|
||||
utilio "github.com/argoproj/argo-cd/v3/util/io"
|
||||
"github.com/argoproj/argo-cd/v3/util/profile"
|
||||
"github.com/argoproj/argo-cd/v3/util/tls"
|
||||
traceutil "github.com/argoproj/argo-cd/v3/util/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
// CLIName is the name of the CLI
|
||||
cliName = "argocd-repo-server"
|
||||
)
|
||||
|
||||
var (
|
||||
gnuPGSourcePath = env.StringFromEnv(common.EnvGPGDataPath, "/app/config/gpg/source")
|
||||
pauseGenerationAfterFailedGenerationAttempts = env.ParseNumFromEnv(common.EnvPauseGenerationAfterFailedAttempts, 3, 0, math.MaxInt32)
|
||||
pauseGenerationOnFailureForMinutes = env.ParseNumFromEnv(common.EnvPauseGenerationMinutes, 60, 0, math.MaxInt32)
|
||||
pauseGenerationOnFailureForRequests = env.ParseNumFromEnv(common.EnvPauseGenerationRequests, 0, 0, math.MaxInt32)
|
||||
gitSubmoduleEnabled = env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true)
|
||||
helmUserAgent = env.StringFromEnv(common.EnvHelmUserAgent, "")
|
||||
)
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
@@ -83,7 +80,7 @@ func NewCommand() *cobra.Command {
|
||||
enableBuiltinGitConfig bool
|
||||
)
|
||||
command := cobra.Command{
|
||||
Use: cliName,
|
||||
Use: common.CommandRepoServer,
|
||||
Short: "Run ArgoCD Repository Server",
|
||||
Long: "ArgoCD Repository Server is an internal service which maintains a local cache of the Git repository holding the application manifests, and is responsible for generating and returning the Kubernetes manifests. This command runs Repository Server in the foreground. It can be configured by following options.",
|
||||
DisableAutoGenTag: true,
|
||||
@@ -157,6 +154,7 @@ func NewCommand() *cobra.Command {
|
||||
CMPUseManifestGeneratePaths: cmpUseManifestGeneratePaths,
|
||||
OCIMediaTypes: ociMediaTypes,
|
||||
EnableBuiltinGitConfig: enableBuiltinGitConfig,
|
||||
HelmUserAgent: helmUserAgent,
|
||||
}, askPassServer)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -175,7 +173,8 @@ func NewCommand() *cobra.Command {
|
||||
listener, err := lc.Listen(ctx, "tcp", fmt.Sprintf("%s:%d", listenHost, listenPort))
|
||||
errors.CheckError(err)
|
||||
|
||||
healthz.ServeHealthCheck(http.DefaultServeMux, func(r *http.Request) error {
|
||||
mux := http.NewServeMux()
|
||||
healthz.ServeHealthCheck(mux, func(r *http.Request) error {
|
||||
if val, ok := r.URL.Query()["full"]; ok && len(val) > 0 && val[0] == "true" {
|
||||
// connect to itself to make sure repo server is able to serve connection
|
||||
// used by liveness probe to auto restart repo server
|
||||
@@ -197,8 +196,9 @@ func NewCommand() *cobra.Command {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
http.Handle("/metrics", metricsServer.GetHandler())
|
||||
go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf("%s:%d", metricsHost, metricsPort), nil)) }()
|
||||
mux.Handle("/metrics", metricsServer.GetHandler())
|
||||
profile.RegisterProfiler(mux)
|
||||
go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf("%s:%d", metricsHost, metricsPort), mux)) }()
|
||||
go func() { errors.CheckError(askPassServer.Run()) }()
|
||||
|
||||
if gpg.IsGPGEnabled() {
|
||||
@@ -223,13 +223,11 @@ func NewCommand() *cobra.Command {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
wg.Go(func() {
|
||||
s := <-sigCh
|
||||
log.Printf("got signal %v, attempting graceful shutdown", s)
|
||||
grpc.GracefulStop()
|
||||
wg.Done()
|
||||
}()
|
||||
})
|
||||
|
||||
log.Println("starting grpc server")
|
||||
err = grpc.Serve(listener)
|
||||
|
||||
@@ -101,7 +101,7 @@ func NewCommand() *cobra.Command {
|
||||
enableK8sEvent []string
|
||||
)
|
||||
command := &cobra.Command{
|
||||
Use: cliName,
|
||||
Use: common.CommandServer,
|
||||
Short: "Run the ArgoCD API server",
|
||||
Long: "The API server is a gRPC/REST server which exposes the API consumed by the Web UI, CLI, and CI/CD systems. This command runs API server in the foreground. It can be configured by following options.",
|
||||
DisableAutoGenTag: true,
|
||||
@@ -307,7 +307,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().BoolVar(&disableAuth, "disable-auth", env.ParseBoolFromEnv("ARGOCD_SERVER_DISABLE_AUTH", false), "Disable client authentication")
|
||||
command.Flags().StringVar(&contentTypes, "api-content-types", env.StringFromEnv("ARGOCD_API_CONTENT_TYPES", "application/json", env.StringFromEnvOpts{AllowEmpty: true}), "Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty.")
|
||||
command.Flags().BoolVar(&enableGZip, "enable-gzip", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_GZIP", true), "Enable GZIP compression")
|
||||
command.AddCommand(cli.NewVersionCmd(cliName))
|
||||
command.AddCommand(cli.NewVersionCmd(common.CommandServer))
|
||||
command.Flags().StringVar(&listenHost, "address", env.StringFromEnv("ARGOCD_SERVER_LISTEN_ADDRESS", common.DefaultAddressAPIServer), "Listen on given address")
|
||||
command.Flags().IntVar(&listenPort, "port", common.DefaultPortAPIServer, "Listen on given port")
|
||||
command.Flags().StringVar(&metricsHost, env.StringFromEnv("ARGOCD_SERVER_METRICS_LISTEN_ADDRESS", "metrics-address"), common.DefaultAddressAPIServerMetrics, "Listen for metrics on given address")
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package commands
|
||||
|
||||
const (
|
||||
// cliName is the name of the CLI
|
||||
cliName = "argocd-server"
|
||||
)
|
||||
@@ -11,11 +11,13 @@ import (
|
||||
"time"
|
||||
|
||||
timeutil "github.com/argoproj/pkg/v2/time"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
jwtutil "github.com/argoproj/argo-cd/v3/util/jwt"
|
||||
"github.com/argoproj/argo-cd/v3/util/rbac"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/cmd/argocd/commands/headless"
|
||||
@@ -60,6 +62,7 @@ func NewAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
command.AddCommand(NewAccountGenerateTokenCommand(clientOpts))
|
||||
command.AddCommand(NewAccountGetCommand(clientOpts))
|
||||
command.AddCommand(NewAccountDeleteTokenCommand(clientOpts))
|
||||
command.AddCommand(NewAccountSessionTokenCommand(clientOpts))
|
||||
command.AddCommand(NewBcryptCmd())
|
||||
return command
|
||||
}
|
||||
@@ -134,7 +137,7 @@ has appropriate RBAC permissions to change other accounts.
|
||||
errors.CheckError(err)
|
||||
claims, err := configCtx.User.Claims()
|
||||
errors.CheckError(err)
|
||||
tokenString := passwordLogin(ctx, acdClient, localconfig.GetUsername(claims.Subject), newPassword)
|
||||
tokenString := passwordLogin(ctx, acdClient, localconfig.GetUsername(jwtutil.StringField(claims, "sub")), newPassword)
|
||||
localCfg.UpsertUser(localconfig.User{
|
||||
Name: localCfg.CurrentContext,
|
||||
AuthToken: tokenString,
|
||||
@@ -448,3 +451,90 @@ argocd account delete-token --account <account-name> ID`,
|
||||
cmd.Flags().StringVarP(&account, "account", "a", "", "Account name. Defaults to the current account.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewAccountSessionTokenCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var output string
|
||||
cmd := &cobra.Command{
|
||||
Use: "session-token",
|
||||
Short: "Display current session token",
|
||||
Long: `Display the current session token for authentication.
|
||||
|
||||
Automatically refreshes expired tokens using refresh token (SSO users).
|
||||
For local users: Shows current token (manual relogin needed if expired)`,
|
||||
Example: `# Display current session token (automatically refreshes if needed)
|
||||
argocd account session-token
|
||||
|
||||
# Show detailed token information
|
||||
argocd account session-token -o json
|
||||
|
||||
# Use in scripts
|
||||
export ARGOCD_AUTH_TOKEN=$(argocd account session-token)
|
||||
curl -H "Authorization: Bearer $ARGOCD_AUTH_TOKEN" $ARGOCD_SERVER/api/v1/applications`,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
// Create client first - this handles token refresh automatically
|
||||
_, err := argocdclient.NewClient(clientOpts)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "invalid_grant") && strings.Contains(err.Error(), "Invalid refresh_token") {
|
||||
log.Fatal("Refresh token is invalid or expired. Please run 'argocd relogin' to re-authenticate")
|
||||
}
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Read config after client creation to get potentially refreshed token
|
||||
localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath)
|
||||
errors.CheckError(err)
|
||||
if localCfg == nil {
|
||||
log.Fatal("No configuration found. Please login first with 'argocd login'")
|
||||
}
|
||||
|
||||
configCtx, err := localCfg.ResolveContext(clientOpts.Context)
|
||||
errors.CheckError(err)
|
||||
if configCtx == nil {
|
||||
log.Fatal("No context found. Please login first with 'argocd login'")
|
||||
}
|
||||
|
||||
if configCtx.User.AuthToken == "" {
|
||||
log.Fatal("No authentication token found. Please login first with 'argocd login'")
|
||||
}
|
||||
|
||||
// Get token claims and validate
|
||||
claims, err := configCtx.User.Claims()
|
||||
if err != nil {
|
||||
log.Fatal("Invalid token format. Please run 'argocd relogin'")
|
||||
}
|
||||
validator := jwt.NewValidator()
|
||||
if validator.Validate(claims) != nil {
|
||||
log.Fatal("Token is invalid or expired. Please run 'argocd relogin'")
|
||||
}
|
||||
|
||||
switch output {
|
||||
case "json":
|
||||
iss := jwtutil.StringField(claims, "iss")
|
||||
tokenInfo := map[string]any{
|
||||
"type": "local",
|
||||
"issuer": iss,
|
||||
"username": localconfig.GetUsername(jwtutil.GetUserIdentifier(claims)),
|
||||
"token": configCtx.User.AuthToken,
|
||||
"has_refresh_token": configCtx.User.RefreshToken != "",
|
||||
}
|
||||
if iss != sessionutil.SessionManagerClaimsIssuer {
|
||||
tokenInfo["type"] = "sso"
|
||||
}
|
||||
if iat, err := jwtutil.IssuedAtTime(claims); err == nil {
|
||||
tokenInfo["issued_at"] = iat.Format(time.RFC3339)
|
||||
}
|
||||
if exp, err := jwtutil.ExpirationTime(claims); err == nil {
|
||||
tokenInfo["expires_at"] = exp.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
jsonBytes, err := json.MarshalIndent(tokenInfo, "", " ")
|
||||
errors.CheckError(err)
|
||||
fmt.Println(string(jsonBytes))
|
||||
default:
|
||||
fmt.Println(configCtx.User.AuthToken)
|
||||
}
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVarP(&output, "output", "o", "", "Output format (json)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -183,9 +183,9 @@ func getAdditionalNamespaces(ctx context.Context, configMapsClient dynamic.Resou
|
||||
namespacesListFromString := func(namespaces string) []string {
|
||||
listOfNamespaces := []string{}
|
||||
|
||||
ss := strings.Split(namespaces, ",")
|
||||
ss := strings.SplitSeq(namespaces, ",")
|
||||
|
||||
for _, namespace := range ss {
|
||||
for namespace := range ss {
|
||||
if namespace != "" {
|
||||
listOfNamespaces = append(listOfNamespaces, strings.TrimSpace(namespace))
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
@@ -3,9 +3,9 @@ package admin
|
||||
import (
|
||||
"testing"
|
||||
|
||||
clustermocks "github.com/argoproj/gitops-engine/pkg/cache/mocks"
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
clustermocks "github.com/argoproj/argo-cd/gitops-engine/pkg/cache/mocks"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v3/util/security"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
|
||||
"github.com/redis/go-redis/v9"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/v3/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v3/common"
|
||||
@@ -128,19 +127,16 @@ func loadClusters(ctx context.Context, kubeClient kubernetes.Interface, appClien
|
||||
|
||||
batchSize := 10
|
||||
batchesCount := int(math.Ceil(float64(len(clusters)) / float64(batchSize)))
|
||||
for batchNum := 0; batchNum < batchesCount; batchNum++ {
|
||||
for batchNum := range batchesCount {
|
||||
batchStart := batchSize * batchNum
|
||||
batchEnd := batchSize * (batchNum + 1)
|
||||
if batchEnd > len(clustersList.Items) {
|
||||
batchEnd = len(clustersList.Items)
|
||||
}
|
||||
batchEnd := min((batchSize * (batchNum + 1)), len(clustersList.Items))
|
||||
batch := clustersList.Items[batchStart:batchEnd]
|
||||
_ = kube.RunAllAsync(len(batch), func(i int) error {
|
||||
clusterShard := 0
|
||||
cluster := batch[i]
|
||||
if replicas > 0 {
|
||||
clusterShard = clusterShards[cluster.Server]
|
||||
cluster.Shard = ptr.To(int64(clusterShard))
|
||||
cluster.Shard = new(int64(clusterShard))
|
||||
log.Infof("Cluster with uid: %s will be processed by shard %d", cluster.ID, clusterShard)
|
||||
}
|
||||
if shard != -1 && clusterShard != shard {
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
func Test_loadClusters(t *testing.T) {
|
||||
@@ -72,9 +71,9 @@ func Test_loadClusters(t *testing.T) {
|
||||
ConnectionState: v1alpha1.ConnectionState{
|
||||
Status: "Successful",
|
||||
},
|
||||
ServerVersion: ".",
|
||||
ServerVersion: "0.0.0",
|
||||
},
|
||||
Shard: ptr.To(int64(0)),
|
||||
Shard: new(int64(0)),
|
||||
},
|
||||
Namespaces: []string{"test"},
|
||||
}}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"log"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
@@ -60,7 +61,11 @@ func NewNotificationsCommand() *cobra.Command {
|
||||
tlsConfig.Certificates = pool
|
||||
}
|
||||
repoClientset := apiclient.NewRepoServerClientset(argocdRepoServer, 5, tlsConfig)
|
||||
argocdService, err = service.NewArgoCDService(kubernetes.NewForConfigOrDie(k8sCfg), ns, repoClientset)
|
||||
dynamicClient, err := dynamic.NewForConfig(k8sCfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create dynamic client: %v", err)
|
||||
}
|
||||
argocdService, err = service.NewArgoCDService(kubernetes.NewForConfigOrDie(k8sCfg), dynamicClient, ns, repoClientset)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to initialize Argo CD service: %v", err)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
utilio "github.com/argoproj/argo-cd/v3/util/io"
|
||||
"github.com/argoproj/argo-cd/v3/util/templates"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
@@ -23,7 +23,7 @@ func generateRandomPassword() (string, error) {
|
||||
const initialPasswordLength = 16
|
||||
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
|
||||
randBytes := make([]byte, initialPasswordLength)
|
||||
for i := 0; i < initialPasswordLength; i++ {
|
||||
for i := range initialPasswordLength {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -7,12 +7,13 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
healthutil "github.com/argoproj/gitops-engine/pkg/health"
|
||||
healthutil "github.com/argoproj/argo-cd/gitops-engine/pkg/health"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -271,9 +272,7 @@ func NewValidateSettingsCommand(cmdCtx commandContext) *cobra.Command {
|
||||
for k := range validatorsByGroup {
|
||||
allGroups = append(allGroups, k)
|
||||
}
|
||||
sort.Slice(allGroups, func(i, j int) bool {
|
||||
return allGroups[i] < allGroups[j]
|
||||
})
|
||||
slices.Sort(allGroups)
|
||||
|
||||
command := &cobra.Command{
|
||||
Use: "validate",
|
||||
|
||||
@@ -322,6 +322,7 @@ func getPolicy(ctx context.Context, policyFile string, kubeClient kubernetes.Int
|
||||
}
|
||||
|
||||
// getPolicyFromFile loads a RBAC policy from given path
|
||||
// nolint:unparam // complains about the error being always nil which is false-positive
|
||||
func getPolicyFromFile(policyFile string) (string, string, string, error) {
|
||||
var (
|
||||
userPolicy string
|
||||
|
||||
@@ -19,11 +19,11 @@ import (
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/hook"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/sync/hook"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/sync/ignore"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
|
||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/retry"
|
||||
"github.com/mattn/go-isatty"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -35,7 +35,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
k8swatch "k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/cmd/argocd/commands/headless"
|
||||
@@ -46,7 +45,7 @@ import (
|
||||
argocdclient "github.com/argoproj/argo-cd/v3/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v3/pkg/apiclient/application"
|
||||
|
||||
resourceutil "github.com/argoproj/gitops-engine/pkg/sync/resource"
|
||||
resourceutil "github.com/argoproj/argo-cd/gitops-engine/pkg/sync/resource"
|
||||
|
||||
clusterpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/cluster"
|
||||
projectpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/project"
|
||||
@@ -602,17 +601,17 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
stream, err := appIf.PodLogs(ctx, &application.ApplicationPodLogsQuery{
|
||||
Name: &appName,
|
||||
Group: &group,
|
||||
Namespace: ptr.To(namespace),
|
||||
Namespace: new(namespace),
|
||||
Kind: &kind,
|
||||
ResourceName: &resourceName,
|
||||
Follow: ptr.To(follow),
|
||||
TailLines: ptr.To(tail),
|
||||
SinceSeconds: ptr.To(sinceSeconds),
|
||||
Follow: new(follow),
|
||||
TailLines: new(tail),
|
||||
SinceSeconds: new(sinceSeconds),
|
||||
UntilTime: &untilTime,
|
||||
Filter: &filter,
|
||||
MatchCase: ptr.To(matchCase),
|
||||
Container: ptr.To(container),
|
||||
Previous: ptr.To(previous),
|
||||
MatchCase: new(matchCase),
|
||||
Container: new(container),
|
||||
Previous: new(previous),
|
||||
AppNamespace: &appNs,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -718,7 +717,7 @@ func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *ar
|
||||
var syncPolicy string
|
||||
if app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.IsAutomatedSyncEnabled() {
|
||||
syncPolicy = "Automated"
|
||||
if app.Spec.SyncPolicy.Automated.Prune {
|
||||
if app.Spec.SyncPolicy.Automated.GetPrune() {
|
||||
syncPolicy += " (Prune)"
|
||||
}
|
||||
} else {
|
||||
@@ -996,6 +995,9 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
if sourceName != "" && sourcePosition != -1 {
|
||||
errors.Fatal(errors.ErrorGeneric, "Only one of source-position and source-name can be specified.")
|
||||
}
|
||||
|
||||
appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace)
|
||||
conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie()
|
||||
@@ -1003,11 +1005,6 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
app, err := appIf.Get(ctx, &application.ApplicationQuery{Name: &appName, AppNamespace: &appNs})
|
||||
errors.CheckError(err)
|
||||
|
||||
sourceName = appOpts.SourceName
|
||||
if sourceName != "" && sourcePosition != -1 {
|
||||
errors.Fatal(errors.ErrorGeneric, "Only one of source-position and source-name can be specified.")
|
||||
}
|
||||
|
||||
if sourceName != "" {
|
||||
sourceNameToPosition := getSourceNameToPositionMap(app)
|
||||
pos, ok := sourceNameToPosition[sourceName]
|
||||
@@ -1070,6 +1067,7 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
command.Flags().BoolVar(&opts.passCredentials, "pass-credentials", false, "Unset passCredentials")
|
||||
command.Flags().BoolVar(&opts.ref, "ref", false, "Unset ref on the source")
|
||||
command.Flags().IntVar(&sourcePosition, "source-position", -1, "Position of the source from the list of sources of the app. Counting starts at 1.")
|
||||
command.Flags().StringVar(&sourceName, "source-name", "", "Name of the source from the list of sources of the app.")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -1872,7 +1870,7 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie()
|
||||
defer utilio.Close(conn)
|
||||
apps, err := appIf.List(ctx, &application.ApplicationQuery{
|
||||
Selector: ptr.To(selector),
|
||||
Selector: new(selector),
|
||||
AppNamespace: &appNamespace,
|
||||
})
|
||||
|
||||
@@ -1920,7 +1918,7 @@ func formatSyncPolicy(app argoappv1.Application) string {
|
||||
return "Manual"
|
||||
}
|
||||
policy := "Auto"
|
||||
if app.Spec.SyncPolicy.Automated.Prune {
|
||||
if app.Spec.SyncPolicy.Automated.GetPrune() {
|
||||
policy = policy + "-Prune"
|
||||
}
|
||||
return policy
|
||||
@@ -1988,8 +1986,8 @@ func parseSelectedResources(resources []string) ([]*argoappv1.SyncOperationResou
|
||||
for _, resource := range resources {
|
||||
isExcluded := false
|
||||
// check if the resource flag starts with a '!'
|
||||
if strings.HasPrefix(resource, resourceExcludeIndicator) {
|
||||
resource = strings.TrimPrefix(resource, resourceExcludeIndicator)
|
||||
if after, ok := strings.CutPrefix(resource, resourceExcludeIndicator); ok {
|
||||
resource = after
|
||||
isExcluded = true
|
||||
}
|
||||
fields := strings.Split(resource, resourceFieldDelimiter)
|
||||
@@ -2073,7 +2071,7 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
closer, appIf := acdClient.NewApplicationClientOrDie()
|
||||
defer utilio.Close(closer)
|
||||
if selector != "" {
|
||||
list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: ptr.To(selector)})
|
||||
list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: new(selector)})
|
||||
errors.CheckError(err)
|
||||
for _, i := range list.Items {
|
||||
appNames = append(appNames, i.QualifiedName())
|
||||
@@ -2257,7 +2255,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
appNames := args
|
||||
if selector != "" || len(projects) > 0 {
|
||||
list, err := appIf.List(ctx, &application.ApplicationQuery{
|
||||
Selector: ptr.To(selector),
|
||||
Selector: new(selector),
|
||||
AppNamespace: &appNamespace,
|
||||
Projects: projects,
|
||||
})
|
||||
@@ -2431,7 +2429,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
Backoff: &argoappv1.Backoff{
|
||||
Duration: retryBackoffDuration.String(),
|
||||
MaxDuration: retryBackoffMaxDuration.String(),
|
||||
Factor: ptr.To(retryBackoffFactor),
|
||||
Factor: new(retryBackoffFactor),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -2525,7 +2523,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
func getAppNamesBySelector(ctx context.Context, appIf application.ApplicationServiceClient, selector string) ([]string, error) {
|
||||
appNames := []string{}
|
||||
if selector != "" {
|
||||
list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: ptr.To(selector)})
|
||||
list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: new(selector)})
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
@@ -2703,7 +2701,7 @@ func checkResourceStatus(watch watchOpts, healthStatus string, syncStatus string
|
||||
func resourceParentChild(ctx context.Context, acdClient argocdclient.Client, appName string, appNs string) (map[string]argoappv1.ResourceNode, map[string][]string, map[string]struct{}, map[string]*resourceState) {
|
||||
_, appIf := acdClient.NewApplicationClientOrDie()
|
||||
mapUIDToNode, mapParentToChild, parentNode := parentChildDetails(ctx, appIf, appName, appNs)
|
||||
app, err := appIf.Get(ctx, &application.ApplicationQuery{Name: ptr.To(appName), AppNamespace: ptr.To(appNs)})
|
||||
app, err := appIf.Get(ctx, &application.ApplicationQuery{Name: new(appName), AppNamespace: new(appNs)})
|
||||
errors.CheckError(err)
|
||||
mapNodeNameToResourceState := make(map[string]*resourceState)
|
||||
for _, res := range getResourceStates(app, nil) {
|
||||
@@ -3135,8 +3133,8 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr
|
||||
_, err = appIf.Rollback(ctx, &application.ApplicationRollbackRequest{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Id: ptr.To(depInfo.ID),
|
||||
Prune: ptr.To(prune),
|
||||
Id: new(depInfo.ID),
|
||||
Prune: new(prune),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -3288,7 +3286,7 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
q := application.ApplicationManifestQuery{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Revision: ptr.To(revision),
|
||||
Revision: new(revision),
|
||||
Revisions: revisions,
|
||||
SourcePositions: sourcePositions,
|
||||
}
|
||||
@@ -3304,7 +3302,7 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
q := application.ApplicationManifestQuery{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Revision: ptr.To(revision),
|
||||
Revision: new(revision),
|
||||
}
|
||||
res, err := appIf.GetManifests(ctx, &q)
|
||||
errors.CheckError(err)
|
||||
@@ -3326,7 +3324,6 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
default:
|
||||
log.Fatalf("Unknown source type '%s'", source)
|
||||
}
|
||||
|
||||
for _, obj := range unstructureds {
|
||||
fmt.Println("---")
|
||||
yamlBytes, err := yaml.Marshal(obj)
|
||||
@@ -3666,7 +3663,7 @@ func NewApplicationConfirmDeletionCommand(clientOpts *argocdclient.ClientOptions
|
||||
|
||||
_, err = appIf.Update(ctx, &application.ApplicationUpdateRequest{
|
||||
Application: app,
|
||||
Validate: ptr.To(false),
|
||||
Validate: new(false),
|
||||
Project: &app.Spec.Project,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc/codes"
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/cmd/argocd/commands/headless"
|
||||
@@ -95,11 +94,11 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt
|
||||
availActionsForResource, err := appIf.ListResourceActions(ctx, &applicationpkg.ApplicationResourceRequest{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Namespace: ptr.To(obj.GetNamespace()),
|
||||
ResourceName: ptr.To(obj.GetName()),
|
||||
Group: ptr.To(gvk.Group),
|
||||
Kind: ptr.To(gvk.Kind),
|
||||
Version: ptr.To(gvk.Version),
|
||||
Namespace: new(obj.GetNamespace()),
|
||||
ResourceName: new(obj.GetName()),
|
||||
Group: new(gvk.Group),
|
||||
Kind: new(gvk.Kind),
|
||||
Version: new(gvk.Version),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
for _, action := range availActionsForResource.Actions {
|
||||
@@ -195,12 +194,12 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti
|
||||
_, err := appIf.RunResourceActionV2(ctx, &applicationpkg.ResourceActionRunRequestV2{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Namespace: ptr.To(obj.GetNamespace()),
|
||||
ResourceName: ptr.To(objResourceName),
|
||||
Group: ptr.To(gvk.Group),
|
||||
Kind: ptr.To(gvk.Kind),
|
||||
Version: ptr.To(gvk.GroupVersion().Version),
|
||||
Action: ptr.To(actionName),
|
||||
Namespace: new(obj.GetNamespace()),
|
||||
ResourceName: new(objResourceName),
|
||||
Group: new(gvk.Group),
|
||||
Kind: new(gvk.Kind),
|
||||
Version: new(gvk.GroupVersion().Version),
|
||||
Action: new(actionName),
|
||||
// TODO: add support for parameters
|
||||
})
|
||||
if err == nil {
|
||||
@@ -214,12 +213,12 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti
|
||||
_, err = appIf.RunResourceAction(ctx, &applicationpkg.ResourceActionRunRequest{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Namespace: ptr.To(obj.GetNamespace()),
|
||||
ResourceName: ptr.To(objResourceName),
|
||||
Group: ptr.To(gvk.Group),
|
||||
Kind: ptr.To(gvk.Kind),
|
||||
Version: ptr.To(gvk.GroupVersion().Version),
|
||||
Action: ptr.To(actionName),
|
||||
Namespace: new(obj.GetNamespace()),
|
||||
ResourceName: new(objResourceName),
|
||||
Group: new(gvk.Group),
|
||||
Kind: new(gvk.Kind),
|
||||
Version: new(gvk.GroupVersion().Version),
|
||||
Action: new(actionName),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
@@ -43,9 +43,49 @@ func TestPrintTreeViewAppResources(t *testing.T) {
|
||||
printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, w)
|
||||
require.NoError(t, w.Flush())
|
||||
output := buf.String()
|
||||
|
||||
t.Logf("Output:\n%s", output)
|
||||
assert.Contains(t, output, "Rollout")
|
||||
assert.Contains(t, output, "argoproj.io")
|
||||
assert.Contains(t, output, "└─apps ReplicaSet sandbox-rollout-numalogic-demo numalogic-rollout-demo-5dcd5457d5 No")
|
||||
assert.Contains(t, output, " └─ Pod sandbox-rollout-numalogic-demo numalogic-rollout-demo-5dcd5457d5-6trpt No")
|
||||
}
|
||||
|
||||
func TestPrintTreeViewAppResourcesWithMultipleChildren(t *testing.T) {
|
||||
var nodes [4]v1alpha1.ResourceNode
|
||||
// Parent
|
||||
nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Kind: "Rollout", Namespace: "ns", Name: "rollout", UID: "root"}
|
||||
// Child 1
|
||||
nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Kind: "ReplicaSet", Namespace: "ns", Name: "rs1", UID: "rs1"}
|
||||
nodes[1].ParentRefs = []v1alpha1.ResourceRef{{UID: "root"}}
|
||||
// Child 2
|
||||
nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Kind: "ReplicaSet", Namespace: "ns", Name: "rs2", UID: "rs2"}
|
||||
nodes[2].ParentRefs = []v1alpha1.ResourceRef{{UID: "root"}}
|
||||
// Grandchild
|
||||
nodes[3].ResourceRef = v1alpha1.ResourceRef{Group: "", Kind: "Pod", Namespace: "ns", Name: "pod1", UID: "pod1"}
|
||||
nodes[3].ParentRefs = []v1alpha1.ResourceRef{{UID: "rs1"}}
|
||||
|
||||
nodeMapping := make(map[string]v1alpha1.ResourceNode)
|
||||
mapParentToChild := make(map[string][]string)
|
||||
parentNode := make(map[string]struct{})
|
||||
for _, node := range nodes {
|
||||
nodeMapping[node.UID] = node
|
||||
if len(node.ParentRefs) > 0 {
|
||||
mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID)
|
||||
} else {
|
||||
parentNode[node.UID] = struct{}{}
|
||||
}
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0)
|
||||
|
||||
printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, w)
|
||||
require.NoError(t, w.Flush())
|
||||
output := buf.String()
|
||||
t.Logf("Output:\n%s", output)
|
||||
|
||||
assert.Contains(t, output, "├─apps ReplicaSet ns rs1")
|
||||
assert.Contains(t, output, "│ └─ Pod ns pod1")
|
||||
assert.Contains(t, output, "└─apps ReplicaSet ns rs2")
|
||||
}
|
||||
|
||||
func TestPrintTreeViewDetailedAppResources(t *testing.T) {
|
||||
@@ -82,10 +122,11 @@ func TestPrintTreeViewDetailedAppResources(t *testing.T) {
|
||||
printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, w)
|
||||
require.NoError(t, w.Flush())
|
||||
output := buf.String()
|
||||
t.Logf("Output:\n%s", output)
|
||||
|
||||
assert.Contains(t, output, "Rollout")
|
||||
assert.Contains(t, output, "Degraded")
|
||||
assert.Contains(t, output, "Readiness Gate failed")
|
||||
assert.Contains(t, output, "argoproj.io Rollout sandbox-rollout-numalogic-demo numalogic-rollout-demo No <unknown> Degraded Readiness Gate failed")
|
||||
assert.Contains(t, output, "└─apps ReplicaSet sandbox-rollout-numalogic-demo numalogic-rollout-demo-5dcd5457d5 No")
|
||||
assert.Contains(t, output, " └─ Pod sandbox-rollout-numalogic-demo numalogic-rollout-demo-5dcd5457d5-6trpt No")
|
||||
}
|
||||
|
||||
func TestPrintResourcesTree(t *testing.T) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/util/templates"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/cmd/argocd/commands/utils"
|
||||
"github.com/argoproj/argo-cd/v3/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
@@ -18,7 +20,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/cmd/argocd/commands/headless"
|
||||
argocdclient "github.com/argoproj/argo-cd/v3/pkg/apiclient"
|
||||
@@ -87,7 +88,7 @@ func NewApplicationGetResourceCommand(clientOpts *argocdclient.ClientOptions) *c
|
||||
// Get manifests of resources
|
||||
// If resource name is "" find all resources of that kind
|
||||
var resources []unstructured.Unstructured
|
||||
var fetchedStr string
|
||||
var resourceNames []string
|
||||
for _, r := range tree.Nodes {
|
||||
if (resourceName != "" && r.Name != resourceName) || (group != "" && r.Group != group) || r.Kind != kind {
|
||||
continue
|
||||
@@ -117,14 +118,11 @@ func NewApplicationGetResourceCommand(clientOpts *argocdclient.ClientOptions) *c
|
||||
obj = filterFieldsFromObject(obj, filteredFields)
|
||||
}
|
||||
|
||||
fetchedStr += obj.GetName() + ", "
|
||||
resourceNames = append(resourceNames, obj.GetName())
|
||||
resources = append(resources, *obj)
|
||||
}
|
||||
fetchedStr := strings.Join(resourceNames, ", ")
|
||||
printManifests(&resources, len(filteredFields) > 0, resourceName == "", output)
|
||||
|
||||
if fetchedStr != "" {
|
||||
fetchedStr = strings.TrimSuffix(fetchedStr, ", ")
|
||||
}
|
||||
log.Infof("Resources '%s' fetched", fetchedStr)
|
||||
}
|
||||
|
||||
@@ -338,14 +336,14 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
_, err = appIf.PatchResource(ctx, &applicationpkg.ApplicationResourcePatchRequest{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Namespace: ptr.To(obj.GetNamespace()),
|
||||
ResourceName: ptr.To(obj.GetName()),
|
||||
Version: ptr.To(gvk.Version),
|
||||
Group: ptr.To(gvk.Group),
|
||||
Kind: ptr.To(gvk.Kind),
|
||||
Patch: ptr.To(patch),
|
||||
PatchType: ptr.To(patchType),
|
||||
Project: ptr.To(project),
|
||||
Namespace: new(obj.GetNamespace()),
|
||||
ResourceName: new(obj.GetName()),
|
||||
Version: new(gvk.Version),
|
||||
Group: new(gvk.Group),
|
||||
Kind: new(gvk.Kind),
|
||||
Patch: new(patch),
|
||||
PatchType: new(patchType),
|
||||
Project: new(project),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
log.Infof("Resource '%s' patched", obj.GetName())
|
||||
@@ -411,14 +409,14 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
_, err = appIf.DeleteResource(ctx, &applicationpkg.ApplicationResourceDeleteRequest{
|
||||
Name: &appName,
|
||||
AppNamespace: &appNs,
|
||||
Namespace: ptr.To(obj.GetNamespace()),
|
||||
ResourceName: ptr.To(obj.GetName()),
|
||||
Version: ptr.To(gvk.Version),
|
||||
Group: ptr.To(gvk.Group),
|
||||
Kind: ptr.To(gvk.Kind),
|
||||
Namespace: new(obj.GetNamespace()),
|
||||
ResourceName: new(obj.GetName()),
|
||||
Version: new(gvk.Version),
|
||||
Group: new(gvk.Group),
|
||||
Kind: new(gvk.Kind),
|
||||
Force: &force,
|
||||
Orphan: &orphan,
|
||||
Project: ptr.To(project),
|
||||
Project: new(project),
|
||||
})
|
||||
errors.CheckError(err)
|
||||
log.Infof("Resource '%s' deleted", obj.GetName())
|
||||
@@ -533,7 +531,20 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions)
|
||||
)
|
||||
command := &cobra.Command{
|
||||
Use: "resources APPNAME",
|
||||
Short: "List resource of application",
|
||||
Short: "List resources of application",
|
||||
Example: templates.Examples(`
|
||||
# List first-level resources of application
|
||||
argocd app resources my-app --refresh
|
||||
|
||||
# List only the orphaned resources of application
|
||||
argocd app resources my-app --orphaned
|
||||
|
||||
# Shows resource hierarchy with parent-child relationships
|
||||
argocd app resources my-app --output tree
|
||||
|
||||
# Shows resource hierarchy with parent-child relationships including information about age, health and reason
|
||||
argocd app resources my-app --output tree=detailed
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
if len(args) != 1 {
|
||||
@@ -554,7 +565,9 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions)
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVar(&orphaned, "orphaned", false, "Lists only orphaned resources")
|
||||
command.Flags().StringVar(&output, "output", "", "Provides the tree view of the resources")
|
||||
command.Flags().StringVar(&output, "output", "", `Output format. One of: tree|tree=detailed.
|
||||
tree: Shows resource hierarchy with parent-child relationships
|
||||
tree=detailed: Same as tree, but includes AGE, HEALTH, and REASON columns`)
|
||||
command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`)
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
@@ -440,7 +443,7 @@ func TestFormatSyncPolicy(t *testing.T) {
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SyncPolicy: &v1alpha1.SyncPolicy{
|
||||
Automated: &v1alpha1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
Prune: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -458,7 +461,7 @@ func TestFormatConditionSummary(t *testing.T) {
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SyncPolicy: &v1alpha1.SyncPolicy{
|
||||
Automated: &v1alpha1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
Prune: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -673,7 +676,7 @@ func TestPrintAppSummaryTable(t *testing.T) {
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SyncPolicy: &v1alpha1.SyncPolicy{
|
||||
Automated: &v1alpha1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
Prune: new(true),
|
||||
},
|
||||
},
|
||||
Project: "default",
|
||||
@@ -761,7 +764,7 @@ func TestPrintAppSummaryTable_MultipleSources(t *testing.T) {
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SyncPolicy: &v1alpha1.SyncPolicy{
|
||||
Automated: &v1alpha1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
Prune: new(true),
|
||||
},
|
||||
},
|
||||
Project: "default",
|
||||
@@ -1047,6 +1050,39 @@ func TestPrintApplicationNames(t *testing.T) {
|
||||
require.Equalf(t, output, expectation, "Incorrect print params output %q, should be %q", output, expectation)
|
||||
}
|
||||
|
||||
func TestNewApplicationUnsetCommand_Validation(t *testing.T) {
|
||||
if os.Getenv("BE_CRASHER") == "1" {
|
||||
cmd := NewApplicationUnsetCommand(nil)
|
||||
cmd.SetArgs([]string{"my-app", "--source-position", "1", "--source-name", "test"})
|
||||
_ = cmd.Execute()
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(context.Background(), os.Args[0], "-test.run=TestNewApplicationUnsetCommand_Validation")
|
||||
cmd.Env = append(os.Environ(), "BE_CRASHER=1")
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
|
||||
if e, ok := errors.AsType[*exec.ExitError](err); ok && !e.Success() {
|
||||
assert.Contains(t, stderr.String(), "Only one of source-position and source-name can be specified.")
|
||||
return
|
||||
}
|
||||
t.Fatalf("process ran with err %v, want exit status 1", err)
|
||||
}
|
||||
|
||||
func TestNewApplicationUnsetCommand_Flags(t *testing.T) {
|
||||
cmd := NewApplicationUnsetCommand(nil)
|
||||
assert.NotNil(t, cmd)
|
||||
|
||||
flag := cmd.Flags().Lookup("source-name")
|
||||
assert.NotNil(t, flag)
|
||||
assert.Equal(t, "source-name", flag.Name)
|
||||
|
||||
flag = cmd.Flags().Lookup("source-position")
|
||||
assert.NotNil(t, flag)
|
||||
assert.Equal(t, "source-position", flag.Name)
|
||||
}
|
||||
|
||||
func Test_unset(t *testing.T) {
|
||||
kustomizeSource := &v1alpha1.ApplicationSource{
|
||||
Kustomize: &v1alpha1.ApplicationSourceKustomize{
|
||||
@@ -2079,7 +2115,7 @@ func (c *fakeAppServiceClient) Get(_ context.Context, _ *applicationpkg.Applicat
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SyncPolicy: &v1alpha1.SyncPolicy{
|
||||
Automated: &v1alpha1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
Prune: new(true),
|
||||
},
|
||||
},
|
||||
Project: "default",
|
||||
@@ -2386,3 +2422,22 @@ func (c *fakeAcdClient) WatchApplicationWithRetry(_ context.Context, _ string, _
|
||||
}()
|
||||
return appEventsCh
|
||||
}
|
||||
|
||||
func (c *fakeAcdClient) WatchApplicationSetWithRetry(_ context.Context, _ string, _ string) chan *v1alpha1.ApplicationSetWatchEvent {
|
||||
appSetEventsCh := make(chan *v1alpha1.ApplicationSetWatchEvent)
|
||||
go func() {
|
||||
defer close(appSetEventsCh)
|
||||
addedEvent := &v1alpha1.ApplicationSetWatchEvent{
|
||||
Type: watch.Added,
|
||||
ApplicationSet: v1alpha1.ApplicationSet{
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Conditions: []v1alpha1.ApplicationSetCondition{
|
||||
{Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, Status: v1alpha1.ApplicationSetConditionStatusTrue},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
appSetEventsCh <- addedEvent
|
||||
}()
|
||||
return appSetEventsCh
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"text/tabwriter"
|
||||
|
||||
k8swatch "k8s.io/apimachinery/pkg/watch"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -115,8 +118,10 @@ func NewApplicationSetGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
|
||||
// NewApplicationSetCreateCommand returns a new instance of an `argocd appset create` command
|
||||
func NewApplicationSetCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var output string
|
||||
var upsert, dryRun bool
|
||||
var (
|
||||
output string
|
||||
upsert, dryRun, wait bool
|
||||
)
|
||||
command := &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create one or more ApplicationSets",
|
||||
@@ -200,11 +205,18 @@ func NewApplicationSetCreateCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
default:
|
||||
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
|
||||
}
|
||||
|
||||
if wait && !dryRun {
|
||||
err := waitForApplicationSetResourcesUpToDate(ctx, argocdClient, created.QualifiedName())
|
||||
errors.CheckError(err)
|
||||
c.PrintErrf("ApplicationSet '%s' resources are up to date\n", created.Name)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVar(&upsert, "upsert", false, "Allows to override ApplicationSet with the same name even if supplied ApplicationSet spec is different from existing spec")
|
||||
command.Flags().BoolVar(&dryRun, "dry-run", false, "Allows to evaluate the ApplicationSet template on the server to get a preview of the applications that would be created")
|
||||
command.Flags().BoolVar(&wait, "wait", false, "Wait until the ApplicationSet's resources are up to date. Will block indefinitely if the ApplicationSet has errors")
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide")
|
||||
return command
|
||||
}
|
||||
@@ -325,7 +337,10 @@ func NewApplicationSetListCommand(clientOpts *argocdclient.ClientOptions) *cobra
|
||||
|
||||
// NewApplicationSetDeleteCommand returns a new instance of an `argocd appset delete` command
|
||||
func NewApplicationSetDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var noPrompt bool
|
||||
var (
|
||||
noPrompt bool
|
||||
wait bool
|
||||
)
|
||||
command := &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Delete one or more ApplicationSets",
|
||||
@@ -340,7 +355,8 @@ func NewApplicationSetDeleteCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationSetClientOrDie()
|
||||
acdClient := headless.NewClientOrDie(clientOpts, c)
|
||||
conn, appIf := acdClient.NewApplicationSetClientOrDie()
|
||||
defer utilio.Close(conn)
|
||||
isTerminal := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
|
||||
numOfApps := len(args)
|
||||
@@ -373,6 +389,20 @@ func NewApplicationSetDeleteCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
if confirm || confirmAll {
|
||||
_, err := appIf.Delete(ctx, &appsetDeleteReq)
|
||||
errors.CheckError(err)
|
||||
|
||||
if wait {
|
||||
_, getErr := appIf.Get(ctx, &applicationset.ApplicationSetGetQuery{
|
||||
Name: appSetName, AppsetNamespace: appSetNs,
|
||||
})
|
||||
if getErr == nil {
|
||||
appEventCh := acdClient.WatchApplicationSetWithRetry(ctx, appSetQualifiedName, "")
|
||||
for appEvent := range appEventCh {
|
||||
if appEvent != nil && appEvent.Type == k8swatch.Deleted {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("applicationset '%s' deleted\n", appSetQualifiedName)
|
||||
} else {
|
||||
fmt.Println("The command to delete '" + appSetQualifiedName + "' was cancelled.")
|
||||
@@ -381,6 +411,7 @@ func NewApplicationSetDeleteCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVarP(&noPrompt, "yes", "y", false, "Turn off prompting to confirm cascaded deletion of Application resources")
|
||||
command.Flags().BoolVar(&wait, "wait", false, "Wait until deletion of the applicationset(s) completes")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -462,7 +493,7 @@ func printAppSetSummaryTable(appSet *arogappsetv1.ApplicationSet) {
|
||||
)
|
||||
if syncPolicy != nil && syncPolicy.IsAutomatedSyncEnabled() {
|
||||
syncPolicyStr = "Automated"
|
||||
if syncPolicy.Automated.Prune {
|
||||
if syncPolicy.Automated.GetPrune() {
|
||||
syncPolicyStr += " (Prune)"
|
||||
}
|
||||
} else {
|
||||
@@ -478,6 +509,31 @@ func printAppSetConditions(w io.Writer, appSet *arogappsetv1.ApplicationSet) {
|
||||
}
|
||||
}
|
||||
|
||||
func isApplicationSetResourcesUpToDate(appSet *arogappsetv1.ApplicationSet) bool {
|
||||
for _, c := range appSet.Status.Conditions {
|
||||
if c.Type == arogappsetv1.ApplicationSetConditionResourcesUpToDate {
|
||||
return c.Status == arogappsetv1.ApplicationSetConditionStatusTrue
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func waitForApplicationSetResourcesUpToDate(ctx context.Context, acdClient argocdclient.Client, appSetName string) error {
|
||||
appEventCh := acdClient.WatchApplicationSetWithRetry(ctx, appSetName, "")
|
||||
for appEvent := range appEventCh {
|
||||
if appEvent == nil {
|
||||
continue
|
||||
}
|
||||
if appEvent.Type == k8swatch.Deleted {
|
||||
return fmt.Errorf("ApplicationSet %q was deleted before reaching ResourcesUpToDate", appSetName)
|
||||
}
|
||||
if isApplicationSetResourcesUpToDate(&appEvent.ApplicationSet) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("watch stream closed for ApplicationSet %q before reaching ResourcesUpToDate", appSetName)
|
||||
}
|
||||
|
||||
func hasAppSetChanged(appReq, appRes *arogappsetv1.ApplicationSet, upsert bool) bool {
|
||||
// upsert==false, no change occurred from create command
|
||||
if !upsert {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -8,10 +10,94 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
// TestAppSetDeleteWaitFlow verifies that when --wait is used and the appset has
|
||||
// finalizers (still exists after Delete), the delete command watches for Deleted.
|
||||
func TestAppSetDeleteWaitFlow(t *testing.T) {
|
||||
appSetEventsCh := make(chan *v1alpha1.ApplicationSetWatchEvent, 1)
|
||||
go func() {
|
||||
defer close(appSetEventsCh)
|
||||
appSetEventsCh <- &v1alpha1.ApplicationSetWatchEvent{
|
||||
Type: watch.Added,
|
||||
ApplicationSet: v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{Name: "test-appset"}},
|
||||
}
|
||||
appSetEventsCh <- &v1alpha1.ApplicationSetWatchEvent{Type: watch.Deleted}
|
||||
}()
|
||||
|
||||
receivedDeleted := false
|
||||
for appEvent := range appSetEventsCh {
|
||||
if appEvent != nil && appEvent.Type == watch.Deleted {
|
||||
receivedDeleted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, receivedDeleted, "wait loop should receive Deleted event from watch")
|
||||
}
|
||||
|
||||
// TestAppSetCreateWaitFlow verifies that when --wait is used, the create command
|
||||
// waits for ResourcesUpToDate from the watch before completing.
|
||||
func TestAppSetCreateWaitFlow(t *testing.T) {
|
||||
fakeClient := &fakeAcdClient{}
|
||||
ctx := context.Background()
|
||||
|
||||
err := waitForApplicationSetResourcesUpToDate(ctx, fakeClient, "test-appset")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestAppSetCreateWaitDeletedError(t *testing.T) {
|
||||
appSetEventsCh := make(chan *v1alpha1.ApplicationSetWatchEvent, 1)
|
||||
go func() {
|
||||
defer close(appSetEventsCh)
|
||||
appSetEventsCh <- &v1alpha1.ApplicationSetWatchEvent{Type: watch.Deleted}
|
||||
}()
|
||||
|
||||
var err error
|
||||
for appEvent := range appSetEventsCh {
|
||||
if appEvent == nil {
|
||||
continue
|
||||
}
|
||||
if appEvent.Type == watch.Deleted {
|
||||
err = errors.New("ApplicationSet was deleted before reaching ResourcesUpToDate")
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "deleted before reaching ResourcesUpToDate")
|
||||
}
|
||||
|
||||
func TestIsApplicationSetResourcesUpToDate(t *testing.T) {
|
||||
t.Run("returns true when ResourcesUpToDate is True", func(t *testing.T) {
|
||||
appSet := &v1alpha1.ApplicationSet{
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Conditions: []v1alpha1.ApplicationSetCondition{
|
||||
{Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, Status: v1alpha1.ApplicationSetConditionStatusTrue},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.True(t, isApplicationSetResourcesUpToDate(appSet))
|
||||
})
|
||||
|
||||
t.Run("returns false when ResourcesUpToDate is False", func(t *testing.T) {
|
||||
appSet := &v1alpha1.ApplicationSet{
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Conditions: []v1alpha1.ApplicationSetCondition{
|
||||
{Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, Status: v1alpha1.ApplicationSetConditionStatusFalse},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.False(t, isApplicationSetResourcesUpToDate(appSet))
|
||||
})
|
||||
|
||||
t.Run("returns false when no conditions", func(t *testing.T) {
|
||||
appSet := &v1alpha1.ApplicationSet{}
|
||||
assert.False(t, isApplicationSetResourcesUpToDate(appSet))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrintApplicationSetNames(t *testing.T) {
|
||||
output, _ := captureOutput(func() error {
|
||||
appSet := &v1alpha1.ApplicationSet{
|
||||
@@ -174,7 +260,7 @@ func TestPrintAppSetSummaryTable(t *testing.T) {
|
||||
appSetTemplateSpecSyncPolicy := baseAppSet.DeepCopy()
|
||||
appSetTemplateSpecSyncPolicy.Spec.Template.Spec.SyncPolicy = &v1alpha1.SyncPolicy{
|
||||
Automated: &v1alpha1.SyncPolicyAutomated{
|
||||
SelfHeal: true,
|
||||
SelfHeal: new(true),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -184,7 +270,7 @@ func TestPrintAppSetSummaryTable(t *testing.T) {
|
||||
}
|
||||
appSetBothSyncPolicies.Spec.Template.Spec.SyncPolicy = &v1alpha1.SyncPolicy{
|
||||
Automated: &v1alpha1.SyncPolicyAutomated{
|
||||
SelfHeal: true,
|
||||
SelfHeal: new(true),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user