Compare commits

..

31 Commits

Author SHA1 Message Date
github-actions[bot]
fd6b7d5b3c Bump version to 3.3.0 on release-3.3 branch (#26206)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: reggie-k <19544836+reggie-k@users.noreply.github.com>
2026-02-02 09:24:38 +02:00
Alexandre Gaudreault
f4e479e3f0 fix: consider Replace/Force sync option on live resource annotations (#26110) (#26189)
Signed-off-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-30 09:52:45 -05:00
github-actions[bot]
436da4e7d8 Bump version to 3.3.0-rc4 on release-3.3 branch (#26119)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: crenshaw-dev <350466+crenshaw-dev@users.noreply.github.com>
2026-01-22 14:35:38 -05:00
Codey Jenkins
cd6a9aaf3f fix: cherry pick #25516 to release-3.3 (#26114)
Signed-off-by: Codey Jenkins <FourFifthsCode@users.noreply.github.com>
Signed-off-by: pbhatnagar-oss <pbhatifiwork@gmail.com>
Co-authored-by: pbhatnagar-oss <pbhatifiwork@gmail.com>
Co-authored-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>
2026-01-22 13:34:47 -05:00
John Soutar
ac071b57a1 chore(deps): update notifications-engine to fix GitHub PR comments nil panic (cherry-pick #26065 for 3.3) (#26075)
Signed-off-by: John Soutar <john@tella.com>
2026-01-21 09:27:32 +02:00
argo-cd-cherry-pick-bot[bot]
3d64c21206 fix: invalid error message on health check failure (#26040) (cherry-pick #26039 for 3.3) (#26063)
Signed-off-by: Eugene Doudine <eugene.doudine@octopus.com>
Co-authored-by: dudinea <eugene.doudine@octopus.com>
2026-01-20 13:43:26 +02:00
argo-cd-cherry-pick-bot[bot]
0fa47b11b2 fix(hydrator): pass destination.namespace to manifest rendering (#25478) (cherry-pick #25699 for 3.3) (#26019)
Signed-off-by: Sean Liao <sean@liao.dev>
Co-authored-by: Sean Liao <seankhliao@gmail.com>
2026-01-15 16:40:33 -05:00
argo-cd-cherry-pick-bot[bot]
48a9dcc23b fix(hydrator): empty links for failed operation (#25025) (cherry-pick #26014 for 3.3) (#26018)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2026-01-15 16:26:49 -05:00
argo-cd-cherry-pick-bot[bot]
b52a0750b2 fix(hydrator): .gitattributes include deeply nested files (#25870) (cherry-pick #26011 for 3.3) (#26013)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2026-01-15 15:39:37 -05:00
argo-cd-cherry-pick-bot[bot]
8fbb44c336 fix: close response body on error paths to prevent connection leak (cherry-pick #25824 for 3.3) (#26005)
Signed-off-by: chentiewen <tiewen.chen@aminer.cn>
Co-authored-by: QingHe <634008786@qq.com>
Co-authored-by: chentiewen <tiewen.chen@aminer.cn>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-15 14:35:13 +01:00
argo-cd-cherry-pick-bot[bot]
28e8472c69 fix: nil and empty ignoredifferences (cherry-pick #25980 for 3.3) (#26000)
Signed-off-by: Blake Pettersson <blake.pettersson@gmail.com>
Co-authored-by: Blake Pettersson <blake.pettersson@gmail.com>
2026-01-15 11:30:38 +01:00
argo-cd-cherry-pick-bot[bot]
a6472c8393 fix: allow docker dhi helm charts to be used (cherry-pick #25835 for 3.3) (#25964)
Signed-off-by: Blake Pettersson <blake.pettersson@gmail.com>
Co-authored-by: Blake Pettersson <blake.pettersson@gmail.com>
2026-01-13 20:00:40 +01:00
argo-cd-cherry-pick-bot[bot]
74de77a24c fix: Toggle automated.enabled to disable auto-sync for rollbacks (cherry-pick #25719 for 3.3) (#25943)
Signed-off-by: Daniel Moran <danxmoran@gmail.com>
Co-authored-by: Daniel Moran <danxmoran@gmail.com>
2026-01-12 18:06:27 -05:00
argo-cd-cherry-pick-bot[bot]
15568cb9d5 fix(appset): do not trigger reconciliation on appsets not part of allowed namespaces when updating a cluster secret (cherry-pick #25622 for 3.3) (#25909)
Signed-off-by: OpenGuidou <guillaume.doussin@gmail.com>
Co-authored-by: OpenGuidou <73480729+OpenGuidou@users.noreply.github.com>
2026-01-09 16:18:34 +01:00
argo-cd-cherry-pick-bot[bot]
32c32a67cb fix: Only show please update resource specification message when spec… (cherry-pick #25066 for 3.3) (#25894)
Signed-off-by: Josh Soref <jsoref@gmail.com>
Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2026-01-07 10:11:03 -05:00
Nitish Kumar
675f8cfe3f chore(cherry-pick-3.3): bump expr to v1.17.7 (#25888)
Signed-off-by: nitishfy <justnitish06@gmail.com>
2026-01-07 13:31:03 +02:00
argo-cd-cherry-pick-bot[bot]
9ae26e4e74 ci: test against k8s 1.34.2 (cherry-pick #25856 for 3.3) (#25858)
Signed-off-by: reggie-k <regina.voloshin@codefresh.io>
Co-authored-by: Regina Voloshin <regina.voloshin@codefresh.io>
2026-01-05 17:30:41 +02:00
argo-cd-cherry-pick-bot[bot]
369fb7577e chore(deps): update notifications-engine to v0.5.1-0.20251223091026-8c0c96d8d530 (cherry-pick #25785 for 3.3) (#25853)
Co-authored-by: Pasha Kostohrys <pasha.kostohrys@gmail.com>
Co-authored-by: pasha <pasha.k@fyxt.com>
2026-01-05 11:08:19 +02:00
argo-cd-cherry-pick-bot[bot]
efca5b9144 chore: bumps golang version everywhere to the latest 1.25.5 (cherry-pick #25716 for 3.3) (#25808)
Signed-off-by: Patroklos Papapetrou <ppapapetrou76@gmail.com>
Signed-off-by: reggie-k <regina.voloshin@codefresh.io>
Co-authored-by: Papapetrou Patroklos <1743100+ppapapetrou76@users.noreply.github.com>
Co-authored-by: reggie-k <regina.voloshin@codefresh.io>
2026-01-04 13:57:55 +02:00
argo-cd-cherry-pick-bot[bot]
2c3bc6f991 test: fix flaky create repository test by resyncing informers (cherry-pick #25706 for 3.3) (#25794)
Signed-off-by: reggie-k <regina.voloshin@codefresh.io>
Co-authored-by: Regina Voloshin <regina.voloshin@codefresh.io>
2025-12-24 17:21:21 +02:00
Regina Voloshin
8639b7be5e docs: added Helm 3.19.4 upgrade to the upgrade guide (#25776)
Signed-off-by: Regina Voloshin <regina.voloshin@codefresh.io>
2025-12-22 12:28:43 +02:00
argo-cd-cherry-pick-bot[bot]
5de1e6472d chore(deps): update to helm 3.19.4 due to cve : https://github.com/helm/helm/releases/tag/v3.19.4 (cherry-pick #25769 for 3.3) (#25774)
Signed-off-by: Bryan Stenson <bryan@siliconvortex.com>
Co-authored-by: Bryan Stenson <bryan.stenson@gmail.com>
2025-12-22 12:16:17 +02:00
github-actions[bot]
51b595b1ee Bump version to 3.3.0-rc3 on release-3.3 branch (#25743)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: crenshaw-dev <350466+crenshaw-dev@users.noreply.github.com>
2025-12-18 18:03:58 -05:00
argo-cd-cherry-pick-bot[bot]
fe0466de51 fix(hydrator): git fetch needs creds (#25727) (cherry-pick #25738 for 3.3) (#25742)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2025-12-18 17:39:16 -05:00
argo-cd-cherry-pick-bot[bot]
05b416906e fix(metrics): more consistent oci metrics (cherry-pick #25549 for 3.3) (#25728)
Signed-off-by: Blake Pettersson <blake.pettersson@gmail.com>
Co-authored-by: Blake Pettersson <blake.pettersson@gmail.com>
2025-12-18 07:13:13 +02:00
argo-cd-cherry-pick-bot[bot]
20604f1b21 fix(server): update resourceVersion on Terminate retry (cherry-pick #25650 for 3.3) (#25717)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2025-12-18 00:43:50 +01:00
github-actions[bot]
fd2d0adae9 Bump version to 3.3.0-rc2 on release-3.3 branch (#25712)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: crenshaw-dev <350466+crenshaw-dev@users.noreply.github.com>
2025-12-17 12:55:58 -05:00
argo-cd-cherry-pick-bot[bot]
708c63683c fix(hydrator): race when pushing notes (cherry-pick #25700 for 3.3) (#25709)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2025-12-17 11:30:44 -05:00
argo-cd-cherry-pick-bot[bot]
393cb97042 fix(hydrator): hydrated sha missing on no-ops (#25694) (cherry-pick #25695 for 3.3) (#25697)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2025-12-17 10:31:28 -05:00
argo-cd-cherry-pick-bot[bot]
99434863c9 chore(deps): update module k8s.io/kubernetes to v1.34.2 [security] (cherry-pick #25682 for 3.3) (#25683)
Signed-off-by: reggie-k <regina.voloshin@codefresh.io>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: reggie-k <regina.voloshin@codefresh.io>
2025-12-16 10:47:00 +02:00
github-actions[bot]
814db444c3 Bump version to 3.3.0-rc1 on release-3.3 branch (#25667)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: reggie-k <19544836+reggie-k@users.noreply.github.com>
2025-12-15 18:47:04 +02:00
715 changed files with 34356 additions and 79991 deletions

View File

@@ -41,7 +41,6 @@ Target GA date: ___. __, ____
Thanks to all the folks who spent their time contributing to this release in any way possible!
```
- [ ] Monitor support channels for issues, cherry-picking bugfixes and docs fixes as appropriate during the RC period (or delegate this task to an Approver and coordinate timing)
- [ ] After creating the RC, open a documentation PR for the next minor version using [this](../../docs/operator-manual/templates/minor_version_upgrade.md) template.
## GA Release Checklist

View File

@@ -4,7 +4,6 @@ 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",

View File

@@ -1,15 +1,15 @@
{
"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)!?(\\(.*\\))?!?:.*"
"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)!?(\\(.*\\))?!?:.*"
}
}
}

View File

@@ -11,7 +11,6 @@
| 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

View File

@@ -10,10 +10,10 @@ jobs:
contents: write # for peter-evans/create-pull-request to create branch
pull-requests: write # for peter-evans/create-pull-request to create a PR
name: Automatically update major version
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
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@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.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@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.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 }}"

View File

@@ -28,7 +28,7 @@ on:
jobs:
cherry-pick:
name: Cherry Pick to ${{ inputs.version_number }}
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- name: Generate a token
id: generate-token
@@ -38,7 +38,7 @@ jobs:
private-key: ${{ secrets.CHERRYPICK_APP_PRIVATE_KEY }}
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
token: ${{ steps.generate-token.outputs.token }}

View File

@@ -14,7 +14,7 @@ jobs:
(github.event.action == 'labeled' && startsWith(github.event.label.name, 'cherry-pick/')) ||
(github.event.action == 'closed' && contains(toJSON(github.event.pull_request.labels.*.name), 'cherry-pick/'))
)
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
outputs:
labels: ${{ steps.extract-labels.outputs.labels }}
steps:

View File

@@ -14,7 +14,7 @@ on:
env:
# Golang version to use across CI steps
# renovate: datasource=golang-version packageName=golang
GOLANG_VERSION: '1.26.0'
GOLANG_VERSION: '1.25.5'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -25,14 +25,14 @@ permissions:
jobs:
changes:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
outputs:
backend: ${{ steps.filter.outputs.backend_any_changed }}
frontend: ${{ steps.filter.outputs.frontend_any_changed }}
docs: ${{ steps.filter.outputs.docs_any_changed }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
id: filter
with:
# Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file
@@ -50,14 +50,14 @@ jobs:
check-go:
name: Ensure Go modules synchronicity
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs:
- changes
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Golang
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Download all Go modules
@@ -70,18 +70,18 @@ jobs:
build-go:
name: Build & cache Go code
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs:
- changes
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Golang
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Restore go build cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -97,27 +97,27 @@ jobs:
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
name: Lint Go code
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs:
- changes
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Golang
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.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/v2 versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
version: v2.9.0
# renovate: datasource=go packageName=github.com/golangci/golangci-lint versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
version: v2.5.0
args: --verbose
test-go:
name: Run unit tests for Go packages
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs:
- build-go
- changes
@@ -128,11 +128,11 @@ jobs:
- name: Create checkout directory
run: mkdir -p ~/go/src/github.com/argoproj
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- 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@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.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@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
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@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: test-results
path: test-results
@@ -181,7 +181,7 @@ jobs:
test-go-race:
name: Run unit tests with -race for Go packages
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs:
- build-go
- changes
@@ -192,11 +192,11 @@ jobs:
- name: Create checkout directory
run: mkdir -p ~/go/src/github.com/argoproj
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Create symlink in GOPATH
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
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@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.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@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
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@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: race-results
path: test-results/
@@ -245,14 +245,14 @@ jobs:
codegen:
name: Check changes to generated code
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.docs == 'true'}}
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs:
- changes
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Golang
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Create symlink in GOPATH
@@ -271,13 +271,13 @@ jobs:
# We need to vendor go modules for codegen yet
go mod download
go mod vendor -v
# generalizing repo name for forks: ${{ github.event.repository.name }}
working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
# generalizing repo name for forks: ${{ github.event.repository.name }}
working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
- name: Install toolchain for codegen
run: |
make install-codegen-tools-local
make install-go-tools-local
# generalizing repo name for forks: ${{ github.event.repository.name }}
# generalizing repo name for forks: ${{ github.event.repository.name }}
working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
# We install kustomize in the dist directory
- name: Add dist to PATH
@@ -302,20 +302,20 @@ jobs:
name: Build, test & lint UI code
# We run UI logic for backend changes so that we have a complete set of coverage documents to send to codecov.
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs:
- changes
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup NodeJS
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.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@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
@@ -338,9 +338,9 @@ jobs:
working-directory: ui/
shellcheck:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- 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
@@ -349,7 +349,7 @@ jobs:
analyze:
name: Process & analyze test artifacts
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs:
- test-go
- build-ui
@@ -357,15 +357,14 @@ jobs:
- test-e2e
env:
sonar_secret: ${{ secrets.SONAR_TOKEN }}
codecov_secret: ${{ secrets.CODECOV_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: Restore node dependency cache
id: cache-dependencies
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
@@ -373,12 +372,12 @@ jobs:
run: |
rm -rf ui/node_modules/argo-ui/node_modules
- name: Get e2e code coverage
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: e2e-code-coverage
path: e2e-code-coverage
- name: Get unit test code coverage
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: test-results
path: test-results
@@ -390,8 +389,6 @@ jobs:
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
- 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
@@ -399,14 +396,12 @@ jobs:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Upload test results to Codecov
# 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
if: github.ref == 'refs/heads/master' && github.event_name == 'push' && github.repository == 'argoproj/argo-cd'
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
with:
files: test-results/junit.xml
file: test-results/junit.xml
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
report_type: test_results
- name: Perform static code analysis using SonarCloud
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -416,26 +411,29 @@ jobs:
test-e2e:
name: Run end-to-end tests
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ${{ github.repository == 'argoproj/argo-cd' && 'oracle-vm-16cpu-64gb-x86-64' || 'ubuntu-24.04' }}
runs-on: ${{ github.repository == 'argoproj/argo-cd' && 'oracle-vm-16cpu-64gb-x86-64' || 'ubuntu-22.04' }}
strategy:
fail-fast: false
matrix:
# 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.35.0
latest: true
- version: v1.34.2
latest: false
latest: true
- version: v1.33.1
latest: false
- version: v1.32.1
latest: false
- version: v1.31.0
latest: false
needs:
- build-go
- changes
env:
ARGOCD_FAKE_IN_CLUSTER: 'true'
ARGOCD_SSH_DATA_PATH: '/tmp/argo-e2e/app/config/ssh'
ARGOCD_TLS_DATA_PATH: '/tmp/argo-e2e/app/config/tls'
ARGOCD_E2E_SSH_KNOWN_HOSTS: '../fixture/certs/ssh_known_hosts'
ARGOCD_E2E_K3S: 'true'
ARGOCD_IN_CI: 'true'
ARGOCD_E2E_APISERVER_PORT: '8088'
@@ -452,9 +450,9 @@ jobs:
swap-storage: false
tool-cache: false
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Golang
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Set GOPATH
@@ -476,7 +474,7 @@ jobs:
sudo chmod go-r $HOME/.kube/config
kubectl version
- name: Restore go build cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -502,7 +500,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.44.0
docker pull ghcr.io/dexidp/dex:v2.43.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
@@ -534,13 +532,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@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.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@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: e2e-server-k8s${{ matrix.k3s.version }}.log
path: /tmp/e2e-server.log
@@ -558,7 +556,7 @@ jobs:
needs:
- test-e2e
- changes
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- run: |
result="${{ needs.test-e2e.result }}"

View File

@@ -26,14 +26,14 @@ jobs:
if: github.repository == 'argoproj/argo-cd' || vars.enable_codeql
# CodeQL runs on ubuntu-latest and windows-latest
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
# 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@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: go.mod

View File

@@ -51,23 +51,23 @@ jobs:
contents: read
packages: write # Used to push images to `ghcr.io` if used.
id-token: write # Needed to create an OIDC token for keyless signing
runs-on: ubuntu-24.04
outputs:
runs-on: ubuntu-22.04
outputs:
image-digest: ${{ steps.image.outputs.digest }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
if: ${{ github.ref_type == 'tag'}}
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
if: ${{ github.ref_type != 'tag'}}
- name: Setup Golang
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ inputs.go-version }}
cache: false
@@ -76,7 +76,7 @@ jobs:
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- 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@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.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@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.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@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.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@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 #v6.19.2
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 #v6.18.0
with:
context: .
platforms: ${{ inputs.platforms }}

View File

@@ -20,7 +20,7 @@ jobs:
permissions:
contents: read
# Always run to calculate variables - other jobs check outputs
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
outputs:
image-tag: ${{ steps.image.outputs.tag}}
platforms: ${{ steps.platforms.outputs.platforms }}
@@ -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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- 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.26.0
go-version: 1.25.5
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.26.0
go-version: 1.25.5
platforms: ${{ needs.set-vars.outputs.platforms }}
push: true
secrets:
@@ -138,9 +138,9 @@ jobs:
contents: write # for git to push upgrade commit if not already deployed
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
env:
TOKEN: ${{ secrets.TOKEN }}

View File

@@ -20,7 +20,7 @@ jobs:
contents: write # for peter-evans/create-pull-request to create branch
pull-requests: write # for peter-evans/create-pull-request to create a PR
name: Automatically generate version and manifests on ${{ inputs.TARGET_BRANCH }}
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
env:
# Calculate image names with defaults, this will be used in the make manifests-local command
# to generate the correct image name in the manifests
@@ -29,7 +29,7 @@ jobs:
IMAGE_REPOSITORY: ${{ vars.IMAGE_REPOSITORY || 'argocd' }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
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@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
with:
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"

View File

@@ -21,7 +21,7 @@ jobs:
contents: read
pull-requests: read
name: Validate PR Title
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- uses: thehanimo/pr-title-checker@7fbfe05602bdd86f926d3fb3bccb6f3aed43bc70 # v1.4.3
with:

View File

@@ -11,7 +11,7 @@ permissions: {}
env:
# renovate: datasource=golang-version packageName=golang
GOLANG_VERSION: '1.26.0' # Note: go-version must also be set in job argocd-image.with.go-version
GOLANG_VERSION: '1.25.5' # 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.26.0
go-version: 1.25.5
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
push: true
secrets:
@@ -36,7 +36,7 @@ jobs:
setup-variables:
name: Setup Release Variables
if: github.repository == 'argoproj/argo-cd' || (github.repository_owner != 'argoproj' && vars.ENABLE_FORK_RELEASES == 'true' && vars.IMAGE_NAMESPACE && vars.IMAGE_NAMESPACE != 'argoproj')
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
outputs:
is_pre_release: ${{ steps.var.outputs.is_pre_release }}
is_latest_release: ${{ steps.var.outputs.is_latest_release }}
@@ -48,7 +48,7 @@ jobs:
allow_fork_release: ${{ steps.var.outputs.allow_fork_release }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
@@ -117,14 +117,14 @@ jobs:
permissions:
contents: write # used for uploading assets
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
env:
GORELEASER_MAKE_LATEST: ${{ needs.setup-variables.outputs.is_latest_release }}
outputs:
hashes: ${{ steps.hash.outputs.hashes }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
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@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ env.GOLANG_VERSION }}
cache: false
@@ -210,16 +210,16 @@ jobs:
outputs:
hashes: ${{ steps.sbom-hash.outputs.hashes }}
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Golang
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ env.GOLANG_VERSION }}
cache: false
@@ -295,12 +295,12 @@ jobs:
contents: write # Needed to push commit to update stable tag
pull-requests: write # Needed to create PR for VERSION update.
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
env:
TAG_STABLE: ${{ needs.setup-variables.outputs.is_latest_release }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
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@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
with:
commit-message: Bump version in master
title: 'chore: Bump version in master'

View File

@@ -9,7 +9,7 @@ permissions:
jobs:
renovate:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
if: github.repository == 'argoproj/argo-cd'
steps:
- name: Get token
@@ -20,10 +20,17 @@ jobs:
private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
# Some codegen commands require Go to be setup
- name: Setup Golang
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
# renovate: datasource=golang-version packageName=golang
go-version: 1.25.5
- name: Self-hosted Renovate
uses: renovatebot/github-action@d65ef9e20512193cc070238b49c3873a361cd50c #46.1.1
uses: renovatebot/github-action@5712c6a41dea6cdf32c72d92a763bd417e6606aa #44.0.5
with:
configurationFile: .github/configs/renovate-config.js
token: '${{ steps.get_token.outputs.token }}'

View File

@@ -17,7 +17,7 @@ permissions: read-all
jobs:
analysis:
name: Scorecards analysis
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
@@ -30,7 +30,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
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@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: SARIF file
path: results.sarif

View File

@@ -1,33 +0,0 @@
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

View File

@@ -14,10 +14,10 @@ jobs:
pull-requests: write
if: github.repository == 'argoproj/argo-cd'
name: Update Snyk report in the docs directory
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build reports

View File

@@ -22,7 +22,6 @@ linters:
- govet
- importas
- misspell
- modernize
- noctx
- perfsprint
- revive
@@ -122,13 +121,6 @@ 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

View File

@@ -79,10 +79,10 @@ packages:
github.com/argoproj/argo-cd/v3/util/workloadidentity:
interfaces:
TokenProvider: {}
github.com/argoproj/argo-cd/gitops-engine/pkg/cache:
github.com/argoproj/gitops-engine/pkg/cache:
interfaces:
ClusterCache: {}
github.com/argoproj/argo-cd/gitops-engine/pkg/diff:
github.com/argoproj/gitops-engine/pkg/diff:
interfaces:
ServerSideDryRunner: {}
github.com/microsoft/azure-devops-go-api/azuredevops/v7/git:

View File

@@ -1,10 +1,10 @@
ARG BASE_IMAGE=docker.io/library/ubuntu:25.10@sha256:4a9232cc47bf99defcc8860ef6222c99773330367fcecbf21ba2edb0b810a31e
ARG BASE_IMAGE=docker.io/library/ubuntu:25.04@sha256:27771fb7b40a58237c98e8d3e6b9ecdd9289cec69a857fccfb85ff36294dac20
####################################################################################################
# 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.26.0@sha256:c83e68f3ebb6943a2904fa66348867d108119890a2c6a2e6f07b38d0eb6c25c5 AS builder
FROM docker.io/library/golang:1.25.5@sha256:31c1e53dfc1cc2d269deec9c83f58729fa3c53dc9a576f6426109d1e319e9e9a AS builder
WORKDIR /tmp
@@ -16,6 +16,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
unzip \
fcgiwrap \
git \
git-lfs \
make \
wget \
gcc \
@@ -28,8 +29,7 @@ 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.sh git-lfs
INSTALL_PATH=/usr/local/bin ./install.sh kustomize
####################################################################################################
# Argo CD Base - used as the base for both the release and dev argocd images
@@ -50,10 +50,10 @@ RUN groupadd -g $ARGOCD_USER_ID argocd && \
chmod g=u /home/argocd && \
apt-get update && \
apt-get dist-upgrade -y && \
apt-get install --no-install-recommends -y \
git tini ca-certificates gpg gpg-agent tzdata connect-proxy openssh-client && \
apt-get install -y \
git git-lfs tini gpg tzdata connect-proxy && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY hack/gpg-wrapper.sh \
hack/git-verify-wrapper.sh \
@@ -61,7 +61,6 @@ 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
@@ -80,12 +79,6 @@ 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
@@ -110,7 +103,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.26.0@sha256:c83e68f3ebb6943a2904fa66348867d108119890a2c6a2e6f07b38d0eb6c25c5 AS argocd-build
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.25.5@sha256:31c1e53dfc1cc2d269deec9c83f58729fa3c53dc9a576f6426109d1e319e9e9a AS argocd-build
WORKDIR /go/src/github.com/argoproj/argo-cd

View File

@@ -1,4 +1,4 @@
FROM docker.io/library/golang:1.26.0@sha256:c83e68f3ebb6943a2904fa66348867d108119890a2c6a2e6f07b38d0eb6c25c5
FROM docker.io/library/golang:1.25.5@sha256:31c1e53dfc1cc2d269deec9c83f58729fa3c53dc9a576f6426109d1e319e9e9a
ENV DEBIAN_FRONTEND=noninteractive
@@ -11,6 +11,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
unzip \
fcgiwrap \
git \
git-lfs \
make \
wget \
gcc \
@@ -27,8 +28,7 @@ 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.sh git-lfs
INSTALL_PATH=/usr/local/bin ./install.sh kustomize
COPY hack/gpg-wrapper.sh \
hack/git-verify-wrapper.sh \

View File

@@ -1,43 +0,0 @@
# Argo CD Maintainers
This document lists the maintainers of the Argo CD project.
## Maintainers
| Maintainer | GitHub ID | Project Roles | Affiliation |
|---------------------------|---------------------------------------------------------|----------------------|-------------------------------------------------|
| Zach Aller | [zachaller](https://github.com/zachaller) | Reviewer | [Intuit](https://www.github.com/intuit/) |
| Leonardo Luz Almeida | [leoluz](https://github.com/leoluz) | Approver | [Intuit](https://www.github.com/intuit/) |
| Chetan Banavikalmutt | [chetan-rns](https://github.com/chetan-rns) | Reviewer | [Red Hat](https://redhat.com/) |
| Keith Chong | [keithchong](https://github.com/keithchong) | Approver | [Red Hat](https://redhat.com/) |
| Alex Collins | [alexec](https://github.com/alexec) | Approver | [Intuit](https://www.github.com/intuit/) |
| Michael Crenshaw | [crenshaw-dev](https://github.com/crenshaw-dev) | Lead | [Intuit](https://www.github.com/intuit/) |
| Soumya Ghosh Dastidar | [gdsoumya](https://github.com/gdsoumya) | Approver | [Akuity](https://akuity.io/) |
| Eugene Doudine | [dudinea](https://github.com/dudinea) | Reviewer | [Octopus Deploy](https://octopus.com/) |
| Jann Fischer | [jannfis](https://github.com/jannfis) | Approver | [Red Hat](https://redhat.com/) |
| 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-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) | 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/) |
| Linghao Su | [linghaoSu](https://github.com/linghaoSu) | Reviewer | [DaoCloud](https://daocloud.io) |
| Jesse Suen | [jessesuen](https://github.com/jessesuen) | Approver | [Akuity](https://akuity.io/) |
| Yuan Tang | [terrytangyuan](https://github.com/terrytangyuan) | Reviewer | [Red Hat](https://redhat.com/) |
| William Tam | [wtam2018](https://github.com/wtam2018) | Reviewer | [Red Hat](https://redhat.com/) |
| Ryan Umstead | [rumstead](https://github.com/rumstead) | Approver | [Black Rock](https://www.github.com/blackrock/) |
| 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/) |

View File

@@ -76,15 +76,15 @@ ARGOCD_E2E_REDIS_PORT?=6379
ARGOCD_E2E_DEX_PORT?=5556
ARGOCD_E2E_YARN_HOST?=localhost
ARGOCD_E2E_DISABLE_AUTH?=
ARGOCD_E2E_DIR?=/tmp/argo-e2e
ARGOCD_E2E_TEST_TIMEOUT?=90m
ARGOCD_E2E_RERUN_FAILS?=5
ARGOCD_IN_CI?=false
ARGOCD_TEST_E2E?=true
ARGOCD_BIN_MODE?=true
ARGOCD_LINT_GOGC?=20
# Depending on where we are (legacy or non-legacy pwd), we need to use
# different Docker volume mounts for our source tree
LEGACY_PATH=$(GOPATH)/src/github.com/argoproj/argo-cd
@@ -144,6 +144,7 @@ define run-in-test-client
-e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) \
-e GITHUB_TOKEN \
-e GOCACHE=/tmp/go-build-cache \
-e ARGOCD_LINT_GOGC=$(ARGOCD_LINT_GOGC) \
-v ${DOCKER_SRC_MOUNT} \
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
@@ -202,35 +203,18 @@ else
IMAGE_TAG?=latest
endif
# defaults for building images and manifests
ifeq (${DOCKER_PUSH},true)
ifndef IMAGE_NAMESPACE
$(error IMAGE_NAMESPACE must be set to push images (e.g. IMAGE_NAMESPACE=argoproj))
endif
endif
# Consruct prefix for docker image
# Note: keeping same logic as in hacks/update_manifests.sh
ifdef IMAGE_REGISTRY
ifdef IMAGE_NAMESPACE
IMAGE_PREFIX=${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/
else
$(error IMAGE_NAMESPACE must be set when IMAGE_REGISTRY is set (e.g. IMAGE_NAMESPACE=argoproj))
endif
else
ifdef IMAGE_NAMESPACE
# for backwards compatibility with the old way like IMAGE_NAMESPACE='quay.io/argoproj'
IMAGE_PREFIX=${IMAGE_NAMESPACE}/
else
# Neither namespace nor registry given - apply the default values
IMAGE_REGISTRY="quay.io"
IMAGE_NAMESPACE="argoproj"
IMAGE_PREFIX=${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/
endif
endif
ifndef IMAGE_REPOSITORY
IMAGE_REPOSITORY=argocd
ifndef IMAGE_REGISTRY
IMAGE_REGISTRY="quay.io"
endif
.PHONY: all
@@ -353,7 +337,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 -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/'
$(DOCKER) run -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/'
.PHONY: image
ifeq ($(DEV_IMAGE), true)
@@ -363,23 +347,23 @@ ifeq ($(DEV_IMAGE), true)
IMAGE_TAG="dev-$(shell git describe --always --dirty)"
image: build-ui
DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t argocd-base --target argocd-base .
GOOS=linux GOARCH=$(TARGET_ARCH:linux/%=%) GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
CGO_ENABLED=${CGO_FLAG} GOOS=linux GOARCH=amd64 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-server
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-application-controller
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-repo-server
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-cmp-server
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-dex
cp Dockerfile.dev dist
DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t $(IMAGE_PREFIX)$(IMAGE_REPOSITORY):$(IMAGE_TAG) -f dist/Dockerfile.dev dist
DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist
else
image:
DOCKER_BUILDKIT=1 $(DOCKER) build -t $(IMAGE_PREFIX)$(IMAGE_REPOSITORY):$(IMAGE_TAG) --platform=$(TARGET_ARCH) .
DOCKER_BUILDKIT=1 $(DOCKER) build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) --platform=$(TARGET_ARCH) .
endif
@if [ "$(DOCKER_PUSH)" = "true" ] ; then $(DOCKER) push $(IMAGE_PREFIX)$(IMAGE_REPOSITORY):$(IMAGE_TAG) ; fi
@if [ "$(DOCKER_PUSH)" = "true" ] ; then $(DOCKER) push $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) ; fi
.PHONY: armimage
armimage:
$(DOCKER) build -t $(IMAGE_PREFIX)(IMAGE_REPOSITORY):$(IMAGE_TAG)-arm .
$(DOCKER) build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm .
.PHONY: builder-image
builder-image:
@@ -411,7 +395,9 @@ lint: test-tools-image
.PHONY: lint-local
lint-local:
golangci-lint --version
golangci-lint run --fix --verbose
# NOTE: If you get a "Killed" OOM message, try reducing the value of GOGC
# See https://github.com/golangci/golangci-lint#memory-usage-of-golangci-lint
GOGC=$(ARGOCD_LINT_GOGC) GOMAXPROCS=2 golangci-lint run --fix --verbose
.PHONY: lint-ui
lint-ui: test-tools-image
@@ -443,24 +429,12 @@ 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)),)
test-local:
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
@@ -487,7 +461,7 @@ test-e2e:
test-e2e-local: cli-local
# NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system
export GO111MODULE=off
DIST_DIR=${DIST_DIR} RERUN_FAILS=$(ARGOCD_E2E_RERUN_FAILS) PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_CONFIG_DIR=$(HOME)/.config/argocd-e2e ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v -args -test.gocoverdir="$(PWD)/test-results"
DIST_DIR=${DIST_DIR} RERUN_FAILS=5 PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_CONFIG_DIR=$(HOME)/.config/argocd-e2e ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v -args -test.gocoverdir="$(PWD)/test-results"
# Spawns a shell in the test server container for debugging purposes
debug-test-server: test-tools-image
@@ -511,13 +485,13 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local
kubectl create ns argocd-e2e-external || true
kubectl create ns argocd-e2e-external-2 || true
kubectl config set-context --current --namespace=argocd-e2e
kustomize build test/manifests/base | kubectl apply --server-side --force-conflicts -f -
kustomize build test/manifests/base | kubectl apply --server-side -f -
kubectl apply -f https://raw.githubusercontent.com/open-cluster-management/api/a6845f2ebcb186ec26b832f60c988537a58f3859/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml
# Create GPG keys and source directories
if test -d $(ARGOCD_E2E_DIR)/app/config/gpg; then rm -rf $(ARGOCD_E2E_DIR)/app/config/gpg/*; fi
mkdir -p $(ARGOCD_E2E_DIR)/app/config/gpg/keys && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/gpg/keys
mkdir -p $(ARGOCD_E2E_DIR)/app/config/gpg/source && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/gpg/source
mkdir -p $(ARGOCD_E2E_DIR)/app/config/plugin && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/plugin
if test -d /tmp/argo-e2e/app/config/gpg; then rm -rf /tmp/argo-e2e/app/config/gpg/*; fi
mkdir -p /tmp/argo-e2e/app/config/gpg/keys && chmod 0700 /tmp/argo-e2e/app/config/gpg/keys
mkdir -p /tmp/argo-e2e/app/config/gpg/source && chmod 0700 /tmp/argo-e2e/app/config/gpg/source
mkdir -p /tmp/argo-e2e/app/config/plugin && chmod 0700 /tmp/argo-e2e/app/config/plugin
# create folders to hold go coverage results for each component
mkdir -p /tmp/coverage/app-controller
mkdir -p /tmp/coverage/api-server
@@ -526,15 +500,13 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local
mkdir -p /tmp/coverage/notification
mkdir -p /tmp/coverage/commit-server
# set paths for locally managed ssh known hosts and tls certs data
ARGOCD_E2E_DIR=$(ARGOCD_E2E_DIR) \
ARGOCD_SSH_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/ssh \
ARGOCD_TLS_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/tls \
ARGOCD_GPG_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/gpg/source \
ARGOCD_GNUPGHOME=$(ARGOCD_E2E_DIR)/app/config/gpg/keys \
ARGOCD_SSH_DATA_PATH=/tmp/argo-e2e/app/config/ssh \
ARGOCD_TLS_DATA_PATH=/tmp/argo-e2e/app/config/tls \
ARGOCD_GPG_DATA_PATH=/tmp/argo-e2e/app/config/gpg/source \
ARGOCD_GNUPGHOME=/tmp/argo-e2e/app/config/gpg/keys \
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
ARGOCD_PLUGINCONFIGFILEPATH=$(ARGOCD_E2E_DIR)/app/config/plugin \
ARGOCD_PLUGINSOCKFILEPATH=$(ARGOCD_E2E_DIR)/app/config/plugin \
ARGOCD_GIT_CONFIG=$(PWD)/test/e2e/fixture/gitconfig \
ARGOCD_PLUGINCONFIGFILEPATH=/tmp/argo-e2e/app/config/plugin \
ARGOCD_PLUGINSOCKFILEPATH=/tmp/argo-e2e/app/config/plugin \
ARGOCD_E2E_DISABLE_AUTH=false \
ARGOCD_ZJWT_FEATURE_FLAG=always \
ARGOCD_IN_CI=$(ARGOCD_IN_CI) \

View File

@@ -2,7 +2,7 @@ controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run
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"
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}"
repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "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}"
commit-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/commit-server} FORCE_LOG_COLORS=1 ARGOCD_BINARY_NAME=argocd-commit-server $COMMAND --loglevel debug --port ${ARGOCD_E2E_COMMITSERVER_PORT:-8086}"
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
@@ -11,4 +11,4 @@ helm-registry: test/fixture/testrepos/start-helm-registry.sh
oci-registry: test/fixture/testrepos/start-authenticated-helm-registry.sh
dev-mounter: [ "$ARGOCD_E2E_TEST" != "true" ] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source}
applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/applicationset-controller} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/notification} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}"
notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/notification} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}"

View File

@@ -13,7 +13,6 @@
[![Twitter Follow](https://img.shields.io/twitter/follow/argoproj?style=social)](https://twitter.com/argoproj)
[![Slack](https://img.shields.io/badge/slack-argoproj-brightgreen.svg?logo=slack)](https://argoproj.github.io/community/join-slack)
[![LinkedIn](https://img.shields.io/badge/LinkedIn-argoproj-blue.svg?logo=linkedin)](https://www.linkedin.com/company/argoproj/)
[![Bluesky](https://img.shields.io/badge/Bluesky-argoproj-blue.svg?style=social&logo=bluesky)](https://bsky.app/profile/argoproj.bsky.social)
# Argo CD - Declarative Continuous Delivery for Kubernetes

View File

@@ -3,9 +3,9 @@ header:
expiration-date: '2024-10-31T00:00:00.000Z' # One year from initial release.
last-updated: '2023-10-27'
last-reviewed: '2023-10-27'
commit-hash: 814db444c36503851dc3d45cf9c44394821ca1a4
commit-hash: 06ef059f9fc7cf9da2dfaef2a505ee1e3c693485
project-url: https://github.com/argoproj/argo-cd
project-release: v3.4.0
project-release: v3.3.0
changelog: https://github.com/argoproj/argo-cd/releases
license: https://github.com/argoproj/argo-cd/blob/master/LICENSE
project-lifecycle:

View File

@@ -60,7 +60,7 @@ k8s_yaml(kustomize('manifests/dev-tilt'))
# build dev image
docker_build_with_restart(
'quay.io/argoproj/argocd:latest',
'argocd',
context='.',
dockerfile='Dockerfile.tilt',
entrypoint=[

View File

@@ -178,7 +178,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [IFS](https://www.ifs.com)
1. [IITS-Consulting](https://iits-consulting.de)
1. [IllumiDesk](https://www.illumidesk.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/)
@@ -209,7 +208,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Kurly](https://www.kurly.com/)
1. [Kvist](https://kvistsolutions.com)
1. [Kyriba](https://www.kyriba.com/)
1. [Lattice](https://lattice.com)
1. [LeFigaro](https://www.lefigaro.fr/)
1. [Lely](https://www.lely.com/)
1. [LexisNexis](https://www.lexisnexis.com/)
@@ -247,7 +245,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Municipality of The Hague](https://www.denhaag.nl/)
1. [My Job Glasses](https://myjobglasses.com)
1. [Natura &Co](https://naturaeco.com/)
1. [Netease Cloud Music](https://music.163.com/)
1. [Nethopper](https://nethopper.io)
1. [New Relic](https://newrelic.com/)
1. [Nextbasket](https://nextbasket.com)
@@ -331,7 +328,6 @@ 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)

View File

@@ -1 +1 @@
3.4.0
3.3.0

View File

@@ -20,7 +20,6 @@ import (
"fmt"
"reflect"
"runtime/debug"
"slices"
"sort"
"strconv"
"strings"
@@ -48,7 +47,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/health"
"github.com/argoproj/argo-cd/v3/applicationset/controllers/template"
"github.com/argoproj/argo-cd/v3/applicationset/generators"
@@ -58,7 +57,6 @@ import (
"github.com/argoproj/argo-cd/v3/common"
applog "github.com/argoproj/argo-cd/v3/util/app/log"
"github.com/argoproj/argo-cd/v3/util/db"
"github.com/argoproj/argo-cd/v3/util/settings"
argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
argoutil "github.com/argoproj/argo-cd/v3/util/argo"
@@ -112,7 +110,6 @@ type ApplicationSetReconciler struct {
GlobalPreservedLabels []string
Metrics *metrics.ApplicationsetMetrics
MaxResourcesStatusCount int
ClusterInformer *settings.ClusterInformer
}
// +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets,verbs=get;list;watch;create;update;patch;delete
@@ -828,7 +825,7 @@ func (r *ApplicationSetReconciler) getCurrentApplications(ctx context.Context, a
// deleteInCluster will delete Applications that are currently on the cluster, but not in appList.
// The function must be called after all generators had been called and generated applications
func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
clusterList, err := utils.ListClusters(r.ClusterInformer)
clusterList, err := utils.ListClusters(ctx, r.KubeClientset, r.ArgoCDNamespace)
if err != nil {
return fmt.Errorf("error listing clusters: %w", err)
}
@@ -1047,10 +1044,12 @@ func labelMatchedExpression(logCtx *log.Entry, val string, matchExpression argov
// if operator == NotIn, default to true
valueMatched := matchExpression.Operator == "NotIn"
if slices.Contains(matchExpression.Values, val) {
// first "In" match returns true
// first "NotIn" match returns false
return matchExpression.Operator == "In"
for _, value := range matchExpression.Values {
if val == value {
// first "In" match returns true
// first "NotIn" match returns false
return matchExpression.Operator == "In"
}
}
return valueMatched
}

View File

@@ -1,7 +1,6 @@
package controllers
import (
"context"
"encoding/json"
"errors"
"fmt"
@@ -20,7 +19,6 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
kubefake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
crtclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -28,8 +26,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/event"
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
"github.com/argoproj/argo-cd/gitops-engine/pkg/sync/common"
"github.com/argoproj/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/sync/common"
"github.com/argoproj/argo-cd/v3/applicationset/generators"
"github.com/argoproj/argo-cd/v3/applicationset/generators/mocks"
@@ -1190,8 +1188,6 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
require.NoError(t, err)
err = corev1.AddToScheme(scheme)
require.NoError(t, err)
for _, c := range []struct {
// name is human-readable test name
@@ -1248,6 +1244,9 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
},
}
initObjs := []crtclient.Object{&app, &appSet}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-secret",
@@ -1265,12 +1264,8 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
},
}
initObjs := []crtclient.Object{&app, &appSet, secret}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
objects := append([]runtime.Object{}, secret)
kubeclientset := kubefake.NewClientset(objects...)
kubeclientset := kubefake.NewSimpleClientset(objects...)
metrics := appsetmetrics.NewFakeAppsetMetrics()
settingsMgr := settings.NewSettingsManager(t.Context(), kubeclientset, "argocd")
@@ -1278,11 +1273,6 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
_ = settingsMgr.ResyncInformers()
argodb := db.NewDB("argocd", settingsMgr, kubeclientset)
clusterInformer, err := settings.NewClusterInformer(kubeclientset, "namespace")
require.NoError(t, err)
defer startAndSyncInformer(t, clusterInformer)()
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
@@ -1291,7 +1281,7 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
Metrics: metrics,
ArgoDB: argodb,
}
clusterList, err := utils.ListClusters(clusterInformer)
clusterList, err := utils.ListClusters(t.Context(), kubeclientset, "namespace")
require.NoError(t, err)
appLog := log.WithFields(applog.GetAppLogFields(&app)).WithField("appSet", "")
@@ -1308,7 +1298,7 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
// App on the cluster should have the expected finalizers
assert.ElementsMatch(t, c.expectedFinalizers, retrievedApp.Finalizers)
// App object passed in as a parameter should have the expected finalizers
// App object passed in as a parameter should have the expected finaliers
assert.ElementsMatch(t, c.expectedFinalizers, appInputParam.Finalizers)
bytes, _ := json.MarshalIndent(retrievedApp, "", " ")
@@ -1321,8 +1311,6 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
require.NoError(t, err)
err = corev1.AddToScheme(scheme)
require.NoError(t, err)
for _, c := range []struct {
// name is human-readable test name
@@ -1415,6 +1403,9 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
},
}
initObjs := []crtclient.Object{&app, &appSet}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-secret",
@@ -1432,10 +1423,6 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
},
}
initObjs := []crtclient.Object{&app, &appSet, secret}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
kubeclientset := getDefaultTestClientSet(secret)
metrics := appsetmetrics.NewFakeAppsetMetrics()
@@ -1444,11 +1431,6 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
_ = settingsMgr.ResyncInformers()
argodb := db.NewDB("argocd", settingsMgr, kubeclientset)
clusterInformer, err := settings.NewClusterInformer(kubeclientset, "argocd")
require.NoError(t, err)
defer startAndSyncInformer(t, clusterInformer)()
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
@@ -1458,7 +1440,7 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
ArgoDB: argodb,
}
clusterList, err := utils.ListClusters(clusterInformer)
clusterList, err := utils.ListClusters(t.Context(), kubeclientset, "argocd")
require.NoError(t, err)
appLog := log.WithFields(applog.GetAppLogFields(&app)).WithField("appSet", "")
@@ -1763,8 +1745,6 @@ func TestDeleteInCluster(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
require.NoError(t, err)
err = corev1.AddToScheme(scheme)
require.NoError(t, err)
for _, c := range []struct {
// appSet is the application set on which the delete function is called
@@ -1877,19 +1857,12 @@ func TestDeleteInCluster(t *testing.T) {
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics()
kubeclientset := kubefake.NewClientset()
clusterInformer, err := settings.NewClusterInformer(kubeclientset, "namespace")
require.NoError(t, err)
defer startAndSyncInformer(t, clusterInformer)()
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)),
KubeClientset: kubeclientset,
Metrics: metrics,
ClusterInformer: clusterInformer,
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)),
KubeClientset: kubefake.NewSimpleClientset(),
Metrics: metrics,
}
err = r.deleteInCluster(t.Context(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps)
@@ -2220,8 +2193,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
require.NoError(t, err)
err = corev1.AddToScheme(scheme)
require.NoError(t, err)
project := v1alpha1.AppProject{
ObjectMeta: metav1.ObjectMeta{Name: "good-project", Namespace: "argocd"},
@@ -2265,9 +2236,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
argodb := db.NewDB("argocd", settings.NewSettingsManager(t.Context(), kubeclientset, "argocd"), kubeclientset)
clusterInformer, err := settings.NewClusterInformer(kubeclientset, "argocd")
require.NoError(t, err)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
@@ -2281,7 +2249,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
Policy: v1alpha1.ApplicationsSyncPolicySync,
ArgoCDNamespace: "argocd",
Metrics: metrics,
ClusterInformer: clusterInformer,
}
req := ctrl.Request{
@@ -2312,7 +2279,7 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
require.NoError(t, err)
kubeclientset := kubefake.NewClientset([]runtime.Object{}...)
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
someTime := &metav1.Time{Time: time.Now().Add(-5 * time.Minute)}
existingParameterGeneratedCondition := getParametersGeneratedCondition(true, "")
existingParameterGeneratedCondition.LastTransitionTime = someTime
@@ -2783,8 +2750,6 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
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"},
@@ -2841,14 +2806,10 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
kubeclientset := getDefaultTestClientSet(secret)
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet, &defaultProject, secret).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet, &defaultProject).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,
@@ -2864,7 +2825,6 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
Policy: v1alpha1.ApplicationsSyncPolicySync,
EnablePolicyOverride: allowPolicyOverride,
Metrics: metrics,
ClusterInformer: clusterInformer,
}
req := ctrl.Request{
@@ -2965,8 +2925,6 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
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"},
@@ -3023,16 +2981,11 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
kubeclientset := getDefaultTestClientSet(secret)
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet, &defaultProject, secret).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet, &defaultProject).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,
@@ -3047,7 +3000,6 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
Policy: v1alpha1.ApplicationsSyncPolicySync,
EnablePolicyOverride: allowPolicyOverride,
Metrics: metrics,
ClusterInformer: clusterInformer,
}
req := ctrl.Request{
@@ -3140,8 +3092,6 @@ func TestPolicies(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"},
@@ -3225,11 +3175,6 @@ func TestPolicies(t *testing.T) {
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,
@@ -3242,7 +3187,6 @@ func TestPolicies(t *testing.T) {
ArgoCDNamespace: "argocd",
KubeClientset: kubeclientset,
Policy: policy,
ClusterInformer: clusterInformer,
Metrics: metrics,
}
@@ -3252,27 +3196,27 @@ func TestPolicies(t *testing.T) {
Name: "name",
},
}
ctx := t.Context()
// Check if the application is created
res, err := r.Reconcile(ctx, req)
// Check if Application is created
res, err := r.Reconcile(t.Context(), req)
require.NoError(t, err)
assert.Equal(t, time.Duration(0), res.RequeueAfter)
var app v1alpha1.Application
err = r.Get(ctx, crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app)
err = r.Get(t.Context(), crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app)
require.NoError(t, err)
assert.Equal(t, "value", app.Annotations["key"])
// Check if the Application is updated
// Check if Application is updated
app.Annotations["key"] = "edited"
err = r.Update(ctx, &app)
err = r.Update(t.Context(), &app)
require.NoError(t, err)
res, err = r.Reconcile(ctx, req)
res, err = r.Reconcile(t.Context(), req)
require.NoError(t, err)
assert.Equal(t, time.Duration(0), res.RequeueAfter)
err = r.Get(ctx, crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app)
err = r.Get(t.Context(), crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app)
require.NoError(t, err)
if c.allowedUpdate {
@@ -3281,22 +3225,22 @@ func TestPolicies(t *testing.T) {
assert.Equal(t, "edited", app.Annotations["key"])
}
// Check if the Application is deleted
err = r.Get(ctx, crtclient.ObjectKey{Namespace: "argocd", Name: "name"}, &appSet)
// Check if Application is deleted
err = r.Get(t.Context(), crtclient.ObjectKey{Namespace: "argocd", Name: "name"}, &appSet)
require.NoError(t, err)
appSet.Spec.Generators[0] = v1alpha1.ApplicationSetGenerator{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{},
},
}
err = r.Update(ctx, &appSet)
err = r.Update(t.Context(), &appSet)
require.NoError(t, err)
res, err = r.Reconcile(ctx, req)
res, err = r.Reconcile(t.Context(), req)
require.NoError(t, err)
assert.Equal(t, time.Duration(0), res.RequeueAfter)
err = r.Get(ctx, crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app)
err = r.Get(t.Context(), crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app)
require.NoError(t, err)
if c.allowedDelete {
assert.NotNil(t, app.DeletionTimestamp)
@@ -3312,7 +3256,7 @@ func TestSetApplicationSetApplicationStatus(t *testing.T) {
err := v1alpha1.AddToScheme(scheme)
require.NoError(t, err)
kubeclientset := kubefake.NewClientset([]runtime.Object{}...)
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
for _, cc := range []struct {
name string
@@ -4189,7 +4133,7 @@ func TestBuildAppDependencyList(t *testing.T) {
},
} {
t.Run(cc.name, func(t *testing.T) {
kubeclientset := kubefake.NewClientset([]runtime.Object{}...)
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
argodb := db.NewDB("argocd", settings.NewSettingsManager(t.Context(), kubeclientset, "argocd"), kubeclientset)
@@ -4624,7 +4568,7 @@ func TestGetAppsToSync(t *testing.T) {
},
} {
t.Run(cc.name, func(t *testing.T) {
kubeclientset := kubefake.NewClientset([]runtime.Object{}...)
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
argodb := db.NewDB("argocd", settings.NewSettingsManager(t.Context(), kubeclientset, "argocd"), kubeclientset)
@@ -4652,7 +4596,7 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
newDefaultAppSet := func(stepsCount int, status []v1alpha1.ApplicationSetApplicationStatus) v1alpha1.ApplicationSet {
steps := []v1alpha1.ApplicationSetRolloutStep{}
for range stepsCount {
for i := 0; i < stepsCount; i++ {
steps = append(steps, v1alpha1.ApplicationSetRolloutStep{MatchExpressions: []v1alpha1.ApplicationMatchExpression{}})
}
return v1alpha1.ApplicationSet{
@@ -5311,7 +5255,7 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
},
} {
t.Run(cc.name, func(t *testing.T) {
kubeclientset := kubefake.NewClientset([]runtime.Object{}...)
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics()
@@ -6064,7 +6008,7 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) {
},
} {
t.Run(cc.name, func(t *testing.T) {
kubeclientset := kubefake.NewClientset([]runtime.Object{}...)
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics()
@@ -6337,7 +6281,7 @@ func TestUpdateResourceStatus(t *testing.T) {
},
} {
t.Run(cc.name, func(t *testing.T) {
kubeclientset := kubefake.NewClientset([]runtime.Object{}...)
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
client := fake.NewClientBuilder().WithScheme(scheme).WithStatusSubresource(&cc.appSet).WithObjects(&cc.appSet).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics()
@@ -6365,7 +6309,7 @@ func TestUpdateResourceStatus(t *testing.T) {
func generateNAppResourceStatuses(n int) []v1alpha1.ResourceStatus {
var r []v1alpha1.ResourceStatus
for i := range n {
for i := 0; i < n; i++ {
r = append(r, v1alpha1.ResourceStatus{
Name: "app" + strconv.Itoa(i),
Status: v1alpha1.SyncStatusCodeSynced,
@@ -6380,7 +6324,7 @@ func generateNAppResourceStatuses(n int) []v1alpha1.ResourceStatus {
func generateNHealthyApps(n int) []v1alpha1.Application {
var r []v1alpha1.Application
for i := range n {
for i := 0; i < n; i++ {
r = append(r, v1alpha1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "app" + strconv.Itoa(i),
@@ -6427,7 +6371,7 @@ func TestResourceStatusAreOrdered(t *testing.T) {
},
} {
t.Run(cc.name, func(t *testing.T) {
kubeclientset := kubefake.NewClientset([]runtime.Object{}...)
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
client := fake.NewClientBuilder().WithScheme(scheme).WithStatusSubresource(&cc.appSet).WithObjects(&cc.appSet).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics()
@@ -7632,7 +7576,7 @@ func TestReconcileProgressiveSyncDisabled(t *testing.T) {
err := v1alpha1.AddToScheme(scheme)
require.NoError(t, err)
kubeclientset := kubefake.NewClientset([]runtime.Object{}...)
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
for _, cc := range []struct {
name string
@@ -7705,14 +7649,3 @@ func TestReconcileProgressiveSyncDisabled(t *testing.T) {
})
}
}
func startAndSyncInformer(t *testing.T, informer cache.SharedIndexInformer) context.CancelFunc {
t.Helper()
ctx, cancel := context.WithCancel(t.Context())
go informer.Run(ctx.Done())
if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced) {
cancel()
t.Fatal("Timed out waiting for caches to sync")
}
return cancel
}

View File

@@ -19,7 +19,6 @@ import (
appsetmetrics "github.com/argoproj/argo-cd/v3/applicationset/metrics"
"github.com/argoproj/argo-cd/v3/applicationset/services/mocks"
argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util/settings"
)
func TestRequeueAfter(t *testing.T) {
@@ -58,17 +57,12 @@ func TestRequeueAfter(t *testing.T) {
}
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, duckType)
scmConfig := generators.NewSCMConfig("", []string{""}, true, true, nil, true)
clusterInformer, err := settings.NewClusterInformer(appClientset, "argocd")
require.NoError(t, err)
defer startAndSyncInformer(t, clusterInformer)()
terminalGenerators := map[string]generators.Generator{
"List": generators.NewListGenerator(),
"Clusters": generators.NewClusterGenerator(k8sClient, "argocd"),
"Clusters": generators.NewClusterGenerator(ctx, k8sClient, appClientset, "argocd"),
"Git": generators.NewGitGenerator(mockServer, "namespace"),
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), scmConfig),
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd", clusterInformer),
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
"PullRequest": generators.NewPullRequestGenerator(k8sClient, scmConfig),
}

View File

@@ -9,6 +9,7 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/argoproj/argo-cd/v3/applicationset/utils"
@@ -21,15 +22,19 @@ var _ Generator = (*ClusterGenerator)(nil)
// ClusterGenerator generates Applications for some or all clusters registered with ArgoCD.
type ClusterGenerator struct {
client.Client
ctx context.Context
clientset kubernetes.Interface
// namespace is the Argo CD namespace
namespace string
}
var render = &utils.Render{}
func NewClusterGenerator(c client.Client, namespace string) Generator {
func NewClusterGenerator(ctx context.Context, c client.Client, clientset kubernetes.Interface, namespace string) Generator {
g := &ClusterGenerator{
Client: c,
ctx: ctx,
clientset: clientset,
namespace: namespace,
}
return g
@@ -59,7 +64,16 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
// - Since local clusters do not have secrets, they do not have labels to match against
ignoreLocalClusters := len(appSetGenerator.Clusters.Selector.MatchExpressions) > 0 || len(appSetGenerator.Clusters.Selector.MatchLabels) > 0
// Get cluster secrets using the cached controller-runtime client
// ListCluster will include the local cluster in the list of clusters
clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace)
if err != nil {
return nil, fmt.Errorf("error listing clusters: %w", err)
}
if clustersFromArgoCD == nil {
return nil, nil
}
clusterSecrets, err := g.getSecretsByClusterName(logCtx, appSetGenerator)
if err != nil {
return nil, fmt.Errorf("error getting cluster secrets: %w", err)
@@ -68,14 +82,32 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
paramHolder := &paramHolder{isFlatMode: appSetGenerator.Clusters.FlatList}
logCtx.Debugf("Using flat mode = %t for cluster generator", paramHolder.isFlatMode)
// Convert map values to slice to check for an in-cluster secret
secretsList := make([]corev1.Secret, 0, len(clusterSecrets))
for _, secret := range clusterSecrets {
secretsList = append(secretsList, secret)
secretsFound := []corev1.Secret{}
for _, cluster := range clustersFromArgoCD {
// If there is a secret for this cluster, then it's a non-local cluster, so it will be
// handled by the next step.
if secretForCluster, exists := clusterSecrets[cluster.Name]; exists {
secretsFound = append(secretsFound, secretForCluster)
} else if !ignoreLocalClusters {
// If there is no secret for the cluster, it's the local cluster, so handle it here.
params := map[string]any{}
params["name"] = cluster.Name
params["nameNormalized"] = cluster.Name
params["server"] = cluster.Server
params["project"] = ""
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
if err != nil {
return nil, fmt.Errorf("error appending templated values for local cluster: %w", err)
}
paramHolder.append(params)
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
}
}
// For each matching cluster secret (non-local clusters only)
for _, cluster := range clusterSecrets {
for _, cluster := range secretsFound {
params := g.getClusterParameters(cluster, appSet)
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
@@ -87,23 +119,6 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
logCtx.WithField("cluster", cluster.Name).Debug("matched cluster secret")
}
// Add the in-cluster last if it doesn't have a secret, and we're not ignoring in-cluster
if !ignoreLocalClusters && !utils.SecretsContainInClusterCredentials(secretsList) {
params := map[string]any{}
params["name"] = argoappsetv1alpha1.KubernetesInClusterName
params["nameNormalized"] = argoappsetv1alpha1.KubernetesInClusterName
params["server"] = argoappsetv1alpha1.KubernetesInternalAPIServerAddr
params["project"] = ""
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
if err != nil {
return nil, fmt.Errorf("error appending templated values for local cluster: %w", err)
}
paramHolder.append(params)
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
}
return paramHolder.consolidate(), nil
}
@@ -171,7 +186,7 @@ func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerat
return nil, fmt.Errorf("error converting label selector: %w", err)
}
if err := g.List(context.Background(), clusterSecretList, client.InNamespace(g.namespace), client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
if err := g.List(context.Background(), clusterSecretList, client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
return nil, err
}
log.Debugf("clusters matching labels: %d", len(clusterSecretList.Items))

View File

@@ -7,9 +7,12 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
kubefake "k8s.io/client-go/kubernetes/fake"
"github.com/argoproj/argo-cd/v3/applicationset/utils"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
@@ -296,15 +299,23 @@ func TestGenerateParams(t *testing.T) {
},
}
// convert []client.Object to []runtime.Object, for use by kubefake package
runtimeClusters := []runtime.Object{}
for _, clientCluster := range clusters {
runtimeClusters = append(runtimeClusters, clientCluster)
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
cl := &possiblyErroringFakeCtrlRuntimeClient{
fakeClient,
testCase.clientError,
}
clusterGenerator := NewClusterGenerator(cl, "namespace")
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
@@ -325,25 +336,12 @@ func TestGenerateParams(t *testing.T) {
require.EqualError(t, err, testCase.expectedError.Error())
} else {
require.NoError(t, err)
assertEqualParamsFlat(t, testCase.expected, got, testCase.isFlatMode)
assert.ElementsMatch(t, testCase.expected, got)
}
})
}
}
func assertEqualParamsFlat(t *testing.T, expected, got []map[string]any, isFlatMode bool) {
t.Helper()
if isFlatMode && len(expected) == 1 && len(got) == 1 {
expectedClusters, ok1 := expected[0]["clusters"].([]map[string]any)
gotClusters, ok2 := got[0]["clusters"].([]map[string]any)
if ok1 && ok2 {
assert.ElementsMatch(t, expectedClusters, gotClusters)
return
}
}
assert.ElementsMatch(t, expected, got)
}
func TestGenerateParamsGoTemplate(t *testing.T) {
clusters := []client.Object{
&corev1.Secret{
@@ -839,15 +837,23 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
},
}
// convert []client.Object to []runtime.Object, for use by kubefake package
runtimeClusters := []runtime.Object{}
for _, clientCluster := range clusters {
runtimeClusters = append(runtimeClusters, clientCluster)
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
cl := &possiblyErroringFakeCtrlRuntimeClient{
fakeClient,
testCase.clientError,
}
clusterGenerator := NewClusterGenerator(cl, "namespace")
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
@@ -870,7 +876,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
require.EqualError(t, err, testCase.expectedError.Error())
} else {
require.NoError(t, err)
assertEqualParamsFlat(t, testCase.expected, got, testCase.isFlatMode)
assert.ElementsMatch(t, testCase.expected, got)
}
})
}

View File

@@ -19,27 +19,24 @@ import (
"github.com/argoproj/argo-cd/v3/applicationset/utils"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util/settings"
)
var _ Generator = (*DuckTypeGenerator)(nil)
// DuckTypeGenerator generates Applications for some or all clusters registered with ArgoCD.
type DuckTypeGenerator struct {
ctx context.Context
dynClient dynamic.Interface
clientset kubernetes.Interface
namespace string // namespace is the Argo CD namespace
clusterInformer *settings.ClusterInformer
ctx context.Context
dynClient dynamic.Interface
clientset kubernetes.Interface
namespace string // namespace is the Argo CD namespace
}
func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clientset kubernetes.Interface, namespace string, clusterInformer *settings.ClusterInformer) Generator {
func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clientset kubernetes.Interface, namespace string) Generator {
g := &DuckTypeGenerator{
ctx: ctx,
dynClient: dynClient,
clientset: clientset,
namespace: namespace,
clusterInformer: clusterInformer,
ctx: ctx,
dynClient: dynClient,
clientset: clientset,
namespace: namespace,
}
return g
}
@@ -68,7 +65,8 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
return nil, ErrEmptyAppSetGenerator
}
clustersFromArgoCD, err := utils.ListClusters(g.clusterInformer)
// ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters
clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace)
if err != nil {
return nil, fmt.Errorf("error listing clusters: %w", err)
}

View File

@@ -11,13 +11,11 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic/fake"
dynfake "k8s.io/client-go/dynamic/fake"
kubefake "k8s.io/client-go/kubernetes/fake"
"sigs.k8s.io/controller-runtime/pkg/client"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/test"
"github.com/argoproj/argo-cd/v3/util/settings"
)
const (
@@ -292,14 +290,9 @@ func TestGenerateParamsForDuckType(t *testing.T) {
Resource: "ducks",
}: "DuckList"}
fakeDynClient := fake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
clusterInformer, err := settings.NewClusterInformer(appClientset, "namespace")
require.NoError(t, err)
defer test.StartInformer(clusterInformer)()
duckTypeGenerator := NewDuckTypeGenerator(t.Context(), fakeDynClient, appClientset, "namespace", clusterInformer)
duckTypeGenerator := NewDuckTypeGenerator(t.Context(), fakeDynClient, appClientset, "namespace")
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
@@ -593,14 +586,9 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
Resource: "ducks",
}: "DuckList"}
fakeDynClient := fake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
clusterInformer, err := settings.NewClusterInformer(appClientset, "namespace")
require.NoError(t, err)
defer test.StartInformer(clusterInformer)()
duckTypeGenerator := NewDuckTypeGenerator(t.Context(), fakeDynClient, appClientset, "namespace", clusterInformer)
duckTypeGenerator := NewDuckTypeGenerator(t.Context(), fakeDynClient, appClientset, "namespace")
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{

View File

@@ -1,6 +1,7 @@
package generators
import (
"context"
"testing"
log "github.com/sirupsen/logrus"
@@ -15,6 +16,8 @@ import (
"github.com/stretchr/testify/mock"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
kubefake "k8s.io/client-go/kubernetes/fake"
crtclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
@@ -220,7 +223,7 @@ func TestTransForm(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
testGenerators := map[string]Generator{
"Clusters": getMockClusterGenerator(),
"Clusters": getMockClusterGenerator(t.Context()),
}
applicationSetInfo := argov1alpha1.ApplicationSet{
@@ -257,7 +260,7 @@ func emptyTemplate() argov1alpha1.ApplicationSetTemplate {
}
}
func getMockClusterGenerator() Generator {
func getMockClusterGenerator(ctx context.Context) Generator {
clusters := []crtclient.Object{
&corev1.Secret{
TypeMeta: metav1.TypeMeta{
@@ -332,8 +335,14 @@ func getMockClusterGenerator() Generator {
Type: corev1.SecretType("Opaque"),
},
}
runtimeClusters := []runtime.Object{}
for _, clientCluster := range clusters {
runtimeClusters = append(runtimeClusters, clientCluster)
}
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
return NewClusterGenerator(fakeClient, "namespace")
return NewClusterGenerator(ctx, fakeClient, appClientset, "namespace")
}
func getMockGitGenerator() Generator {
@@ -345,7 +354,7 @@ func getMockGitGenerator() Generator {
func TestGetRelevantGenerators(t *testing.T) {
testGenerators := map[string]Generator{
"Clusters": getMockClusterGenerator(),
"Clusters": getMockClusterGenerator(t.Context()),
"Git": getMockGitGenerator(),
}

View File

@@ -3,7 +3,6 @@ package generators
import (
"context"
"fmt"
"maps"
"path"
"sort"
"strconv"
@@ -169,7 +168,9 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al
if err != nil {
return nil, err
}
maps.Copy(fileContentMap, retrievedFiles)
for absPath, content := range retrievedFiles {
fileContentMap[absPath] = content
}
}
// Now remove files matching any exclude pattern
@@ -241,7 +242,9 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
params := map[string]any{}
if useGoTemplate {
maps.Copy(params, objectFound)
for k, v := range objectFound {
params[k] = v
}
paramPath := map[string]any{}
@@ -313,7 +316,7 @@ func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryG
appExclude = true
}
}
// Whenever there is a path with exclude: true it won't be included, even if it is included in a different path pattern
// Whenever there is a path with exclude: true it wont be included, even if it is included in a different path pattern
if appInclude && !appExclude {
res = append(res, appPath)
}

View File

@@ -8,6 +8,7 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kubefake "k8s.io/client-go/kubernetes/fake"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -623,6 +624,11 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
Type: corev1.SecretType("Opaque"),
},
}
// convert []client.Object to []runtime.Object, for use by kubefake package
runtimeClusters := []runtime.Object{}
for _, clientCluster := range clusters {
runtimeClusters = append(runtimeClusters, clientCluster)
}
for _, testCase := range testCases {
testCaseCopy := testCase // Since tests may run in parallel
@@ -631,12 +637,13 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
genMock := &generatorsMock.Generator{}
appSet := &v1alpha1.ApplicationSet{}
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
cl := &possiblyErroringFakeCtrlRuntimeClient{
fakeClient,
testCase.clientError,
}
clusterGenerator := NewClusterGenerator(cl, "namespace")
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
for _, g := range testCaseCopy.baseGenerators {
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
@@ -796,6 +803,11 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
Type: corev1.SecretType("Opaque"),
},
}
// convert []client.Object to []runtime.Object, for use by kubefake package
runtimeClusters := []runtime.Object{}
for _, clientCluster := range clusters {
runtimeClusters = append(runtimeClusters, clientCluster)
}
for _, testCase := range testCases {
testCaseCopy := testCase // Since tests may run in parallel
@@ -808,12 +820,13 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
},
}
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
cl := &possiblyErroringFakeCtrlRuntimeClient{
fakeClient,
testCase.clientError,
}
clusterGenerator := NewClusterGenerator(cl, "namespace")
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
for _, g := range testCaseCopy.baseGenerators {
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{

View File

@@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"maps"
"strconv"
"strings"
"time"
@@ -116,7 +115,9 @@ func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.App
params := map[string]any{}
if useGoTemplate {
maps.Copy(params, objectFound)
for k, v := range objectFound {
params[k] = v
}
} else {
flat, err := flatten.Flatten(objectFound, "", flatten.DotStyle)
if err != nil {

View File

@@ -96,9 +96,15 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
var shortSHALength int
var shortSHALength7 int
for _, pull := range pulls {
shortSHALength = min(len(pull.HeadSHA), 8)
shortSHALength = 8
if len(pull.HeadSHA) < 8 {
shortSHALength = len(pull.HeadSHA)
}
shortSHALength7 = min(len(pull.HeadSHA), 7)
shortSHALength7 = 7
if len(pull.HeadSHA) < 7 {
shortSHALength7 = len(pull.HeadSHA)
}
paramMap := map[string]any{
"number": strconv.FormatInt(pull.Number, 10),

View File

@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"net/http"
"slices"
"strings"
"time"
@@ -106,8 +105,10 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, g
return nil
}
if slices.Contains(allowedScmProviders, url) {
return nil
for _, allowedScmProvider := range allowedScmProviders {
if url == allowedScmProvider {
return nil
}
}
log.WithFields(log.Fields{
@@ -243,9 +244,15 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
var shortSHALength int
var shortSHALength7 int
for _, repo := range repos {
shortSHALength = min(len(repo.SHA), 8)
shortSHALength = 8
if len(repo.SHA) < 8 {
shortSHALength = len(repo.SHA)
}
shortSHALength7 = min(len(repo.SHA), 7)
shortSHALength7 = 7
if len(repo.SHA) < 7 {
shortSHALength7 = len(repo.SHA)
}
params := map[string]any{
"organization": repo.Organization,

View File

@@ -8,16 +8,15 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/argoproj/argo-cd/v3/applicationset/services"
"github.com/argoproj/argo-cd/v3/util/settings"
)
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, controllerNamespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig, clusterInformer *settings.ClusterInformer) map[string]Generator {
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, controllerNamespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
terminalGenerators := map[string]Generator{
"List": NewListGenerator(),
"Clusters": NewClusterGenerator(c, controllerNamespace),
"Clusters": NewClusterGenerator(ctx, c, k8sClient, controllerNamespace),
"Git": NewGitGenerator(argoCDService, controllerNamespace),
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, controllerNamespace, clusterInformer),
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, controllerNamespace),
"PullRequest": NewPullRequestGenerator(c, scmConfig),
"Plugin": NewPluginGenerator(c, controllerNamespace),
}

View File

@@ -2,7 +2,6 @@ package generators
import (
"fmt"
"maps"
)
func appendTemplatedValues(values map[string]string, params map[string]any, useGoTemplate bool, goTemplateOptions []string) error {
@@ -27,7 +26,9 @@ func appendTemplatedValues(values map[string]string, params map[string]any, useG
}
}
maps.Copy(params, tmp)
for key, value := range tmp {
params[key] = value
}
return nil
}

View File

@@ -151,9 +151,9 @@ spec:
func newFakeAppsets(fakeAppsetYAML string) []argoappv1.ApplicationSet {
var results []argoappv1.ApplicationSet
appsetRawYamls := strings.SplitSeq(fakeAppsetYAML, "---")
appsetRawYamls := strings.Split(fakeAppsetYAML, "---")
for appsetRawYaml := range appsetRawYamls {
for _, appsetRawYaml := range appsetRawYamls {
var appset argoappv1.ApplicationSet
err := yaml.Unmarshal([]byte(appsetRawYaml), &appset)
if err != nil {

View File

@@ -3,7 +3,6 @@ package pull_request
import (
"context"
"fmt"
"slices"
"strings"
"github.com/microsoft/azure-devops-go-api/azuredevops/v7"
@@ -137,7 +136,13 @@ 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 := slices.Contains(gotLabels, expected)
found := false
for _, got := range gotLabels {
if expected == got {
found = true
break
}
}
if !found {
return false
}

View File

@@ -268,6 +268,7 @@ 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)

View File

@@ -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 := range gotLabels {
for i := 0; i < len(gotLabels); i++ {
gotLabelNamesMap[gotLabels[i].Name] = true
}
for _, expected := range expectedLabels {

View File

@@ -158,6 +158,7 @@ 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)

View File

@@ -445,6 +445,7 @@ 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)

View File

@@ -1301,6 +1301,7 @@ 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)

View File

@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"regexp"
"slices"
"strings"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
@@ -59,7 +58,13 @@ func matchFilter(ctx context.Context, provider SCMProviderService, repo *Reposit
}
if filter.LabelMatch != nil {
found := slices.ContainsFunc(repo.Labels, filter.LabelMatch.MatchString)
found := false
for _, label := range repo.Labels {
if filter.LabelMatch.MatchString(label) {
found = true
break
}
}
if !found {
return false, nil
}

View File

@@ -1,12 +1,15 @@
package utils
import (
"context"
"fmt"
corev1 "k8s.io/api/core/v1"
"github.com/argoproj/argo-cd/v3/common"
appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util/settings"
"github.com/argoproj/argo-cd/v3/util/db"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
// ClusterSpecifier contains only the name and server URL of a cluster. We use this struct to avoid partially-populating
@@ -16,44 +19,42 @@ type ClusterSpecifier struct {
Server string
}
// SecretsContainInClusterCredentials checks if any of the provided secrets represent the in-cluster configuration.
func SecretsContainInClusterCredentials(secrets []corev1.Secret) bool {
for _, secret := range secrets {
if string(secret.Data["server"]) == appv1.KubernetesInternalAPIServerAddr {
return true
}
}
return false
}
// ListClusters returns a list of cluster specifiers using the ClusterInformer.
func ListClusters(clusterInformer *settings.ClusterInformer) ([]ClusterSpecifier, error) {
clusters, err := clusterInformer.ListClusters()
func ListClusters(ctx context.Context, clientset kubernetes.Interface, namespace string) ([]ClusterSpecifier, error) {
clusterSecretsList, err := clientset.CoreV1().Secrets(namespace).List(ctx,
metav1.ListOptions{LabelSelector: common.LabelKeySecretType + "=" + common.LabelValueSecretTypeCluster})
if err != nil {
return nil, fmt.Errorf("error listing clusters: %w", err)
return nil, err
}
// len of clusters +1 for the in cluster secret
clusterList := make([]ClusterSpecifier, 0, len(clusters)+1)
hasInCluster := false
for _, cluster := range clusters {
clusterList = append(clusterList, ClusterSpecifier{
if clusterSecretsList == nil {
return nil, nil
}
clusterSecrets := clusterSecretsList.Items
clusterList := make([]ClusterSpecifier, len(clusterSecrets))
hasInClusterCredentials := false
for i, clusterSecret := range clusterSecrets {
cluster, err := db.SecretToCluster(&clusterSecret)
if err != nil || cluster == nil {
return nil, fmt.Errorf("unable to convert cluster secret to cluster object '%s': %w", clusterSecret.Name, err)
}
clusterList[i] = ClusterSpecifier{
Name: cluster.Name,
Server: cluster.Server,
})
}
if cluster.Server == appv1.KubernetesInternalAPIServerAddr {
hasInCluster = true
hasInClusterCredentials = true
}
}
if !hasInCluster {
if !hasInClusterCredentials {
// There was no secret for the in-cluster config, so we add it here. We don't fully-populate the Cluster struct,
// since only the name and server fields are used by the generator.
clusterList = append(clusterList, ClusterSpecifier{
Name: appv1.KubernetesInClusterName,
Name: "in-cluster",
Server: appv1.KubernetesInternalAPIServerAddr,
})
}
return clusterList, nil
}

View File

@@ -216,6 +216,7 @@ spec:
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
foundApp := v1alpha1.Application{TypeMeta: appMeta}

View File

@@ -2,7 +2,6 @@ package utils
import (
"fmt"
"slices"
"sort"
"strconv"
"strings"
@@ -208,7 +207,12 @@ type Requirement struct {
}
func (r *Requirement) hasValue(value string) bool {
return slices.Contains(r.strValues, value)
for i := range r.strValues {
if r.strValues[i] == value {
return true
}
}
return false
}
func (r *Requirement) Matches(ls labels.Labels) bool {

View File

@@ -388,7 +388,8 @@ func invalidGenerators(applicationSetInfo *argoappsv1.ApplicationSet) (bool, map
for index, generator := range applicationSetInfo.Spec.Generators {
v := reflect.Indirect(reflect.ValueOf(generator))
found := false
for _, field := range v.Fields() {
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
if !field.CanInterface() {
continue
}

View File

@@ -107,8 +107,10 @@ func NewWebhookHandler(webhookParallelism int, argocdSettingsMgr *argosettings.S
func (h *WebhookHandler) startWorkerPool(webhookParallelism int) {
compLog := log.WithField("component", "applicationset-webhook")
for range webhookParallelism {
h.Go(func() {
for i := 0; i < webhookParallelism; i++ {
h.Add(1)
go func() {
defer h.Done()
for {
payload, ok := <-h.queue
if !ok {
@@ -116,7 +118,7 @@ func (h *WebhookHandler) startWorkerPool(webhookParallelism int) {
}
guard.RecoverAndLog(func() { h.HandleEvent(payload) }, compLog, panicMsgAppSet)
}
})
}()
}
}

View File

@@ -609,7 +609,7 @@ func fakeAppWithMatrixAndNestedGitGenerator(name, namespace, repo string) *v1alp
},
{
Matrix: &apiextensionsv1.JSON{
Raw: fmt.Appendf(nil, `{
Raw: []byte(fmt.Sprintf(`{
"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: fmt.Appendf(nil, `{
Raw: []byte(fmt.Sprintf(`{
"MergeKeys": ["server"],
"Generators": [
{
@@ -719,7 +719,7 @@ func fakeAppWithMergeAndNestedGitGenerator(name, namespace, repo string) *v1alph
}
}
]
}`, repo),
}`, repo)),
},
},
},

43
assets/swagger.json generated
View File

@@ -2265,44 +2265,6 @@
}
}
},
"/api/v1/applicationsets/{name}/events": {
"get": {
"tags": [
"ApplicationSetService"
],
"summary": "ListResourceEvents returns a list of event resources",
"operationId": "ApplicationSetService_ListResourceEvents",
"parameters": [
{
"type": "string",
"description": "the applicationsets's name",
"name": "name",
"in": "path",
"required": true
},
{
"type": "string",
"description": "The application set namespace. Default empty is argocd control plane namespace.",
"name": "appsetNamespace",
"in": "query"
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1EventList"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
}
}
},
"/api/v1/applicationsets/{name}/resource-tree": {
"get": {
"tags": [
@@ -7299,7 +7261,7 @@
"type": "object",
"properties": {
"applyNestedSelectors": {
"description": "ApplyNestedSelectors enables selectors defined within the generators of two level-nested matrix or merge generators.\n\nDeprecated: This field is ignored, and the behavior is always enabled. The field will be removed in a future\nversion of the ApplicationSet CRD.",
"description": "ApplyNestedSelectors enables selectors defined within the generators of two level-nested matrix or merge generators\nDeprecated: This field is ignored, and the behavior is always enabled. The field will be removed in a future\nversion of the ApplicationSet CRD.",
"type": "boolean"
},
"generators": {
@@ -7357,9 +7319,6 @@
"$ref": "#/definitions/v1alpha1ApplicationSetCondition"
}
},
"health": {
"$ref": "#/definitions/v1alpha1HealthStatus"
},
"resources": {
"description": "Resources is a list of Applications resources managed by this application set.",
"type": "array",

View File

@@ -41,6 +41,8 @@ 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
@@ -97,7 +99,7 @@ func NewCommand() *cobra.Command {
hydratorEnabled bool
)
command := cobra.Command{
Use: common.CommandApplicationController,
Use: cliName,
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,

View File

@@ -32,7 +32,6 @@ import (
"k8s.io/client-go/kubernetes"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -49,6 +48,10 @@ import (
var gitSubmoduleEnabled = env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true)
const (
cliName = common.ApplicationSetController
)
func NewCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
@@ -83,7 +86,7 @@ func NewCommand() *cobra.Command {
_ = clientgoscheme.AddToScheme(scheme)
_ = appv1alpha1.AddToScheme(scheme)
command := cobra.Command{
Use: common.CommandApplicationSetController,
Use: cliName,
Short: "Starts Argo CD ApplicationSet controller",
DisableAutoGenTag: true,
RunE: func(c *cobra.Command, _ []string) error {
@@ -190,18 +193,6 @@ func NewCommand() *cobra.Command {
argoSettingsMgr := argosettings.NewSettingsManager(ctx, k8sClient, namespace)
argoCDDB := db.NewDB(namespace, argoSettingsMgr, k8sClient)
clusterInformer, err := argosettings.NewClusterInformer(k8sClient, namespace)
if err != nil {
log.Error(err, "unable to create cluster informer")
os.Exit(1)
}
go clusterInformer.Run(ctx.Done())
if !cache.WaitForCacheSync(ctx.Done(), clusterInformer.HasSynced) {
log.Error("Timed out waiting for cluster cache to sync")
os.Exit(1)
}
scmConfig := generators.NewSCMConfig(scmRootCAPath, allowedScmProviders, enableScmProviders, enableGitHubAPIMetrics, github_app.NewAuthCredentials(argoCDDB.(db.RepoCredsDB)), tokenRefStrictMode)
tlsConfig := apiclient.TLSConfiguration{
@@ -221,7 +212,7 @@ func NewCommand() *cobra.Command {
repoClientset := apiclient.NewRepoServerClientset(argocdRepoServer, repoServerTimeoutSeconds, tlsConfig)
argoCDService := services.NewArgoCDService(argoCDDB, gitSubmoduleEnabled, repoClientset, enableNewGitFileGlobbing)
topLevelGenerators := generators.GetGenerators(ctx, mgr.GetClient(), k8sClient, namespace, argoCDService, dynamicClient, scmConfig, clusterInformer)
topLevelGenerators := generators.GetGenerators(ctx, mgr.GetClient(), k8sClient, namespace, argoCDService, dynamicClient, scmConfig)
// start a webhook server that listens to incoming webhook payloads
webhookHandler, err := webhook.NewWebhookHandler(webhookParallelism, argoSettingsMgr, mgr.GetClient(), topLevelGenerators)
@@ -257,7 +248,6 @@ func NewCommand() *cobra.Command {
GlobalPreservedLabels: globalPreservedLabels,
Metrics: &metrics,
MaxResourcesStatusCount: maxResourcesStatusCount,
ClusterInformer: clusterInformer,
}).SetupWithManager(mgr, enableProgressiveSyncs, maxConcurrentReconciliations); err != nil {
log.Error(err, "unable to create controller", "controller", "ApplicationSet")
os.Exit(1)
@@ -302,7 +292,7 @@ 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", 5000, 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", 0, 0, math.MaxInt), "Max number of resources stored in appset status.")
return &command
}

View File

@@ -18,6 +18,11 @@ 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
@@ -27,7 +32,7 @@ func NewCommand() *cobra.Command {
otlpAttrs []string
)
command := cobra.Command{
Use: common.CommandCMPServer,
Use: cliName,
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,

View File

@@ -35,7 +35,7 @@ func NewCommand() *cobra.Command {
metricsHost string
)
command := &cobra.Command{
Use: common.CommandCommitServer,
Use: "argocd-commit-server",
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,11 +91,13 @@ func NewCommand() *cobra.Command {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
wg := sync.WaitGroup{}
wg.Go(func() {
wg.Add(1)
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)

View File

@@ -25,9 +25,13 @@ import (
"github.com/argoproj/argo-cd/v3/util/tls"
)
const (
cliName = "argocd-dex"
)
func NewCommand() *cobra.Command {
command := &cobra.Command{
Use: common.CommandDex,
Use: cliName,
Short: "argocd-dex tools used by Argo CD",
Long: "argocd-dex has internal utility tools used by Argo CD",
DisableAutoGenTag: true,

View File

@@ -9,16 +9,20 @@ 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: common.CommandGitAskPass,
Use: cliName,
Short: "Argo CD git credential helper",
DisableAutoGenTag: true,
Run: func(c *cobra.Command, _ []string) {

View File

@@ -2,13 +2,15 @@ package commands
import (
"github.com/spf13/cobra"
)
"github.com/argoproj/argo-cd/v3/common"
const (
cliName = "argocd-k8s-auth"
)
func NewCommand() *cobra.Command {
command := &cobra.Command{
Use: common.CommandK8sAuth,
Use: cliName,
Short: "argocd-k8s-auth a set of commands to generate k8s auth token",
DisableAutoGenTag: true,
Run: func(c *cobra.Command, args []string) {

View File

@@ -6,14 +6,12 @@ import (
"encoding/json"
"fmt"
"os"
"strconv"
"time"
"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/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/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientauthv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
@@ -60,13 +58,13 @@ func newAWSCommand() *cobra.Command {
return command
}
type getSignedRequestFunc func(ctx context.Context, clusterName, roleARN string, profile string) (string, error)
type getSignedRequestFunc func(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(ctx, clusterName, roleARN, profile)
signed, err := fn(clusterName, roleARN, profile)
if err == nil {
return signed, nil
}
@@ -78,53 +76,25 @@ func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Durat
}
}
func getSignedRequest(ctx context.Context, clusterName, roleARN string, profile string) (string, error) {
cfg, err := loadAWSConfig(ctx, profile)
func getSignedRequest(clusterName, roleARN string, profile string) (string, error) {
sess, err := session.NewSessionWithOptions(session.Options{
Profile: profile,
})
if err != nil {
return "", err
return "", fmt.Errorf("error creating new AWS session: %w", err)
}
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)
stsAPI := sts.New(sess)
if roleARN != "" {
appCreds := stscreds.NewAssumeRoleProvider(client, roleARN)
cfg.Credentials = aws.NewCredentialsCache(appCreds)
client = sts.NewFromConfig(cfg)
creds := stscreds.NewCredentials(sess, roleARN)
stsAPI = sts.New(sess, &aws.Config{Credentials: creds})
}
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)))
})
})
request, _ := stsAPI.GetCallerIdentityRequest(&sts.GetCallerIdentityInput{})
request.HTTPRequest.Header.Add(clusterIDHeader, clusterName)
signed, err := request.Presign(requestPresignParam)
if err != nil {
return "", fmt.Errorf("error presigning AWS request: %w", err)
}
return presigned.URL, nil
return signed, nil
}
func formatJSON(token string, expiration time.Time) string {

View File

@@ -1,60 +1,14 @@
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()
@@ -118,7 +72,7 @@ type signedRequestMock struct {
returnFunc func(m *signedRequestMock) (string, error)
}
func (m *signedRequestMock) getSignedRequestMock(_ context.Context, _, _ string, _ string) (string, error) {
func (m *signedRequestMock) getSignedRequestMock(_, _ string, _ string) (string, error) {
m.getSignedRequestCalls++
return m.returnFunc(m)
}

View File

@@ -52,7 +52,7 @@ func NewCommand() *cobra.Command {
selfServiceNotificationEnabled bool
)
command := cobra.Command{
Use: common.CommandNotifications,
Use: "controller",
Short: "Starts Argo CD Notifications controller",
RunE: func(_ *cobra.Command, _ []string) error {
ctx, cancel := context.WithCancel(context.Background())
@@ -150,11 +150,13 @@ func NewCommand() *cobra.Command {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
wg := sync.WaitGroup{}
wg.Go(func() {
wg.Add(1)
go func() {
defer wg.Done()
s := <-sigCh
log.Printf("got signal %v, attempting graceful shutdown", s)
cancel()
})
}()
go ctrl.Run(ctx, processorsCount)
<-ctx.Done()

View File

@@ -34,18 +34,21 @@ 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 {
@@ -80,7 +83,7 @@ func NewCommand() *cobra.Command {
enableBuiltinGitConfig bool
)
command := cobra.Command{
Use: common.CommandRepoServer,
Use: cliName,
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,
@@ -154,7 +157,6 @@ func NewCommand() *cobra.Command {
CMPUseManifestGeneratePaths: cmpUseManifestGeneratePaths,
OCIMediaTypes: ociMediaTypes,
EnableBuiltinGitConfig: enableBuiltinGitConfig,
HelmUserAgent: helmUserAgent,
}, askPassServer)
errors.CheckError(err)
@@ -173,8 +175,7 @@ func NewCommand() *cobra.Command {
listener, err := lc.Listen(ctx, "tcp", fmt.Sprintf("%s:%d", listenHost, listenPort))
errors.CheckError(err)
mux := http.NewServeMux()
healthz.ServeHealthCheck(mux, func(r *http.Request) error {
healthz.ServeHealthCheck(http.DefaultServeMux, 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
@@ -196,9 +197,8 @@ func NewCommand() *cobra.Command {
}
return nil
})
mux.Handle("/metrics", metricsServer.GetHandler())
profile.RegisterProfiler(mux)
go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf("%s:%d", metricsHost, metricsPort), mux)) }()
http.Handle("/metrics", metricsServer.GetHandler())
go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf("%s:%d", metricsHost, metricsPort), nil)) }()
go func() { errors.CheckError(askPassServer.Run()) }()
if gpg.IsGPGEnabled() {
@@ -223,11 +223,13 @@ func NewCommand() *cobra.Command {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
wg := sync.WaitGroup{}
wg.Go(func() {
wg.Add(1)
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)

View File

@@ -101,7 +101,7 @@ func NewCommand() *cobra.Command {
enableK8sEvent []string
)
command := &cobra.Command{
Use: common.CommandServer,
Use: cliName,
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(common.CommandServer))
command.AddCommand(cli.NewVersionCmd(cliName))
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")

View File

@@ -0,0 +1,6 @@
package commands
const (
// cliName is the name of the CLI
cliName = "argocd-server"
)

View File

@@ -183,9 +183,9 @@ func getAdditionalNamespaces(ctx context.Context, configMapsClient dynamic.Resou
namespacesListFromString := func(namespaces string) []string {
listOfNamespaces := []string{}
ss := strings.SplitSeq(namespaces, ",")
ss := strings.Split(namespaces, ",")
for namespace := range ss {
for _, namespace := range ss {
if namespace != "" {
listOfNamespaces = append(listOfNamespaces, strings.TrimSpace(namespace))
}

View File

@@ -10,8 +10,8 @@ import (
"sort"
"time"
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
"github.com/argoproj/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

View File

@@ -3,9 +3,9 @@ package admin
import (
"testing"
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"
clustermocks "github.com/argoproj/gitops-engine/pkg/cache/mocks"
"github.com/argoproj/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"

View File

@@ -8,7 +8,7 @@ import (
"strings"
"time"
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors"

View File

@@ -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/argo-cd/gitops-engine/pkg/utils/kube"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -295,8 +295,7 @@ spec:
spec:
destination: {}
project: ""
status:
health: {}
status: {}
---
`,
},
@@ -326,8 +325,7 @@ spec:
spec:
destination: {}
project: ""
status:
health: {}
status: {}
---
`,
},

View File

@@ -10,7 +10,7 @@ import (
"text/tabwriter"
"time"
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/redis/go-redis/v9"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -128,9 +128,12 @@ func loadClusters(ctx context.Context, kubeClient kubernetes.Interface, appClien
batchSize := 10
batchesCount := int(math.Ceil(float64(len(clusters)) / float64(batchSize)))
for batchNum := range batchesCount {
for batchNum := 0; batchNum < batchesCount; batchNum++ {
batchStart := batchSize * batchNum
batchEnd := min((batchSize * (batchNum + 1)), len(clustersList.Items))
batchEnd := batchSize * (batchNum + 1)
if batchEnd > len(clustersList.Items) {
batchEnd = len(clustersList.Items)
}
batch := clustersList.Items[batchStart:batchEnd]
_ = kube.RunAllAsync(len(batch), func(i int) error {
clusterShard := 0

View File

@@ -60,7 +60,8 @@ func Test_loadClusters(t *testing.T) {
require.NoError(t, err)
for i := range clusters {
// This changes, nil it to avoid testing it.
clusters[i].Info.ConnectionState.ModifiedAt = nil
//nolint:staticcheck
clusters[i].ConnectionState.ModifiedAt = nil
}
expected := []ClusterWithInfo{{
@@ -68,13 +69,11 @@ func Test_loadClusters(t *testing.T) {
ID: "",
Server: "https://kubernetes.default.svc",
Name: "in-cluster",
Info: v1alpha1.ClusterInfo{
ConnectionState: v1alpha1.ConnectionState{
Status: "Successful",
},
ServerVersion: ".",
ConnectionState: v1alpha1.ConnectionState{
Status: "Successful",
},
Shard: ptr.To(int64(0)),
ServerVersion: ".",
Shard: ptr.To(int64(0)),
},
Namespaces: []string{"test"},
}}

View File

@@ -7,7 +7,7 @@ import (
"io"
"os"
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"

View File

@@ -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/argo-cd/gitops-engine/pkg/utils/kube"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"

View File

@@ -23,7 +23,7 @@ func generateRandomPassword() (string, error) {
const initialPasswordLength = 16
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
randBytes := make([]byte, initialPasswordLength)
for i := range initialPasswordLength {
for i := 0; i < initialPasswordLength; i++ {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
if err != nil {
return "", err

View File

@@ -7,13 +7,12 @@ import (
"fmt"
"os"
"reflect"
"slices"
"sort"
"strconv"
"strings"
"text/tabwriter"
healthutil "github.com/argoproj/argo-cd/gitops-engine/pkg/health"
healthutil "github.com/argoproj/gitops-engine/pkg/health"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
@@ -272,7 +271,9 @@ func NewValidateSettingsCommand(cmdCtx commandContext) *cobra.Command {
for k := range validatorsByGroup {
allGroups = append(allGroups, k)
}
slices.Sort(allGroups)
sort.Slice(allGroups, func(i, j int) bool {
return allGroups[i] < allGroups[j]
})
command := &cobra.Command{
Use: "validate",

View File

@@ -322,7 +322,6 @@ 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

View File

@@ -17,13 +17,11 @@ import (
"time"
"unicode/utf8"
"golang.org/x/sync/errgroup"
"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"
"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"
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/retry"
"github.com/mattn/go-isatty"
log "github.com/sirupsen/logrus"
@@ -46,7 +44,7 @@ import (
argocdclient "github.com/argoproj/argo-cd/v3/pkg/apiclient"
"github.com/argoproj/argo-cd/v3/pkg/apiclient/application"
resourceutil "github.com/argoproj/argo-cd/gitops-engine/pkg/sync/resource"
resourceutil "github.com/argoproj/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"
@@ -1277,32 +1275,24 @@ type objKeyLiveTarget struct {
target *unstructured.Unstructured
}
// addServerSideDiffPerfFlags adds server-side diff performance tuning flags to a command
func addServerSideDiffPerfFlags(command *cobra.Command, serverSideDiffConcurrency *int, serverSideDiffMaxBatchKB *int) {
command.Flags().IntVar(serverSideDiffConcurrency, "server-side-diff-concurrency", -1, "Max concurrent batches for server-side diff. -1 = unlimited, 1 = sequential, 2+ = concurrent (0 = invalid)")
command.Flags().IntVar(serverSideDiffMaxBatchKB, "server-side-diff-max-batch-kb", 250, "Max batch size in KB for server-side diff. Smaller values are safer for proxies")
}
// NewApplicationDiffCommand returns a new instance of an `argocd app diff` command
func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
refresh bool
hardRefresh bool
exitCode bool
diffExitCode int
local string
revision string
localRepoRoot string
serverSideGenerate bool
serverSideDiff bool
serverSideDiffConcurrency int
serverSideDiffMaxBatchKB int
localIncludes []string
appNamespace string
revisions []string
sourcePositions []int64
sourceNames []string
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
refresh bool
hardRefresh bool
exitCode bool
diffExitCode int
local string
revision string
localRepoRoot string
serverSideGenerate bool
serverSideDiff bool
localIncludes []string
appNamespace string
revisions []string
sourcePositions []int64
sourceNames []string
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
)
shortDesc := "Perform a diff against the target and live state."
command := &cobra.Command{
@@ -1433,7 +1423,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
}
proj := getProject(ctx, c, clientOpts, app.Spec.Project)
foundDiffs := findAndPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption, ignoreNormalizerOpts, serverSideDiff, appIf, app.GetName(), app.GetNamespace(), serverSideDiffConcurrency, serverSideDiffMaxBatchKB)
foundDiffs := findAndPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption, ignoreNormalizerOpts, serverSideDiff, appIf, app.GetName(), app.GetNamespace())
if foundDiffs && exitCode {
os.Exit(diffExitCode)
}
@@ -1448,7 +1438,6 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root")
command.Flags().BoolVar(&serverSideGenerate, "server-side-generate", false, "Used with --local, this will send your manifests to the server for diffing")
command.Flags().BoolVar(&serverSideDiff, "server-side-diff", false, "Use server-side diff to calculate the diff. This will default to true if the ServerSideDiff annotation is set on the application.")
addServerSideDiffPerfFlags(command, &serverSideDiffConcurrency, &serverSideDiffMaxBatchKB)
command.Flags().StringArrayVar(&localIncludes, "local-include", []string{"*.yaml", "*.yml", "*.json"}, "Used with --server-side-generate, specify patterns of filenames to send. Matching is based on filename and not path.")
command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only render the difference in namespace")
command.Flags().StringArrayVar(&revisions, "revisions", []string{}, "Show manifests at specific revisions for source position in source-positions")
@@ -1465,14 +1454,7 @@ func printResourceDiff(group, kind, namespace, name string, live, target *unstru
}
// findAndPrintServerSideDiff performs a server-side diff by making requests to the api server and prints the response
func findAndPrintServerSideDiff(ctx context.Context, app *argoappv1.Application, items []objKeyLiveTarget, resources *application.ManagedResourcesResponse, appIf application.ApplicationServiceClient, appName, appNs string, maxConcurrency int, maxBatchSizeKB int) bool {
if maxConcurrency == 0 {
errors.CheckError(stderrors.New("invalid value for --server-side-diff-concurrency: 0 is not allowed (use -1 for unlimited, or a positive number to limit concurrency)"))
}
liveResources := make([]*argoappv1.ResourceDiff, 0, len(items))
targetManifests := make([]string, 0, len(items))
func findAndPrintServerSideDiff(ctx context.Context, app *argoappv1.Application, items []objKeyLiveTarget, resources *application.ManagedResourcesResponse, appIf application.ApplicationServiceClient, appName, appNs string) bool {
// Process each item for server-side diff
foundDiffs := false
for _, item := range items {
@@ -1506,7 +1488,6 @@ func findAndPrintServerSideDiff(ctx context.Context, app *argoappv1.Application,
Modified: true,
}
}
liveResources = append(liveResources, liveResource)
if item.target != nil {
jsonBytes, err := json.Marshal(item.target)
@@ -1515,63 +1496,23 @@ func findAndPrintServerSideDiff(ctx context.Context, app *argoappv1.Application,
}
targetManifest = string(jsonBytes)
}
targetManifests = append(targetManifests, targetManifest)
}
if len(liveResources) == 0 {
return false
}
// Batch by size to avoid proxy limits
maxBatchSize := maxBatchSizeKB * 1024
var batches []struct{ start, end int }
for i := 0; i < len(liveResources); {
start := i
size := 0
for i < len(liveResources) {
resourceSize := len(liveResources[i].LiveState) + len(targetManifests[i])
if size+resourceSize > maxBatchSize && i > start {
break
}
size += resourceSize
i++
// Call server-side diff for this individual resource
serverSideDiffQuery := &application.ApplicationServerSideDiffQuery{
AppName: &appName,
AppNamespace: &appNs,
Project: &app.Spec.Project,
LiveResources: []*argoappv1.ResourceDiff{liveResource},
TargetManifests: []string{targetManifest},
}
batches = append(batches, struct{ start, end int }{start, i})
}
// Process batches in parallel
g, errGroupCtx := errgroup.WithContext(ctx)
g.SetLimit(maxConcurrency)
serverSideDiffRes, err := appIf.ServerSideDiff(ctx, serverSideDiffQuery)
if err != nil {
errors.CheckError(err)
}
results := make([][]*argoappv1.ResourceDiff, len(batches))
for idx, batch := range batches {
i := idx
b := batch
g.Go(func() error {
// Call server-side diff for this batch of resources
serverSideDiffQuery := &application.ApplicationServerSideDiffQuery{
AppName: &appName,
AppNamespace: &appNs,
Project: &app.Spec.Project,
LiveResources: liveResources[b.start:b.end],
TargetManifests: targetManifests[b.start:b.end],
}
serverSideDiffRes, err := appIf.ServerSideDiff(errGroupCtx, serverSideDiffQuery)
if err != nil {
return err
}
results[i] = serverSideDiffRes.Items
return nil
})
}
if err := g.Wait(); err != nil {
errors.CheckError(err)
}
for _, items := range results {
for _, resultItem := range items {
// Extract diff for this resource
for _, resultItem := range serverSideDiffRes.Items {
if resultItem.Hook || (!resultItem.Modified && resultItem.TargetState != "" && resultItem.LiveState != "") {
continue
}
@@ -1581,12 +1522,13 @@ func findAndPrintServerSideDiff(ctx context.Context, app *argoappv1.Application,
if resultItem.TargetState != "" && resultItem.TargetState != "null" {
target = &unstructured.Unstructured{}
err := json.Unmarshal([]byte(resultItem.TargetState), target)
err = json.Unmarshal([]byte(resultItem.TargetState), target)
errors.CheckError(err)
}
if resultItem.LiveState != "" && resultItem.LiveState != "null" {
live = &unstructured.Unstructured{}
err := json.Unmarshal([]byte(resultItem.LiveState), live)
err = json.Unmarshal([]byte(resultItem.LiveState), live)
errors.CheckError(err)
}
@@ -1612,14 +1554,14 @@ type DifferenceOption struct {
}
// findAndPrintDiff ... Prints difference between application current state and state stored in git or locally, returns boolean as true if difference is found else returns false
func findAndPrintDiff(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts, useServerSideDiff bool, appIf application.ApplicationServiceClient, appName, appNs string, serverSideDiffConcurrency int, serverSideDiffMaxBatchKB int) bool {
func findAndPrintDiff(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts, useServerSideDiff bool, appIf application.ApplicationServiceClient, appName, appNs string) bool {
var foundDiffs bool
items, err := prepareObjectsForDiff(ctx, app, proj, resources, argoSettings, diffOptions)
errors.CheckError(err)
if useServerSideDiff {
return findAndPrintServerSideDiff(ctx, app, items, resources, appIf, appName, appNs, serverSideDiffConcurrency, serverSideDiffMaxBatchKB)
return findAndPrintServerSideDiff(ctx, app, items, resources, appIf, appName, appNs)
}
for _, item := range items {
@@ -1988,8 +1930,8 @@ func parseSelectedResources(resources []string) ([]*argoappv1.SyncOperationResou
for _, resource := range resources {
isExcluded := false
// check if the resource flag starts with a '!'
if after, ok := strings.CutPrefix(resource, resourceExcludeIndicator); ok {
resource = after
if strings.HasPrefix(resource, resourceExcludeIndicator) {
resource = strings.TrimPrefix(resource, resourceExcludeIndicator)
isExcluded = true
}
fields := strings.Split(resource, resourceFieldDelimiter)
@@ -2133,38 +2075,36 @@ func printTreeViewDetailed(nodeMapping map[string]argoappv1.ResourceNode, parent
// NewApplicationSyncCommand returns a new instance of an `argocd app sync` command
func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
revision string
revisions []string
sourcePositions []int64
sourceNames []string
resources []string
labels []string
selector string
prune bool
dryRun bool
timeout uint
strategy string
force bool
replace bool
serverSideApply bool
applyOutOfSyncOnly bool
async bool
retryLimit int64
retryRefresh bool
retryBackoffDuration time.Duration
retryBackoffMaxDuration time.Duration
retryBackoffFactor int64
local string
localRepoRoot string
infos []string
diffChanges bool
diffChangesConfirm bool
projects []string
output string
appNamespace string
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
serverSideDiffConcurrency int
serverSideDiffMaxBatchKB int
revision string
revisions []string
sourcePositions []int64
sourceNames []string
resources []string
labels []string
selector string
prune bool
dryRun bool
timeout uint
strategy string
force bool
replace bool
serverSideApply bool
applyOutOfSyncOnly bool
async bool
retryLimit int64
retryRefresh bool
retryBackoffDuration time.Duration
retryBackoffMaxDuration time.Duration
retryBackoffFactor int64
local string
localRepoRoot string
infos []string
diffChanges bool
diffChangesConfirm bool
projects []string
output string
appNamespace string
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
)
command := &cobra.Command{
Use: "sync [APPNAME... | -l selector | --project project-name]",
@@ -2453,7 +2393,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
// Check if application has ServerSideDiff annotation
serverSideDiff := resourceutil.HasAnnotationOption(app, argocommon.AnnotationCompareOptions, "ServerSideDiff=true")
foundDiffs = findAndPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption, ignoreNormalizerOpts, serverSideDiff, appIf, appName, appNs, serverSideDiffConcurrency, serverSideDiffMaxBatchKB)
foundDiffs = findAndPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption, ignoreNormalizerOpts, serverSideDiff, appIf, appName, appNs)
if !foundDiffs {
fmt.Printf("====== No Differences found ======\n")
// if no differences found, then no need to sync
@@ -2518,7 +2458,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
command.Flags().StringArrayVar(&revisions, "revisions", []string{}, "Show manifests at specific revisions for source position in source-positions")
command.Flags().Int64SliceVar(&sourcePositions, "source-positions", []int64{}, "List of source positions. Default is empty array. Counting start at 1.")
command.Flags().StringArrayVar(&sourceNames, "source-names", []string{}, "List of source names. Default is an empty array.")
addServerSideDiffPerfFlags(command, &serverSideDiffConcurrency, &serverSideDiffMaxBatchKB)
return command
}
@@ -3283,7 +3222,8 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob
errors.CheckError(err)
proj := getProject(ctx, c, clientOpts, app.Spec.Project)
unstructureds = getLocalObjects(context.Background(), app, proj.Project, local, localRepoRoot, argoSettings.AppLabelKey, cluster.Info.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod)
//nolint:staticcheck
unstructureds = getLocalObjects(context.Background(), app, proj.Project, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod)
case len(revisions) > 0 && len(sourcePositions) > 0:
q := application.ApplicationManifestQuery{
Name: &appName,

View File

@@ -43,49 +43,9 @@ 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) {
@@ -122,11 +82,10 @@ 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, "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")
assert.Contains(t, output, "Rollout")
assert.Contains(t, output, "Degraded")
assert.Contains(t, output, "Readiness Gate failed")
}
func TestPrintResourcesTree(t *testing.T) {

View File

@@ -10,8 +10,6 @@ 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"
@@ -89,7 +87,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 resourceNames []string
var fetchedStr string
for _, r := range tree.Nodes {
if (resourceName != "" && r.Name != resourceName) || (group != "" && r.Group != group) || r.Kind != kind {
continue
@@ -119,11 +117,14 @@ func NewApplicationGetResourceCommand(clientOpts *argocdclient.ClientOptions) *c
obj = filterFieldsFromObject(obj, filteredFields)
}
resourceNames = append(resourceNames, obj.GetName())
fetchedStr += 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)
}
@@ -532,20 +533,7 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions)
)
command := &cobra.Command{
Use: "resources APPNAME",
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
`),
Short: "List resource of application",
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
if len(args) != 1 {
@@ -566,9 +554,7 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions)
},
}
command.Flags().BoolVar(&orphaned, "orphaned", false, "Lists only orphaned 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(&output, "output", "", "Provides the tree view of the resources")
command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`)
return command
}

View File

@@ -12,8 +12,8 @@ import (
"testing"
"time"
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
"github.com/argoproj/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"

View File

@@ -395,12 +395,12 @@ func printApplicationSetNames(apps []arogappsetv1.ApplicationSet) {
func printApplicationSetTable(apps []arogappsetv1.ApplicationSet, output *string) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
var fmtStr string
headers := []any{"NAME", "PROJECT", "SYNCPOLICY", "HEALTH", "CONDITIONS"}
headers := []any{"NAME", "PROJECT", "SYNCPOLICY", "CONDITIONS"}
if *output == "wide" {
fmtStr = "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
fmtStr = "%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
headers = append(headers, "REPO", "PATH", "TARGET")
} else {
fmtStr = "%s\t%s\t%s\t%s\t%s\n"
fmtStr = "%s\t%s\t%s\t%s\n"
}
_, _ = fmt.Fprintf(w, fmtStr, headers...)
for _, app := range apps {
@@ -414,7 +414,6 @@ func printApplicationSetTable(apps []arogappsetv1.ApplicationSet, output *string
app.QualifiedName(),
app.Spec.Template.Spec.Project,
app.Spec.SyncPolicy,
app.Status.Health.Status,
conditions,
}
if *output == "wide" {
@@ -438,7 +437,6 @@ func printAppSetSummaryTable(appSet *arogappsetv1.ApplicationSet) {
fmt.Printf(printOpFmtStr, "Project:", appSet.Spec.Template.Spec.GetProject())
fmt.Printf(printOpFmtStr, "Server:", getServerForAppSet(appSet))
fmt.Printf(printOpFmtStr, "Namespace:", appSet.Spec.Template.Spec.Destination.Namespace)
fmt.Printf(printOpFmtStr, "Health Status:", appSet.Status.Health.Status)
if !appSet.Spec.Template.Spec.HasMultipleSources() {
fmt.Println("Source:")
} else {

View File

@@ -107,7 +107,7 @@ func TestPrintApplicationSetTable(t *testing.T) {
return nil
})
require.NoError(t, err)
expectation := "NAME PROJECT SYNCPOLICY HEALTH CONDITIONS\napp-name default nil [{ResourcesUpToDate <nil> True }]\nteam-two/app-name default nil [{ResourcesUpToDate <nil> True }]\n"
expectation := "NAME PROJECT SYNCPOLICY CONDITIONS\napp-name default nil [{ResourcesUpToDate <nil> True }]\nteam-two/app-name default nil [{ResourcesUpToDate <nil> True }]\n"
assert.Equal(t, expectation, output)
}
@@ -200,7 +200,6 @@ func TestPrintAppSetSummaryTable(t *testing.T) {
Project: default
Server:
Namespace:
Health Status:
Source:
- Repo:
Target:
@@ -214,7 +213,6 @@ SyncPolicy: <none>
Project: default
Server:
Namespace:
Health Status:
Source:
- Repo:
Target:
@@ -228,7 +226,6 @@ SyncPolicy: Automated
Project: default
Server:
Namespace:
Health Status:
Source:
- Repo:
Target:
@@ -242,7 +239,6 @@ SyncPolicy: Automated
Project: default
Server:
Namespace:
Health Status:
Source:
- Repo: test1
Target: master1
@@ -257,7 +253,6 @@ SyncPolicy: <none>
Project: default
Server:
Namespace:
Health Status:
Sources:
- Repo: test1
Target: master1

View File

@@ -91,7 +91,7 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
)
command := &cobra.Command{
Use: "add CONTEXT",
Short: common.CommandCLI + " cluster add CONTEXT",
Short: cliName + " cluster add CONTEXT",
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -380,7 +380,8 @@ func printClusterDetails(clusters []argoappv1.Cluster) {
fmt.Printf("Cluster information\n\n")
fmt.Printf(" Server URL: %s\n", cluster.Server)
fmt.Printf(" Server Name: %s\n", strWithDefault(cluster.Name, "-"))
fmt.Printf(" Server Version: %s\n", cluster.Info.ServerVersion)
//nolint:staticcheck
fmt.Printf(" Server Version: %s\n", cluster.ServerVersion)
fmt.Printf(" Namespaces: %s\n", formatNamespaces(cluster))
fmt.Printf("\nTLS configuration\n\n")
fmt.Printf(" Client cert: %v\n", len(cluster.Config.CertData) != 0)
@@ -474,7 +475,8 @@ func printClusterTable(clusters []argoappv1.Cluster) {
if len(c.Namespaces) > 0 {
server = fmt.Sprintf("%s (%d namespaces)", c.Server, len(c.Namespaces))
}
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", server, c.Name, c.Info.ServerVersion, c.Info.ConnectionState.Status, c.Info.ConnectionState.Message, c.Project)
//nolint:staticcheck
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", server, c.Name, c.ServerVersion, c.ConnectionState.Status, c.ConnectionState.Message, c.Project)
}
_ = w.Flush()
}
@@ -549,7 +551,7 @@ argocd cluster list -o server <ARGOCD_SERVER_ADDRESS>
func NewClusterRotateAuthCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
command := &cobra.Command{
Use: "rotate-auth SERVER/NAME",
Short: common.CommandCLI + " cluster rotate-auth SERVER/NAME",
Short: cliName + " cluster rotate-auth SERVER/NAME",
Example: `argocd cluster rotate-auth https://12.34.567.89
argocd cluster rotate-auth cluster-name`,
Run: func(c *cobra.Command, args []string) {

View File

@@ -39,14 +39,12 @@ func Test_printClusterTable(_ *testing.T) {
AWSAuthConfig: nil,
DisableCompression: false,
},
Info: v1alpha1.ClusterInfo{
ConnectionState: v1alpha1.ConnectionState{
Status: "my-status",
Message: "my-message",
ModifiedAt: &metav1.Time{},
},
ServerVersion: "my-version",
ConnectionState: v1alpha1.ConnectionState{
Status: "my-status",
Message: "my-message",
ModifiedAt: &metav1.Time{},
},
ServerVersion: "my-version",
},
})
}

View File

@@ -9,6 +9,8 @@ import (
)
const (
cliName = "argocd"
// DefaultSSOLocalPort is the localhost port to listen on for the temporary web server performing
// the OAuth2 login flow.
DefaultSSOLocalPort = 8085

View File

@@ -302,7 +302,7 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
}
tries := 5
for range tries {
for i := 0; i < tries; i++ {
err = testAPI(ctx, clientOpts)
if err == nil {
break

View File

@@ -170,9 +170,9 @@ func (h *DefaultPluginHandler) ListAvailablePlugins() []string {
// Check if the file is a valid argocd plugin
pluginPrefix := prefix + "-"
if after, ok := strings.CutPrefix(name, pluginPrefix); ok {
if strings.HasPrefix(name, pluginPrefix) {
// Extract the plugin command name (everything after the prefix)
pluginName := after
pluginName := strings.TrimPrefix(name, pluginPrefix)
// Skip empty plugin names or names with path separators
if pluginName == "" || strings.Contains(pluginName, "/") || strings.Contains(pluginName, "\\") {

View File

@@ -362,7 +362,7 @@ argocd proj windows list test-project`,
func printSyncWindows(proj *v1alpha1.AppProject) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
var fmtStr string
headers := []any{"ID", "STATUS", "KIND", "SCHEDULE", "DURATION", "APPLICATIONS", "NAMESPACES", "CLUSTERS", "MANUALSYNC", "TIMEZONE", "USEANDOPERATOR"}
headers := []any{"ID", "STATUS", "KIND", "SCHEDULE", "DURATION", "APPLICATIONS", "NAMESPACES", "CLUSTERS", "MANUALSYNC", "TIMEZONE"}
fmtStr = strings.Repeat("%s\t", len(headers)) + "\n"
fmt.Fprintf(w, fmtStr, headers...)
if proj.Spec.SyncWindows.HasWindows() {

View File

@@ -1,40 +0,0 @@
package commands
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
)
func TestPrintSyncWindows(t *testing.T) {
proj := &v1alpha1.AppProject{
ObjectMeta: metav1.ObjectMeta{Name: "test-project"},
Spec: v1alpha1.AppProjectSpec{
SyncWindows: v1alpha1.SyncWindows{
{
Kind: "allow",
Schedule: "* * * * *",
Duration: "1h",
Applications: []string{"app1"},
Namespaces: []string{"ns1"},
Clusters: []string{"cluster1"},
ManualSync: true,
UseAndOperator: true,
},
},
},
}
output, err := captureOutput(func() error {
printSyncWindows(proj)
return nil
})
require.NoError(t, err)
t.Log(output)
assert.Contains(t, output, "ID STATUS KIND SCHEDULE DURATION APPLICATIONS NAMESPACES CLUSTERS MANUALSYNC TIMEZONE USEANDOPERATOR")
assert.Contains(t, output, "0 Active allow * * * * * 1h app1 ns1 cluster1 Enabled Enabled")
}

View File

@@ -37,7 +37,7 @@ func NewCommand() *cobra.Command {
)
command := &cobra.Command{
Use: common.CommandCLI,
Use: cliName,
Short: "argocd controls a Argo CD server",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)

View File

@@ -6,7 +6,7 @@ import (
"text/tabwriter"
"time"
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/health"
"k8s.io/apimachinery/pkg/util/duration"
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
@@ -15,6 +15,7 @@ import (
const (
firstElemPrefix = `├─`
lastElemPrefix = `└─`
indent = " "
pipe = ``
)
@@ -74,7 +75,9 @@ func detailedTreeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.Reso
}
func treeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) {
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Group, parent.Kind, parent.Namespace, parent.Name, "No")
if len(parent.ParentRefs) == 0 {
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "No")
}
chs := parentChildMap[parent.UID]
for i, child := range chs {
var p string
@@ -89,7 +92,7 @@ func treeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[string]v1al
}
func treeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) {
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes")
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes")
chs := parentChildMap[parent.UID]
for i, child := range chs {
var p string
@@ -104,12 +107,14 @@ func treeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha
}
func detailedTreeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) {
healthStatus, reason := extractHealthStatusAndReason(parent)
age := "<unknown>"
if parent.CreatedAt != nil {
age = duration.HumanDuration(time.Since(parent.CreatedAt.Time))
if len(parent.ParentRefs) == 0 {
healthStatus, reason := extractHealthStatusAndReason(parent)
age := "<unknown>"
if parent.CreatedAt != nil {
age = duration.HumanDuration(time.Since(parent.CreatedAt.Time))
}
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "No", age, healthStatus, reason)
}
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Group, parent.Kind, parent.Namespace, parent.Name, "No", age, healthStatus, reason)
chs := parentChildMap[parent.UID]
for i, child := range chs {
var p string
@@ -129,7 +134,7 @@ func detailedTreeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string
if parent.CreatedAt != nil {
age = duration.HumanDuration(time.Since(parent.CreatedAt.Time))
}
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes", age, healthStatus, reason)
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes", age, healthStatus, reason)
chs := parentChildMap[parent.UID]
for i, child := range chs {

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